check in new version of the winamp3 plugin
[platform/upstream/flac.git] / src / plugin_winamp3 / flacpcm.cpp
1 /* FLAC input plugin for Winamp3\r
2  * Copyright (C) 2000,2001  Josh Coalson\r
3  *\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
8  *\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
13  *\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
17  *\r
18  * NOTE: this code is derived from the 'rawpcm' example by\r
19  * Nullsoft; the original license for the 'rawpcm' example follows.\r
20  */\r
21 /*\r
22 \r
23   Nullsoft WASABI Source File License\r
24 \r
25   Copyright 1999-2001 Nullsoft, Inc.\r
26 \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
30 \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
34 \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
42 \r
43 \r
44   Brennan Underwood\r
45   brennan@nullsoft.com\r
46 \r
47 */\r
48 \r
49 #include "flacpcm.h"\r
50 extern "C" {\r
51 #include "FLAC/utility.h"\r
52 };\r
53 \r
54 \r
55 struct id3v1_struct {\r
56         FLAC__byte raw[128];\r
57         char title[31];\r
58         char artist[31];\r
59         char album[31];\r
60         char comment[31];\r
61         unsigned year;\r
62         unsigned track; /* may be 0 if v1 (not v1.1) tag */\r
63         unsigned genre;\r
64         char description[1024]; /* the formatted description passed to the gui */\r
65 };\r
66 \r
67 \r
68 static bool get_id3v1_tag_(const char *filename, id3v1_struct *tag);\r
69 \r
70 \r
71 FlacPcm::FlacPcm():\r
72 needs_seek(false),\r
73 seek_sample(0),\r
74 samples_in_reservoir(0),\r
75 abort_flag(false),\r
76 reader(0),\r
77 decoder(0)\r
78 {\r
79 }\r
80 \r
81 FlacPcm::~FlacPcm()\r
82 {\r
83         cleanup();\r
84 }\r
85 \r
86 int FlacPcm::getInfos(MediaInfo *infos)\r
87 {\r
88         reader = infos->getReader();\r
89         if(!reader) return 0;\r
90 \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
93                 return 1;\r
94 \r
95         id3v1_struct tag;\r
96 \r
97         //@@@ ditto here...\r
98         bool has_tag = get_id3v1_tag_(infos->getFilename(), &tag);\r
99 \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
104         if(has_tag) {\r
105                 infos->setData("Title", tag.title);\r
106                 infos->setData("Artist", tag.artist);\r
107                 infos->setData("Album", tag.album);\r
108         }\r
109 \r
110         return 0;\r
111 }\r
112 \r
113 int FlacPcm::processData(MediaInfo *infos, ChunkList *chunk_list, bool *killswitch)\r
114 {\r
115         reader = infos->getReader();\r
116         if(reader == 0)\r
117                 return 0;\r
118 \r
119         if(decoder == 0) {\r
120                 decoder = FLAC__seekable_stream_decoder_new();\r
121                 if(decoder == 0)\r
122                         return 0;\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
133 \r
134                 if(FLAC__seekable_stream_decoder_init(decoder) != FLAC__SEEKABLE_STREAM_DECODER_OK) {\r
135                         cleanup();\r
136                         return 0;\r
137                 }\r
138                 if(!FLAC__seekable_stream_decoder_process_metadata(decoder)) {\r
139                         cleanup();\r
140                         return 0;\r
141                 }\r
142         }\r
143 \r
144         if(needs_seek) {\r
145                 FLAC__seekable_stream_decoder_seek_absolute(decoder, seek_sample);\r
146                 needs_seek = false;\r
147         }\r
148 \r
149         bool eof = false;\r
150 \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
153                         eof = true;\r
154                         break;\r
155                 }\r
156                 else if(!FLAC__seekable_stream_decoder_process_one_frame(decoder))\r
157                         break;\r
158         }\r
159 \r
160         if(samples_in_reservoir == 0) {\r
161                 eof = true;\r
162         }\r
163         else {\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
170 \r
171                 for(i = 0; i < n*channels; i++)\r
172                         ssbuffer[i] = reservoir[i];\r
173                 delta = i;\r
174                 for( ; i < samples_in_reservoir*channels; i++)\r
175                         reservoir[i-delta] = reservoir[i];\r
176                 samples_in_reservoir -= n;\r
177 \r
178                 const int bytes = n * channels * bytes_per_sample;\r
179 \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
184 \r
185                 chunk_list->setChunk("PCM", output, bytes, ci);\r
186         }\r
187 \r
188         if(eof)\r
189                 return 0;\r
190 \r
191         return 1;\r
192 }\r
193 \r
194 int FlacPcm::corecb_onSeeked(int newpos)\r
195 {\r
196         if(stream_info.total_samples == 0 || newpos < 0)\r
197                 return 1;\r
198 \r
199         needs_seek = true;\r
200         seek_sample = (FLAC__uint64)((double)newpos / (double)lengthInMsec() * (double)(FLAC__int64)stream_info.total_samples);\r
201         return 0;\r
202 }\r
203 \r
204 void FlacPcm::cleanup()\r
205 {\r
206         if(decoder) {\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
210                 decoder = 0;\r
211         }\r
212 }\r
213 FLAC__SeekableStreamDecoderReadStatus FlacPcm::readCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)\r
214 {\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
218 }\r
219 \r
220 FLAC__SeekableStreamDecoderSeekStatus FlacPcm::seekCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)\r
221 {\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
225         else\r
226                 return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;\r
227 }\r
228 \r
229 FLAC__SeekableStreamDecoderTellStatus FlacPcm::tellCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)\r
230 {\r
231         FlacPcm *instance = (FlacPcm*)client_data;\r
232         int pos = instance->reader->getPos();\r
233         if(pos < 0)\r
234                 return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR;\r
235         else {\r
236                 *absolute_byte_offset = pos;\r
237                 return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;\r
238         }\r
239 }\r
240 \r
241 FLAC__SeekableStreamDecoderLengthStatus FlacPcm::lengthCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)\r
242 {\r
243         FlacPcm *instance = (FlacPcm*)client_data;\r
244         int length = instance->reader->getPos();\r
245         if(length < 0)\r
246                 return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR;\r
247         else {\r
248                 *stream_length = length;\r
249                 return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;\r
250         }\r
251 }\r
252 \r
253 FLAC__bool FlacPcm::eofCallback_(const FLAC__SeekableStreamDecoder *decoder, void *client_data)\r
254 {\r
255         FlacPcm *instance = (FlacPcm*)client_data;\r
256         return instance->reader->getPos() == instance->reader->getLength();\r
257 }\r
258 \r
259 FLAC__StreamDecoderWriteStatus FlacPcm::writeCallback_(const FLAC__SeekableStreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data)\r
260 {\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
264 \r
265         (void)decoder;\r
266 \r
267         if(instance->abort_flag)\r
268                 return FLAC__STREAM_DECODER_WRITE_ABORT;\r
269 \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
273 \r
274         instance->samples_in_reservoir += wide_samples;\r
275 \r
276         return FLAC__STREAM_DECODER_WRITE_CONTINUE;\r
277 }\r
278 \r
279 void FlacPcm::metadataCallback_(const FLAC__SeekableStreamDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)\r
280 {\r
281         FlacPcm *instance = (FlacPcm*)client_data;\r
282         (void)decoder;\r
283         if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {\r
284                 instance->stream_info = metadata->data.stream_info;\r
285 \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
289                         return;\r
290                 }\r
291         }\r
292 }\r
293 \r
294 void FlacPcm::errorCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)\r
295 {\r
296         FlacPcm *instance = (FlacPcm*)client_data;\r
297         (void)decoder;\r
298         if(status != FLAC__STREAM_DECODER_ERROR_LOST_SYNC)\r
299                 instance->abort_flag = true;\r
300 }\r
301 \r
302 /***********************************************************************\r
303  * local routines\r
304  **********************************************************************/\r
305 \r
306 bool get_id3v1_tag_(const char *filename, id3v1_struct *tag)\r
307 {\r
308         const char *temp;\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
315 \r
316         /* set the title and description to the filename by default */\r
317         temp = strrchr(filename, '/');\r
318         if(!temp)\r
319                 temp = filename;\r
320         else\r
321                 temp++;\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
325 \r
326         if(0 == f)\r
327                 return false;\r
328         if(-1 == fseek(f, -128, SEEK_END)) {\r
329                 fclose(f);\r
330                 return false;\r
331         }\r
332         if(fread(tag->raw, 1, 128, f) < 128) {\r
333                 fclose(f);\r
334                 return false;\r
335         }\r
336         fclose(f);\r
337         if(strncmp((const char*)tag->raw, "TAG", 3))\r
338                 return false;\r
339         else {\r
340                 char year_str[5];\r
341 \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
349 \r
350                 sprintf(tag->description, "%s - %s", tag->artist, tag->title);\r
351 \r
352                 return true;\r
353         }\r
354 }\r