diff options
Diffstat (limited to 'apps/enc_config.c')
-rw-r--r-- | apps/enc_config.c | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/apps/enc_config.c b/apps/enc_config.c new file mode 100644 index 0000000000..384e679c42 --- /dev/null +++ b/apps/enc_config.c | |||
@@ -0,0 +1,432 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 Michael Sevakis | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include <stdio.h> | ||
20 | #include <sprintf.h> | ||
21 | #include <string.h> | ||
22 | #include "config.h" | ||
23 | #include "atoi.h" | ||
24 | #include "lang.h" | ||
25 | #include "misc.h" | ||
26 | #include "talk.h" | ||
27 | #include "general.h" | ||
28 | #include "codecs.h" | ||
29 | #include "menu.h" | ||
30 | #include "statusbar.h" | ||
31 | #include "settings.h" | ||
32 | #include "audio.h" | ||
33 | #include "pcm_record.h" | ||
34 | #include "enc_config.h" | ||
35 | |||
36 | #define MENU_ITEM_FN(fn) \ | ||
37 | ((bool (*)(void))fn) | ||
38 | |||
39 | #define ENC_MENU_ITEM_FN(fn) \ | ||
40 | ((bool (*)(struct encoder_config *))fn) | ||
41 | |||
42 | #define CALL_FN_(fn, ...) \ | ||
43 | if (fn) fn(__VA_ARGS__) | ||
44 | |||
45 | static bool enc_run_menu(int m, const struct menu_item items[], | ||
46 | struct encoder_config *cfg); | ||
47 | static bool enc_no_config_menu(struct encoder_config *cfg); | ||
48 | |||
49 | /** Function definitions for each codec - add these to enc_data | ||
50 | list following the definitions **/ | ||
51 | |||
52 | /** mp3_enc.codec **/ | ||
53 | /* mp3_enc: return encoder capabilities */ | ||
54 | static void mp3_enc_get_caps(const struct encoder_config *cfg, | ||
55 | struct encoder_caps *caps, | ||
56 | bool for_config) | ||
57 | { | ||
58 | int i; | ||
59 | unsigned long bitr; | ||
60 | |||
61 | if (!for_config) | ||
62 | { | ||
63 | /* Overall encoder capabilities */ | ||
64 | caps->samplerate_caps = MPEG1_SAMPR_CAPS | MPEG2_SAMPR_CAPS; | ||
65 | caps->channel_caps = CHN_CAP_ALL; | ||
66 | return; | ||
67 | } | ||
68 | |||
69 | /* Restrict caps based on config */ | ||
70 | i = round_value_to_list32(cfg->mp3_enc.bitrate, mp3_enc_bitr, | ||
71 | MP3_ENC_NUM_BITR, false); | ||
72 | bitr = mp3_enc_bitr[i]; | ||
73 | |||
74 | /* sample rate caps */ | ||
75 | |||
76 | /* check if MPEG1 sample rates are available */ | ||
77 | if ((bitr >= 32 && bitr <= 128) || bitr >= 160) | ||
78 | caps->samplerate_caps |= MPEG1_SAMPR_CAPS; | ||
79 | |||
80 | /* check if MPEG2 sample rates and mono are available */ | ||
81 | if (bitr <= 160) | ||
82 | { | ||
83 | caps->samplerate_caps |= MPEG2_SAMPR_CAPS; | ||
84 | caps->channel_caps |= CHN_CAP_MONO; | ||
85 | } | ||
86 | |||
87 | /* check if stereo is available */ | ||
88 | if (bitr >= 32) | ||
89 | caps->channel_caps |= CHN_CAP_STEREO; | ||
90 | } /* mp3_enc_get_caps */ | ||
91 | |||
92 | /* mp3_enc: return the default configuration */ | ||
93 | static void mp3_enc_default_config(struct encoder_config *cfg) | ||
94 | { | ||
95 | cfg->mp3_enc.bitrate = 128; /* default that works for all types */ | ||
96 | } /* mp3_enc_default_config */ | ||
97 | |||
98 | static void mp3_enc_convert_config(struct encoder_config *cfg, | ||
99 | bool to_encoder) | ||
100 | { | ||
101 | if (to_encoder) | ||
102 | { | ||
103 | if ((unsigned)global_settings.mp3_enc_config.bitrate > MP3_ENC_NUM_BITR) | ||
104 | global_settings.mp3_enc_config.bitrate = MP3_ENC_BITRATE_CFG_DEFAULT; | ||
105 | cfg->mp3_enc.bitrate = mp3_enc_bitr[global_settings.mp3_enc_config.bitrate]; | ||
106 | } | ||
107 | else | ||
108 | { | ||
109 | global_settings.mp3_enc_config.bitrate = | ||
110 | round_value_to_list32(cfg->mp3_enc.bitrate, mp3_enc_bitr, | ||
111 | MP3_ENC_NUM_BITR, false); | ||
112 | } | ||
113 | } /* mp3_enc_convert_config */ | ||
114 | |||
115 | /* mp3_enc: show the bitrate setting options */ | ||
116 | static bool mp3_enc_bitrate(struct encoder_config *cfg) | ||
117 | { | ||
118 | static const struct opt_items items[] = | ||
119 | { | ||
120 | /* Available in MPEG Version: */ | ||
121 | #ifdef HAVE_MPEG2_SAMPR | ||
122 | #if 0 | ||
123 | /* this sounds awful no matter what */ | ||
124 | { "8 kBit/s", TALK_ID(8, UNIT_KBIT) }, /* 2 */ | ||
125 | #endif | ||
126 | /* mono only */ | ||
127 | { "16 kBit/s", TALK_ID(16, UNIT_KBIT) }, /* 2 */ | ||
128 | { "24 kBit/s", TALK_ID(24, UNIT_KBIT) }, /* 2 */ | ||
129 | #endif | ||
130 | /* stereo/mono */ | ||
131 | { "32 kBit/s", TALK_ID(32, UNIT_KBIT) }, /* 1,2 */ | ||
132 | { "40 kBit/s", TALK_ID(40, UNIT_KBIT) }, /* 1,2 */ | ||
133 | { "48 kBit/s", TALK_ID(48, UNIT_KBIT) }, /* 1,2 */ | ||
134 | { "56 kBit/s", TALK_ID(56, UNIT_KBIT) }, /* 1,2 */ | ||
135 | { "64 kBit/s", TALK_ID(64, UNIT_KBIT) }, /* 1,2 */ | ||
136 | { "80 kBit/s", TALK_ID(80, UNIT_KBIT) }, /* 1,2 */ | ||
137 | { "96 kBit/s", TALK_ID(96, UNIT_KBIT) }, /* 1,2 */ | ||
138 | { "112 kBit/s", TALK_ID(112, UNIT_KBIT) }, /* 1,2 */ | ||
139 | { "128 kBit/s", TALK_ID(128, UNIT_KBIT) }, /* 1,2 */ | ||
140 | #if 0 | ||
141 | /* oddball MPEG2-only rate stuck in the middle */ | ||
142 | { "144 kBit/s", TALK_ID(144, UNIT_KBIT) }, /* 2 */ | ||
143 | #endif | ||
144 | { "160 kBit/s", TALK_ID(160, UNIT_KBIT) }, /* 1,2 */ | ||
145 | /* stereo only */ | ||
146 | { "192 kBit/s", TALK_ID(192, UNIT_KBIT) }, /* 1 */ | ||
147 | { "224 kBit/s", TALK_ID(224, UNIT_KBIT) }, /* 1 */ | ||
148 | { "256 kBit/s", TALK_ID(256, UNIT_KBIT) }, /* 1 */ | ||
149 | { "320 kBit/s", TALK_ID(320, UNIT_KBIT) }, /* 1 */ | ||
150 | }; | ||
151 | |||
152 | unsigned long rate_list[ARRAYLEN(items)]; | ||
153 | |||
154 | /* This is rather constant based upon the build but better than | ||
155 | storing and maintaining yet another list of numbers */ | ||
156 | int n_rates = make_list_from_caps32( | ||
157 | MPEG1_BITR_CAPS | MPEG2_BITR_CAPS, mp3_enc_bitr, | ||
158 | MPEG1_BITR_CAPS | ||
159 | #ifdef HAVE_MPEG2_SAMPR | ||
160 | | (MPEG2_BITR_CAPS & ~(MP3_BITR_CAP_144 | MP3_BITR_CAP_8)), | ||
161 | #endif | ||
162 | rate_list); | ||
163 | |||
164 | int index = round_value_to_list32(cfg->mp3_enc.bitrate, rate_list, | ||
165 | n_rates, false); | ||
166 | bool res = set_option(str(LANG_BITRATE), &index, INT, | ||
167 | items, n_rates, NULL); | ||
168 | index = round_value_to_list32(rate_list[index], mp3_enc_bitr, | ||
169 | MP3_ENC_NUM_BITR, false); | ||
170 | cfg->mp3_enc.bitrate = mp3_enc_bitr[index]; | ||
171 | |||
172 | return res; | ||
173 | } /* mp3_enc_bitrate */ | ||
174 | |||
175 | /* mp3_enc: show the configuration menu */ | ||
176 | static bool mp3_enc_menu(struct encoder_config *cfg) | ||
177 | { | ||
178 | static const struct menu_item items[] = | ||
179 | { | ||
180 | { ID2P(LANG_BITRATE), MENU_ITEM_FN(mp3_enc_bitrate) } | ||
181 | }; | ||
182 | |||
183 | bool result; | ||
184 | int m = menu_init(items, ARRAYLEN(items), NULL, NULL, NULL, NULL); | ||
185 | result = enc_run_menu(m, items, cfg); | ||
186 | menu_exit(m); | ||
187 | return result; | ||
188 | } /* mp3_enc_menu */ | ||
189 | |||
190 | /** wav_enc.codec **/ | ||
191 | /* wav_enc: show the configuration menu */ | ||
192 | #if 0 | ||
193 | static bool wav_enc_menu(struct encoder_config *cfg); | ||
194 | #endif | ||
195 | |||
196 | /** wavpack_enc.codec **/ | ||
197 | /* wavpack_enc: show the configuration menu */ | ||
198 | #if 0 | ||
199 | static bool wavpack_enc_menu(struct encoder_config *cfg); | ||
200 | #endif | ||
201 | |||
202 | /** config function pointers and/or data for each codec **/ | ||
203 | static const struct encoder_data | ||
204 | { | ||
205 | void (*get_caps)(const struct encoder_config *, struct encoder_caps *, | ||
206 | bool); | ||
207 | void (*default_cfg)(struct encoder_config *); | ||
208 | void (*convert_cfg)(struct encoder_config *, bool to_encoder); | ||
209 | bool (*menu)(struct encoder_config *); | ||
210 | } enc_data[REC_NUM_FORMATS] = | ||
211 | { | ||
212 | /* mp3_enc.codec */ | ||
213 | [REC_FORMAT_MPA_L3] = { | ||
214 | mp3_enc_get_caps, | ||
215 | mp3_enc_default_config, | ||
216 | mp3_enc_convert_config, | ||
217 | mp3_enc_menu, | ||
218 | }, | ||
219 | /* wav_enc.codec */ | ||
220 | [REC_FORMAT_PCM_WAV] = { | ||
221 | NULL, | ||
222 | NULL, | ||
223 | NULL, | ||
224 | enc_no_config_menu, | ||
225 | }, | ||
226 | /* wavpack_enc.codec */ | ||
227 | [REC_FORMAT_WAVPACK] = { | ||
228 | NULL, | ||
229 | NULL, | ||
230 | NULL, | ||
231 | enc_no_config_menu, | ||
232 | }, | ||
233 | }; | ||
234 | |||
235 | static inline bool rec_format_ok(int rec_format) | ||
236 | { | ||
237 | return (unsigned)rec_format < REC_NUM_FORMATS; | ||
238 | } | ||
239 | |||
240 | static bool enc_run_menu(int m, const struct menu_item items[], | ||
241 | struct encoder_config *cfg) | ||
242 | { | ||
243 | int selected; | ||
244 | while (1) | ||
245 | { | ||
246 | switch (selected=menu_show(m)) | ||
247 | { | ||
248 | case MENU_SELECTED_EXIT: | ||
249 | return false; | ||
250 | |||
251 | case MENU_ATTACHED_USB: | ||
252 | return true; | ||
253 | |||
254 | default: | ||
255 | if (items[selected].function && | ||
256 | ENC_MENU_ITEM_FN(items[selected].function)(cfg)) | ||
257 | return true; | ||
258 | gui_syncstatusbar_draw(&statusbars, true); | ||
259 | } | ||
260 | } | ||
261 | } /* enc_run_menu */ | ||
262 | |||
263 | /* menu created when encoder has no configuration options */ | ||
264 | static bool enc_no_config_menu(struct encoder_config *cfg) | ||
265 | { | ||
266 | static const struct menu_item items[] = | ||
267 | { | ||
268 | { ID2P(LANG_NO_SETTINGS), NULL } | ||
269 | }; | ||
270 | int m; | ||
271 | bool result; | ||
272 | |||
273 | m = menu_init(items, ARRAYLEN(items), NULL, NULL, NULL, NULL); | ||
274 | result = enc_run_menu(m, items, NULL); | ||
275 | menu_exit(m); | ||
276 | |||
277 | return result; | ||
278 | (void)cfg; | ||
279 | } /* enc_no_config_menu */ | ||
280 | |||
281 | /* update settings dependent upon encoder settings */ | ||
282 | static void enc_rec_settings_changed(struct encoder_config *cfg) | ||
283 | { | ||
284 | struct encoder_config enc_config; | ||
285 | struct encoder_caps caps; | ||
286 | long table[MAX(CHN_NUM_MODES, REC_NUM_FREQ)]; | ||
287 | int n; | ||
288 | |||
289 | if (cfg == NULL) | ||
290 | { | ||
291 | cfg = &enc_config; | ||
292 | cfg->rec_format = global_settings.rec_format; | ||
293 | global_to_encoder_config(cfg); | ||
294 | } | ||
295 | |||
296 | /* have to sync other settings when encoder settings change */ | ||
297 | if (!enc_get_caps(cfg, &caps, true)) | ||
298 | return; | ||
299 | |||
300 | /* rec_channels */ | ||
301 | n = make_list_from_caps32(CHN_CAP_ALL, NULL, | ||
302 | caps.channel_caps, table); | ||
303 | |||
304 | /* no zero check needed: encoder must support at least one | ||
305 | sample rate that recording supports or it shouldn't be in | ||
306 | available in the recording options */ | ||
307 | n = round_value_to_list32(global_settings.rec_channels, | ||
308 | table, n, true); | ||
309 | global_settings.rec_channels = table[n]; | ||
310 | |||
311 | /* rec_frequency */ | ||
312 | n = make_list_from_caps32(REC_SAMPR_CAPS, rec_freq_sampr, | ||
313 | caps.samplerate_caps, table); | ||
314 | |||
315 | n = round_value_to_list32( | ||
316 | rec_freq_sampr[global_settings.rec_frequency], | ||
317 | table, n, false); | ||
318 | |||
319 | global_settings.rec_frequency = round_value_to_list32( | ||
320 | table[n], rec_freq_sampr, REC_NUM_FREQ, false); | ||
321 | } /* enc_rec_settings_changed */ | ||
322 | |||
323 | /** public stuff **/ | ||
324 | void global_to_encoder_config(struct encoder_config *cfg) | ||
325 | { | ||
326 | const struct encoder_data *data = &enc_data[cfg->rec_format]; | ||
327 | CALL_FN_(data->convert_cfg, cfg, true); | ||
328 | } /* global_to_encoder_config */ | ||
329 | |||
330 | void encoder_config_to_global(const struct encoder_config *cfg) | ||
331 | { | ||
332 | const struct encoder_data *data = &enc_data[cfg->rec_format]; | ||
333 | CALL_FN_(data->convert_cfg, (struct encoder_config *)cfg, false); | ||
334 | } /* encoder_config_to_global */ | ||
335 | |||
336 | bool enc_get_caps(const struct encoder_config *cfg, | ||
337 | struct encoder_caps *caps, | ||
338 | bool for_config) | ||
339 | { | ||
340 | /* get_caps expects caps to be zeroed first */ | ||
341 | memset(caps, 0, sizeof (*caps)); | ||
342 | |||
343 | if (!rec_format_ok(cfg->rec_format)) | ||
344 | return false; | ||
345 | |||
346 | if (enc_data[cfg->rec_format].get_caps) | ||
347 | { | ||
348 | enc_data[cfg->rec_format].get_caps(cfg, caps, for_config); | ||
349 | } | ||
350 | else | ||
351 | { | ||
352 | /* If no function provided...defaults to all */ | ||
353 | caps->samplerate_caps = SAMPR_CAP_ALL; | ||
354 | caps->channel_caps = CHN_CAP_ALL; | ||
355 | } | ||
356 | |||
357 | return true; | ||
358 | } /* enc_get_caps */ | ||
359 | |||
360 | /* Initializes the config struct with default values */ | ||
361 | bool enc_init_config(struct encoder_config *cfg) | ||
362 | { | ||
363 | if (!rec_format_ok(cfg->rec_format)) | ||
364 | return false; | ||
365 | CALL_FN_(enc_data[cfg->rec_format].default_cfg, cfg); | ||
366 | return true; | ||
367 | } /* enc_init_config */ | ||
368 | |||
369 | /** Encoder Menus **/ | ||
370 | bool enc_config_menu(struct encoder_config *cfg) | ||
371 | { | ||
372 | if (!rec_format_ok(cfg->rec_format)) | ||
373 | return false; | ||
374 | return enc_data[cfg->rec_format].menu(cfg); | ||
375 | } /* enc_config_menu */ | ||
376 | |||
377 | /** Global Settings **/ | ||
378 | |||
379 | /* Reset all codecs to defaults */ | ||
380 | void enc_global_settings_reset(void) | ||
381 | { | ||
382 | struct encoder_config cfg; | ||
383 | cfg.rec_format = 0; | ||
384 | |||
385 | do | ||
386 | { | ||
387 | global_to_encoder_config(&cfg); | ||
388 | enc_init_config(&cfg); | ||
389 | encoder_config_to_global(&cfg); | ||
390 | if (cfg.rec_format == global_settings.rec_format) | ||
391 | enc_rec_settings_changed(&cfg); | ||
392 | } | ||
393 | while (++cfg.rec_format < REC_NUM_FORMATS); | ||
394 | } /* enc_global_settings_reset */ | ||
395 | |||
396 | /* Apply new settings */ | ||
397 | void enc_global_settings_apply(void) | ||
398 | { | ||
399 | struct encoder_config cfg; | ||
400 | if (!rec_format_ok(global_settings.rec_format)) | ||
401 | global_settings.rec_format = REC_FORMAT_DEFAULT; | ||
402 | |||
403 | cfg.rec_format = global_settings.rec_format; | ||
404 | global_to_encoder_config(&cfg); | ||
405 | enc_rec_settings_changed(&cfg); | ||
406 | encoder_config_to_global(&cfg); | ||
407 | } /* enc_global_settings_apply */ | ||
408 | |||
409 | /* Show an encoder's config menu based on the global_settings. | ||
410 | Modified settings are placed in global_settings.enc_config. */ | ||
411 | bool enc_global_config_menu(void) | ||
412 | { | ||
413 | struct encoder_config cfg; | ||
414 | |||
415 | bool res; | ||
416 | |||
417 | if (!rec_format_ok(global_settings.rec_format)) | ||
418 | global_settings.rec_format = REC_FORMAT_DEFAULT; | ||
419 | |||
420 | cfg.rec_format = global_settings.rec_format; | ||
421 | |||
422 | global_to_encoder_config(&cfg); | ||
423 | |||
424 | res = enc_config_menu(&cfg); | ||
425 | if (!res) | ||
426 | { | ||
427 | enc_rec_settings_changed(&cfg); | ||
428 | encoder_config_to_global(&cfg); | ||
429 | } | ||
430 | |||
431 | return res; | ||
432 | } /* enc_global_config_menu */ | ||