Mercurial > audlegacy-plugins
comparison src/flac/plugin.c @ 722:454ad11020ec trunk
[svn] * Delete flac112
* Rename flac113 -> flac
* Change configure.ac
| author | js |
|---|---|
| date | Sat, 24 Feb 2007 16:17:26 -0800 |
| parents | src/flac113/plugin.c@a9199ee8e5c0 |
| children | 1820b4026fe2 |
comparison
equal
deleted
inserted
replaced
| 721:574de61036a3 | 722:454ad11020ec |
|---|---|
| 1 /* libxmms-flac - XMMS FLAC input plugin | |
| 2 * Copyright (C) 2000,2001,2002,2003,2004,2005,2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
| 17 */ | |
| 18 | |
| 19 #include <limits.h> | |
| 20 #include <stdlib.h> | |
| 21 #include <string.h> | |
| 22 #include <stdio.h> | |
| 23 #include <glib.h> | |
| 24 #include <pwd.h> | |
| 25 #include <sys/types.h> | |
| 26 #include <unistd.h> | |
| 27 | |
| 28 #include <audacious/plugin.h> | |
| 29 #include <audacious/output.h> | |
| 30 #include <audacious/util.h> | |
| 31 #include <audacious/configdb.h> | |
| 32 #include <audacious/titlestring.h> | |
| 33 #include <audacious/vfs.h> | |
| 34 | |
| 35 #ifdef HAVE_CONFIG_H | |
| 36 #include <config.h> | |
| 37 #endif | |
| 38 | |
| 39 #ifdef HAVE_LANGINFO_CODESET | |
| 40 #include <langinfo.h> | |
| 41 #endif | |
| 42 | |
| 43 #include "FLAC/all.h" | |
| 44 #include "plugin_common/all.h" | |
| 45 #include "grabbag.h" | |
| 46 #include "replaygain_synthesis.h" | |
| 47 #include "configure.h" | |
| 48 #include "charset.h" | |
| 49 #include "tag.h" | |
| 50 | |
| 51 #ifdef min | |
| 52 #undef min | |
| 53 #endif | |
| 54 #define min(x,y) ((x)<(y)?(x):(y)) | |
| 55 | |
| 56 extern void FLAC_XMMS__file_info_box(char *filename); | |
| 57 | |
| 58 typedef struct { | |
| 59 FLAC__bool abort_flag; | |
| 60 FLAC__bool is_playing; | |
| 61 FLAC__bool eof; | |
| 62 FLAC__bool play_thread_open; /* if true, is_playing must also be true */ | |
| 63 FLAC__uint64 total_samples; | |
| 64 unsigned bits_per_sample; | |
| 65 unsigned channels; | |
| 66 unsigned sample_rate; | |
| 67 int length_in_msec; /* int (instead of FLAC__uint64) only because that's what XMMS uses; seeking won't work right if this maxes out */ | |
| 68 gchar *title; | |
| 69 AFormat sample_format; | |
| 70 unsigned sample_format_bytes_per_sample; | |
| 71 int seek_to_in_sec; | |
| 72 FLAC__bool has_replaygain; | |
| 73 double replay_scale; | |
| 74 DitherContext dither_context; | |
| 75 VFSFile *vfsfile; | |
| 76 } stream_data_struct; | |
| 77 | |
| 78 static void FLAC_XMMS__init(); | |
| 79 static int FLAC_XMMS__is_our_file(char *filename); | |
| 80 static int FLAC_XMMS__is_our_file_from_vfs(char *filename, VFSFile *vfsfile); | |
| 81 static void FLAC_XMMS__play_file(InputPlayback *playback); | |
| 82 static void FLAC_XMMS__stop(InputPlayback *playback); | |
| 83 static void FLAC_XMMS__pause(InputPlayback *playback, short p); | |
| 84 static void FLAC_XMMS__seek(InputPlayback *playback, int time); | |
| 85 static int FLAC_XMMS__get_time(); | |
| 86 static void FLAC_XMMS__cleanup(); | |
| 87 static void FLAC_XMMS__get_song_info(char *filename, char **title, int *length); | |
| 88 | |
| 89 static void *play_loop_(void *arg); | |
| 90 | |
| 91 static FLAC__bool safe_decoder_init_(char *filename, FLAC__StreamDecoder *decoder); | |
| 92 static void safe_decoder_finish_(FLAC__StreamDecoder *decoder); | |
| 93 static void safe_decoder_delete_(FLAC__StreamDecoder *decoder); | |
| 94 | |
| 95 static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); | |
| 96 static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); | |
| 97 static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); | |
| 98 static FLAC__StreamDecoderReadStatus read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); | |
| 99 static FLAC__StreamDecoderSeekStatus seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); | |
| 100 static FLAC__StreamDecoderTellStatus tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); | |
| 101 static FLAC__StreamDecoderLengthStatus length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); | |
| 102 static FLAC__bool eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data); | |
| 103 | |
| 104 gchar *flac_fmts[] = { "flac", NULL }; | |
| 105 | |
| 106 InputPlugin flac_ip = | |
| 107 { | |
| 108 NULL, | |
| 109 NULL, | |
| 110 NULL, | |
| 111 FLAC_XMMS__init, | |
| 112 FLAC_XMMS__aboutbox, | |
| 113 FLAC_XMMS__configure, | |
| 114 FLAC_XMMS__is_our_file, | |
| 115 NULL, | |
| 116 FLAC_XMMS__play_file, | |
| 117 FLAC_XMMS__stop, | |
| 118 FLAC_XMMS__pause, | |
| 119 FLAC_XMMS__seek, | |
| 120 NULL, | |
| 121 FLAC_XMMS__get_time, | |
| 122 NULL, | |
| 123 NULL, | |
| 124 FLAC_XMMS__cleanup, | |
| 125 NULL, | |
| 126 NULL, | |
| 127 NULL, | |
| 128 NULL, | |
| 129 FLAC_XMMS__get_song_info, | |
| 130 FLAC_XMMS__file_info_box, | |
| 131 NULL, | |
| 132 flac_get_tuple, | |
| 133 NULL, // set tuple | |
| 134 NULL, | |
| 135 FLAC_XMMS__is_our_file_from_vfs, | |
| 136 flac_fmts, | |
| 137 }; | |
| 138 | |
| 139 #define SAMPLES_PER_WRITE 512 | |
| 140 #define SAMPLE_BUFFER_SIZE ((FLAC__MAX_BLOCK_SIZE + SAMPLES_PER_WRITE) * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS * (24/8)) | |
| 141 static FLAC__byte sample_buffer_[SAMPLE_BUFFER_SIZE]; | |
| 142 static unsigned sample_buffer_first_, sample_buffer_last_; | |
| 143 | |
| 144 static FLAC__StreamDecoder *decoder_ = 0, *decoder2 = 0; | |
| 145 static stream_data_struct stream_data_; | |
| 146 static GThread *decode_thread_; | |
| 147 static FLAC__bool audio_error_ = false; | |
| 148 static FLAC__bool is_big_endian_host_; | |
| 149 | |
| 150 #define BITRATE_HIST_SEGMENT_MSEC 500 | |
| 151 /* 500ms * 50 = 25s should be enough */ | |
| 152 #define BITRATE_HIST_SIZE 50 | |
| 153 static unsigned bitrate_history_[BITRATE_HIST_SIZE]; | |
| 154 | |
| 155 #ifdef SUPPORT_ATTRIBUTE_VISIBILITY | |
| 156 InputPlugin *get_iplugin_info() __attribute__((visibility("default"))); | |
| 157 #endif | |
| 158 | |
| 159 InputPlugin *get_iplugin_info() | |
| 160 { | |
| 161 flac_ip.description = g_strdup_printf(_("FLAC Audio Plugin")); | |
| 162 return &flac_ip; | |
| 163 } | |
| 164 | |
| 165 void set_track_info(const char* title, int length_in_msec) | |
| 166 { | |
| 167 if (stream_data_.is_playing) { | |
| 168 flac_ip.set_info((char*) title, length_in_msec, stream_data_.sample_rate * stream_data_.channels * stream_data_.bits_per_sample, stream_data_.sample_rate, stream_data_.channels); | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 static gchar* homedir() | |
| 173 { | |
| 174 gchar *result; | |
| 175 char *env_home = getenv("HOME"); | |
| 176 if (env_home) { | |
| 177 result = g_strdup (env_home); | |
| 178 } else { | |
| 179 uid_t uid = getuid(); | |
| 180 struct passwd *pwent; | |
| 181 do { | |
| 182 pwent = getpwent(); | |
| 183 } while (pwent && pwent->pw_uid != uid); | |
| 184 result = pwent ? g_strdup (pwent->pw_dir) : NULL; | |
| 185 endpwent(); | |
| 186 } | |
| 187 return result; | |
| 188 } | |
| 189 | |
| 190 void FLAC_XMMS__init() | |
| 191 { | |
| 192 ConfigDb *db; | |
| 193 FLAC__uint32 test = 1; | |
| 194 gchar *tmp = NULL; | |
| 195 | |
| 196 is_big_endian_host_ = (*((FLAC__byte*)(&test)))? false : true; | |
| 197 | |
| 198 flac_cfg.title.tag_override = FALSE; | |
| 199 if (flac_cfg.title.tag_format) | |
| 200 g_free(flac_cfg.title.tag_format); | |
| 201 flac_cfg.title.convert_char_set = FALSE; | |
| 202 | |
| 203 db = bmp_cfg_db_open(); | |
| 204 | |
| 205 /* title */ | |
| 206 | |
| 207 bmp_cfg_db_get_bool(db, "flac", "title.tag_override", &flac_cfg.title.tag_override); | |
| 208 | |
| 209 bmp_cfg_db_get_bool(db, "flac", "title.disable_bitrate_update", &flac_cfg.title.disable_bitrate_update); | |
| 210 | |
| 211 if(!bmp_cfg_db_get_string(db, "flac", "title.tag_format", &flac_cfg.title.tag_format)) | |
| 212 flac_cfg.title.tag_format = g_strdup("%p - %t"); | |
| 213 | |
| 214 bmp_cfg_db_get_bool(db, "flac", "title.convert_char_set", &flac_cfg.title.convert_char_set); | |
| 215 | |
| 216 if(!bmp_cfg_db_get_string(db, "flac", "title.user_char_set", &flac_cfg.title.user_char_set)) | |
| 217 flac_cfg.title.user_char_set = FLAC_plugin__charset_get_current(); | |
| 218 | |
| 219 /* replaygain */ | |
| 220 | |
| 221 bmp_cfg_db_get_bool(db, "flac", "output.replaygain.enable", &flac_cfg.output.replaygain.enable); | |
| 222 | |
| 223 bmp_cfg_db_get_bool(db, "flac", "output.replaygain.album_mode", &flac_cfg.output.replaygain.album_mode); | |
| 224 | |
| 225 if(!bmp_cfg_db_get_int(db, "flac", "output.replaygain.preamp", &flac_cfg.output.replaygain.preamp)) | |
| 226 flac_cfg.output.replaygain.preamp = 0; | |
| 227 | |
| 228 bmp_cfg_db_get_bool(db, "flac", "output.replaygain.hard_limit", &flac_cfg.output.replaygain.hard_limit); | |
| 229 | |
| 230 bmp_cfg_db_get_bool(db, "flac", "output.resolution.normal.dither_24_to_16", &flac_cfg.output.resolution.normal.dither_24_to_16); | |
| 231 bmp_cfg_db_get_bool(db, "flac", "output.resolution.replaygain.dither", &flac_cfg.output.resolution.replaygain.dither); | |
| 232 | |
| 233 if(!bmp_cfg_db_get_int(db, "flac", "output.resolution.replaygain.noise_shaping", &flac_cfg.output.resolution.replaygain.noise_shaping)) | |
| 234 flac_cfg.output.resolution.replaygain.noise_shaping = 1; | |
| 235 | |
| 236 if(!bmp_cfg_db_get_int(db, "flac", "output.resolution.replaygain.bps_out", &flac_cfg.output.resolution.replaygain.bps_out)) | |
| 237 flac_cfg.output.resolution.replaygain.bps_out = 16; | |
| 238 | |
| 239 bmp_cfg_db_close(db); | |
| 240 decoder_ = FLAC__stream_decoder_new(); | |
| 241 | |
| 242 stream_data_.vfsfile = NULL; | |
| 243 } | |
| 244 | |
| 245 int FLAC_XMMS__is_our_file_from_vfs( gchar * filename , VFSFile * vfsfile ) | |
| 246 { | |
| 247 gchar magic_bytes[4]; | |
| 248 | |
| 249 if ( vfsfile == NULL ) | |
| 250 return 0; | |
| 251 | |
| 252 if ( vfs_fread( magic_bytes , 1 , 4 , vfsfile ) != 4 ) | |
| 253 return 0; | |
| 254 | |
| 255 if ( !strncmp( magic_bytes , "fLaC" , 4 ) ) | |
| 256 return 1; | |
| 257 else | |
| 258 return 0; | |
| 259 } | |
| 260 | |
| 261 int FLAC_XMMS__is_our_file(char *filename) | |
| 262 { | |
| 263 VFSFile *vfsfile; | |
| 264 gint result = 0; | |
| 265 | |
| 266 vfsfile = vfs_fopen( filename , "rb" ); | |
| 267 | |
| 268 if ( vfsfile == NULL ) return 0; | |
| 269 | |
| 270 result = FLAC_XMMS__is_our_file_from_vfs( filename , vfsfile ); | |
| 271 | |
| 272 vfs_fclose( vfsfile ); | |
| 273 | |
| 274 return result; | |
| 275 } | |
| 276 | |
| 277 void FLAC_XMMS__play_file(InputPlayback *playback) | |
| 278 { | |
| 279 char *filename = playback->filename; | |
| 280 sample_buffer_first_ = sample_buffer_last_ = 0; | |
| 281 audio_error_ = false; | |
| 282 stream_data_.abort_flag = false; | |
| 283 stream_data_.is_playing = false; | |
| 284 stream_data_.eof = false; | |
| 285 stream_data_.play_thread_open = false; | |
| 286 stream_data_.has_replaygain = false; | |
| 287 | |
| 288 if(decoder_ == 0) | |
| 289 return; | |
| 290 | |
| 291 if(!safe_decoder_init_(filename, decoder_)) | |
| 292 return; | |
| 293 | |
| 294 if(stream_data_.has_replaygain && flac_cfg.output.replaygain.enable) { | |
| 295 if(flac_cfg.output.resolution.replaygain.bps_out == 8) { | |
| 296 stream_data_.sample_format = FMT_U8; | |
| 297 stream_data_.sample_format_bytes_per_sample = 1; | |
| 298 } | |
| 299 else if(flac_cfg.output.resolution.replaygain.bps_out == 16) { | |
| 300 stream_data_.sample_format = (is_big_endian_host_) ? FMT_S16_BE : FMT_S16_LE; | |
| 301 stream_data_.sample_format_bytes_per_sample = 2; | |
| 302 } | |
| 303 else { | |
| 304 /*@@@ 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); */ | |
| 305 fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", flac_cfg.output.resolution.replaygain.bps_out); | |
| 306 safe_decoder_finish_(decoder_); | |
| 307 return; | |
| 308 } | |
| 309 } | |
| 310 else { | |
| 311 if(stream_data_.bits_per_sample == 8) { | |
| 312 stream_data_.sample_format = FMT_U8; | |
| 313 stream_data_.sample_format_bytes_per_sample = 1; | |
| 314 } | |
| 315 else if(stream_data_.bits_per_sample == 16 || (stream_data_.bits_per_sample == 24 && flac_cfg.output.resolution.normal.dither_24_to_16)) { | |
| 316 stream_data_.sample_format = (is_big_endian_host_) ? FMT_S16_BE : FMT_S16_LE; | |
| 317 stream_data_.sample_format_bytes_per_sample = 2; | |
| 318 } | |
| 319 else { | |
| 320 /*@@@ 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); */ | |
| 321 fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", stream_data_.bits_per_sample); | |
| 322 safe_decoder_finish_(decoder_); | |
| 323 return; | |
| 324 } | |
| 325 } | |
| 326 FLAC__replaygain_synthesis__init_dither_context(&stream_data_.dither_context, stream_data_.sample_format_bytes_per_sample * 8, flac_cfg.output.resolution.replaygain.noise_shaping); | |
| 327 stream_data_.is_playing = true; | |
| 328 | |
| 329 if(playback->output->open_audio(stream_data_.sample_format, stream_data_.sample_rate, stream_data_.channels) == 0) { | |
| 330 audio_error_ = true; | |
| 331 safe_decoder_finish_(decoder_); | |
| 332 return; | |
| 333 } | |
| 334 | |
| 335 stream_data_.title = flac_format_song_title(filename); | |
| 336 flac_ip.set_info(stream_data_.title, stream_data_.length_in_msec, stream_data_.sample_rate * stream_data_.channels * stream_data_.bits_per_sample, stream_data_.sample_rate, stream_data_.channels); | |
| 337 | |
| 338 stream_data_.seek_to_in_sec = -1; | |
| 339 stream_data_.play_thread_open = true; | |
| 340 decode_thread_ = g_thread_create((GThreadFunc)play_loop_, playback, TRUE, NULL); | |
| 341 } | |
| 342 | |
| 343 void FLAC_XMMS__stop(InputPlayback *playback) | |
| 344 { | |
| 345 if(stream_data_.is_playing) { | |
| 346 stream_data_.is_playing = false; | |
| 347 if(stream_data_.play_thread_open) { | |
| 348 stream_data_.play_thread_open = false; | |
| 349 g_thread_join(decode_thread_); | |
| 350 } | |
| 351 playback->output->close_audio(); | |
| 352 safe_decoder_finish_(decoder_); | |
| 353 } | |
| 354 } | |
| 355 | |
| 356 void FLAC_XMMS__pause(InputPlayback *playback, short p) | |
| 357 { | |
| 358 playback->output->pause(p); | |
| 359 } | |
| 360 | |
| 361 void FLAC_XMMS__seek(InputPlayback *playback, int time) | |
| 362 { | |
| 363 stream_data_.seek_to_in_sec = time; | |
| 364 stream_data_.eof = false; | |
| 365 | |
| 366 while(stream_data_.seek_to_in_sec != -1) | |
| 367 xmms_usleep(10000); | |
| 368 } | |
| 369 | |
| 370 int FLAC_XMMS__get_time(InputPlayback *playback) | |
| 371 { | |
| 372 if(audio_error_) | |
| 373 return -2; | |
| 374 if(!stream_data_.is_playing || (stream_data_.eof && !playback->output->buffer_playing())) | |
| 375 return -1; | |
| 376 else | |
| 377 return playback->output->output_time(); | |
| 378 } | |
| 379 | |
| 380 void FLAC_XMMS__cleanup() | |
| 381 { | |
| 382 g_free(flac_ip.description); | |
| 383 flac_ip.description = NULL; | |
| 384 | |
| 385 if (flac_cfg.title.tag_format) { | |
| 386 g_free(flac_cfg.title.tag_format); | |
| 387 flac_cfg.title.tag_format = NULL; | |
| 388 } | |
| 389 | |
| 390 if (flac_cfg.title.user_char_set) { | |
| 391 g_free(flac_cfg.title.user_char_set); | |
| 392 flac_cfg.title.user_char_set = NULL; | |
| 393 } | |
| 394 | |
| 395 safe_decoder_delete_(decoder_); | |
| 396 decoder_ = 0; | |
| 397 } | |
| 398 | |
| 399 void FLAC_XMMS__get_song_info(char *filename, char **title, int *length_in_msec) | |
| 400 { | |
| 401 FLAC__StreamMetadata streaminfo; | |
| 402 | |
| 403 /* NOTE vfs is not used here, so only try | |
| 404 to pick tags if you can do it with flac library stdio */ | |
| 405 if ( strncmp(filename,"/",1) ) | |
| 406 { | |
| 407 *title = g_strdup(filename); | |
| 408 *length_in_msec = -1; | |
| 409 return; | |
| 410 } | |
| 411 | |
| 412 if(!FLAC__metadata_get_streaminfo(filename, &streaminfo)) { | |
| 413 /* @@@ how to report the error? */ | |
| 414 if(title) { | |
| 415 static const char *errtitle = "Invalid FLAC File: "; | |
| 416 *title = g_malloc(strlen(errtitle) + 1 + strlen(filename) + 1 + 1); | |
| 417 sprintf(*title, "%s\"%s\"", errtitle, filename); | |
| 418 } | |
| 419 if(length_in_msec) | |
| 420 *length_in_msec = -1; | |
| 421 return; | |
| 422 } | |
| 423 | |
| 424 if(title) { | |
| 425 *title = flac_format_song_title(filename); | |
| 426 } | |
| 427 if(length_in_msec) { | |
| 428 FLAC__uint64 l = (FLAC__uint64)((double)streaminfo.data.stream_info.total_samples / (double)streaminfo.data.stream_info.sample_rate * 1000.0 + 0.5); | |
| 429 if (l > INT_MAX) | |
| 430 l = INT_MAX; | |
| 431 *length_in_msec = (int)l; | |
| 432 } | |
| 433 } | |
| 434 | |
| 435 /*********************************************************************** | |
| 436 * local routines | |
| 437 **********************************************************************/ | |
| 438 | |
| 439 static void *play_loop_(void *arg) | |
| 440 { | |
| 441 InputPlayback *playback = arg; | |
| 442 unsigned written_time_last = 0, bh_index_last_w = 0, bh_index_last_o = BITRATE_HIST_SIZE, blocksize = 1; | |
| 443 FLAC__uint64 decode_position_last = 0, decode_position_frame_last = 0, decode_position_frame = 0; | |
| 444 | |
| 445 while(stream_data_.is_playing) { | |
| 446 if(!stream_data_.eof) { | |
| 447 while(sample_buffer_last_ - sample_buffer_first_ < SAMPLES_PER_WRITE) { | |
| 448 unsigned s; | |
| 449 | |
| 450 s = sample_buffer_last_ - sample_buffer_first_; | |
| 451 if(FLAC__stream_decoder_get_state(decoder_) == FLAC__STREAM_DECODER_END_OF_STREAM) { | |
| 452 stream_data_.eof = true; | |
| 453 break; | |
| 454 } | |
| 455 else if(!FLAC__stream_decoder_process_single(decoder_)) { | |
| 456 /*@@@ this should probably be a dialog */ | |
| 457 fprintf(stderr, "libxmms-flac: READ ERROR processing frame\n"); | |
| 458 stream_data_.eof = true; | |
| 459 break; | |
| 460 } | |
| 461 blocksize = sample_buffer_last_ - sample_buffer_first_ - s; | |
| 462 decode_position_frame_last = decode_position_frame; | |
| 463 if(!FLAC__stream_decoder_get_decode_position(decoder_, &decode_position_frame)) | |
| 464 decode_position_frame = 0; | |
| 465 } | |
| 466 if(sample_buffer_last_ - sample_buffer_first_ > 0) { | |
| 467 const unsigned n = min(sample_buffer_last_ - sample_buffer_first_, SAMPLES_PER_WRITE); | |
| 468 int bytes = n * stream_data_.channels * stream_data_.sample_format_bytes_per_sample; | |
| 469 FLAC__byte *sample_buffer_start = sample_buffer_ + sample_buffer_first_ * stream_data_.channels * stream_data_.sample_format_bytes_per_sample; | |
| 470 unsigned written_time, bh_index_w; | |
| 471 FLAC__uint64 decode_position; | |
| 472 | |
| 473 sample_buffer_first_ += n; | |
| 474 while(playback->output->buffer_free() < (int)bytes && stream_data_.is_playing && stream_data_.seek_to_in_sec == -1) | |
| 475 xmms_usleep(10000); | |
| 476 if(stream_data_.is_playing && stream_data_.seek_to_in_sec == -1) | |
| 477 produce_audio(playback->output->written_time(), stream_data_.sample_format, | |
| 478 stream_data_.channels, bytes, sample_buffer_start, NULL); | |
| 479 | |
| 480 /* compute current bitrate */ | |
| 481 | |
| 482 written_time = playback->output->written_time(); | |
| 483 bh_index_w = written_time / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; | |
| 484 if(bh_index_w != bh_index_last_w) { | |
| 485 bh_index_last_w = bh_index_w; | |
| 486 decode_position = decode_position_frame - (double)(sample_buffer_last_ - sample_buffer_first_) * (double)(decode_position_frame - decode_position_frame_last) / (double)blocksize; | |
| 487 bitrate_history_[(bh_index_w + BITRATE_HIST_SIZE - 1) % BITRATE_HIST_SIZE] = | |
| 488 decode_position > decode_position_last && written_time > written_time_last ? | |
| 489 8000 * (decode_position - decode_position_last) / (written_time - written_time_last) : | |
| 490 stream_data_.sample_rate * stream_data_.channels * stream_data_.bits_per_sample; | |
| 491 decode_position_last = decode_position; | |
| 492 written_time_last = written_time; | |
| 493 } | |
| 494 } | |
| 495 else { | |
| 496 stream_data_.eof = true; | |
| 497 xmms_usleep(10000); | |
| 498 } | |
| 499 } | |
| 500 else | |
| 501 xmms_usleep(10000); | |
| 502 if(stream_data_.seek_to_in_sec != -1) { | |
| 503 const double distance = (double)stream_data_.seek_to_in_sec * 1000.0 / (double)stream_data_.length_in_msec; | |
| 504 FLAC__uint64 target_sample = (FLAC__uint64)(distance * (double)stream_data_.total_samples); | |
| 505 if(stream_data_.total_samples > 0 && target_sample >= stream_data_.total_samples) | |
| 506 target_sample = stream_data_.total_samples - 1; | |
| 507 if(FLAC__stream_decoder_seek_absolute(decoder_, target_sample)) { | |
| 508 playback->output->flush(stream_data_.seek_to_in_sec * 1000); | |
| 509 bh_index_last_w = bh_index_last_o = playback->output->output_time() / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; | |
| 510 if(!FLAC__stream_decoder_get_decode_position(decoder_, &decode_position_frame)) | |
| 511 decode_position_frame = 0; | |
| 512 stream_data_.eof = false; | |
| 513 sample_buffer_first_ = sample_buffer_last_ = 0; | |
| 514 } | |
| 515 else if(FLAC__stream_decoder_get_state(decoder_) == FLAC__STREAM_DECODER_SEEK_ERROR) { | |
| 516 /*@@@ this should probably be a dialog */ | |
| 517 fprintf(stderr, "libxmms-flac: SEEK ERROR\n"); | |
| 518 FLAC__stream_decoder_flush(decoder_); | |
| 519 stream_data_.eof = false; | |
| 520 sample_buffer_first_ = sample_buffer_last_ = 0; | |
| 521 } | |
| 522 stream_data_.seek_to_in_sec = -1; | |
| 523 } | |
| 524 else if ( !flac_cfg.title.disable_bitrate_update ) | |
| 525 { | |
| 526 /* display the right bitrate from history */ | |
| 527 unsigned bh_index_o = playback->output->output_time() / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; | |
| 528 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) { | |
| 529 bh_index_last_o = bh_index_o; | |
| 530 flac_ip.set_info(stream_data_.title, stream_data_.length_in_msec, bitrate_history_[bh_index_o], stream_data_.sample_rate, stream_data_.channels); | |
| 531 } | |
| 532 } | |
| 533 } | |
| 534 | |
| 535 safe_decoder_finish_(decoder_); | |
| 536 | |
| 537 /* are these two calls necessary? */ | |
| 538 playback->output->buffer_free(); | |
| 539 playback->output->buffer_free(); | |
| 540 | |
| 541 g_free(stream_data_.title); | |
| 542 | |
| 543 g_thread_exit(NULL); | |
| 544 return 0; /* to silence the compiler warning about not returning a value */ | |
| 545 } | |
| 546 | |
| 547 static FLAC__bool safe_decoder_init_(char *filename, FLAC__StreamDecoder *decoder) | |
| 548 { | |
| 549 if(decoder == 0) | |
| 550 return false; | |
| 551 | |
| 552 safe_decoder_finish_(decoder); | |
| 553 | |
| 554 if ( ( stream_data_.vfsfile = vfs_fopen( filename , "rb" ) ) == NULL ) | |
| 555 return false; | |
| 556 | |
| 557 FLAC__stream_decoder_set_md5_checking(decoder, false); | |
| 558 FLAC__stream_decoder_set_metadata_ignore_all(decoder); | |
| 559 FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO); | |
| 560 FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); | |
| 561 | |
| 562 if ( FLAC__stream_decoder_init_stream(decoder, read_callback_, seek_callback_, | |
| 563 tell_callback_, length_callback_, eof_callback_, write_callback_, metadata_callback_, | |
| 564 error_callback_, /*client_data=*/&stream_data_) != FLAC__STREAM_DECODER_INIT_STATUS_OK ) | |
| 565 { | |
| 566 vfs_fclose( stream_data_.vfsfile ); | |
| 567 return false; | |
| 568 } | |
| 569 | |
| 570 if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) | |
| 571 { | |
| 572 vfs_fclose( stream_data_.vfsfile ); | |
| 573 return false; | |
| 574 } | |
| 575 | |
| 576 return true; | |
| 577 } | |
| 578 | |
| 579 static void safe_decoder_finish_(FLAC__StreamDecoder *decoder) | |
| 580 { | |
| 581 if ( stream_data_.vfsfile != NULL ) | |
| 582 { | |
| 583 vfs_fclose( stream_data_.vfsfile ); | |
| 584 stream_data_.vfsfile = NULL; | |
| 585 } | |
| 586 if(decoder && FLAC__stream_decoder_get_state(decoder) != FLAC__STREAM_DECODER_UNINITIALIZED) | |
| 587 FLAC__stream_decoder_finish(decoder); | |
| 588 } | |
| 589 | |
| 590 static void safe_decoder_delete_(FLAC__StreamDecoder *decoder) | |
| 591 { | |
| 592 if(decoder) { | |
| 593 safe_decoder_finish_(decoder); | |
| 594 FLAC__stream_decoder_delete(decoder); | |
| 595 } | |
| 596 } | |
| 597 | |
| 598 FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) | |
| 599 { | |
| 600 stream_data_struct *stream_data = (stream_data_struct *)client_data; | |
| 601 const unsigned channels = stream_data->channels, wide_samples = frame->header.blocksize; | |
| 602 const unsigned bits_per_sample = stream_data->bits_per_sample; | |
| 603 FLAC__byte *sample_buffer_start; | |
| 604 | |
| 605 (void)decoder; | |
| 606 | |
| 607 if(stream_data->abort_flag) | |
| 608 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; | |
| 609 | |
| 610 if((sample_buffer_last_ + wide_samples) > (SAMPLE_BUFFER_SIZE / (channels * stream_data->sample_format_bytes_per_sample))) { | |
| 611 memmove(sample_buffer_, sample_buffer_ + sample_buffer_first_ * channels * stream_data->sample_format_bytes_per_sample, (sample_buffer_last_ - sample_buffer_first_) * channels * stream_data->sample_format_bytes_per_sample); | |
| 612 sample_buffer_last_ -= sample_buffer_first_; | |
| 613 sample_buffer_first_ = 0; | |
| 614 } | |
| 615 sample_buffer_start = sample_buffer_ + sample_buffer_last_ * channels * stream_data->sample_format_bytes_per_sample; | |
| 616 if(stream_data->has_replaygain && flac_cfg.output.replaygain.enable) { | |
| 617 FLAC__replaygain_synthesis__apply_gain( | |
| 618 sample_buffer_start, | |
| 619 !is_big_endian_host_, | |
| 620 stream_data->sample_format_bytes_per_sample == 1, /* unsigned_data_out */ | |
| 621 buffer, | |
| 622 wide_samples, | |
| 623 channels, | |
| 624 bits_per_sample, | |
| 625 stream_data->sample_format_bytes_per_sample * 8, | |
| 626 stream_data->replay_scale, | |
| 627 flac_cfg.output.replaygain.hard_limit, | |
| 628 flac_cfg.output.resolution.replaygain.dither, | |
| 629 &stream_data->dither_context | |
| 630 ); | |
| 631 } | |
| 632 else if(is_big_endian_host_) { | |
| 633 FLAC__plugin_common__pack_pcm_signed_big_endian( | |
| 634 sample_buffer_start, | |
| 635 buffer, | |
| 636 wide_samples, | |
| 637 channels, | |
| 638 bits_per_sample, | |
| 639 stream_data->sample_format_bytes_per_sample * 8 | |
| 640 ); | |
| 641 } | |
| 642 else { | |
| 643 FLAC__plugin_common__pack_pcm_signed_little_endian( | |
| 644 sample_buffer_start, | |
| 645 buffer, | |
| 646 wide_samples, | |
| 647 channels, | |
| 648 bits_per_sample, | |
| 649 stream_data->sample_format_bytes_per_sample * 8 | |
| 650 ); | |
| 651 } | |
| 652 | |
| 653 sample_buffer_last_ += wide_samples; | |
| 654 | |
| 655 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; | |
| 656 } | |
| 657 | |
| 658 static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) | |
| 659 { | |
| 660 stream_data_struct *stream_data = (stream_data_struct *)client_data; | |
| 661 (void)decoder; | |
| 662 if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { | |
| 663 stream_data->total_samples = metadata->data.stream_info.total_samples; | |
| 664 stream_data->bits_per_sample = metadata->data.stream_info.bits_per_sample; | |
| 665 stream_data->channels = metadata->data.stream_info.channels; | |
| 666 stream_data->sample_rate = metadata->data.stream_info.sample_rate; | |
| 667 { | |
| 668 FLAC__uint64 l = (FLAC__uint64)((double)stream_data->total_samples / (double)stream_data->sample_rate * 1000.0 + 0.5); | |
| 669 if (l > INT_MAX) | |
| 670 l = INT_MAX; | |
| 671 stream_data->length_in_msec = (int)l; | |
| 672 } | |
| 673 } | |
| 674 else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { | |
| 675 double reference, gain, peak; | |
| 676 if(grabbag__replaygain_load_from_vorbiscomment(metadata, flac_cfg.output.replaygain.album_mode, /*strict=*/false, &reference, &gain, &peak)) { | |
| 677 stream_data->has_replaygain = true; | |
| 678 stream_data->replay_scale = grabbag__replaygain_compute_scale_factor(peak, gain, (double)flac_cfg.output.replaygain.preamp, /*prevent_clipping=*/!flac_cfg.output.replaygain.hard_limit); | |
| 679 } | |
| 680 } | |
| 681 } | |
| 682 | |
| 683 static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) | |
| 684 { | |
| 685 stream_data_struct *stream_data = (stream_data_struct *)client_data; | |
| 686 (void)decoder; | |
| 687 if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) | |
| 688 stream_data->abort_flag = true; | |
| 689 } | |
| 690 | |
| 691 static FLAC__StreamDecoderReadStatus read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) | |
| 692 { | |
| 693 stream_data_struct *stream_data = (stream_data_struct *)client_data; | |
| 694 (void)decoder; | |
| 695 | |
| 696 if( *bytes > 0 ) | |
| 697 { | |
| 698 *bytes = vfs_fread( buffer , sizeof(FLAC__byte) , *bytes , stream_data->vfsfile ); | |
| 699 if ( *bytes == 0 ) | |
| 700 return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; | |
| 701 else | |
| 702 return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; | |
| 703 } | |
| 704 else | |
| 705 return FLAC__STREAM_DECODER_READ_STATUS_ABORT; | |
| 706 } | |
| 707 | |
| 708 static FLAC__StreamDecoderSeekStatus seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) | |
| 709 { | |
| 710 stream_data_struct *stream_data = (stream_data_struct *)client_data; | |
| 711 (void)decoder; | |
| 712 | |
| 713 if ( vfs_fseek( stream_data->vfsfile , (glong)absolute_byte_offset , SEEK_SET ) < 0 ) | |
| 714 return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; | |
| 715 else | |
| 716 return FLAC__STREAM_DECODER_SEEK_STATUS_OK; | |
| 717 } | |
| 718 | |
| 719 static FLAC__StreamDecoderTellStatus tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) | |
| 720 { | |
| 721 stream_data_struct *stream_data = (stream_data_struct *)client_data; | |
| 722 glong pos; | |
| 723 (void)decoder; | |
| 724 | |
| 725 if ( (pos = vfs_ftell(stream_data->vfsfile)) < 0 ) | |
| 726 return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; | |
| 727 else | |
| 728 { | |
| 729 *absolute_byte_offset = (FLAC__uint64)pos; | |
| 730 return FLAC__STREAM_DECODER_TELL_STATUS_OK; | |
| 731 } | |
| 732 } | |
| 733 | |
| 734 static FLAC__StreamDecoderLengthStatus length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) | |
| 735 { | |
| 736 stream_data_struct *stream_data = (stream_data_struct *)client_data; | |
| 737 glong current_pos = 0; | |
| 738 glong length = 0; | |
| 739 (void)decoder; | |
| 740 | |
| 741 current_pos = vfs_ftell(stream_data->vfsfile); | |
| 742 if ( current_pos < 0 ) | |
| 743 return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; | |
| 744 | |
| 745 if ( vfs_fseek( stream_data->vfsfile , 0 , SEEK_END ) < 0 ) | |
| 746 return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; | |
| 747 | |
| 748 length = vfs_ftell(stream_data->vfsfile); | |
| 749 if ( length < 0 ) | |
| 750 return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; | |
| 751 | |
| 752 /* put back stream position */ | |
| 753 if ( vfs_fseek( stream_data->vfsfile , current_pos , SEEK_SET ) < 0 ) | |
| 754 return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; | |
| 755 else | |
| 756 { | |
| 757 *stream_length = (FLAC__uint64)length; | |
| 758 return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; | |
| 759 } | |
| 760 } | |
| 761 | |
| 762 static FLAC__bool eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data) | |
| 763 { | |
| 764 stream_data_struct *stream_data = (stream_data_struct *)client_data; | |
| 765 (void)decoder; | |
| 766 | |
| 767 return vfs_feof( stream_data->vfsfile ); | |
| 768 } |
