1 /* FLAC input plugin for Winamp3
\r
2 * Copyright (C) 2000,2001,2002 Josh Coalson
\r
4 * This program is free software; you can redistribute it and/or
\r
5 * modify it under the terms of the GNU General Public License
\r
6 * as published by the Free Software Foundation; either version 2
\r
7 * of the License, or (at your option) any later version.
\r
9 * This program is distributed in the hope that it will be useful,
\r
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
12 * GNU General Public License for more details.
\r
14 * You should have received a copy of the GNU General Public License
\r
15 * along with this program; if not, write to the Free Software
\r
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
\r
18 * NOTE: this code is derived from the 'rawpcm' example by
\r
19 * Nullsoft; the original license for the 'rawpcm' example follows.
\r
23 Nullsoft WASABI Source File License
\r
25 Copyright 1999-2001 Nullsoft, Inc.
\r
27 This software is provided 'as-is', without any express or implied
\r
28 warranty. In no event will the authors be held liable for any damages
\r
29 arising from the use of this software.
\r
31 Permission is granted to anyone to use this software for any purpose,
\r
32 including commercial applications, and to alter it and redistribute it
\r
33 freely, subject to the following restrictions:
\r
35 1. The origin of this software must not be misrepresented; you must not
\r
36 claim that you wrote the original software. If you use this software
\r
37 in a product, an acknowledgment in the product documentation would be
\r
38 appreciated but is not required.
\r
39 2. Altered source versions must be plainly marked as such, and must not be
\r
40 misrepresented as being the original software.
\r
41 3. This notice may not be removed or altered from any source distribution.
\r
45 brennan@nullsoft.com
\r
49 #include "flacpcm.h"
\r
51 #include "FLAC/metadata.h"
\r
55 struct id3v1_struct {
\r
56 FLAC__byte raw[128];
\r
62 unsigned track; /* may be 0 if v1 (not v1.1) tag */
\r
64 char description[1024]; /* the formatted description passed to the gui */
\r
68 static bool get_id3v1_tag_(const char *filename, id3v1_struct *tag);
\r
74 samples_in_reservoir(0),
\r
86 int FlacPcm::getInfos(MediaInfo *infos)
\r
88 reader = infos->getReader();
\r
89 if(!reader) return 0;
\r
91 //@@@ to be really "clean" we should go through the reader instead of directly to the file...
\r
92 if(!FLAC__metadata_get_streaminfo(infos->getFilename(), &stream_info))
\r
98 bool has_tag = get_id3v1_tag_(infos->getFilename(), &tag);
\r
100 infos->setLength(lengthInMsec());
\r
101 //@@@ infos->setTitle(Std::filename(infos->getFilename()));
\r
102 infos->setTitle(tag.description);
\r
103 infos->setInfo(StringPrintf("FLAC:<%ihz:%ibps:%dch>", stream_info.sample_rate, stream_info.bits_per_sample, stream_info.channels)); //@@@ fix later
\r
105 infos->setData("Title", tag.title);
\r
106 infos->setData("Artist", tag.artist);
\r
107 infos->setData("Album", tag.album);
\r
113 int FlacPcm::processData(MediaInfo *infos, ChunkList *chunk_list, bool *killswitch)
\r
115 reader = infos->getReader();
\r
120 decoder = FLAC__seekable_stream_decoder_new();
\r
123 FLAC__seekable_stream_decoder_set_md5_checking(decoder, false);
\r
124 FLAC__seekable_stream_decoder_set_read_callback(decoder, readCallback_);
\r
125 FLAC__seekable_stream_decoder_set_seek_callback(decoder, seekCallback_);
\r
126 FLAC__seekable_stream_decoder_set_tell_callback(decoder, tellCallback_);
\r
127 FLAC__seekable_stream_decoder_set_length_callback(decoder, lengthCallback_);
\r
128 FLAC__seekable_stream_decoder_set_eof_callback(decoder, eofCallback_);
\r
129 FLAC__seekable_stream_decoder_set_write_callback(decoder, writeCallback_);
\r
130 FLAC__seekable_stream_decoder_set_metadata_callback(decoder, metadataCallback_);
\r
131 FLAC__seekable_stream_decoder_set_error_callback(decoder, errorCallback_);
\r
132 FLAC__seekable_stream_decoder_set_client_data(decoder, this);
\r
134 if(FLAC__seekable_stream_decoder_init(decoder) != FLAC__SEEKABLE_STREAM_DECODER_OK) {
\r
138 if(!FLAC__seekable_stream_decoder_process_metadata(decoder)) {
\r
145 FLAC__seekable_stream_decoder_seek_absolute(decoder, seek_sample);
\r
146 needs_seek = false;
\r
151 while(samples_in_reservoir < 576) {
\r
152 if(FLAC__seekable_stream_decoder_get_state(decoder) == FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM) {
\r
156 else if(!FLAC__seekable_stream_decoder_process_one_frame(decoder))
\r
160 if(samples_in_reservoir == 0) {
\r
164 const unsigned channels = stream_info.channels;
\r
165 const unsigned bits_per_sample = stream_info.bits_per_sample;
\r
166 const unsigned bytes_per_sample = (bits_per_sample+7)/8;
\r
167 const unsigned sample_rate = stream_info.sample_rate;
\r
168 unsigned i, n = min(samples_in_reservoir, 576), delta;
\r
169 signed short *ssbuffer = (signed short*)output;
\r
171 for(i = 0; i < n*channels; i++)
\r
172 ssbuffer[i] = reservoir[i];
\r
174 for( ; i < samples_in_reservoir*channels; i++)
\r
175 reservoir[i-delta] = reservoir[i];
\r
176 samples_in_reservoir -= n;
\r
178 const int bytes = n * channels * bytes_per_sample;
\r
180 ChunkInfosI *ci=new ChunkInfosI();
\r
181 ci->addInfo("srate", sample_rate);
\r
182 ci->addInfo("bps", bits_per_sample);
\r
183 ci->addInfo("nch", channels);
\r
185 chunk_list->setChunk("PCM", output, bytes, ci);
\r
194 int FlacPcm::corecb_onSeeked(int newpos)
\r
196 if(stream_info.total_samples == 0 || newpos < 0)
\r
200 seek_sample = (FLAC__uint64)((double)newpos / (double)lengthInMsec() * (double)(FLAC__int64)stream_info.total_samples);
\r
204 void FlacPcm::cleanup()
\r
207 FLAC__seekable_stream_decoder_finish(decoder);
\r
208 FLAC__seekable_stream_decoder_delete(decoder);
\r
212 FLAC__SeekableStreamDecoderReadStatus FlacPcm::readCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
\r
214 FlacPcm *instance = (FlacPcm*)client_data;
\r
215 *bytes = instance->reader->read((char*)buffer, *bytes);
\r
216 return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
\r
219 FLAC__SeekableStreamDecoderSeekStatus FlacPcm::seekCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
\r
221 FlacPcm *instance = (FlacPcm*)client_data;
\r
222 if(instance->reader->seek((int)absolute_byte_offset) < 0)
\r
223 return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
\r
225 return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
\r
228 FLAC__SeekableStreamDecoderTellStatus FlacPcm::tellCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
\r
230 FlacPcm *instance = (FlacPcm*)client_data;
\r
231 int pos = instance->reader->getPos();
\r
233 return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR;
\r
235 *absolute_byte_offset = pos;
\r
236 return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
\r
240 FLAC__SeekableStreamDecoderLengthStatus FlacPcm::lengthCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
\r
242 FlacPcm *instance = (FlacPcm*)client_data;
\r
243 int length = instance->reader->getPos();
\r
245 return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR;
\r
247 *stream_length = length;
\r
248 return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
\r
252 FLAC__bool FlacPcm::eofCallback_(const FLAC__SeekableStreamDecoder *decoder, void *client_data)
\r
254 FlacPcm *instance = (FlacPcm*)client_data;
\r
255 return instance->reader->getPos() == instance->reader->getLength();
\r
258 FLAC__StreamDecoderWriteStatus FlacPcm::writeCallback_(const FLAC__SeekableStreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data)
\r
260 FlacPcm *instance = (FlacPcm*)client_data;
\r
261 const unsigned bps = instance->stream_info.bits_per_sample, channels = instance->stream_info.channels, wide_samples = frame->header.blocksize;
\r
262 unsigned wide_sample, sample, channel;
\r
266 if(instance->abort_flag)
\r
267 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
\r
269 for(sample = instance->samples_in_reservoir*channels, wide_sample = 0; wide_sample < wide_samples; wide_sample++)
\r
270 for(channel = 0; channel < channels; channel++, sample++)
\r
271 instance->reservoir[sample] = (FLAC__int16)buffer[channel][wide_sample];
\r
273 instance->samples_in_reservoir += wide_samples;
\r
275 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
\r
278 void FlacPcm::metadataCallback_(const FLAC__SeekableStreamDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
\r
280 FlacPcm *instance = (FlacPcm*)client_data;
\r
282 if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
\r
283 instance->stream_info = metadata->data.stream_info;
\r
285 if(instance->stream_info.bits_per_sample != 16) {
\r
286 //@@@ how to do this? MessageBox(mod.hMainWindow, "ERROR: plugin can only handle 16-bit samples\n", "ERROR: plugin can only handle 16-bit samples", 0);
\r
287 instance->abort_flag = true;
\r
293 void FlacPcm::errorCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
\r
295 FlacPcm *instance = (FlacPcm*)client_data;
\r
297 if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC)
\r
298 instance->abort_flag = true;
\r
301 /***********************************************************************
\r
303 **********************************************************************/
\r
305 bool get_id3v1_tag_(const char *filename, id3v1_struct *tag)
\r
308 FILE *f = fopen(filename, "rb");
\r
309 memset(tag, 0, sizeof(id3v1_struct));
\r
310 strcpy(tag->title,"Sonata K.42");
\r
311 strcpy(tag->artist,"Domenico Scarlatti");
\r
312 strcpy(tag->album,"Narcisso Yepes Plays Scarlatti");
\r
313 sprintf(tag->description, "%s - %s", tag->artist, tag->title);
\r
315 /* set the title and description to the filename by default */
\r
316 temp = strrchr(filename, '/');
\r
321 strcpy(tag->description, temp);
\r
322 *strrchr(tag->description, '.') = '\0';
\r
323 strncpy(tag->title, tag->description, 30); tag->title[30] = '\0';
\r
327 if(-1 == fseek(f, -128, SEEK_END)) {
\r
331 if(fread(tag->raw, 1, 128, f) < 128) {
\r
336 if(strncmp((const char*)tag->raw, "TAG", 3))
\r
341 memcpy(tag->title, tag->raw+3, 30);
\r
342 memcpy(tag->artist, tag->raw+33, 30);
\r
343 memcpy(tag->album, tag->raw+63, 30);
\r
344 memcpy(year_str, tag->raw+93, 4); year_str[4] = '\0'; tag->year = atoi(year_str);
\r
345 memcpy(tag->comment, tag->raw+97, 30);
\r
346 tag->genre = (unsigned)((FLAC__byte)tag->raw[127]);
\r
347 tag->track = (unsigned)((FLAC__byte)tag->raw[126]);
\r
349 sprintf(tag->description, "%s - %s", tag->artist, tag->title);
\r