update copyright date to include 2002
[platform/upstream/flac.git] / src / plugin_winamp3 / flacpcm.cpp
1 /* FLAC input plugin for Winamp3\r
2  * Copyright (C) 2000,2001,2002  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                 FLAC__seekable_stream_decoder_finish(decoder);\r
208                 FLAC__seekable_stream_decoder_delete(decoder);\r
209                 decoder = 0;\r
210         }\r
211 }\r
212 FLAC__SeekableStreamDecoderReadStatus FlacPcm::readCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)\r
213 {\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
217 }\r
218 \r
219 FLAC__SeekableStreamDecoderSeekStatus FlacPcm::seekCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)\r
220 {\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
224         else\r
225                 return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;\r
226 }\r
227 \r
228 FLAC__SeekableStreamDecoderTellStatus FlacPcm::tellCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)\r
229 {\r
230         FlacPcm *instance = (FlacPcm*)client_data;\r
231         int pos = instance->reader->getPos();\r
232         if(pos < 0)\r
233                 return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR;\r
234         else {\r
235                 *absolute_byte_offset = pos;\r
236                 return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;\r
237         }\r
238 }\r
239 \r
240 FLAC__SeekableStreamDecoderLengthStatus FlacPcm::lengthCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)\r
241 {\r
242         FlacPcm *instance = (FlacPcm*)client_data;\r
243         int length = instance->reader->getPos();\r
244         if(length < 0)\r
245                 return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR;\r
246         else {\r
247                 *stream_length = length;\r
248                 return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;\r
249         }\r
250 }\r
251 \r
252 FLAC__bool FlacPcm::eofCallback_(const FLAC__SeekableStreamDecoder *decoder, void *client_data)\r
253 {\r
254         FlacPcm *instance = (FlacPcm*)client_data;\r
255         return instance->reader->getPos() == instance->reader->getLength();\r
256 }\r
257 \r
258 FLAC__StreamDecoderWriteStatus FlacPcm::writeCallback_(const FLAC__SeekableStreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data)\r
259 {\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
263 \r
264         (void)decoder;\r
265 \r
266         if(instance->abort_flag)\r
267                 return FLAC__STREAM_DECODER_WRITE_ABORT;\r
268 \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
272 \r
273         instance->samples_in_reservoir += wide_samples;\r
274 \r
275         return FLAC__STREAM_DECODER_WRITE_CONTINUE;\r
276 }\r
277 \r
278 void FlacPcm::metadataCallback_(const FLAC__SeekableStreamDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)\r
279 {\r
280         FlacPcm *instance = (FlacPcm*)client_data;\r
281         (void)decoder;\r
282         if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {\r
283                 instance->stream_info = metadata->data.stream_info;\r
284 \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
288                         return;\r
289                 }\r
290         }\r
291 }\r
292 \r
293 void FlacPcm::errorCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)\r
294 {\r
295         FlacPcm *instance = (FlacPcm*)client_data;\r
296         (void)decoder;\r
297         if(status != FLAC__STREAM_DECODER_ERROR_LOST_SYNC)\r
298                 instance->abort_flag = true;\r
299 }\r
300 \r
301 /***********************************************************************\r
302  * local routines\r
303  **********************************************************************/\r
304 \r
305 bool get_id3v1_tag_(const char *filename, id3v1_struct *tag)\r
306 {\r
307         const char *temp;\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
314 \r
315         /* set the title and description to the filename by default */\r
316         temp = strrchr(filename, '/');\r
317         if(!temp)\r
318                 temp = filename;\r
319         else\r
320                 temp++;\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
324 \r
325         if(0 == f)\r
326                 return false;\r
327         if(-1 == fseek(f, -128, SEEK_END)) {\r
328                 fclose(f);\r
329                 return false;\r
330         }\r
331         if(fread(tag->raw, 1, 128, f) < 128) {\r
332                 fclose(f);\r
333                 return false;\r
334         }\r
335         fclose(f);\r
336         if(strncmp((const char*)tag->raw, "TAG", 3))\r
337                 return false;\r
338         else {\r
339                 char year_str[5];\r
340 \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
348 \r
349                 sprintf(tag->description, "%s - %s", tag->artist, tag->title);\r
350 \r
351                 return true;\r
352         }\r
353 }\r