Mercurial > audlegacy
annotate Plugins/Input/flac/plugin.c @ 368:fb59081b528f trunk
[svn] Use file magic instead of extensions.
author | chainsaw |
---|---|
date | Sat, 31 Dec 2005 14:47:56 -0800 |
parents | 57d01c5bd39f |
children | 7e81e9ef40f2 |
rev | line source |
---|---|
61 | 1 /* libxmms-flac - XMMS FLAC input plugin |
2 * Copyright (C) 2000,2001,2002,2003,2004,2005 Josh Coalson | |
3 * | |
4 * This program is free software; you can redistribute it and/or | |
5 * modify it under the terms of the GNU General Public License | |
6 * as published by the Free Software Foundation; either version 2 | |
7 * of the License, or (at your option) any later version. | |
8 * | |
9 * This program is distributed in the hope that it will be useful, | |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 * GNU General Public License for more details. | |
13 * | |
14 * You should have received a copy of the GNU General Public License | |
15 * along with this program; if not, write to the Free Software | |
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
17 */ | |
18 | |
19 #include <stdlib.h> | |
20 #include <string.h> | |
21 #include <stdio.h> | |
22 #include <glib.h> | |
23 #include <pwd.h> | |
24 #include <sys/types.h> | |
25 #include <unistd.h> | |
26 | |
27 #include <audacious/plugin.h> | |
188
6bdc6841b0a9
[svn] Fix includes to avoid implicit declaration warnings.
chainsaw
parents:
181
diff
changeset
|
28 #include <libaudacious/util.h> |
303 | 29 #include <libaudacious/configdb.h> |
61 | 30 #include <libaudacious/titlestring.h> |
368 | 31 #include <libaudacious/vfs.h> |
61 | 32 |
33 #ifdef HAVE_CONFIG_H | |
34 #include <config.h> | |
35 #endif | |
36 | |
37 #ifdef HAVE_LANGINFO_CODESET | |
38 #include <langinfo.h> | |
39 #endif | |
40 | |
41 #include "FLAC/all.h" | |
42 #include "plugin_common/all.h" | |
43 #include "grabbag.h" | |
44 #include "replaygain_synthesis.h" | |
45 #include "configure.h" | |
46 #include "charset.h" | |
47 #include "http.h" | |
48 #include "tag.h" | |
49 | |
50 #ifdef min | |
51 #undef min | |
52 #endif | |
53 #define min(x,y) ((x)<(y)?(x):(y)) | |
54 | |
55 /* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ | |
56 #ifdef _MSC_VER | |
57 #define FLAC__U64L(x) x | |
58 #else | |
59 #define FLAC__U64L(x) x##LLU | |
60 #endif | |
61 | |
62 extern void FLAC_XMMS__file_info_box(char *filename); | |
63 | |
64 typedef struct { | |
65 FLAC__bool abort_flag; | |
66 FLAC__bool is_playing; | |
67 FLAC__bool eof; | |
68 FLAC__bool play_thread_open; /* if true, is_playing must also be true */ | |
69 unsigned total_samples; | |
70 unsigned bits_per_sample; | |
71 unsigned channels; | |
72 unsigned sample_rate; | |
73 unsigned length_in_msec; | |
74 gchar *title; | |
75 AFormat sample_format; | |
76 unsigned sample_format_bytes_per_sample; | |
77 int seek_to_in_sec; | |
78 FLAC__bool has_replaygain; | |
79 double replay_scale; | |
80 DitherContext dither_context; | |
81 } file_info_struct; | |
82 | |
83 typedef FLAC__StreamDecoderWriteStatus (*WriteCallback) (const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); | |
84 typedef void (*MetadataCallback) (const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data); | |
85 typedef void (*ErrorCallback) (const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); | |
86 | |
87 typedef struct { | |
88 FLAC__bool seekable; | |
89 void* (*new_decoder) (void); | |
90 FLAC__bool (*set_md5_checking) (void *decoder, FLAC__bool value); | |
91 FLAC__bool (*set_source) (void *decoder, const char* source); | |
92 FLAC__bool (*set_metadata_ignore_all) (void *decoder); | |
93 FLAC__bool (*set_metadata_respond) (void *decoder, FLAC__MetadataType type); | |
94 FLAC__bool (*set_write_callback) (void *decoder, WriteCallback value); | |
95 FLAC__bool (*set_metadata_callback) (void *decoder, MetadataCallback value); | |
96 FLAC__bool (*set_error_callback) (void *decoder, ErrorCallback value); | |
97 FLAC__bool (*set_client_data) (void *decoder, void *value); | |
98 FLAC__bool (*decoder_init) (void *decoder); | |
99 void (*safe_decoder_finish) (void *decoder); | |
100 void (*safe_decoder_delete) (void *decoder); | |
101 FLAC__bool (*process_until_end_of_metadata) (void *decoder); | |
102 FLAC__bool (*process_single) (void *decoder); | |
103 FLAC__bool (*is_eof) (void *decoder); | |
104 } decoder_funcs_t; | |
105 | |
106 #define NUM_DECODER_TYPES 2 | |
107 typedef enum { | |
108 DECODER_FILE, | |
109 DECODER_HTTP | |
110 } decoder_t; | |
111 | |
112 static void FLAC_XMMS__init(); | |
113 static int FLAC_XMMS__is_our_file(char *filename); | |
114 static void FLAC_XMMS__play_file(char *filename); | |
115 static void FLAC_XMMS__stop(); | |
116 static void FLAC_XMMS__pause(short p); | |
117 static void FLAC_XMMS__seek(int time); | |
118 static int FLAC_XMMS__get_time(); | |
119 static void FLAC_XMMS__cleanup(); | |
120 static void FLAC_XMMS__get_song_info(char *filename, char **title, int *length); | |
121 | |
122 static void *play_loop_(void *arg); | |
123 | |
124 static FLAC__bool safe_decoder_init_(const char *filename, void **decoderp, decoder_funcs_t const ** fnsp); | |
125 static void file_decoder_safe_decoder_finish_(void *decoder); | |
126 static void file_decoder_safe_decoder_delete_(void *decoder); | |
127 static FLAC__StreamDecoderWriteStatus write_callback_(const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); | |
128 static void metadata_callback_(const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data); | |
129 static void error_callback_(const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); | |
130 | |
131 static void init_decoder_func_tables(); | |
132 static decoder_t source_to_decoder_type (const char *source); | |
133 | |
134 InputPlugin flac_ip = | |
135 { | |
136 NULL, | |
137 NULL, | |
138 "FLAC Player v" VERSION, | |
139 FLAC_XMMS__init, | |
140 FLAC_XMMS__aboutbox, | |
141 FLAC_XMMS__configure, | |
142 FLAC_XMMS__is_our_file, | |
143 NULL, | |
144 FLAC_XMMS__play_file, | |
145 FLAC_XMMS__stop, | |
146 FLAC_XMMS__pause, | |
147 FLAC_XMMS__seek, | |
148 NULL, | |
149 FLAC_XMMS__get_time, | |
150 NULL, | |
151 NULL, | |
152 FLAC_XMMS__cleanup, | |
153 NULL, | |
154 NULL, | |
155 NULL, | |
156 NULL, | |
157 FLAC_XMMS__get_song_info, | |
158 FLAC_XMMS__file_info_box, | |
159 NULL | |
160 }; | |
161 | |
162 #define SAMPLES_PER_WRITE 512 | |
163 #define SAMPLE_BUFFER_SIZE ((FLAC__MAX_BLOCK_SIZE + SAMPLES_PER_WRITE) * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS * (24/8)) | |
164 static FLAC__byte sample_buffer_[SAMPLE_BUFFER_SIZE]; | |
165 static unsigned sample_buffer_first_, sample_buffer_last_; | |
166 | |
167 static void *decoder_ = 0; | |
168 static file_info_struct file_info_; | |
181 | 169 static GThread *decode_thread_; |
61 | 170 static FLAC__bool audio_error_ = false; |
171 static FLAC__bool is_big_endian_host_; | |
172 | |
173 #define BITRATE_HIST_SEGMENT_MSEC 500 | |
174 /* 500ms * 50 = 25s should be enough */ | |
175 #define BITRATE_HIST_SIZE 50 | |
176 static unsigned bitrate_history_[BITRATE_HIST_SIZE]; | |
177 | |
178 /* A table of sets of decoder functions, indexed by decoder_t */ | |
179 static const decoder_funcs_t* DECODER_FUNCS[NUM_DECODER_TYPES]; | |
180 | |
181 static decoder_funcs_t const * decoder_func_table_; | |
182 | |
183 | |
184 InputPlugin *get_iplugin_info() | |
185 { | |
186 flac_ip.description = g_strdup_printf("Reference FLAC Player v%s", FLAC__VERSION_STRING); | |
187 return &flac_ip; | |
188 } | |
189 | |
190 void set_track_info(const char* title, int length_in_msec) | |
191 { | |
192 if (file_info_.is_playing) { | |
193 flac_ip.set_info((char*) title, length_in_msec, file_info_.sample_rate * file_info_.channels * file_info_.bits_per_sample, file_info_.sample_rate, file_info_.channels); | |
194 } | |
195 } | |
196 | |
197 static gchar* homedir() | |
198 { | |
199 gchar *result; | |
200 char *env_home = getenv("HOME"); | |
201 if (env_home) { | |
202 result = g_strdup (env_home); | |
203 } else { | |
204 uid_t uid = getuid(); | |
205 struct passwd *pwent; | |
206 do { | |
207 pwent = getpwent(); | |
208 } while (pwent && pwent->pw_uid != uid); | |
209 result = pwent ? g_strdup (pwent->pw_dir) : NULL; | |
210 endpwent(); | |
211 } | |
212 return result; | |
213 } | |
214 | |
215 void FLAC_XMMS__init() | |
216 { | |
303 | 217 ConfigDb *db; |
61 | 218 FLAC__uint32 test = 1; |
219 | |
220 is_big_endian_host_ = (*((FLAC__byte*)(&test)))? false : true; | |
221 | |
222 flac_cfg.title.tag_override = FALSE; | |
223 g_free(flac_cfg.title.tag_format); | |
224 flac_cfg.title.convert_char_set = FALSE; | |
225 | |
303 | 226 db = bmp_cfg_db_open(); |
61 | 227 |
228 /* title */ | |
229 | |
303 | 230 bmp_cfg_db_get_bool(db, "flac", "title.tag_override", &flac_cfg.title.tag_override); |
61 | 231 |
303 | 232 if(!bmp_cfg_db_get_string(db, "flac", "title.tag_format", &flac_cfg.title.tag_format)) |
61 | 233 flac_cfg.title.tag_format = g_strdup("%p - %t"); |
234 | |
303 | 235 bmp_cfg_db_get_bool(db, "flac", "title.convert_char_set", &flac_cfg.title.convert_char_set); |
61 | 236 |
303 | 237 if(!bmp_cfg_db_get_string(db, "flac", "title.user_char_set", &flac_cfg.title.user_char_set)) |
61 | 238 flac_cfg.title.user_char_set = FLAC_plugin__charset_get_current(); |
239 | |
240 /* replaygain */ | |
241 | |
303 | 242 bmp_cfg_db_get_bool(db, "flac", "output.replaygain.enable", &flac_cfg.output.replaygain.enable); |
61 | 243 |
303 | 244 bmp_cfg_db_get_bool(db, "flac", "output.replaygain.album_mode", &flac_cfg.output.replaygain.album_mode); |
61 | 245 |
303 | 246 if(!bmp_cfg_db_get_int(db, "flac", "output.replaygain.preamp", &flac_cfg.output.replaygain.preamp)) |
61 | 247 flac_cfg.output.replaygain.preamp = 0; |
248 | |
303 | 249 bmp_cfg_db_get_bool(db, "flac", "output.replaygain.hard_limit", &flac_cfg.output.replaygain.hard_limit); |
61 | 250 |
303 | 251 bmp_cfg_db_get_bool(db, "flac", "output.resolution.normal.dither_24_to_16", &flac_cfg.output.resolution.normal.dither_24_to_16); |
252 bmp_cfg_db_get_bool(db, "flac", "output.resolution.replaygain.dither", &flac_cfg.output.resolution.replaygain.dither); | |
61 | 253 |
303 | 254 if(!bmp_cfg_db_get_int(db, "flac", "output.resolution.replaygain.noise_shaping", &flac_cfg.output.resolution.replaygain.noise_shaping)) |
61 | 255 flac_cfg.output.resolution.replaygain.noise_shaping = 1; |
256 | |
303 | 257 if(!bmp_cfg_db_get_int(db, "flac", "output.resolution.replaygain.bps_out", &flac_cfg.output.resolution.replaygain.bps_out)) |
61 | 258 flac_cfg.output.resolution.replaygain.bps_out = 16; |
259 | |
260 /* stream */ | |
261 | |
303 | 262 bmp_cfg_db_get_int(db, "flac", "stream.http_buffer_size", &flac_cfg.stream.http_buffer_size); |
263 bmp_cfg_db_get_int(db, "flac", "stream.http_prebuffer", &flac_cfg.stream.http_prebuffer); | |
264 bmp_cfg_db_get_bool(db, "flac", "stream.use_proxy", &flac_cfg.stream.use_proxy); | |
265 bmp_cfg_db_get_string(db, "flac", "stream.proxy_host", &flac_cfg.stream.proxy_host); | |
266 bmp_cfg_db_get_int(db, "flac", "stream.proxy_port", &flac_cfg.stream.proxy_port); | |
267 bmp_cfg_db_get_bool(db, "flac", "stream.proxy_use_auth", &flac_cfg.stream.proxy_use_auth); | |
268 bmp_cfg_db_get_string(db, "flac", "stream.proxy_user", &flac_cfg.stream.proxy_user); | |
269 bmp_cfg_db_get_string(db, "flac", "stream.proxy_pass", &flac_cfg.stream.proxy_pass); | |
270 bmp_cfg_db_get_bool(db, "flac", "stream.save_http_stream", &flac_cfg.stream.save_http_stream); | |
271 if (!bmp_cfg_db_get_string(db, "flac", "stream.save_http_path", &flac_cfg.stream.save_http_path) || | |
61 | 272 ! *flac_cfg.stream.save_http_path) { |
273 /* TODO: Is this a memory leak ?? */ | |
274 /* | |
275 if (flac_cfg.stream.save_http_path) | |
276 g_free (flac_cfg.stream.save_http_path); | |
277 */ | |
278 flac_cfg.stream.save_http_path = homedir(); | |
279 } | |
303 | 280 bmp_cfg_db_get_bool(db, "flac", "stream.cast_title_streaming", &flac_cfg.stream.cast_title_streaming); |
281 bmp_cfg_db_get_bool(db, "flac", "stream.use_udp_channel", &flac_cfg.stream.use_udp_channel); | |
61 | 282 |
283 init_decoder_func_tables(); | |
284 decoder_func_table_ = DECODER_FUNCS [DECODER_FILE]; | |
285 decoder_ = decoder_func_table_ -> new_decoder(); | |
286 | |
303 | 287 bmp_cfg_db_close(db); |
61 | 288 } |
289 | |
290 int FLAC_XMMS__is_our_file(char *filename) | |
291 { | |
368 | 292 VFSFile *file; |
293 gchar magic[4]; | |
294 if (file = vfs_fopen(filename, "rb")) { | |
295 vfs_fread(magic, 1, 4, file); | |
296 if (!strncmp(magic, "fLaC", 4)) { | |
297 vfs_fclose(file); | |
61 | 298 return 1; |
368 | 299 } |
300 vfs_fclose(file); | |
301 } | |
61 | 302 return 0; |
303 } | |
304 | |
305 void FLAC_XMMS__play_file(char *filename) | |
306 { | |
307 FILE *f; | |
308 | |
309 sample_buffer_first_ = sample_buffer_last_ = 0; | |
310 audio_error_ = false; | |
311 file_info_.abort_flag = false; | |
312 file_info_.is_playing = false; | |
313 file_info_.eof = false; | |
314 file_info_.play_thread_open = false; | |
315 file_info_.has_replaygain = false; | |
316 | |
317 if (source_to_decoder_type (filename) == DECODER_FILE) { | |
318 if(0 == (f = fopen(filename, "r"))) | |
319 return; | |
320 fclose(f); | |
321 } | |
322 | |
323 if(decoder_ == 0) | |
324 return; | |
325 | |
326 if(!safe_decoder_init_(filename, &decoder_, &decoder_func_table_)) | |
327 return; | |
328 | |
329 if(file_info_.has_replaygain && flac_cfg.output.replaygain.enable) { | |
330 if(flac_cfg.output.resolution.replaygain.bps_out == 8) { | |
331 file_info_.sample_format = FMT_U8; | |
332 file_info_.sample_format_bytes_per_sample = 1; | |
333 } | |
334 else if(flac_cfg.output.resolution.replaygain.bps_out == 16) { | |
335 file_info_.sample_format = (is_big_endian_host_) ? FMT_S16_BE : FMT_S16_LE; | |
336 file_info_.sample_format_bytes_per_sample = 2; | |
337 } | |
338 else { | |
339 /*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16-bit samples\n", "ERROR: plugin can only handle 8/16-bit samples", 0); */ | |
340 fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", flac_cfg.output.resolution.replaygain.bps_out); | |
341 decoder_func_table_ -> safe_decoder_finish(decoder_); | |
342 return; | |
343 } | |
344 } | |
345 else { | |
346 if(file_info_.bits_per_sample == 8) { | |
347 file_info_.sample_format = FMT_U8; | |
348 file_info_.sample_format_bytes_per_sample = 1; | |
349 } | |
350 else if(file_info_.bits_per_sample == 16 || (file_info_.bits_per_sample == 24 && flac_cfg.output.resolution.normal.dither_24_to_16)) { | |
351 file_info_.sample_format = (is_big_endian_host_) ? FMT_S16_BE : FMT_S16_LE; | |
352 file_info_.sample_format_bytes_per_sample = 2; | |
353 } | |
354 else { | |
355 /*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16-bit samples\n", "ERROR: plugin can only handle 8/16-bit samples", 0); */ | |
356 fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", file_info_.bits_per_sample); | |
357 decoder_func_table_ -> safe_decoder_finish(decoder_); | |
358 return; | |
359 } | |
360 } | |
361 FLAC__replaygain_synthesis__init_dither_context(&file_info_.dither_context, file_info_.sample_format_bytes_per_sample * 8, flac_cfg.output.resolution.replaygain.noise_shaping); | |
362 file_info_.is_playing = true; | |
363 | |
364 if(flac_ip.output->open_audio(file_info_.sample_format, file_info_.sample_rate, file_info_.channels) == 0) { | |
365 audio_error_ = true; | |
366 decoder_func_table_ -> safe_decoder_finish(decoder_); | |
367 return; | |
368 } | |
369 | |
370 file_info_.title = flac_format_song_title(filename); | |
371 flac_ip.set_info(file_info_.title, file_info_.length_in_msec, file_info_.sample_rate * file_info_.channels * file_info_.bits_per_sample, file_info_.sample_rate, file_info_.channels); | |
372 | |
373 file_info_.seek_to_in_sec = -1; | |
374 file_info_.play_thread_open = true; | |
181 | 375 decode_thread_ = g_thread_create((GThreadFunc)play_loop_, NULL, TRUE, NULL); |
61 | 376 } |
377 | |
378 void FLAC_XMMS__stop() | |
379 { | |
380 if(file_info_.is_playing) { | |
381 file_info_.is_playing = false; | |
382 if(file_info_.play_thread_open) { | |
383 file_info_.play_thread_open = false; | |
181 | 384 g_thread_join(decode_thread_); |
61 | 385 } |
386 flac_ip.output->close_audio(); | |
387 decoder_func_table_ -> safe_decoder_finish (decoder_); | |
388 } | |
389 } | |
390 | |
391 void FLAC_XMMS__pause(short p) | |
392 { | |
393 flac_ip.output->pause(p); | |
394 } | |
395 | |
396 void FLAC_XMMS__seek(int time) | |
397 { | |
398 if (decoder_func_table_->seekable) { | |
399 file_info_.seek_to_in_sec = time; | |
400 file_info_.eof = false; | |
401 | |
402 while(file_info_.seek_to_in_sec != -1) | |
403 xmms_usleep(10000); | |
404 } | |
405 } | |
406 | |
407 int FLAC_XMMS__get_time() | |
408 { | |
409 if(audio_error_) | |
410 return -2; | |
411 if(!file_info_.is_playing || (file_info_.eof && !flac_ip.output->buffer_playing())) | |
412 return -1; | |
413 else | |
414 return flac_ip.output->output_time(); | |
415 } | |
416 | |
417 void FLAC_XMMS__cleanup() | |
418 { | |
419 decoder_func_table_ -> safe_decoder_delete(decoder_); | |
420 decoder_ = 0; | |
421 } | |
422 | |
423 void FLAC_XMMS__get_song_info(char *filename, char **title, int *length_in_msec) | |
424 { | |
425 FLAC__StreamMetadata streaminfo; | |
426 | |
427 if(0 == filename) | |
428 filename = ""; | |
429 | |
430 if(!FLAC__metadata_get_streaminfo(filename, &streaminfo)) { | |
431 /* @@@ how to report the error? */ | |
432 if(title) { | |
433 if (source_to_decoder_type (filename) == DECODER_FILE) { | |
434 static const char *errtitle = "Invalid FLAC File: "; | |
435 *title = g_malloc(strlen(errtitle) + 1 + strlen(filename) + 1 + 1); | |
436 sprintf(*title, "%s\"%s\"", errtitle, filename); | |
437 } else { | |
438 *title = NULL; | |
439 } | |
440 } | |
441 if(length_in_msec) | |
442 *length_in_msec = -1; | |
443 return; | |
444 } | |
445 | |
446 if(title) { | |
447 *title = flac_format_song_title(filename); | |
448 } | |
449 if(length_in_msec) | |
450 *length_in_msec = (unsigned)((double)streaminfo.data.stream_info.total_samples / (double)streaminfo.data.stream_info.sample_rate * 1000.0 + 0.5); | |
451 } | |
452 | |
453 /*********************************************************************** | |
454 * local routines | |
455 **********************************************************************/ | |
456 | |
457 void *play_loop_(void *arg) | |
458 { | |
459 unsigned written_time_last = 0, bh_index_last_w = 0, bh_index_last_o = BITRATE_HIST_SIZE, blocksize = 1; | |
460 FLAC__uint64 decode_position_last = 0, decode_position_frame_last = 0, decode_position_frame = 0; | |
461 | |
462 (void)arg; | |
463 | |
464 while(file_info_.is_playing) { | |
465 if(!file_info_.eof) { | |
466 while(sample_buffer_last_ - sample_buffer_first_ < SAMPLES_PER_WRITE) { | |
467 unsigned s; | |
468 | |
469 s = sample_buffer_last_ - sample_buffer_first_; | |
470 if(decoder_func_table_ -> is_eof(decoder_)) { | |
471 file_info_.eof = true; | |
472 break; | |
473 } | |
474 else if (!decoder_func_table_ -> process_single(decoder_)) { | |
475 /*@@@ this should probably be a dialog */ | |
476 fprintf(stderr, "libxmms-flac: READ ERROR processing frame\n"); | |
477 file_info_.eof = true; | |
478 break; | |
479 } | |
480 blocksize = sample_buffer_last_ - sample_buffer_first_ - s; | |
481 decode_position_frame_last = decode_position_frame; | |
482 if(!decoder_func_table_->seekable || !FLAC__file_decoder_get_decode_position(decoder_, &decode_position_frame)) | |
483 decode_position_frame = 0; | |
484 } | |
485 if(sample_buffer_last_ - sample_buffer_first_ > 0) { | |
486 const unsigned n = min(sample_buffer_last_ - sample_buffer_first_, SAMPLES_PER_WRITE); | |
487 int bytes = n * file_info_.channels * file_info_.sample_format_bytes_per_sample; | |
488 FLAC__byte *sample_buffer_start = sample_buffer_ + sample_buffer_first_ * file_info_.channels * file_info_.sample_format_bytes_per_sample; | |
489 unsigned written_time, bh_index_w; | |
490 FLAC__uint64 decode_position; | |
491 | |
492 sample_buffer_first_ += n; | |
493 flac_ip.add_vis_pcm(flac_ip.output->written_time(), file_info_.sample_format, file_info_.channels, bytes, sample_buffer_start); | |
494 while(flac_ip.output->buffer_free() < (int)bytes && file_info_.is_playing && file_info_.seek_to_in_sec == -1) | |
495 xmms_usleep(10000); | |
496 if(file_info_.is_playing && file_info_.seek_to_in_sec == -1) | |
497 flac_ip.output->write_audio(sample_buffer_start, bytes); | |
498 | |
499 /* compute current bitrate */ | |
500 | |
501 written_time = flac_ip.output->written_time(); | |
502 bh_index_w = written_time / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; | |
503 if(bh_index_w != bh_index_last_w) { | |
504 bh_index_last_w = bh_index_w; | |
505 decode_position = decode_position_frame - (double)(sample_buffer_last_ - sample_buffer_first_) * (double)(decode_position_frame - decode_position_frame_last) / (double)blocksize; | |
506 bitrate_history_[(bh_index_w + BITRATE_HIST_SIZE - 1) % BITRATE_HIST_SIZE] = | |
507 decode_position > decode_position_last && written_time > written_time_last ? | |
508 8000 * (decode_position - decode_position_last) / (written_time - written_time_last) : | |
509 file_info_.sample_rate * file_info_.channels * file_info_.bits_per_sample; | |
510 decode_position_last = decode_position; | |
511 written_time_last = written_time; | |
512 } | |
513 } | |
514 else { | |
515 file_info_.eof = true; | |
516 xmms_usleep(10000); | |
517 } | |
518 } | |
519 else | |
520 xmms_usleep(10000); | |
521 if(decoder_func_table_->seekable && file_info_.seek_to_in_sec != -1) { | |
522 const double distance = (double)file_info_.seek_to_in_sec * 1000.0 / (double)file_info_.length_in_msec; | |
523 unsigned target_sample = (unsigned)(distance * (double)file_info_.total_samples); | |
524 if(FLAC__file_decoder_seek_absolute(decoder_, (FLAC__uint64)target_sample)) { | |
525 flac_ip.output->flush(file_info_.seek_to_in_sec * 1000); | |
526 bh_index_last_w = bh_index_last_o = flac_ip.output->output_time() / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; | |
527 if(!FLAC__file_decoder_get_decode_position(decoder_, &decode_position_frame)) | |
528 decode_position_frame = 0; | |
529 file_info_.seek_to_in_sec = -1; | |
530 file_info_.eof = false; | |
531 sample_buffer_first_ = sample_buffer_last_ = 0; | |
532 } | |
533 } | |
534 else { | |
535 /* display the right bitrate from history */ | |
536 unsigned bh_index_o = flac_ip.output->output_time() / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; | |
537 if(bh_index_o != bh_index_last_o && bh_index_o != bh_index_last_w && bh_index_o != (bh_index_last_w + 1) % BITRATE_HIST_SIZE) { | |
538 bh_index_last_o = bh_index_o; | |
539 flac_ip.set_info(file_info_.title, file_info_.length_in_msec, bitrate_history_[bh_index_o], file_info_.sample_rate, file_info_.channels); | |
540 } | |
541 } | |
542 } | |
543 | |
544 decoder_func_table_ -> safe_decoder_finish(decoder_); | |
545 | |
546 /* are these two calls necessary? */ | |
547 flac_ip.output->buffer_free(); | |
548 flac_ip.output->buffer_free(); | |
549 | |
550 g_free(file_info_.title); | |
551 | |
181 | 552 g_thread_exit(NULL); |
61 | 553 return 0; /* to silence the compiler warning about not returning a value */ |
554 } | |
555 | |
556 /*********** File decoder functions */ | |
557 | |
558 static FLAC__bool file_decoder_init (void *decoder) | |
559 { | |
560 return FLAC__file_decoder_init( (FLAC__FileDecoder*) decoder) == FLAC__FILE_DECODER_OK; | |
561 } | |
562 | |
563 static void file_decoder_safe_decoder_finish_(void *decoder) | |
564 { | |
565 if(decoder && FLAC__file_decoder_get_state((FLAC__FileDecoder *) decoder) != FLAC__FILE_DECODER_UNINITIALIZED) | |
566 FLAC__file_decoder_finish((FLAC__FileDecoder *) decoder); | |
567 } | |
568 | |
569 static void file_decoder_safe_decoder_delete_(void *decoder) | |
570 { | |
571 if(decoder) { | |
572 file_decoder_safe_decoder_finish_(decoder); | |
573 FLAC__file_decoder_delete( (FLAC__FileDecoder *) decoder); | |
574 } | |
575 } | |
576 | |
577 static FLAC__bool file_decoder_is_eof(void *decoder) | |
578 { | |
579 return FLAC__file_decoder_get_state((FLAC__FileDecoder *) decoder) == FLAC__FILE_DECODER_END_OF_FILE; | |
580 } | |
581 | |
582 static const decoder_funcs_t FILE_DECODER_FUNCTIONS = { | |
583 true, | |
584 (void* (*) (void)) FLAC__file_decoder_new, | |
585 (FLAC__bool (*) (void *, FLAC__bool)) FLAC__file_decoder_set_md5_checking, | |
586 (FLAC__bool (*) (void *, const char*)) FLAC__file_decoder_set_filename, | |
587 (FLAC__bool (*) (void *)) FLAC__file_decoder_set_metadata_ignore_all, | |
588 (FLAC__bool (*) (void *, FLAC__MetadataType)) FLAC__file_decoder_set_metadata_respond, | |
589 (FLAC__bool (*) (void *, WriteCallback)) FLAC__file_decoder_set_write_callback, | |
590 (FLAC__bool (*) (void *, MetadataCallback)) FLAC__file_decoder_set_metadata_callback, | |
591 (FLAC__bool (*) (void *, ErrorCallback)) FLAC__file_decoder_set_error_callback, | |
592 (FLAC__bool (*) (void *, void *)) FLAC__file_decoder_set_client_data, | |
593 (FLAC__bool (*) (void *)) file_decoder_init, | |
594 (void (*) (void *)) file_decoder_safe_decoder_finish_, | |
595 (void (*) (void *)) file_decoder_safe_decoder_delete_, | |
596 (FLAC__bool (*) (void *)) FLAC__file_decoder_process_until_end_of_metadata, | |
597 (FLAC__bool (*) (void *)) FLAC__file_decoder_process_single, | |
598 file_decoder_is_eof | |
599 }; | |
600 | |
601 /*********** HTTP decoder functions */ | |
602 | |
603 static gchar *url_; | |
604 | |
605 static FLAC__bool http_decoder_set_md5_checking (void *decoder, FLAC__bool value) | |
606 { | |
607 (void) value; | |
608 // operation unsupported | |
609 return FLAC__stream_decoder_get_state ((const FLAC__StreamDecoder *) decoder) == | |
610 FLAC__STREAM_DECODER_UNINITIALIZED; | |
611 } | |
612 | |
613 static FLAC__bool http_decoder_set_url (void *decoder, const char* url) | |
614 { | |
615 (void) decoder; | |
616 url_ = g_strdup (url); | |
617 return true; | |
618 } | |
619 | |
620 static FLAC__StreamDecoderReadStatus http_decoder_read_callback (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data) | |
621 { | |
622 (void) decoder; | |
623 (void) client_data; | |
624 *bytes = flac_http_read (buffer, *bytes); | |
625 return *bytes ? FLAC__STREAM_DECODER_READ_STATUS_CONTINUE : FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; | |
626 } | |
627 | |
628 static FLAC__bool http_decoder_init (void *decoder) | |
629 { | |
630 flac_http_open (url_, 0); | |
631 g_free (url_); | |
632 FLAC__stream_decoder_set_read_callback (decoder, http_decoder_read_callback); | |
633 return FLAC__stream_decoder_init( (FLAC__StreamDecoder*) decoder) == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA; | |
634 } | |
635 | |
636 static void http_decoder_safe_decoder_finish_(void *decoder) | |
637 { | |
638 if(decoder && FLAC__stream_decoder_get_state((FLAC__StreamDecoder *) decoder) != FLAC__STREAM_DECODER_UNINITIALIZED) { | |
639 FLAC__stream_decoder_finish((FLAC__StreamDecoder *) decoder); | |
640 flac_http_close(); | |
641 } | |
642 } | |
643 | |
644 static void http_decoder_safe_decoder_delete_(void *decoder) | |
645 { | |
646 if(decoder) { | |
647 http_decoder_safe_decoder_finish_(decoder); | |
648 FLAC__stream_decoder_delete( (FLAC__StreamDecoder *) decoder); | |
649 } | |
650 } | |
651 | |
652 static FLAC__bool http_decoder_is_eof(void *decoder) | |
653 { | |
654 return FLAC__stream_decoder_get_state((FLAC__StreamDecoder *) decoder) == FLAC__STREAM_DECODER_END_OF_STREAM; | |
655 } | |
656 | |
657 static const decoder_funcs_t HTTP_DECODER_FUNCTIONS = { | |
658 false, | |
659 (void* (*) (void)) FLAC__stream_decoder_new, | |
660 http_decoder_set_md5_checking, | |
661 (FLAC__bool (*) (void *, const char*)) http_decoder_set_url, | |
662 (FLAC__bool (*) (void *)) FLAC__stream_decoder_set_metadata_ignore_all, | |
663 (FLAC__bool (*) (void *, FLAC__MetadataType)) FLAC__stream_decoder_set_metadata_respond, | |
664 (FLAC__bool (*) (void *, WriteCallback)) FLAC__stream_decoder_set_write_callback, | |
665 (FLAC__bool (*) (void *, MetadataCallback)) FLAC__stream_decoder_set_metadata_callback, | |
666 (FLAC__bool (*) (void *, ErrorCallback)) FLAC__stream_decoder_set_error_callback, | |
667 (FLAC__bool (*) (void *, void *)) FLAC__stream_decoder_set_client_data, | |
668 (FLAC__bool (*) (void *)) http_decoder_init, | |
669 (void (*) (void *)) http_decoder_safe_decoder_finish_, | |
670 (void (*) (void *)) http_decoder_safe_decoder_delete_, | |
671 (FLAC__bool (*) (void *)) FLAC__stream_decoder_process_until_end_of_metadata, | |
672 (FLAC__bool (*) (void *)) FLAC__stream_decoder_process_single, | |
673 http_decoder_is_eof | |
674 }; | |
675 | |
676 static decoder_funcs_t const *decoder_func_table_; | |
677 | |
678 static void init_decoder_func_tables() | |
679 { | |
680 DECODER_FUNCS [DECODER_FILE] = & FILE_DECODER_FUNCTIONS; | |
681 DECODER_FUNCS [DECODER_HTTP] = & HTTP_DECODER_FUNCTIONS; | |
682 } | |
683 | |
684 static decoder_t source_to_decoder_type (const char *source) | |
685 { | |
686 return strncasecmp(source, "http://", 7) ? DECODER_FILE : DECODER_HTTP; | |
687 } | |
688 | |
689 static void change_decoder_if_needed (decoder_t new_decoder_type, void **decoderp, decoder_funcs_t const ** fntabp) | |
690 { | |
691 const decoder_funcs_t *new_fn_table = DECODER_FUNCS [new_decoder_type]; | |
692 if (*fntabp != new_fn_table) { | |
693 (*fntabp)->safe_decoder_delete(*decoderp); | |
694 *fntabp = new_fn_table; | |
695 *decoderp = new_fn_table -> new_decoder(); | |
696 } | |
697 } | |
698 | |
699 FLAC__bool safe_decoder_init_(const char *filename, void **decoderp, decoder_funcs_t const ** fntabp) | |
700 { | |
701 if(decoderp == 0 || *decoderp == 0) | |
702 return false; | |
703 | |
704 (*fntabp)->safe_decoder_finish(*decoderp); | |
705 | |
706 change_decoder_if_needed(source_to_decoder_type(filename), decoderp, fntabp); | |
707 | |
708 { | |
709 decoder_funcs_t const *fntab = *fntabp; | |
710 void *decoder = *decoderp; | |
711 | |
712 decoder = *decoderp; | |
713 fntab = *fntabp; | |
714 | |
715 fntab -> set_md5_checking(decoder, false); | |
716 fntab -> set_source(decoder, filename); | |
717 fntab -> set_metadata_ignore_all(decoder); | |
718 fntab -> set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO); | |
719 fntab -> set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); | |
720 fntab -> set_write_callback(decoder, write_callback_); | |
721 fntab -> set_metadata_callback(decoder, metadata_callback_); | |
722 fntab -> set_error_callback(decoder, error_callback_); | |
723 fntab -> set_client_data(decoder, &file_info_); | |
724 if(!fntab -> decoder_init(decoder)) | |
725 return false; | |
726 | |
727 if(!fntab -> process_until_end_of_metadata(decoder)) | |
728 return false; | |
729 } | |
730 | |
731 return true; | |
732 } | |
733 | |
734 FLAC__StreamDecoderWriteStatus write_callback_(const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) | |
735 { | |
736 file_info_struct *file_info = (file_info_struct *)client_data; | |
737 const unsigned channels = file_info->channels, wide_samples = frame->header.blocksize; | |
738 const unsigned bits_per_sample = file_info->bits_per_sample; | |
739 FLAC__byte *sample_buffer_start; | |
740 | |
741 (void)decoder; | |
742 | |
743 if(file_info->abort_flag) | |
744 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; | |
745 | |
746 if((sample_buffer_last_ + wide_samples) > (SAMPLE_BUFFER_SIZE / (channels * file_info->sample_format_bytes_per_sample))) { | |
747 memmove(sample_buffer_, sample_buffer_ + sample_buffer_first_ * channels * file_info->sample_format_bytes_per_sample, (sample_buffer_last_ - sample_buffer_first_) * channels * file_info->sample_format_bytes_per_sample); | |
748 sample_buffer_last_ -= sample_buffer_first_; | |
749 sample_buffer_first_ = 0; | |
750 } | |
751 sample_buffer_start = sample_buffer_ + sample_buffer_last_ * channels * file_info->sample_format_bytes_per_sample; | |
752 if(file_info->has_replaygain && flac_cfg.output.replaygain.enable) { | |
753 FLAC__replaygain_synthesis__apply_gain( | |
754 sample_buffer_start, | |
755 !is_big_endian_host_, | |
756 file_info->sample_format_bytes_per_sample == 1, /* unsigned_data_out */ | |
757 buffer, | |
758 wide_samples, | |
759 channels, | |
760 bits_per_sample, | |
761 file_info->sample_format_bytes_per_sample * 8, | |
762 file_info->replay_scale, | |
763 flac_cfg.output.replaygain.hard_limit, | |
764 flac_cfg.output.resolution.replaygain.dither, | |
765 &file_info->dither_context | |
766 ); | |
767 } | |
768 else if(is_big_endian_host_) { | |
769 FLAC__plugin_common__pack_pcm_signed_big_endian( | |
770 sample_buffer_start, | |
771 buffer, | |
772 wide_samples, | |
773 channels, | |
774 bits_per_sample, | |
775 file_info->sample_format_bytes_per_sample * 8 | |
776 ); | |
777 } | |
778 else { | |
779 FLAC__plugin_common__pack_pcm_signed_little_endian( | |
780 sample_buffer_start, | |
781 buffer, | |
782 wide_samples, | |
783 channels, | |
784 bits_per_sample, | |
785 file_info->sample_format_bytes_per_sample * 8 | |
786 ); | |
787 } | |
788 | |
789 sample_buffer_last_ += wide_samples; | |
790 | |
791 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; | |
792 } | |
793 | |
794 void metadata_callback_(const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data) | |
795 { | |
796 file_info_struct *file_info = (file_info_struct *)client_data; | |
797 (void)decoder; | |
798 if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { | |
799 FLAC__ASSERT(metadata->data.stream_info.total_samples < FLAC__U64L(0x100000000)); /* this plugin can only handle < 4 gigasamples */ | |
800 file_info->total_samples = (unsigned)(metadata->data.stream_info.total_samples&0xffffffff); | |
801 file_info->bits_per_sample = metadata->data.stream_info.bits_per_sample; | |
802 file_info->channels = metadata->data.stream_info.channels; | |
803 file_info->sample_rate = metadata->data.stream_info.sample_rate; | |
804 file_info->length_in_msec = (unsigned)((double)file_info->total_samples / (double)file_info->sample_rate * 1000.0 + 0.5); | |
805 } | |
806 else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { | |
807 double gain, peak; | |
808 if(grabbag__replaygain_load_from_vorbiscomment(metadata, flac_cfg.output.replaygain.album_mode, &gain, &peak)) { | |
809 file_info->has_replaygain = true; | |
810 file_info->replay_scale = grabbag__replaygain_compute_scale_factor(peak, gain, (double)flac_cfg.output.replaygain.preamp, /*prevent_clipping=*/!flac_cfg.output.replaygain.hard_limit); | |
811 } | |
812 } | |
813 } | |
814 | |
815 void error_callback_(const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) | |
816 { | |
817 file_info_struct *file_info = (file_info_struct *)client_data; | |
818 (void)decoder; | |
819 if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) | |
820 file_info->abort_flag = true; | |
821 } |