Mercurial > audlegacy-plugins
annotate src/musepack/libmpc.cxx @ 472:69b8baad0b24 trunk
[svn] - stuff from vfs_get_metadata is already strdup'd
author | nenolod |
---|---|
date | Sun, 21 Jan 2007 02:16:17 -0800 |
parents | a157306caf03 |
children | 1e987b380776 |
rev | line source |
---|---|
232 | 1 #include "libmpc.h" |
2 | |
3 #define FORCED_THREAD_STACKSIZE 1024 * 1000 | |
4 #define REMOVE_NONEXISTANT_TAG(x) if (!*x) { x = NULL; } | |
5 | |
6 using TagLib::MPC::File; | |
7 using TagLib::Tag; | |
8 using TagLib::String; | |
9 using TagLib::APE::ItemListMap; | |
10 | |
372
a157306caf03
[svn] - finalize the plugin-side of the extension-assist ABI
nenolod
parents:
368
diff
changeset
|
11 gchar *mpc_fmts[] = { "mpc", NULL }; |
a157306caf03
[svn] - finalize the plugin-side of the extension-assist ABI
nenolod
parents:
368
diff
changeset
|
12 |
232 | 13 InputPlugin MpcPlugin = { |
14 NULL, //File Handle FILE* handle | |
15 NULL, //Filename char* filename | |
16 NULL, //Name of Plugin char* filename | |
17 mpcOpenPlugin, //Open Plugin [CALLBACK] | |
18 mpcAboutBox, //Show About box [CALLBACK] | |
19 mpcConfigBox, //Show Configure box [CALLBACK] | |
260
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
20 mpcIsOurFile, //Check if it's our file [CALLBACK] |
232 | 21 NULL, //Scan the directory [UNUSED] |
22 mpcPlay, //Play [CALLBACK] | |
23 mpcStop, //Stop [CALLBACK] | |
24 mpcPause, //Pause [CALLBACK] | |
25 mpcSeek, //Seek [CALLBACK] | |
26 NULL, //Set EQ [CALLBACK] | |
27 mpcGetTime, //Get Time [CALLBACK] | |
28 NULL, //Get Volume [UNUSED] | |
29 NULL, //Set Volume [UNUSED] | |
30 NULL, //Close Plugin [UNUSED] | |
31 NULL, //Obsolete [UNUSED] | |
32 NULL, //Visual plugins add_vis_pcm(int time, AFormat fmt, int nch, int length, void *ptr) | |
33 NULL, //Set Info Settings set_info(char *title, int length, int rate, int freq, int nch) | |
34 NULL, //set Info Text set_info_text(char* text) | |
35 mpcGetSongInfo, //Get Title String callback [CALLBACK] | |
36 mpcFileInfoBox, //Show File Info Box [CALLBACK] | |
37 NULL, //Output Plugin Handle OutputPlugin output | |
254 | 38 mpcGetSongTuple,//Acquire tuple for song [CALLBACK] |
39 NULL, | |
40 NULL, | |
41 mpcIsOurFD, | |
372
a157306caf03
[svn] - finalize the plugin-side of the extension-assist ABI
nenolod
parents:
368
diff
changeset
|
42 mpc_fmts |
232 | 43 }; |
44 | |
45 extern "C" | |
46 InputPlugin* get_iplugin_info() | |
47 { | |
48 MpcPlugin.description = g_strdup_printf("Musepack Audio Plugin"); | |
49 return &MpcPlugin; | |
50 } | |
51 | |
52 static PluginConfig pluginConfig = {0}; | |
53 static Widgets widgets = {0}; | |
54 static MpcDecoder mpcDecoder = {0}; | |
55 static TrackInfo track = {0}; | |
56 | |
57 static GThread *threadHandle; | |
58 GStaticMutex threadMutex = G_STATIC_MUTEX_INIT; | |
59 | |
60 /* | |
61 * VFS callback implementation, adapted from mpc_reader.c. | |
62 * This _IS_ very sick, but it works. -nenolod | |
63 */ | |
64 static mpc_int32_t | |
65 vfs_fread_impl(void *data, void *ptr, mpc_int32_t size) | |
66 { | |
67 mpc_reader_file *d = (mpc_reader_file *) data; | |
68 VFSFile *file = (VFSFile *) d->file; | |
69 | |
70 return (mpc_int32_t) vfs_fread(ptr, 1, size, file); | |
71 } | |
72 | |
73 static mpc_bool_t | |
74 vfs_fseek_impl(void *data, mpc_int32_t offset) | |
75 { | |
76 mpc_reader_file *d = (mpc_reader_file *) data; | |
77 VFSFile *file = (VFSFile *) d->file; | |
78 | |
79 return d->is_seekable ? vfs_fseek(file, offset, SEEK_SET) == 0 : FALSE; | |
80 } | |
81 | |
82 static mpc_int32_t | |
83 vfs_ftell_impl(void *data) | |
84 { | |
85 mpc_reader_file *d = (mpc_reader_file *) data; | |
86 VFSFile *file = (VFSFile *) d->file; | |
87 | |
88 return vfs_ftell(file); | |
89 } | |
90 | |
91 static mpc_int32_t | |
92 vfs_getsize_impl(void *data) | |
93 { | |
94 mpc_reader_file *d = (mpc_reader_file *) data; | |
95 | |
96 return d->file_size; | |
97 } | |
98 | |
99 static mpc_bool_t | |
100 vfs_canseek_impl(void *data) | |
101 { | |
102 mpc_reader_file *d = (mpc_reader_file *) data; | |
103 | |
104 return d->is_seekable; | |
105 } | |
106 | |
107 /* | |
108 * This sets up an mpc_reader_file object to read from VFS instead of libc. | |
109 * Essentially, we use this instead of the normal constructor. | |
110 * - nenolod | |
111 */ | |
112 void | |
113 mpc_reader_setup_file_vfs(mpc_reader_file *p_reader, VFSFile *input) | |
114 { | |
115 p_reader->reader.seek = vfs_fseek_impl; | |
116 p_reader->reader.read = vfs_fread_impl; | |
117 p_reader->reader.tell = vfs_ftell_impl; | |
118 p_reader->reader.get_size = vfs_getsize_impl; | |
119 p_reader->reader.canseek = vfs_canseek_impl; | |
120 p_reader->reader.data = p_reader; | |
121 | |
122 p_reader->file = (FILE *) input; // no worries, it gets cast back -nenolod | |
123 p_reader->is_seekable = TRUE; // XXX streams | |
124 | |
125 vfs_fseek(input, 0, SEEK_END); | |
126 p_reader->file_size = vfs_ftell(input); | |
127 vfs_fseek(input, 0, SEEK_SET); | |
128 } | |
129 | |
130 static void mpcOpenPlugin() | |
131 { | |
132 ConfigDb *cfg; | |
133 cfg = bmp_cfg_db_open(); | |
134 bmp_cfg_db_get_bool(cfg, "musepack", "clipPrevention", &pluginConfig.clipPrevention); | |
135 bmp_cfg_db_get_bool(cfg, "musepack", "albumGain", &pluginConfig.albumGain); | |
136 bmp_cfg_db_get_bool(cfg, "musepack", "dynamicBitrate", &pluginConfig.dynamicBitrate); | |
137 bmp_cfg_db_get_bool(cfg, "musepack", "replaygain", &pluginConfig.replaygain); | |
138 bmp_cfg_db_close(cfg); | |
139 } | |
140 | |
141 static void mpcAboutBox() | |
142 { | |
143 GtkWidget* aboutBox = widgets.aboutBox; | |
144 if (aboutBox) | |
145 gdk_window_raise(aboutBox->window); | |
146 else | |
147 { | |
148 char* titleText = g_strdup_printf("Musepack Decoder Plugin 1.2"); | |
149 char* contentText = "Plugin code by\nBenoit Amiaux\nMartin Spuler\nKuniklo\n\nGet latest version at http://musepack.net\n"; | |
150 char* buttonText = "Nevermind"; | |
151 aboutBox = xmms_show_message(titleText, contentText, buttonText, FALSE, NULL, NULL); | |
152 widgets.aboutBox = aboutBox; | |
153 g_signal_connect(G_OBJECT(aboutBox), "destroy", G_CALLBACK(gtk_widget_destroyed), &widgets.aboutBox); | |
154 } | |
155 } | |
156 | |
157 static void mpcConfigBox() | |
158 { | |
159 GtkWidget* configBox = widgets.configBox; | |
160 if(configBox) | |
161 gdk_window_raise(configBox->window); | |
162 else | |
163 { | |
164 configBox = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
165 gtk_window_set_type_hint(GTK_WINDOW(configBox), GDK_WINDOW_TYPE_HINT_DIALOG); | |
166 widgets.configBox = configBox; | |
167 g_signal_connect(G_OBJECT(configBox), "destroy", G_CALLBACK(gtk_widget_destroyed), &widgets.configBox); | |
168 gtk_window_set_title(GTK_WINDOW(configBox), "Musepack Decoder Configuration"); | |
169 gtk_window_set_policy(GTK_WINDOW(configBox), FALSE, FALSE, FALSE); | |
170 gtk_container_border_width(GTK_CONTAINER(configBox), 10); | |
171 | |
172 GtkWidget* notebook = gtk_notebook_new(); | |
173 GtkWidget* vbox = gtk_vbox_new(FALSE, 10); | |
174 gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); | |
175 gtk_container_add(GTK_CONTAINER(configBox), vbox); | |
176 | |
177 //General Settings Tab | |
178 GtkWidget* generalSet = gtk_frame_new("General Settings"); | |
179 gtk_container_border_width(GTK_CONTAINER(generalSet), 5); | |
180 | |
181 GtkWidget* gSvbox = gtk_vbox_new(FALSE, 10); | |
182 gtk_container_border_width(GTK_CONTAINER(gSvbox), 5); | |
183 gtk_container_add(GTK_CONTAINER(generalSet), gSvbox); | |
184 | |
185 GtkWidget* bitrateCheck = gtk_check_button_new_with_label("Enable Dynamic Bitrate Display"); | |
186 widgets.bitrateCheck = bitrateCheck; | |
187 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bitrateCheck), pluginConfig.dynamicBitrate); | |
188 gtk_box_pack_start(GTK_BOX(gSvbox), bitrateCheck, FALSE, FALSE, 0); | |
189 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), generalSet, gtk_label_new("Plugin")); | |
190 | |
191 //ReplayGain Settings Tab | |
192 GtkWidget* replaygainSet = gtk_frame_new("ReplayGain Settings"); | |
193 gtk_container_border_width(GTK_CONTAINER(replaygainSet), 5); | |
194 | |
195 GtkWidget* rSVbox = gtk_vbox_new(FALSE, 10); | |
196 gtk_container_border_width(GTK_CONTAINER(rSVbox), 5); | |
197 gtk_container_add(GTK_CONTAINER(replaygainSet), rSVbox); | |
198 | |
199 GtkWidget* clippingCheck = gtk_check_button_new_with_label("Enable Clipping Prevention"); | |
200 widgets.clippingCheck = clippingCheck; | |
201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(clippingCheck), pluginConfig.clipPrevention); | |
202 gtk_box_pack_start(GTK_BOX(rSVbox), clippingCheck, FALSE, FALSE, 0); | |
203 | |
204 GtkWidget* replaygainCheck = gtk_check_button_new_with_label("Enable ReplayGain"); | |
205 widgets.replaygainCheck = replaygainCheck; | |
206 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(replaygainCheck), pluginConfig.replaygain); | |
207 gtk_box_pack_start(GTK_BOX(rSVbox), replaygainCheck, FALSE, FALSE, 0); | |
208 | |
209 GtkWidget* replaygainType = gtk_frame_new("ReplayGain Type"); | |
210 gtk_box_pack_start(GTK_BOX(rSVbox), replaygainType, FALSE, FALSE, 0); | |
211 g_signal_connect(G_OBJECT(replaygainCheck), "toggled", G_CALLBACK(toggleSwitch), replaygainType); | |
212 | |
213 GtkWidget* rgVbox = gtk_vbox_new(FALSE, 5); | |
214 gtk_container_set_border_width(GTK_CONTAINER(rgVbox), 5); | |
215 gtk_container_add(GTK_CONTAINER(replaygainType), rgVbox); | |
216 | |
217 GtkWidget* trackCheck = gtk_radio_button_new_with_label(NULL, "Use Track Gain"); | |
218 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(trackCheck), !pluginConfig.albumGain); | |
219 gtk_box_pack_start(GTK_BOX(rgVbox), trackCheck, FALSE, FALSE, 0); | |
220 | |
221 GtkWidget* albumCheck = gtk_radio_button_new_with_label(gtk_radio_button_group(GTK_RADIO_BUTTON(trackCheck)), "Use Album Gain"); | |
222 widgets.albumCheck = albumCheck; | |
223 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(albumCheck), pluginConfig.albumGain); | |
224 gtk_box_pack_start(GTK_BOX(rgVbox), albumCheck, FALSE, FALSE, 0); | |
225 gtk_widget_set_sensitive(replaygainType, pluginConfig.replaygain); | |
226 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), replaygainSet, gtk_label_new("ReplayGain")); | |
227 | |
228 //Buttons | |
229 GtkWidget* buttonBox = gtk_hbutton_box_new(); | |
230 gtk_button_box_set_layout(GTK_BUTTON_BOX(buttonBox), GTK_BUTTONBOX_END); | |
231 gtk_button_box_set_spacing(GTK_BUTTON_BOX(buttonBox), 5); | |
232 gtk_box_pack_start(GTK_BOX(vbox), buttonBox, FALSE, FALSE, 0); | |
233 | |
234 GtkWidget* okButton = gtk_button_new_with_label("Ok"); | |
235 g_signal_connect(G_OBJECT(okButton), "clicked", G_CALLBACK(saveConfigBox), NULL); | |
236 GTK_WIDGET_SET_FLAGS(okButton, GTK_CAN_DEFAULT); | |
237 gtk_box_pack_start(GTK_BOX(buttonBox), okButton, TRUE, TRUE, 0); | |
238 | |
239 GtkWidget* cancelButton = gtk_button_new_with_label("Cancel"); | |
240 g_signal_connect_swapped(G_OBJECT(cancelButton), "clicked", G_CALLBACK(gtk_widget_destroy), GTK_OBJECT(widgets.configBox)); | |
241 GTK_WIDGET_SET_FLAGS(cancelButton, GTK_CAN_DEFAULT); | |
242 gtk_widget_grab_default(cancelButton); | |
243 gtk_box_pack_start(GTK_BOX(buttonBox), cancelButton, TRUE, TRUE, 0); | |
244 | |
245 gtk_widget_show_all(configBox); | |
246 } | |
247 } | |
248 | |
249 static void toggleSwitch(GtkWidget* p_Widget, gpointer p_Data) | |
250 { | |
251 gtk_widget_set_sensitive(GTK_WIDGET(p_Data), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p_Widget))); | |
252 } | |
253 | |
254 static void saveConfigBox(GtkWidget* p_Widget, gpointer p_Data) | |
255 { | |
256 ConfigDb* cfg; | |
257 GtkToggleButton* tb; | |
258 | |
259 tb = GTK_TOGGLE_BUTTON(widgets.replaygainCheck); | |
260 pluginConfig.replaygain = gtk_toggle_button_get_active(tb); | |
261 tb = GTK_TOGGLE_BUTTON(widgets.clippingCheck); | |
262 pluginConfig.clipPrevention = gtk_toggle_button_get_active(tb); | |
263 tb = GTK_TOGGLE_BUTTON(widgets.bitrateCheck); | |
264 pluginConfig.dynamicBitrate = gtk_toggle_button_get_active(tb); | |
265 tb = GTK_TOGGLE_BUTTON(widgets.albumCheck); | |
266 pluginConfig.albumGain = gtk_toggle_button_get_active(tb); | |
267 | |
268 cfg = bmp_cfg_db_open(); | |
269 | |
270 bmp_cfg_db_set_bool(cfg, "musepack", "clipPrevention", pluginConfig.clipPrevention); | |
271 bmp_cfg_db_set_bool(cfg, "musepack", "albumGain", pluginConfig.albumGain); | |
272 bmp_cfg_db_set_bool(cfg, "musepack", "dynamicBitrate", pluginConfig.dynamicBitrate); | |
273 bmp_cfg_db_set_bool(cfg, "musepack", "replaygain", pluginConfig.replaygain); | |
274 | |
275 bmp_cfg_db_close(cfg); | |
276 | |
277 gtk_widget_destroy (widgets.configBox); | |
278 } | |
279 | |
260
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
280 static int mpcIsOurFile(char* p_Filename) |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
281 { |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
282 VFSFile *file; |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
283 gchar magic[3]; |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
284 if ((file = vfs_fopen(p_Filename, "rb"))) { |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
285 vfs_fread(magic, 1, 3, file); |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
286 if (!memcmp(magic, "MP+", 3)) { |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
287 vfs_fclose(file); |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
288 return 1; |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
289 } |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
290 vfs_fclose(file); |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
291 } |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
292 return 0; |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
293 } |
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
294 |
254 | 295 static int mpcIsOurFD(char* p_Filename, VFSFile* file) |
232 | 296 { |
254 | 297 gchar magic[3]; |
298 vfs_fread(magic, 1, 3, file); | |
260
4f7b72c88319
[svn] So input.c wants to have the old-style function available...
chainsaw
parents:
254
diff
changeset
|
299 if (!memcmp(magic, "MP+", 3)) |
254 | 300 return 1; |
301 return 0; | |
232 | 302 } |
303 | |
304 static void mpcPlay(char* p_Filename) | |
305 { | |
306 mpcDecoder.offset = -1; | |
307 mpcDecoder.isAlive = true; | |
308 mpcDecoder.isOutput = false; | |
309 mpcDecoder.isPause = false; | |
310 threadHandle = g_thread_create(GThreadFunc(decodeStream), (void *) g_strdup(p_Filename), TRUE, NULL); | |
311 } | |
312 | |
313 static void mpcStop() | |
314 { | |
315 setAlive(false); | |
316 if (threadHandle) | |
317 { | |
318 g_thread_join(threadHandle); | |
319 if (mpcDecoder.isOutput) | |
320 { | |
321 MpcPlugin.output->buffer_free(); | |
322 MpcPlugin.output->close_audio(); | |
323 mpcDecoder.isOutput = false; | |
324 } | |
325 } | |
326 } | |
327 | |
328 static void mpcPause(short p_Pause) | |
329 { | |
330 lockAcquire(); | |
331 mpcDecoder.isPause = p_Pause; | |
332 MpcPlugin.output->pause(p_Pause); | |
333 lockRelease(); | |
334 } | |
335 | |
336 static void mpcSeek(int p_Offset) | |
337 { | |
338 lockAcquire(); | |
339 mpcDecoder.offset = static_cast<double> (p_Offset); | |
340 MpcPlugin.output->flush(1000 * p_Offset); | |
341 lockRelease(); | |
342 } | |
343 | |
344 static int mpcGetTime() | |
345 { | |
346 if(!isAlive()) | |
347 return -1; | |
348 return MpcPlugin.output->output_time(); | |
349 } | |
350 | |
351 static TitleInput *mpcGetSongTuple(char* p_Filename) | |
352 { | |
353 VFSFile *input = vfs_fopen(p_Filename, "rb"); | |
354 TitleInput *tuple = NULL; | |
355 | |
356 if(input) | |
357 { | |
358 tuple = bmp_title_input_new(); | |
359 gchar *filename_proxy = g_strdup(p_Filename); | |
360 | |
361 tuple->file_name = g_path_get_basename(filename_proxy); | |
362 tuple->file_path = g_path_get_dirname(filename_proxy); | |
363 tuple->file_ext = "mpc"; // XXX: I can't be assed. -nenolod | |
364 | |
365 MpcInfo tags = getTags(p_Filename); | |
366 | |
367 tuple->date = g_strdup(tags.date); | |
368 tuple->track_name = g_strdup(tags.title); | |
369 tuple->performer = g_strdup(tags.artist); | |
370 tuple->album_name = g_strdup(tags.album); | |
371 tuple->track_number = tags.track; | |
372 tuple->year = tags.year; | |
373 tuple->genre = g_strdup(tags.genre); | |
374 tuple->comment = g_strdup(tags.comment); | |
375 | |
376 freeTags(tags); | |
377 | |
378 mpc_streaminfo info; | |
379 mpc_reader_file reader; | |
380 mpc_reader_setup_file_vfs(&reader, input); | |
381 mpc_streaminfo_read(&info, &reader.reader); | |
382 | |
383 tuple->length = static_cast<int> (1000 * mpc_streaminfo_get_length(&info)); | |
384 vfs_fclose(input); | |
385 } | |
386 else | |
387 { | |
388 char* temp = g_strdup_printf("[xmms-musepack] mpcGetSongInfo is unable to open %s\n", p_Filename); | |
389 perror(temp); | |
390 free(temp); | |
391 } | |
392 | |
393 return tuple; | |
394 } | |
395 | |
396 static void mpcGetSongInfo(char* p_Filename, char** p_Title, int* p_Length) | |
397 { | |
398 VFSFile *input = vfs_fopen(p_Filename, "rb"); | |
399 if(input) | |
400 { | |
401 MpcInfo tags = getTags(p_Filename); | |
402 *p_Title = mpcGenerateTitle(tags, p_Filename); | |
403 freeTags(tags); | |
404 mpc_streaminfo info; | |
405 mpc_reader_file reader; | |
406 mpc_reader_setup_file_vfs(&reader, input); | |
407 mpc_streaminfo_read(&info, &reader.reader); | |
408 *p_Length = static_cast<int> (1000 * mpc_streaminfo_get_length(&info)); | |
409 vfs_fclose(input); | |
410 } | |
411 else | |
412 { | |
413 char* temp = g_strdup_printf("[xmms-musepack] mpcGetSongInfo is unable to open %s\n", p_Filename); | |
414 perror(temp); | |
415 free(temp); | |
416 } | |
417 } | |
418 | |
419 static void freeTags(MpcInfo& tags) | |
420 { | |
421 free(tags.title); | |
422 free(tags.artist); | |
423 free(tags.album); | |
424 free(tags.comment); | |
425 free(tags.genre); | |
426 free(tags.date); | |
427 } | |
428 | |
429 static MpcInfo getTags(const char* p_Filename) | |
430 { | |
431 File oFile(p_Filename, false); | |
432 Tag* poTag = oFile.tag(); | |
433 MpcInfo tags = {0}; | |
434 tags.title = g_strdup(poTag->title().toCString(true)); | |
435 REMOVE_NONEXISTANT_TAG(tags.title); | |
436 tags.artist = g_strdup(poTag->artist().toCString(true)); | |
437 REMOVE_NONEXISTANT_TAG(tags.artist); | |
438 tags.album = g_strdup(poTag->album().toCString(true)); | |
439 REMOVE_NONEXISTANT_TAG(tags.album); | |
440 tags.genre = g_strdup(poTag->genre().toCString(true)); | |
441 REMOVE_NONEXISTANT_TAG(tags.genre); | |
442 tags.comment = g_strdup(poTag->comment().toCString(true)); | |
443 REMOVE_NONEXISTANT_TAG(tags.comment); | |
444 tags.year = poTag->year(); | |
445 tags.track = poTag->track(); | |
446 TagLib::APE::Tag* ape = oFile.APETag(false); | |
447 if(ape) | |
448 { | |
449 ItemListMap map = ape->itemListMap(); | |
450 if(map.contains("YEAR")) | |
451 { | |
452 tags.date = g_strdup(map["YEAR"].toString().toCString(true)); | |
453 } | |
454 else | |
455 { | |
456 tags.date = g_strdup_printf("%d", tags.year); | |
457 } | |
458 } | |
459 return tags; | |
460 } | |
461 | |
462 static void mpcFileInfoBox(char* p_Filename) | |
463 { | |
464 GtkWidget* infoBox = widgets.infoBox; | |
465 | |
466 if(infoBox) | |
467 gdk_window_raise(infoBox->window); | |
468 else | |
469 { | |
470 infoBox = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
471 gtk_window_set_type_hint(GTK_WINDOW(infoBox), GDK_WINDOW_TYPE_HINT_DIALOG); | |
472 widgets.infoBox = infoBox; | |
473 gtk_window_set_policy(GTK_WINDOW(infoBox), FALSE, FALSE, FALSE); | |
474 g_signal_connect(G_OBJECT(infoBox), "destroy", G_CALLBACK(closeInfoBox), NULL); | |
475 gtk_container_set_border_width(GTK_CONTAINER(infoBox), 10); | |
476 | |
477 GtkWidget* iVbox = gtk_vbox_new(FALSE, 10); | |
478 gtk_container_add(GTK_CONTAINER(infoBox), iVbox); | |
479 | |
480 GtkWidget* filenameHbox = gtk_hbox_new(FALSE, 5); | |
481 gtk_box_pack_start(GTK_BOX(iVbox), filenameHbox, FALSE, TRUE, 0); | |
482 | |
483 GtkWidget* fileLabel = gtk_label_new("Filename:"); | |
484 gtk_box_pack_start(GTK_BOX(filenameHbox), fileLabel, FALSE, TRUE, 0); | |
485 | |
486 GtkWidget* fileEntry = gtk_entry_new(); | |
487 widgets.fileEntry = fileEntry; | |
488 gtk_editable_set_editable(GTK_EDITABLE(fileEntry), FALSE); | |
489 gtk_box_pack_start(GTK_BOX(filenameHbox), fileEntry, TRUE, TRUE, 0); | |
490 | |
491 GtkWidget* iHbox = gtk_hbox_new(FALSE, 10); | |
492 gtk_box_pack_start(GTK_BOX(iVbox), iHbox, FALSE, TRUE, 0); | |
493 | |
494 GtkWidget* leftBox = gtk_vbox_new(FALSE, 10); | |
495 gtk_box_pack_start(GTK_BOX(iHbox), leftBox, FALSE, FALSE, 0); | |
496 | |
497 //Tag labels | |
498 GtkWidget* tagFrame = gtk_frame_new("Musepack Tag"); | |
499 gtk_box_pack_start(GTK_BOX(leftBox), tagFrame, FALSE, FALSE, 0); | |
500 gtk_widget_set_sensitive(tagFrame, TRUE); | |
501 | |
502 GtkWidget* iTable = gtk_table_new(5, 5, FALSE); | |
503 gtk_container_set_border_width(GTK_CONTAINER(iTable), 5); | |
504 gtk_container_add(GTK_CONTAINER(tagFrame), iTable); | |
505 | |
506 mpcGtkTagLabel("Title:", 0, 1, 0, 1, iTable); | |
507 GtkWidget* titleEntry = mpcGtkTagEntry(1, 4, 0, 1, 0, iTable); | |
508 widgets.titleEntry = titleEntry; | |
509 | |
510 mpcGtkTagLabel("Artist:", 0, 1, 1, 2, iTable); | |
511 GtkWidget* artistEntry = mpcGtkTagEntry(1, 4, 1, 2, 0, iTable); | |
512 widgets.artistEntry = artistEntry; | |
513 | |
514 mpcGtkTagLabel("Album:", 0, 1, 2, 3, iTable); | |
515 GtkWidget* albumEntry = mpcGtkTagEntry(1, 4, 2, 3, 0, iTable); | |
516 widgets.albumEntry = albumEntry; | |
517 | |
518 mpcGtkTagLabel("Comment:", 0, 1, 3, 4, iTable); | |
519 GtkWidget* commentEntry = mpcGtkTagEntry(1, 4, 3, 4, 0, iTable); | |
520 widgets.commentEntry = commentEntry; | |
521 | |
522 mpcGtkTagLabel("Year:", 0, 1, 4, 5, iTable); | |
523 GtkWidget* yearEntry = mpcGtkTagEntry(1, 2, 4, 5, 4, iTable); | |
524 widgets.yearEntry = yearEntry; | |
525 gtk_widget_set_usize(yearEntry, 4, -1); | |
526 | |
527 mpcGtkTagLabel("Track:", 2, 3, 4, 5, iTable); | |
528 GtkWidget* trackEntry = mpcGtkTagEntry(3, 4, 4, 5, 4, iTable); | |
529 widgets.trackEntry = trackEntry; | |
530 gtk_widget_set_usize(trackEntry, 3, -1); | |
531 | |
532 mpcGtkTagLabel("Genre:", 0, 1, 5, 6, iTable); | |
533 GtkWidget* genreEntry = mpcGtkTagEntry(1, 4, 5, 6, 0, iTable); | |
534 widgets.genreEntry = genreEntry; | |
535 gtk_widget_set_usize(genreEntry, 20, -1); | |
536 | |
537 //Buttons | |
538 GtkWidget* buttonBox = gtk_hbutton_box_new(); | |
539 gtk_button_box_set_layout(GTK_BUTTON_BOX(buttonBox), GTK_BUTTONBOX_END); | |
540 gtk_button_box_set_spacing(GTK_BUTTON_BOX(buttonBox), 5); | |
541 gtk_box_pack_start(GTK_BOX(leftBox), buttonBox, FALSE, FALSE, 0); | |
542 | |
543 GtkWidget* saveButton = mpcGtkButton("Save", buttonBox); | |
544 g_signal_connect(G_OBJECT(saveButton), "clicked", G_CALLBACK(saveTags), NULL); | |
545 | |
546 GtkWidget* removeButton = mpcGtkButton("Remove Tag", buttonBox); | |
547 g_signal_connect_swapped(G_OBJECT(removeButton), "clicked", G_CALLBACK(removeTags), NULL); | |
548 | |
549 GtkWidget* cancelButton = mpcGtkButton("Cancel", buttonBox); | |
550 g_signal_connect_swapped(G_OBJECT(cancelButton), "clicked", G_CALLBACK(closeInfoBox), NULL); | |
551 gtk_widget_grab_default(cancelButton); | |
552 | |
553 //File information | |
554 GtkWidget* infoFrame = gtk_frame_new("Musepack Info"); | |
555 gtk_box_pack_start(GTK_BOX(iHbox), infoFrame, FALSE, FALSE, 0); | |
556 | |
557 GtkWidget* infoVbox = gtk_vbox_new(FALSE, 5); | |
558 gtk_container_add(GTK_CONTAINER(infoFrame), infoVbox); | |
559 gtk_container_set_border_width(GTK_CONTAINER(infoVbox), 10); | |
560 gtk_box_set_spacing(GTK_BOX(infoVbox), 0); | |
561 | |
562 GtkWidget* streamLabel = mpcGtkLabel(infoVbox); | |
563 GtkWidget* encoderLabel = mpcGtkLabel(infoVbox); | |
564 GtkWidget* profileLabel = mpcGtkLabel(infoVbox); | |
565 GtkWidget* bitrateLabel = mpcGtkLabel(infoVbox); | |
566 GtkWidget* rateLabel = mpcGtkLabel(infoVbox); | |
567 GtkWidget* channelsLabel = mpcGtkLabel(infoVbox); | |
568 GtkWidget* lengthLabel = mpcGtkLabel(infoVbox); | |
569 GtkWidget* fileSizeLabel = mpcGtkLabel(infoVbox); | |
570 GtkWidget* trackPeakLabel = mpcGtkLabel(infoVbox); | |
571 GtkWidget* trackGainLabel = mpcGtkLabel(infoVbox); | |
572 GtkWidget* albumPeakLabel = mpcGtkLabel(infoVbox); | |
573 GtkWidget* albumGainLabel = mpcGtkLabel(infoVbox); | |
574 | |
575 VFSFile *input = vfs_fopen(p_Filename, "rb"); | |
576 if(input) | |
577 { | |
578 mpc_streaminfo info; | |
579 mpc_reader_file reader; | |
580 mpc_reader_setup_file_vfs(&reader, input); | |
581 mpc_streaminfo_read(&info, &reader.reader); | |
582 | |
583 int time = static_cast<int> (mpc_streaminfo_get_length(&info)); | |
584 int minutes = time / 60; | |
585 int seconds = time % 60; | |
586 | |
587 mpcGtkPrintLabel(streamLabel, "Streamversion %d", info.stream_version); | |
588 mpcGtkPrintLabel(encoderLabel, "Encoder: \%s", info.encoder); | |
589 mpcGtkPrintLabel(profileLabel, "Profile: \%s", info.profile_name); | |
590 mpcGtkPrintLabel(bitrateLabel, "Average bitrate: \%6.1f kbps", info.average_bitrate * 1.e-3); | |
591 mpcGtkPrintLabel(rateLabel, "Samplerate: \%d Hz", info.sample_freq); | |
592 mpcGtkPrintLabel(channelsLabel, "Channels: \%d", info.channels); | |
593 mpcGtkPrintLabel(lengthLabel, "Length: \%d:\%.2d", minutes, seconds); | |
594 mpcGtkPrintLabel(fileSizeLabel, "File size: \%d Bytes", info.total_file_length); | |
595 mpcGtkPrintLabel(trackPeakLabel, "Track Peak: \%5u", info.peak_title); | |
596 mpcGtkPrintLabel(trackGainLabel, "Track Gain: \%-+2.2f dB", 0.01 * info.gain_title); | |
597 mpcGtkPrintLabel(albumPeakLabel, "Album Peak: \%5u", info.peak_album); | |
598 mpcGtkPrintLabel(albumGainLabel, "Album Gain: \%-+5.2f dB", 0.01 * info.gain_album); | |
599 | |
600 MpcInfo tags = getTags(p_Filename); | |
601 gtk_entry_set_text(GTK_ENTRY(titleEntry), tags.title); | |
602 gtk_entry_set_text(GTK_ENTRY(artistEntry), tags.artist); | |
603 gtk_entry_set_text(GTK_ENTRY(albumEntry), tags.album); | |
604 gtk_entry_set_text(GTK_ENTRY(commentEntry), tags.comment); | |
605 gtk_entry_set_text(GTK_ENTRY(genreEntry), tags.genre); | |
606 char* entry = g_strdup_printf ("%d", tags.track); | |
607 gtk_entry_set_text(GTK_ENTRY(trackEntry), entry); | |
608 free(entry); | |
609 entry = g_strdup_printf ("%d", tags.year); | |
610 gtk_entry_set_text(GTK_ENTRY(yearEntry), entry); | |
611 free(entry); | |
612 entry = g_filename_display_name(p_Filename); | |
613 gtk_entry_set_text(GTK_ENTRY(fileEntry), entry); | |
614 free(entry); | |
615 freeTags(tags); | |
616 vfs_fclose(input); | |
617 } | |
618 else | |
619 { | |
620 char* temp = g_strdup_printf("[xmms-musepack] mpcFileInfoBox is unable to read tags from %s", p_Filename); | |
621 perror(temp); | |
622 free(temp); | |
623 } | |
624 | |
625 char* name = g_filename_display_basename(p_Filename); | |
626 char* text = g_strdup_printf("File Info - %s", name); | |
627 free(name); | |
628 gtk_window_set_title(GTK_WINDOW(infoBox), text); | |
629 free(text); | |
630 | |
631 gtk_widget_show_all(infoBox); | |
632 } | |
633 } | |
634 | |
635 static void mpcGtkPrintLabel(GtkWidget* widget, char* format,...) | |
636 { | |
637 va_list args; | |
638 | |
639 va_start(args, format); | |
640 char* temp = g_strdup_vprintf(format, args); | |
641 va_end(args); | |
642 | |
643 gtk_label_set_text(GTK_LABEL(widget), temp); | |
644 free(temp); | |
645 } | |
646 | |
647 static GtkWidget* mpcGtkTagLabel(char* p_Text, int a, int b, int c, int d, GtkWidget* p_Box) | |
648 { | |
649 GtkWidget* label = gtk_label_new(p_Text); | |
650 gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); | |
651 gtk_table_attach(GTK_TABLE(p_Box), label, a, b, c, d, GTK_FILL, GTK_FILL, 5, 5); | |
652 return label; | |
653 } | |
654 | |
655 static GtkWidget* mpcGtkTagEntry(int a, int b, int c, int d, int p_Size, GtkWidget* p_Box) | |
656 { | |
657 GtkWidget* entry; | |
658 if(p_Size == 0) | |
659 entry = gtk_entry_new(); | |
660 else | |
661 entry = gtk_entry_new_with_max_length(p_Size); | |
662 gtk_table_attach(GTK_TABLE(p_Box), entry, a, b, c, d, | |
663 (GtkAttachOptions) (GTK_FILL | GTK_EXPAND | GTK_SHRINK), | |
664 (GtkAttachOptions) (GTK_FILL | GTK_EXPAND | GTK_SHRINK), 0, 5); | |
665 return entry; | |
666 } | |
667 | |
668 static GtkWidget* mpcGtkLabel(GtkWidget* p_Box) | |
669 { | |
670 GtkWidget* label = gtk_label_new(""); | |
671 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); | |
672 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); | |
673 gtk_box_pack_start(GTK_BOX(p_Box), label, FALSE, FALSE, 0); | |
674 return label; | |
675 } | |
676 | |
677 static GtkWidget* mpcGtkButton(char* p_Text, GtkWidget* p_Box) | |
678 { | |
679 GtkWidget* button = gtk_button_new_with_label(p_Text); | |
680 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); | |
681 gtk_box_pack_start(GTK_BOX(p_Box), button, TRUE, TRUE, 0); | |
682 return button; | |
683 } | |
684 | |
685 static void removeTags(GtkWidget * w, gpointer data) | |
686 { | |
687 File oFile(gtk_entry_get_text(GTK_ENTRY(widgets.fileEntry))); | |
688 oFile.remove(); | |
689 oFile.save(); | |
690 closeInfoBox(NULL, NULL); | |
691 } | |
692 | |
693 static void saveTags(GtkWidget* w, gpointer data) | |
694 { | |
695 File oFile(gtk_entry_get_text(GTK_ENTRY(widgets.fileEntry))); | |
696 Tag* poTag = oFile.tag(); | |
697 | |
698 char* cAlbum = g_strdup(gtk_entry_get_text(GTK_ENTRY(widgets.albumEntry))); | |
699 char* cArtist = g_strdup(gtk_entry_get_text(GTK_ENTRY(widgets.artistEntry))); | |
700 char* cTitle = g_strdup(gtk_entry_get_text(GTK_ENTRY(widgets.titleEntry))); | |
701 char* cGenre = g_strdup(gtk_entry_get_text(GTK_ENTRY(widgets.genreEntry))); | |
702 char* cComment = g_strdup(gtk_entry_get_text(GTK_ENTRY(widgets.commentEntry))); | |
703 | |
704 const String album = String(cAlbum, TagLib::String::UTF8); | |
705 const String artist = String(cArtist, TagLib::String::UTF8); | |
706 const String title = String(cTitle, TagLib::String::UTF8); | |
707 const String genre = String(cGenre, TagLib::String::UTF8); | |
708 const String comment = String(cComment, TagLib::String::UTF8); | |
709 | |
710 poTag->setAlbum(album); | |
711 poTag->setArtist(artist); | |
712 poTag->setTitle(title); | |
713 poTag->setGenre(genre); | |
714 poTag->setComment(comment); | |
715 poTag->setYear(atoi(gtk_entry_get_text(GTK_ENTRY(widgets.yearEntry)))); | |
716 poTag->setTrack(atoi(gtk_entry_get_text(GTK_ENTRY(widgets.trackEntry)))); | |
717 | |
718 free(cAlbum); | |
719 free(cArtist); | |
720 free(cTitle); | |
721 free(cGenre); | |
722 free(cComment); | |
723 | |
724 oFile.save(); | |
725 closeInfoBox(NULL, NULL); | |
726 } | |
727 | |
728 static void closeInfoBox(GtkWidget* w, gpointer data) | |
729 { | |
730 gtk_widget_destroy(widgets.infoBox); | |
731 widgets.infoBox = NULL; | |
732 } | |
733 | |
734 static char* mpcGenerateTitle(const MpcInfo& p_Tags, char* p_Filename) | |
735 { | |
736 TitleInput* tuple = mpcGetSongTuple(p_Filename); | |
737 | |
738 char* title = xmms_get_titlestring (xmms_get_gentitle_format(), tuple); | |
739 if(!title) | |
740 title = g_strdup(tuple->file_name); | |
741 else if (!*title) | |
742 title = g_strdup(tuple->file_name); | |
743 | |
744 bmp_title_input_free(tuple); | |
745 return title; | |
746 } | |
747 | |
748 static void* endThread(char* p_FileName, VFSFile * p_FileHandle, bool release) | |
749 { | |
750 free(p_FileName); | |
751 if(release) | |
752 lockRelease(); | |
753 if(mpcDecoder.isError) | |
754 { | |
755 perror(mpcDecoder.isError); | |
756 free(mpcDecoder.isError); | |
757 mpcDecoder.isError = NULL; | |
758 } | |
759 setAlive(false); | |
760 if(p_FileHandle) | |
761 vfs_fclose(p_FileHandle); | |
762 if(track.display) | |
763 { | |
764 free(track.display); | |
765 track.display = NULL; | |
766 } | |
767 g_thread_exit(NULL); | |
768 return 0; | |
769 } | |
770 | |
771 static void* decodeStream(void* data) | |
772 { | |
773 lockAcquire(); | |
774 char* filename = static_cast<char*> (data); | |
775 VFSFile *input = vfs_fopen(filename, "rb"); | |
776 if (!input) | |
777 { | |
778 mpcDecoder.isError = g_strdup_printf("[xmms-musepack] decodeStream is unable to open %s", filename); | |
779 return endThread(filename, input, true); | |
780 } | |
781 | |
782 mpc_reader_file reader; | |
783 mpc_reader_setup_file_vfs(&reader, input); | |
784 | |
785 mpc_streaminfo info; | |
786 if (mpc_streaminfo_read(&info, &reader.reader) != ERROR_CODE_OK) | |
787 { | |
788 mpcDecoder.isError = g_strdup_printf("[xmms-musepack] decodeStream is unable to read %s", filename); | |
789 return endThread(filename, input, true); | |
790 } | |
791 | |
792 MpcInfo tags = getTags(filename); | |
793 track.display = mpcGenerateTitle(tags, filename); | |
794 track.length = static_cast<int> (1000 * mpc_streaminfo_get_length(&info)); | |
795 track.bitrate = static_cast<int> (info.average_bitrate); | |
796 track.sampleFreq = info.sample_freq; | |
797 track.channels = info.channels; | |
798 freeTags(tags); | |
799 | |
800 MpcPlugin.set_info(track.display, track.length, track.bitrate, track.sampleFreq, track.channels); | |
801 | |
802 mpc_decoder decoder; | |
803 mpc_decoder_setup(&decoder, &reader.reader); | |
804 if (!mpc_decoder_initialize(&decoder, &info)) | |
805 { | |
806 mpcDecoder.isError = g_strdup_printf("[xmms-musepack] decodeStream is unable to initialize decoder on %s", filename); | |
807 return endThread(filename, input, true); | |
808 } | |
809 | |
810 setReplaygain(info, decoder); | |
811 | |
812 MPC_SAMPLE_FORMAT sampleBuffer[MPC_DECODER_BUFFER_LENGTH]; | |
813 char xmmsBuffer[MPC_DECODER_BUFFER_LENGTH * 4]; | |
814 | |
815 if (!MpcPlugin.output->open_audio(FMT_S16_LE, track.sampleFreq, track.channels)) | |
816 { | |
817 mpcDecoder.isError = g_strdup_printf("[xmms-musepack] decodeStream is unable to open an audio output"); | |
818 return endThread(filename, input, true); | |
819 } | |
820 else | |
821 { | |
822 mpcDecoder.isOutput = true; | |
823 } | |
824 | |
825 lockRelease(); | |
826 | |
827 int counter = 2 * track.sampleFreq / 3; | |
828 while (isAlive()) | |
829 { | |
830 if (getOffset() != -1) | |
831 { | |
832 mpc_decoder_seek_seconds(&decoder, mpcDecoder.offset); | |
833 setOffset(-1); | |
834 } | |
835 | |
836 lockAcquire(); | |
837 short iPlaying = MpcPlugin.output->buffer_playing()? 1 : 0; | |
838 int iFree = MpcPlugin.output->buffer_free(); | |
839 if (!mpcDecoder.isPause && iFree >= ((1152 * 4) << iPlaying)) | |
840 { | |
841 unsigned status = processBuffer(sampleBuffer, xmmsBuffer, decoder); | |
842 if (status == (unsigned) (-1)) | |
843 { | |
844 mpcDecoder.isError = g_strdup_printf("[xmms-musepack] error from internal decoder on %s", filename); | |
845 return endThread(filename, input, true); | |
846 } | |
847 if (status == 0 && iPlaying == 0) | |
848 return endThread(filename, input, true); | |
849 | |
850 lockRelease(); | |
851 | |
852 if(pluginConfig.dynamicBitrate) | |
853 { | |
854 counter -= status; | |
855 if(counter < 0) | |
856 { | |
857 MpcPlugin.set_info(track.display, track.length, track.bitrate, track.sampleFreq, track.channels); | |
858 counter = 2 * track.sampleFreq / 3; | |
859 } | |
860 } | |
861 } | |
862 else | |
863 { | |
864 lockRelease(); | |
865 xmms_usleep(10000); | |
866 } | |
867 } | |
868 return endThread(filename, input, false); | |
869 } | |
870 | |
871 static int processBuffer(MPC_SAMPLE_FORMAT* sampleBuffer, char* xmmsBuffer, mpc_decoder& decoder) | |
872 { | |
873 mpc_uint32_t vbrAcc = 0; | |
874 mpc_uint32_t vbrUpd = 0; | |
875 | |
876 unsigned status = mpc_decoder_decode(&decoder, sampleBuffer, &vbrAcc, &vbrUpd); | |
877 copyBuffer(sampleBuffer, xmmsBuffer, status); | |
878 | |
879 if (pluginConfig.dynamicBitrate) | |
880 { | |
881 track.bitrate = static_cast<int> (vbrUpd * track.sampleFreq / 1152); | |
882 } | |
883 | |
884 produce_audio(MpcPlugin.output->written_time(), FMT_S16_LE, track.channels, status * 4, xmmsBuffer, NULL); | |
885 return status; | |
886 } | |
887 | |
888 static void setReplaygain(mpc_streaminfo& info, mpc_decoder& decoder) | |
889 { | |
890 if(!pluginConfig.replaygain && !pluginConfig.clipPrevention) | |
891 return; | |
892 | |
893 int peak = pluginConfig.albumGain ? info.peak_album : info.peak_title; | |
894 double gain = pluginConfig.albumGain ? info.gain_album : info.gain_title; | |
895 | |
896 if(!peak) | |
897 peak = 32767; | |
898 if(!gain) | |
899 gain = 1.; | |
900 | |
901 double clip = 32767. / peak; | |
902 gain = exp((M_LN10 / 2000.) * gain); | |
903 | |
904 if(pluginConfig.clipPrevention && !pluginConfig.replaygain) | |
905 gain = clip; | |
906 else if(pluginConfig.replaygain && pluginConfig.clipPrevention) | |
907 if(clip < gain) | |
908 gain = clip; | |
909 | |
910 mpc_decoder_scale_output(&decoder, gain); | |
911 } | |
912 | |
913 inline static void lockAcquire() | |
914 { | |
915 g_static_mutex_lock(&threadMutex); | |
916 } | |
917 | |
918 inline static void lockRelease() | |
919 { | |
920 g_static_mutex_unlock(&threadMutex); | |
921 } | |
922 | |
923 inline static bool isAlive() | |
924 { | |
925 lockAcquire(); | |
926 bool isAlive = mpcDecoder.isAlive; | |
927 lockRelease(); | |
928 return isAlive; | |
929 } | |
930 | |
931 inline static bool isPause() | |
932 { | |
933 lockAcquire(); | |
934 bool isPause = mpcDecoder.isPause; | |
935 lockRelease(); | |
936 return isPause; | |
937 } | |
938 | |
939 inline static void setAlive(bool isAlive) | |
940 { | |
941 lockAcquire(); | |
942 mpcDecoder.isAlive = isAlive; | |
943 lockRelease(); | |
944 } | |
945 | |
946 inline static double getOffset() | |
947 { | |
948 lockAcquire(); | |
949 double offset = mpcDecoder.offset; | |
950 lockRelease(); | |
951 return offset; | |
952 } | |
953 | |
954 inline static void setOffset(double offset) | |
955 { | |
956 lockAcquire(); | |
957 mpcDecoder.offset = offset; | |
958 lockRelease(); | |
959 } |