A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <stdio.h>
22#include <string.h>
23#include <stdlib.h>
24#include "config.h"
25#include "action.h"
26#include "lang.h"
27#include "misc.h"
28#include "talk.h"
29#include "general.h"
30#include "codecs.h"
31#include "menu.h"
32#include "settings.h"
33#include "audio.h"
34#include "pcm_record.h"
35#include "enc_config.h"
36#include "splash.h"
37
38
39#define CALL_FN_(fn, ...) \
40 if (fn) fn(__VA_ARGS__)
41
42static int enc_menuitem_callback(int action,
43 const struct menu_item_ex *this_item,
44 struct gui_synclist *this_list);
45static int enc_menuitem_enteritem(int action,
46 const struct menu_item_ex *this_item,
47 struct gui_synclist *this_list);
48static void enc_rec_settings_changed(struct encoder_config *cfg);
49/* this is used by all encoder menu items,
50 MUST be initialised before the call to do_menu() */
51static struct menucallback_data {
52 struct encoder_config *cfg;
53 bool global;
54} menu_callback_data;
55
56/** Function definitions for each codec - add these to enc_data
57 list following the definitions **/
58
59/** aiff_enc.codec **/
60
61/** mp3_enc.codec **/
62/* mp3_enc: return encoder capabilities */
63static void mp3_enc_get_caps(const struct encoder_config *cfg,
64 struct encoder_caps *caps,
65 bool for_config)
66{
67 int i;
68 unsigned long bitr;
69
70 if (!for_config)
71 {
72 /* Overall encoder capabilities */
73 caps->samplerate_caps = MPEG1_SAMPR_CAPS | MPEG2_SAMPR_CAPS;
74 caps->channel_caps = CHN_CAP_ALL;
75 return;
76 }
77
78 /* Restrict caps based on config */
79 i = round_value_to_list32(cfg->mp3_enc.bitrate, mp3_enc_bitr,
80 MP3_ENC_NUM_BITR, false);
81 bitr = mp3_enc_bitr[i];
82
83 /* sample rate caps */
84
85 /* check if MPEG1 sample rates are available */
86 if ((bitr >= 32 && bitr <= 128) || bitr >= 160)
87 caps->samplerate_caps |= MPEG1_SAMPR_CAPS;
88
89 /* check if MPEG2 sample rates and mono are available */
90 if (bitr <= 160)
91 {
92 caps->samplerate_caps |= MPEG2_SAMPR_CAPS;
93 caps->channel_caps |= CHN_CAP_MONO;
94 }
95
96 /* check if stereo is available */
97 if (bitr >= 32)
98 caps->channel_caps |= CHN_CAP_STEREO;
99} /* mp3_enc_get_caps */
100
101/* mp3_enc: return the default configuration */
102static void mp3_enc_default_config(struct encoder_config *cfg)
103{
104 cfg->mp3_enc.bitrate = 128; /* default that works for all types */
105} /* mp3_enc_default_config */
106
107static void mp3_enc_convert_config(struct encoder_config *cfg,
108 bool global)
109{
110 if (global)
111 {
112 global_settings.mp3_enc_config.bitrate =
113 round_value_to_list32(cfg->mp3_enc.bitrate, mp3_enc_bitr,
114 MP3_ENC_NUM_BITR, false);
115 }
116 else
117 {
118 if ((unsigned)global_settings.mp3_enc_config.bitrate >= MP3_ENC_NUM_BITR)
119 global_settings.mp3_enc_config.bitrate = MP3_ENC_BITRATE_CFG_DEFAULT;
120 cfg->mp3_enc.bitrate = mp3_enc_bitr[global_settings.mp3_enc_config.bitrate];
121 }
122} /* mp3_enc_convert_config */
123
124/* mp3_enc: show the bitrate setting options */
125static bool mp3_enc_bitrate(struct menucallback_data *data)
126{
127 struct encoder_config *cfg = data->cfg;
128 static const struct opt_items items[] =
129 {
130 /* Available in MPEG Version: */
131#ifdef HAVE_MPEG2_SAMPR
132#if 0
133 /* this sounds awful no matter what */
134 { "8 kBit/s", TALK_ID(8, UNIT_KBIT) }, /* 2 */
135#endif
136 /* mono only */
137 { "16 kBit/s", TALK_ID(16, UNIT_KBIT) }, /* 2 */
138 { "24 kBit/s", TALK_ID(24, UNIT_KBIT) }, /* 2 */
139#endif /* HAVE_MPEG2_SAMPR */
140 /* stereo/mono */
141 { "32 kBit/s", TALK_ID(32, UNIT_KBIT) }, /* 1,2 */
142 { "40 kBit/s", TALK_ID(40, UNIT_KBIT) }, /* 1,2 */
143 { "48 kBit/s", TALK_ID(48, UNIT_KBIT) }, /* 1,2 */
144 { "56 kBit/s", TALK_ID(56, UNIT_KBIT) }, /* 1,2 */
145 { "64 kBit/s", TALK_ID(64, UNIT_KBIT) }, /* 1,2 */
146 { "80 kBit/s", TALK_ID(80, UNIT_KBIT) }, /* 1,2 */
147 { "96 kBit/s", TALK_ID(96, UNIT_KBIT) }, /* 1,2 */
148 { "112 kBit/s", TALK_ID(112, UNIT_KBIT) }, /* 1,2 */
149 { "128 kBit/s", TALK_ID(128, UNIT_KBIT) }, /* 1,2 */
150 /* Leave out 144 when there is both MPEG 1 and 2 */
151#if defined(HAVE_MPEG2_SAMPR) && !defined (HAVE_MPEG1_SAMPR)
152 /* oddball MPEG2-only rate stuck in the middle */
153 { "144 kBit/s", TALK_ID(144, UNIT_KBIT) }, /* 2 */
154#endif
155 { "160 kBit/s", TALK_ID(160, UNIT_KBIT) }, /* 1,2 */
156#ifdef HAVE_MPEG1_SAMPR
157 /* stereo only */
158 { "192 kBit/s", TALK_ID(192, UNIT_KBIT) }, /* 1 */
159 { "224 kBit/s", TALK_ID(224, UNIT_KBIT) }, /* 1 */
160 { "256 kBit/s", TALK_ID(256, UNIT_KBIT) }, /* 1 */
161 { "320 kBit/s", TALK_ID(320, UNIT_KBIT) }, /* 1 */
162#endif
163 };
164
165 unsigned long rate_list[ARRAYLEN(items)];
166
167 /* This is rather constant based upon the build but better than
168 storing and maintaining yet another list of numbers */
169 int n_rates = make_list_from_caps32(
170 MPEG1_BITR_CAPS | MPEG2_BITR_CAPS, mp3_enc_bitr,
171 0
172#ifdef HAVE_MPEG1_SAMPR
173 | MPEG1_BITR_CAPS
174#endif
175#ifdef HAVE_MPEG2_SAMPR
176#ifdef HAVE_MPEG1_SAMPR
177 | (MPEG2_BITR_CAPS & ~(MP3_BITR_CAP_144 | MP3_BITR_CAP_8))
178#else
179 | (MPEG2_BITR_CAPS & ~(MP3_BITR_CAP_8))
180#endif
181#endif /* HAVE_MPEG2_SAMPR */
182 , rate_list);
183
184 int index = round_value_to_list32(cfg->mp3_enc.bitrate, rate_list,
185 n_rates, false);
186 bool res = set_option(str(LANG_BITRATE), &index, RB_INT,
187 items, n_rates, NULL);
188 index = round_value_to_list32(rate_list[index], mp3_enc_bitr,
189 MP3_ENC_NUM_BITR, false);
190 cfg->mp3_enc.bitrate = mp3_enc_bitr[index];
191
192 return res;
193} /* mp3_enc_bitrate */
194
195/* mp3_enc configuration menu */
196MENUITEM_FUNCTION_W_PARAM(mp3_bitrate, 0, ID2P(LANG_BITRATE),
197 mp3_enc_bitrate, &menu_callback_data,
198 enc_menuitem_callback, Icon_NOICON);
199MAKE_MENU( mp3_enc_menu, ID2P(LANG_ENCODER_SETTINGS),
200 enc_menuitem_enteritem, Icon_NOICON,
201 &mp3_bitrate);
202
203
204/** wav_enc.codec **/
205/* wav_enc: show the configuration menu */
206#if 0
207MAKE_MENU( wav_enc_menu, ID2P(LANG_ENCODER_SETTINGS),
208 enc_menuitem_enteritem, Icon_NOICON,
209 );
210#endif
211
212/** wavpack_enc.codec **/
213/* wavpack_enc: show the configuration menu */
214#if 0
215MAKE_MENU( wavpack_enc_menu, ID2P(LANG_ENCODER_SETTINGS),
216 enc_menuitem_enteritem, Icon_NOICON,
217 );
218#endif
219
220/** config function pointers and/or data for each codec **/
221static const struct encoder_data
222{
223 void (*get_caps)(const struct encoder_config *cfg,
224 struct encoder_caps *caps, bool for_config);
225 void (*default_cfg)(struct encoder_config *cfg);
226 void (*convert_cfg)(struct encoder_config *cfg , bool global);
227 const struct menu_item_ex *menu;
228} enc_data[REC_NUM_FORMATS] =
229{
230 /* aiff_enc.codec */
231 [REC_FORMAT_AIFF] = {
232 NULL,
233 NULL,
234 NULL,
235 NULL,
236 },
237 /* mp3_enc.codec */
238 [REC_FORMAT_MPA_L3] = {
239 mp3_enc_get_caps,
240 mp3_enc_default_config,
241 mp3_enc_convert_config,
242 &mp3_enc_menu,
243 },
244 /* wav_enc.codec */
245 [REC_FORMAT_PCM_WAV] = {
246 NULL,
247 NULL,
248 NULL,
249 NULL,
250 },
251 /* wavpack_enc.codec */
252 [REC_FORMAT_WAVPACK] = {
253 NULL,
254 NULL,
255 NULL,
256 NULL,
257 },
258};
259
260static inline bool rec_format_ok(int rec_format)
261{
262 return (unsigned)rec_format < REC_NUM_FORMATS;
263}
264/* This is called before entering the menu with the encoder settings
265 Its needed to make sure the settings can take effect. */
266static int enc_menuitem_enteritem(int action,
267 const struct menu_item_ex *this_item,
268 struct gui_synclist *this_list)
269{
270 (void)this_item;
271 (void)this_list;
272 /* this struct must be init'ed before calling do_menu() so this is safe */
273 struct menucallback_data *data = &menu_callback_data;
274 if (action == ACTION_STD_OK) /* entering the item */
275 {
276 if (data->global)
277 global_to_encoder_config(data->cfg);
278 }
279 return action;
280}
281/* this is called when a encoder setting is exited
282 It is used to update the status bar and save the setting */
283static int enc_menuitem_callback(int action,
284 const struct menu_item_ex *this_item,
285 struct gui_synclist *this_list)
286{
287 (void)this_list;
288 struct menucallback_data *data =
289 (struct menucallback_data*)this_item->function_param->param;
290
291 if (action == ACTION_EXIT_MENUITEM)
292 {
293 /* If the setting being configured is global, it must be placed
294 in global_settings before updating the status bar for the
295 change to show upon exiting the item. */
296 if (data->global)
297 {
298 enc_rec_settings_changed(data->cfg);
299 encoder_config_to_global(data->cfg);
300 }
301
302 }
303 return action;
304}
305
306/* update settings dependent upon encoder settings */
307static void enc_rec_settings_changed(struct encoder_config *cfg)
308{
309 struct encoder_config enc_config;
310 struct encoder_caps caps;
311 long table[MAX((int)CHN_NUM_MODES, (int)REC_NUM_FREQ)];
312 int n;
313
314 if (cfg == NULL)
315 {
316 cfg = &enc_config;
317 cfg->rec_format = global_settings.rec_format;
318 global_to_encoder_config(cfg);
319 }
320
321 /* have to sync other settings when encoder settings change */
322 if (!enc_get_caps(cfg, &caps, true))
323 return;
324
325 /* rec_channels */
326 n = make_list_from_caps32(CHN_CAP_ALL, NULL,
327 caps.channel_caps, table);
328
329 /* no zero check needed: encoder must support at least one
330 sample rate that recording supports or it shouldn't be in
331 available in the recording options */
332 n = round_value_to_list32(global_settings.rec_channels,
333 table, n, true);
334 global_settings.rec_channels = table[n];
335
336 /* rec_frequency */
337 n = make_list_from_caps32(REC_SAMPR_CAPS, rec_freq_sampr,
338 caps.samplerate_caps, table);
339
340 n = round_value_to_list32(
341 rec_freq_sampr[global_settings.rec_frequency],
342 table, n, false);
343
344 global_settings.rec_frequency = round_value_to_list32(
345 table[n], rec_freq_sampr, REC_NUM_FREQ, false);
346} /* enc_rec_settings_changed */
347
348/** public stuff **/
349void global_to_encoder_config(struct encoder_config *cfg)
350{
351 const struct encoder_data *data = &enc_data[cfg->rec_format];
352 CALL_FN_(data->convert_cfg, cfg, false);
353} /* global_to_encoder_config */
354
355void encoder_config_to_global(const struct encoder_config *cfg)
356{
357 const struct encoder_data *data = &enc_data[cfg->rec_format];
358 CALL_FN_(data->convert_cfg, (struct encoder_config *)cfg, true);
359} /* encoder_config_to_global */
360
361bool enc_get_caps(const struct encoder_config *cfg,
362 struct encoder_caps *caps,
363 bool for_config)
364{
365 /* get_caps expects caps to be zeroed first */
366 memset(caps, 0, sizeof (*caps));
367
368 if (!rec_format_ok(cfg->rec_format))
369 return false;
370
371 if (enc_data[cfg->rec_format].get_caps)
372 {
373 enc_data[cfg->rec_format].get_caps(cfg, caps, for_config);
374 }
375 else
376 {
377 /* If no function provided...defaults to all */
378 caps->samplerate_caps = SAMPR_CAP_ALL_192;
379 caps->channel_caps = CHN_CAP_ALL;
380 }
381
382 return true;
383} /* enc_get_caps */
384
385/* Initializes the config struct with default values */
386bool enc_init_config(struct encoder_config *cfg)
387{
388 if (!rec_format_ok(cfg->rec_format))
389 return false;
390 CALL_FN_(enc_data[cfg->rec_format].default_cfg, cfg);
391 return true;
392} /* enc_init_config */
393
394/** Encoder Menus **/
395#if 0
396bool enc_config_menu(struct encoder_config *cfg)
397{
398 if (!rec_format_ok(cfg->rec_format))
399 return false;
400 if (enc_data[cfg->rec_format].menu)
401 {
402 menu_callback_data.cfg = &cfg;
403 menu_callback_data.global = false;
404 return do_menu(enc_data[cfg->rec_format].menu, NULL, NULL, false)
405 == MENU_ATTACHED_USB;
406 }
407 else
408 {
409 splash(HZ, ID2P(LANG_NO_SETTINGS));
410 return false;
411 }
412} /* enc_config_menu */
413#endif
414
415/** Global Settings **/
416
417/* Reset all codecs to defaults */
418void enc_global_settings_reset(void)
419{
420 struct encoder_config cfg;
421 cfg.rec_format = 0;
422
423 do
424 {
425 global_to_encoder_config(&cfg);
426 enc_init_config(&cfg);
427 encoder_config_to_global(&cfg);
428 if (cfg.rec_format == global_settings.rec_format)
429 enc_rec_settings_changed(&cfg);
430 }
431 while (++cfg.rec_format < REC_NUM_FORMATS);
432} /* enc_global_settings_reset */
433
434/* Apply new settings */
435void enc_global_settings_apply(void)
436{
437 struct encoder_config cfg;
438 if (!rec_format_ok(global_settings.rec_format))
439 global_settings.rec_format = REC_FORMAT_DEFAULT;
440
441 cfg.rec_format = global_settings.rec_format;
442 global_to_encoder_config(&cfg);
443 enc_rec_settings_changed(&cfg);
444 encoder_config_to_global(&cfg);
445} /* enc_global_settings_apply */
446
447/* Show an encoder's config menu based on the global_settings.
448 Modified settings are placed in global_settings.enc_config. */
449int enc_global_config_menu(void)
450{
451 struct encoder_config cfg;
452
453 if (!rec_format_ok(global_settings.rec_format))
454 global_settings.rec_format = REC_FORMAT_DEFAULT;
455
456 cfg.rec_format = global_settings.rec_format;
457
458 if (enc_data[cfg.rec_format].menu)
459 {
460 menu_callback_data.cfg = &cfg;
461 menu_callback_data.global = true;
462 int retmenu = do_menu(enc_data[cfg.rec_format].menu, NULL, NULL, false);
463 return (retmenu == MENU_ATTACHED_USB) ? 1 : 0;
464 }
465 else
466 {
467 splash(HZ, ID2P(LANG_NO_SETTINGS));
468 return 0;
469 }
470} /* enc_global_config_menu */