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