1 /* FLAC input plugin for Winamp3
\r
2 * Copyright (C) 2000,2001 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/utility.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__utility_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 if(FLAC__seekable_stream_decoder_get_state(decoder) != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
\r
208 FLAC__seekable_stream_decoder_finish(decoder);
\r
209 FLAC__seekable_stream_decoder_delete(decoder);
\r
213 FLAC__SeekableStreamDecoderReadStatus FlacPcm::readCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
\r
215 FlacPcm *instance = (FlacPcm*)client_data;
\r
216 *bytes = instance->reader->read((char*)buffer, *bytes);
\r
217 return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
\r
220 FLAC__SeekableStreamDecoderSeekStatus FlacPcm::seekCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
\r
222 FlacPcm *instance = (FlacPcm*)client_data;
\r
223 if(instance->reader->seek((int)absolute_byte_offset) < 0)
\r
224 return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
\r
226 return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
\r
229 FLAC__SeekableStreamDecoderTellStatus FlacPcm::tellCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
\r
231 FlacPcm *instance = (FlacPcm*)client_data;
\r
232 int pos = instance->reader->getPos();
\r
234 return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR;
\r
236 *absolute_byte_offset = pos;
\r
237 return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
\r
241 FLAC__SeekableStreamDecoderLengthStatus FlacPcm::lengthCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
\r
243 FlacPcm *instance = (FlacPcm*)client_data;
\r
244 int length = instance->reader->getPos();
\r
246 return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR;
\r
248 *stream_length = length;
\r
249 return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
\r
253 FLAC__bool FlacPcm::eofCallback_(const FLAC__SeekableStreamDecoder *decoder, void *client_data)
\r
255 FlacPcm *instance = (FlacPcm*)client_data;
\r
256 return instance->reader->getPos() == instance->reader->getLength();
\r
259 FLAC__StreamDecoderWriteStatus FlacPcm::writeCallback_(const FLAC__SeekableStreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data)
\r
261 FlacPcm *instance = (FlacPcm*)client_data;
\r
262 const unsigned bps = instance->stream_info.bits_per_sample, channels = instance->stream_info.channels, wide_samples = frame->header.blocksize;
\r
263 unsigned wide_sample, sample, channel;
\r
267 if(instance->abort_flag)
\r
268 return FLAC__STREAM_DECODER_WRITE_ABORT;
\r
270 for(sample = instance->samples_in_reservoir*channels, wide_sample = 0; wide_sample < wide_samples; wide_sample++)
\r
271 for(channel = 0; channel < channels; channel++, sample++)
\r
272 instance->reservoir[sample] = (FLAC__int16)buffer[channel][wide_sample];
\r
274 instance->samples_in_reservoir += wide_samples;
\r
276 return FLAC__STREAM_DECODER_WRITE_CONTINUE;
\r
279 void FlacPcm::metadataCallback_(const FLAC__SeekableStreamDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
\r
281 FlacPcm *instance = (FlacPcm*)client_data;
\r
283 if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
\r
284 instance->stream_info = metadata->data.stream_info;
\r
286 if(instance->stream_info.bits_per_sample != 16) {
\r
287 //@@@ 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
288 instance->abort_flag = true;
\r
294 void FlacPcm::errorCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
\r
296 FlacPcm *instance = (FlacPcm*)client_data;
\r
298 if(status != FLAC__STREAM_DECODER_ERROR_LOST_SYNC)
\r
299 instance->abort_flag = true;
\r
302 /***********************************************************************
\r
304 **********************************************************************/
\r
306 bool get_id3v1_tag_(const char *filename, id3v1_struct *tag)
\r
309 FILE *f = fopen(filename, "rb");
\r
310 memset(tag, 0, sizeof(id3v1_struct));
\r
311 strcpy(tag->title,"Sonata K.42");
\r
312 strcpy(tag->artist,"Domenico Scarlatti");
\r
313 strcpy(tag->album,"Narcisso Yepes Plays Scarlatti");
\r
314 sprintf(tag->description, "%s - %s", tag->artist, tag->title);
\r
316 /* set the title and description to the filename by default */
\r
317 temp = strrchr(filename, '/');
\r
322 strcpy(tag->description, temp);
\r
323 *strrchr(tag->description, '.') = '\0';
\r
324 strncpy(tag->title, tag->description, 30); tag->title[30] = '\0';
\r
328 if(-1 == fseek(f, -128, SEEK_END)) {
\r
332 if(fread(tag->raw, 1, 128, f) < 128) {
\r
337 if(strncmp((const char*)tag->raw, "TAG", 3))
\r
342 memcpy(tag->title, tag->raw+3, 30);
\r
343 memcpy(tag->artist, tag->raw+33, 30);
\r
344 memcpy(tag->album, tag->raw+63, 30);
\r
345 memcpy(year_str, tag->raw+93, 4); year_str[4] = '\0'; tag->year = atoi(year_str);
\r
346 memcpy(tag->comment, tag->raw+97, 30);
\r
347 tag->genre = (unsigned)((FLAC__byte)tag->raw[127]);
\r
348 tag->track = (unsigned)((FLAC__byte)tag->raw[126]);
\r
350 sprintf(tag->description, "%s - %s", tag->artist, tag->title);
\r