summaryrefslogtreecommitdiff
path: root/apps/plugins/test_sampr.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/test_sampr.c')
-rw-r--r--apps/plugins/test_sampr.c410
1 files changed, 224 insertions, 186 deletions
diff --git a/apps/plugins/test_sampr.c b/apps/plugins/test_sampr.c
index 01f24b5987..77f9b8a779 100644
--- a/apps/plugins/test_sampr.c
+++ b/apps/plugins/test_sampr.c
@@ -21,122 +21,177 @@
21#include "plugin.h" 21#include "plugin.h"
22#include "lib/oldmenuapi.h" 22#include "lib/oldmenuapi.h"
23 23
24/* This plugin generates a 1kHz tone + noise in order to quickly verify
25 * hardware samplerate setup is operating correctly.
26 *
27 * While switching to different frequencies, the pitch of the tone should
28 * remain constant whereas the upper harmonics of the noise should vary
29 * with sample rate.
30 */
31
24PLUGIN_HEADER 32PLUGIN_HEADER
33PLUGIN_IRAM_DECLARE;
25 34
26const struct plugin_api *rb; 35const struct plugin_api *rb;
27 36
28enum 37static int hw_freq IDATA_ATTR = HW_FREQ_DEFAULT;
29{ 38static unsigned long hw_sampr IDATA_ATTR = HW_SAMPR_DEFAULT;
30 TONE_SINE = 0, 39
31 TONE_TRIANGLE, 40static int gen_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)] IBSS_ATTR;
32 TONE_SAWTOOTH, 41static bool gen_quit IBSS_ATTR;
33 TONE_SQUARE, 42static struct thread_entry *gen_thread_p;
34 NUM_WAVEFORMS 43
35}; 44#define OUTPUT_CHUNK_COUNT (1 << 1)
36 45#define OUTPUT_CHUNK_MASK (OUTPUT_CHUNK_COUNT-1)
37static int freq = HW_FREQ_DEFAULT; 46#define OUTPUT_CHUNK_SAMPLES 1152
38static int waveform = TONE_SINE; 47#define OUTPUT_CHUNK_SIZE (OUTPUT_CHUNK_SAMPLES*sizeof(int16_t)*2)
39 48static uint16_t output_buf[OUTPUT_CHUNK_COUNT][OUTPUT_CHUNK_SAMPLES*2]
40/* A441 at 44100Hz. Pitch will change with changing samplerate. 49 IBSS_ATTR __attribute__((aligned(4)));
41 Test different waveforms to detect any aliasing in signal which 50static int output_head IBSS_ATTR;
42 indicates duplicated/dropped samples */ 51static int output_tail IBSS_ATTR;
43static const int16_t A441[NUM_WAVEFORMS][100] = 52static int output_step IBSS_ATTR;
53
54static uint32_t gen_phase_step IBSS_ATTR;
55static const uint32_t gen_frequency = 1000;
56
57/* fsin shamelessly stolen from signal_gen.c by Thom Johansen (preglow) */
58
59/* Good quality sine calculated by linearly interpolating
60 * a 128 sample sine table. First harmonic has amplitude of about -84 dB.
61 * phase has range from 0 to 0xffffffff, representing 0 and
62 * 2*pi respectively.
63 * Return value is a signed value from LONG_MIN to LONG_MAX, representing
64 * -1 and 1 respectively.
65 */
66static int16_t ICODE_ATTR fsin(uint32_t phase)
44{ 67{
45 [TONE_SINE] = 68 /* 128 sixteen bit sine samples + guard point */
69 static const int16_t sinetab[129] ICONST_ATTR =
46 { 70 {
47 0, 2057, 4106, 6139, 8148, 71 0, 1607, 3211, 4807, 6392, 7961, 9511, 11038,
48 10125, 12062, 13951, 15785, 17557, 72 12539, 14009, 15446, 16845, 18204, 19519, 20787, 22004,
49 19259, 20886, 22430, 23886, 25247, 73 23169, 24278, 25329, 26318, 27244, 28105, 28897, 29621,
50 26509, 27666, 28713, 29648, 30465, 74 30272, 30851, 31356, 31785, 32137, 32412, 32609, 32727,
51 31163, 31737, 32186, 32508, 32702, 75 32767, 32727, 32609, 32412, 32137, 31785, 31356, 30851,
52 32767, 32702, 32508, 32186, 31737, 76 30272, 29621, 28897, 28105, 27244, 26318, 25329, 24278,
53 31163, 30465, 29648, 28713, 27666, 77 23169, 22004, 20787, 19519, 18204, 16845, 15446, 14009,
54 26509, 25247, 23886, 22430, 20886, 78 12539, 11038, 9511, 7961, 6392, 4807, 3211, 1607,
55 19259, 17557, 15785, 13951, 12062, 79 0, -1607, -3211, -4807, -6392, -7961, -9511, -11038,
56 10125, 8148, 6139, 4106, 2057, 80 -12539, -14009, -15446, -16845, -18204, -19519, -20787, -22004,
57 0, -2057, -4106, -6139, -8148, 81 -23169, -24278, -25329, -26318, -27244, -28105, -28897, -29621,
58 -10125, -12062, -13951, -15785, -17557, 82 -30272, -30851, -31356, -31785, -32137, -32412, -32609, -32727,
59 -19259, -20886, -22430, -23886, -25247, 83 -32767, -32727, -32609, -32412, -32137, -31785, -31356, -30851,
60 -26509, -27666, -28713, -29648, -30465, 84 -30272, -29621, -28897, -28105, -27244, -26318, -25329, -24278,
61 -31163, -31737, -32186, -32508, -32702, 85 -23169, -22004, -20787, -19519, -18204, -16845, -15446, -14009,
62 -32767, -32702, -32508, -32186, -31737, 86 -12539, -11038, -9511, -7961, -6392, -4807, -3211, -1607,
63 -31163, -30465, -29648, -28713, -27666, 87 0,
64 -26509, -25247, -23886, -22430, -20886, 88 };
65 -19259, -17557, -15785, -13951, -12062, 89
66 -10125, -8148, -6139, -4106, -2057, 90 unsigned int pos = phase >> 25;
67 }, 91 unsigned short frac = (phase & 0x01ffffff) >> 9;
68 [TONE_TRIANGLE] = 92 short diff = sinetab[pos + 1] - sinetab[pos];
69 { 93
70 0, 1310, 2621, 3932, 5242, 94 return sinetab[pos] + (frac*diff >> 16);
71 6553, 7864, 9174, 10485, 11796, 95}
72 13106, 14417, 15728, 17038, 18349, 96
73 19660, 20970, 22281, 23592, 24902, 97/* ISR handler to get next block of data */
74 26213, 27524, 28834, 30145, 31456, 98static void get_more(unsigned char **start, size_t *size)
75 32767, 31456, 30145, 28834, 27524, 99{
76 26213, 24902, 23592, 22281, 20970, 100 /* Free previous buffer */
77 19660, 18349, 17038, 15728, 14417, 101 output_head += output_step;
78 13106, 11796, 10485, 9174, 7864, 102 output_step = 0;
79 6553, 5242, 3932, 2621, 1310, 103
80 0, -1310, -2621, -3932, -5242, 104 *start = (unsigned char *)output_buf[output_head & OUTPUT_CHUNK_MASK];
81 -6553, -7864, -9174, -10485, -11796, 105 *size = OUTPUT_CHUNK_SIZE;
82 -13106, -14417, -15728, -17038, -18349, 106
83 -19660, -20970, -22281, -23592, -24902, 107 /* Keep repeating previous if source runs low */
84 -26213, -27524, -28834, -30145, -31456, 108 if (output_head != output_tail)
85 -32767, -31456, -30145, -28834, -27524, 109 output_step = 1;
86 -26213, -24902, -23592, -22281, -20970, 110}
87 -19660, -18349, -17038, -15728, -14417, 111
88 -13106, -11796, -10485, -9174, -7864, 112static void ICODE_ATTR gen_thread_func(void)
89 -6553, -5242, -3932, -2621, -1310, 113{
90 }, 114 uint32_t gen_random = *rb->current_tick;
91 [TONE_SAWTOOTH] = 115 uint32_t gen_phase = 0;
92 { 116
93 -32767, -32111, -31456, -30800, -30145, 117 while (!gen_quit)
94 -29490, -28834, -28179, -27524, -26868,
95 -26213, -25558, -24902, -24247, -23592,
96 -22936, -22281, -21626, -20970, -20315,
97 -19660, -19004, -18349, -17694, -17038,
98 -16383, -15728, -15072, -14417, -13762,
99 -13106, -12451, -11796, -11140, -10485,
100 -9830, -9174, -8519, -7864, -7208,
101 -6553, -5898, -5242, -4587, -3932,
102 -3276, -2621, -1966, -1310, -655,
103 0, 655, 1310, 1966, 2621,
104 3276, 3932, 4587, 5242, 5898,
105 6553, 7208, 7864, 8519, 9174,
106 9830, 10485, 11140, 11796, 12451,
107 13106, 13762, 14417, 15072, 15728,
108 16383, 17038, 17694, 18349, 19004,
109 19660, 20315, 20970, 21626, 22281,
110 22936, 23592, 24247, 24902, 25558,
111 26213, 26868, 27524, 28179, 28834,
112 29490, 30145, 30800, 31456, 32111,
113 },
114 [TONE_SQUARE] =
115 { 118 {
116 32767, 32767, 32767, 32767, 32767, 119 int16_t *p = output_buf[output_tail & OUTPUT_CHUNK_MASK];
117 32767, 32767, 32767, 32767, 32767, 120 int i = OUTPUT_CHUNK_SAMPLES;
118 32767, 32767, 32767, 32767, 32767, 121
119 32767, 32767, 32767, 32767, 32767, 122 while (output_tail - output_head >= OUTPUT_CHUNK_COUNT)
120 32767, 32767, 32767, 32767, 32767, 123 {
121 32767, 32767, 32767, 32767, 32767, 124 rb->sleep(0);
122 32767, 32767, 32767, 32767, 32767, 125 if (gen_quit)
123 32767, 32767, 32767, 32767, 32767, 126 return;
124 32767, 32767, 32767, 32767, 32767, 127 }
125 32767, 32767, 32767, 32767, 32767, 128
126 -32767, -32767, -32767, -32767, -32767, 129 while (--i >= 0)
127 -32767, -32767, -32767, -32767, -32767, 130 {
128 -32767, -32767, -32767, -32767, -32767, 131 int32_t val = fsin(gen_phase);
129 -32767, -32767, -32767, -32767, -32767, 132 int32_t rnd = (int16_t)gen_random;
130 -32767, -32767, -32767, -32767, -32767, 133
131 -32767, -32767, -32767, -32767, -32767, 134 gen_random = gen_random*0x0019660dL + 0x3c6ef35fL;
132 -32767, -32767, -32767, -32767, -32767, 135
133 -32767, -32767, -32767, -32767, -32767, 136 val = (rnd + 2*val) / 3;
134 -32767, -32767, -32767, -32767, -32767, 137
135 -32767, -32767, -32767, -32767, -32767, 138 *p++ = val;
139 *p++ = val;
140
141 gen_phase += gen_phase_step;
142 }
143
144 output_tail++;
145
146 rb->yield();
136 } 147 }
137}; 148}
149
150static void update_gen_step(void)
151{
152 gen_phase_step = 0x100000000ull*gen_frequency / hw_sampr;
153}
154
155static void output_clear(void)
156{
157 rb->pcm_play_lock();
158
159 rb->memset(output_buf, 0, sizeof (output_buf));
160 output_head = 0;
161 output_tail = 0;
162
163 rb->pcm_play_unlock();
164}
165
166/* Called to switch samplerate on the fly */
167static void set_frequency(int index)
168{
169 hw_freq = index;
170 hw_sampr = rb->hw_freq_sampr[index];
171
172 output_clear();
173 update_gen_step();
174
175 rb->pcm_set_frequency(hw_sampr);
176 rb->pcm_apply_settings();
177}
178
179#ifndef HAVE_VOLUME_IN_LIST
180static void set_volume(int value)
181{
182 rb->global_settings->volume = value;
183 rb->sound_set(SOUND_VOLUME, value);
184}
138 185
139void play_waveform(void) 186static void format_volume(char *buf, size_t len, int value, const char *unit)
187{
188 rb->snprintf(buf, len, "%d %s", rb->sound_val2phys(SOUND_VOLUME, value),
189 rb->sound_unit(SOUND_VOLUME));
190 (void)unit;
191}
192#endif /* HAVE_VOLUME_IN_LIST */
193
194static void play_tone(bool volume_set)
140{ 195{
141 static struct opt_items names[HW_NUM_FREQ] = 196 static struct opt_items names[HW_NUM_FREQ] =
142 { 197 {
@@ -154,52 +209,9 @@ void play_waveform(void)
154 HW_HAVE_8_( [HW_FREQ_8 ] = { "8kHz", -1 },) 209 HW_HAVE_8_( [HW_FREQ_8 ] = { "8kHz", -1 },)
155 }; 210 };
156 211
157 /* 50 cycles of wavform */ 212 int freq = hw_freq;
158 static int32_t audio[5000];
159
160 void init_audio(int type)
161 {
162 int i;
163 /* Signal amplitudes to adjust for somewhat equal percieved
164 volume */
165 int amps[NUM_WAVEFORMS] =
166 {
167 [TONE_SINE] = 8191,
168 [TONE_TRIANGLE] = 5119,
169 [TONE_SAWTOOTH] = 2047,
170 [TONE_SQUARE] = 1535
171 };
172
173 /* Initialize one cycle of the waveform */
174 for (i = 0; i < 100; i++)
175 {
176 uint16_t val = amps[type]*A441[type][i]/32767;
177 audio[i] = (val << 16) | val;
178 }
179
180 /* Duplicate it 49 more times */
181 for (i = 1; i < 50; i++)
182 {
183 rb->memcpy(audio + i*100, audio, 100*sizeof(int32_t));
184 }
185 }
186
187 /* ISR handler to get next block of data */
188 void get_more(unsigned char **start, size_t *size)
189 {
190 *start = (unsigned char *)audio;
191 *size = sizeof (audio);
192 }
193
194 /* Called to switch samplerate on the fly */
195 void set_frequency(int index)
196 {
197 rb->pcm_set_frequency(rb->hw_freq_sampr[index]);
198 rb->pcm_apply_settings();
199 }
200 213
201 rb->audio_stop(); 214 rb->audio_stop();
202 rb->sound_set(SOUND_VOLUME, rb->sound_default(SOUND_VOLUME));
203 215
204#if INPUT_SRC_CAPS != 0 216#if INPUT_SRC_CAPS != 0
205 /* Select playback */ 217 /* Select playback */
@@ -217,18 +229,40 @@ void play_waveform(void)
217 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); 229 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
218#endif 230#endif
219 231
220 rb->pcm_apply_settings(); 232 gen_quit = false;
233 output_clear();
234 update_gen_step();
235
236 gen_thread_p = rb->create_thread(gen_thread_func, gen_thread_stack,
237 sizeof(gen_thread_stack), 0,
238 "test_sampr generator"
239 IF_PRIO(, PRIORITY_PLAYBACK)
240 IF_COP(, CPU));
221 241
222 init_audio(waveform);
223 rb->pcm_play_data(get_more, NULL, 0); 242 rb->pcm_play_data(get_more, NULL, 0);
224 243
225 rb->set_option("Sample Rate", &freq, INT, names, 244#ifndef HAVE_VOLUME_IN_LIST
226 HW_NUM_FREQ, set_frequency); 245 if (volume_set)
246 {
247 int volume = rb->global_settings->volume;
227 248
228 rb->pcm_play_stop(); 249 rb->set_int("Volume", NULL, -1, &volume,
250 set_volume, 1, rb->sound_min(SOUND_VOLUME),
251 rb->sound_max(SOUND_VOLUME), format_volume);
252 }
253 else
254#endif /* HAVE_VOLUME_IN_LIST */
255 {
256 rb->set_option("Sample Rate", &freq, INT, names,
257 HW_NUM_FREQ, set_frequency);
258 (void)volume_set;
259 }
229 260
230 while (rb->pcm_is_playing()) 261 gen_quit = true;
231 rb->yield(); 262
263 rb->thread_wait(gen_thread_p);
264
265 rb->pcm_play_stop();
232 266
233#ifdef HAVE_ADJUSTABLE_CPU_FREQ 267#ifdef HAVE_ADJUSTABLE_CPU_FREQ
234 rb->cpu_boost(false); 268 rb->cpu_boost(false);
@@ -239,40 +273,41 @@ void play_waveform(void)
239 rb->pcm_set_frequency(HW_FREQ_DEFAULT); 273 rb->pcm_set_frequency(HW_FREQ_DEFAULT);
240} 274}
241 275
242void set_waveform(void) 276/* Tests hardware sample rate switching */
277/* TODO: needs a volume control */
278enum plugin_status plugin_start(const struct plugin_api *api,
279 const void *parameter)
243{ 280{
244 static struct opt_items names[NUM_WAVEFORMS] = 281 enum
245 { 282 {
246 [TONE_SINE] = { "Sine", -1 }, 283 __TEST_SAMPR_MENUITEM_FIRST = -1,
247 [TONE_TRIANGLE] = { "Triangle", -1 }, 284#ifndef HAVE_VOLUME_IN_LIST
248 [TONE_SAWTOOTH] = { "Sawtooth", -1 }, 285 MENU_VOL_SET,
249 [TONE_SQUARE] = { "Square", -1 }, 286#endif /* HAVE_VOLUME_IN_LIST */
287 MENU_SAMPR_SET,
288 MENU_QUIT,
250 }; 289 };
251 290
252 rb->set_option("Waveform", &waveform, INT, names,
253 NUM_WAVEFORMS, NULL);
254}
255
256/* Tests hardware sample rate switching */
257/* TODO: needs a volume control */
258enum plugin_status plugin_start(const struct plugin_api *api, const void *parameter)
259{
260 static const struct menu_item items[] = 291 static const struct menu_item items[] =
261 { 292 {
262 { "Set Waveform", NULL }, 293#ifndef HAVE_VOLUME_IN_LIST
263 { "Play Waveform", NULL }, 294 [MENU_VOL_SET] =
264 { "Quit", NULL }, 295 { "Set Volume", NULL },
296#endif /* HAVE_VOLUME_IN_LIST */
297 [MENU_SAMPR_SET] =
298 { "Set Samplerate", NULL },
299 [MENU_QUIT] =
300 { "Quit", NULL },
265 }; 301 };
266 302
267 bool exit = false; 303 bool exit = false;
268 int m; 304 int m;
269 bool talk_menu;
270 305
271 rb = api; 306 /* Disable all talking before initializing IRAM */
307 api->talk_disable(true);
272 308
273 /* Have to shut up voice menus or it will mess up our waveform playback */ 309 PLUGIN_IRAM_INIT(api);
274 talk_menu = rb->global_settings->talk_menu; 310 rb = api;
275 rb->global_settings->talk_menu = false;
276 311
277 m = menu_init(rb, items, ARRAYLEN(items), 312 m = menu_init(rb, items, ARRAYLEN(items),
278 NULL, NULL, NULL, NULL); 313 NULL, NULL, NULL, NULL);
@@ -283,13 +318,16 @@ enum plugin_status plugin_start(const struct plugin_api *api, const void *parame
283 318
284 switch (result) 319 switch (result)
285 { 320 {
286 case 0: 321#ifndef HAVE_VOLUME_IN_LIST
287 set_waveform(); 322 case MENU_VOL_SET:
323 play_tone(true);
288 break; 324 break;
289 case 1: 325#endif /* HAVE_VOLUME_IN_LIST */
290 play_waveform(); 326 case MENU_SAMPR_SET:
327 play_tone(false);
291 break; 328 break;
292 case 2: 329
330 case MENU_QUIT:
293 exit = true; 331 exit = true;
294 break; 332 break;
295 } 333 }
@@ -297,7 +335,7 @@ enum plugin_status plugin_start(const struct plugin_api *api, const void *parame
297 335
298 menu_exit(m); 336 menu_exit(m);
299 337
300 rb->global_settings->talk_menu = talk_menu; 338 rb->talk_disable(false);
301 339
302 return PLUGIN_OK; 340 return PLUGIN_OK;
303 (void)parameter; 341 (void)parameter;