Manual: Bookmarking update (FS#11227 by Magnus Holmgren)
[kugel-rb.git] / apps / plugins / test_sampr.c
blob1f6ee351e13421d8344da549d670d27f46658011
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 Michael Sevakis
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
23 /* This plugin generates a 1kHz tone + noise in order to quickly verify
24 * hardware samplerate setup is operating correctly.
26 * While switching to different frequencies, the pitch of the tone should
27 * remain constant whereas the upper harmonics of the noise should vary
28 * with sample rate.
31 PLUGIN_HEADER
32 PLUGIN_IRAM_DECLARE;
34 static int hw_freq IDATA_ATTR = HW_FREQ_DEFAULT;
35 static unsigned long hw_sampr IDATA_ATTR = HW_SAMPR_DEFAULT;
37 static int gen_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)] IBSS_ATTR;
38 static bool gen_quit IBSS_ATTR;
39 static unsigned int gen_thread_id;
41 #define OUTPUT_CHUNK_COUNT (1 << 1)
42 #define OUTPUT_CHUNK_MASK (OUTPUT_CHUNK_COUNT-1)
43 #define OUTPUT_CHUNK_SAMPLES 1152
44 #define OUTPUT_CHUNK_SIZE (OUTPUT_CHUNK_SAMPLES*sizeof(int16_t)*2)
45 static uint16_t output_buf[OUTPUT_CHUNK_COUNT][OUTPUT_CHUNK_SAMPLES*2]
46 __attribute__((aligned(4)));
47 static int output_head IBSS_ATTR;
48 static int output_tail IBSS_ATTR;
49 static int output_step IBSS_ATTR;
51 static uint32_t gen_phase_step IBSS_ATTR;
52 static const uint32_t gen_frequency = 1000;
54 /* fsin shamelessly stolen from signal_gen.c by Thom Johansen (preglow) */
56 /* Good quality sine calculated by linearly interpolating
57 * a 128 sample sine table. First harmonic has amplitude of about -84 dB.
58 * phase has range from 0 to 0xffffffff, representing 0 and
59 * 2*pi respectively.
60 * Return value is a signed value from LONG_MIN to LONG_MAX, representing
61 * -1 and 1 respectively.
63 static int16_t ICODE_ATTR fsin(uint32_t phase)
65 /* 128 sixteen bit sine samples + guard point */
66 static const int16_t sinetab[129] ICONST_ATTR =
68 0, 1607, 3211, 4807, 6392, 7961, 9511, 11038,
69 12539, 14009, 15446, 16845, 18204, 19519, 20787, 22004,
70 23169, 24278, 25329, 26318, 27244, 28105, 28897, 29621,
71 30272, 30851, 31356, 31785, 32137, 32412, 32609, 32727,
72 32767, 32727, 32609, 32412, 32137, 31785, 31356, 30851,
73 30272, 29621, 28897, 28105, 27244, 26318, 25329, 24278,
74 23169, 22004, 20787, 19519, 18204, 16845, 15446, 14009,
75 12539, 11038, 9511, 7961, 6392, 4807, 3211, 1607,
76 0, -1607, -3211, -4807, -6392, -7961, -9511, -11038,
77 -12539, -14009, -15446, -16845, -18204, -19519, -20787, -22004,
78 -23169, -24278, -25329, -26318, -27244, -28105, -28897, -29621,
79 -30272, -30851, -31356, -31785, -32137, -32412, -32609, -32727,
80 -32767, -32727, -32609, -32412, -32137, -31785, -31356, -30851,
81 -30272, -29621, -28897, -28105, -27244, -26318, -25329, -24278,
82 -23169, -22004, -20787, -19519, -18204, -16845, -15446, -14009,
83 -12539, -11038, -9511, -7961, -6392, -4807, -3211, -1607,
87 unsigned int pos = phase >> 25;
88 unsigned short frac = (phase & 0x01ffffff) >> 9;
89 short diff = sinetab[pos + 1] - sinetab[pos];
91 return sinetab[pos] + (frac*diff >> 16);
94 /* ISR handler to get next block of data */
95 static void get_more(unsigned char **start, size_t *size)
97 /* Free previous buffer */
98 output_head += output_step;
99 output_step = 0;
101 *start = (unsigned char *)output_buf[output_head & OUTPUT_CHUNK_MASK];
102 *size = OUTPUT_CHUNK_SIZE;
104 /* Keep repeating previous if source runs low */
105 if (output_head != output_tail)
106 output_step = 1;
109 static void ICODE_ATTR gen_thread_func(void)
111 uint32_t gen_random = *rb->current_tick;
112 uint32_t gen_phase = 0;
114 while (!gen_quit)
116 int16_t *p = output_buf[output_tail & OUTPUT_CHUNK_MASK];
117 int i = OUTPUT_CHUNK_SAMPLES;
119 while (output_tail - output_head >= OUTPUT_CHUNK_COUNT)
121 rb->sleep(0);
122 if (gen_quit)
123 return;
126 while (--i >= 0)
128 int32_t val = fsin(gen_phase);
129 int32_t rnd = (int16_t)gen_random;
131 gen_random = gen_random*0x0019660dL + 0x3c6ef35fL;
133 val = (rnd + 2*val) / 3;
135 *p++ = val;
136 *p++ = val;
138 gen_phase += gen_phase_step;
141 output_tail++;
143 rb->yield();
147 static void update_gen_step(void)
149 gen_phase_step = 0x100000000ull*gen_frequency / hw_sampr;
152 static void output_clear(void)
154 rb->pcm_play_lock();
156 rb->memset(output_buf, 0, sizeof (output_buf));
157 output_head = 0;
158 output_tail = 0;
160 rb->pcm_play_unlock();
163 /* Called to switch samplerate on the fly */
164 static void set_frequency(int index)
166 hw_freq = index;
167 hw_sampr = rb->hw_freq_sampr[index];
169 output_clear();
170 update_gen_step();
172 rb->pcm_set_frequency(hw_sampr);
173 rb->pcm_apply_settings();
176 #ifndef HAVE_VOLUME_IN_LIST
177 static void set_volume(int value)
179 rb->global_settings->volume = value;
180 rb->sound_set(SOUND_VOLUME, value);
183 static const char *format_volume(char *buf, size_t len, int value,
184 const char *unit)
186 (void)unit;
187 rb->snprintf(buf, len, "%d %s", rb->sound_val2phys(SOUND_VOLUME, value),
188 rb->sound_unit(SOUND_VOLUME));
189 return buf;
191 #endif /* HAVE_VOLUME_IN_LIST */
193 static void play_tone(bool volume_set)
195 static struct opt_items names[HW_NUM_FREQ] =
197 HW_HAVE_96_([HW_FREQ_96] = { "96kHz", -1 },)
198 HW_HAVE_88_([HW_FREQ_88] = { "88.2kHz", -1 },)
199 HW_HAVE_64_([HW_FREQ_64] = { "64kHz", -1 },)
200 HW_HAVE_48_([HW_FREQ_48] = { "48kHz", -1 },)
201 HW_HAVE_44_([HW_FREQ_44] = { "44.1kHz", -1 },)
202 HW_HAVE_32_([HW_FREQ_32] = { "32kHz", -1 },)
203 HW_HAVE_24_([HW_FREQ_24] = { "24kHz", -1 },)
204 HW_HAVE_22_([HW_FREQ_22] = { "22.05kHz", -1 },)
205 HW_HAVE_16_([HW_FREQ_16] = { "16kHz", -1 },)
206 HW_HAVE_12_([HW_FREQ_12] = { "12kHz", -1 },)
207 HW_HAVE_11_([HW_FREQ_11] = { "11.025kHz", -1 },)
208 HW_HAVE_8_( [HW_FREQ_8 ] = { "8kHz", -1 },)
211 int freq = hw_freq;
213 rb->audio_stop();
215 #if INPUT_SRC_CAPS != 0
216 /* Select playback */
217 rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
218 #endif
220 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
221 rb->cpu_boost(true);
222 #endif
224 rb->pcm_set_frequency(rb->hw_freq_sampr[freq]);
226 #if INPUT_SRC_CAPS != 0
227 /* Recordable targets can play back from other sources */
228 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
229 #endif
231 gen_quit = false;
232 output_clear();
233 update_gen_step();
235 gen_thread_id = rb->create_thread(gen_thread_func, gen_thread_stack,
236 sizeof(gen_thread_stack), 0,
237 "test_sampr generator"
238 IF_PRIO(, PRIORITY_PLAYBACK)
239 IF_COP(, CPU));
241 rb->pcm_play_data(get_more, NULL, 0);
243 #ifndef HAVE_VOLUME_IN_LIST
244 if (volume_set)
246 int volume = rb->global_settings->volume;
248 rb->set_int("Volume", NULL, -1, &volume,
249 set_volume, 1, rb->sound_min(SOUND_VOLUME),
250 rb->sound_max(SOUND_VOLUME), format_volume);
252 else
253 #endif /* HAVE_VOLUME_IN_LIST */
255 rb->set_option("Sample Rate", &freq, INT, names,
256 HW_NUM_FREQ, set_frequency);
257 (void)volume_set;
260 gen_quit = true;
262 rb->thread_wait(gen_thread_id);
264 rb->pcm_play_stop();
266 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
267 rb->cpu_boost(false);
268 #endif
270 /* restore default - user of apis is responsible for restoring
271 default state - normally playback at 44100Hz */
272 rb->pcm_set_frequency(HW_FREQ_DEFAULT);
275 /* Tests hardware sample rate switching */
276 /* TODO: needs a volume control */
277 enum plugin_status plugin_start(const void *parameter)
279 enum
281 __TEST_SAMPR_MENUITEM_FIRST = -1,
282 #ifndef HAVE_VOLUME_IN_LIST
283 MENU_VOL_SET,
284 #endif /* HAVE_VOLUME_IN_LIST */
285 MENU_SAMPR_SET,
286 MENU_QUIT,
289 MENUITEM_STRINGLIST(menu, "Test Sampr Menu", NULL,
290 #ifndef HAVE_VOLUME_IN_LIST
291 "Set Volume",
292 #endif /* HAVE_VOLUME_IN_LIST */
293 "Set Samplerate", "Quit");
295 bool exit = false;
296 int selected = 0;
298 /* Disable all talking before initializing IRAM */
299 rb->talk_disable(true);
301 PLUGIN_IRAM_INIT(rb);
303 while (!exit)
305 int result = rb->do_menu(&menu, &selected, NULL, false);
307 switch (result)
309 #ifndef HAVE_VOLUME_IN_LIST
310 case MENU_VOL_SET:
311 play_tone(true);
312 break;
313 #endif /* HAVE_VOLUME_IN_LIST */
314 case MENU_SAMPR_SET:
315 play_tone(false);
316 break;
318 case MENU_QUIT:
319 exit = true;
320 break;
324 rb->talk_disable(false);
326 return PLUGIN_OK;
327 (void)parameter;