diff options
Diffstat (limited to 'firmware/target/arm/pcm-pp.c')
-rw-r--r-- | firmware/target/arm/pcm-pp.c | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/firmware/target/arm/pcm-pp.c b/firmware/target/arm/pcm-pp.c new file mode 100644 index 0000000000..d9a3b6d7d7 --- /dev/null +++ b/firmware/target/arm/pcm-pp.c | |||
@@ -0,0 +1,578 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 by 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 <stdlib.h> | ||
20 | #include "system.h" | ||
21 | #include "kernel.h" | ||
22 | #include "logf.h" | ||
23 | #include "audio.h" | ||
24 | #if defined(HAVE_WM8975) | ||
25 | #include "wm8975.h" | ||
26 | #elif defined(HAVE_WM8758) | ||
27 | #include "wm8758.h" | ||
28 | #elif defined(HAVE_WM8731) | ||
29 | #include "wm8731l.h" | ||
30 | #endif | ||
31 | |||
32 | |||
33 | |||
34 | /* peaks */ | ||
35 | static int play_peak_left, play_peak_right; | ||
36 | static unsigned long *rec_peak_addr; | ||
37 | static int rec_peak_left, rec_peak_right; | ||
38 | |||
39 | /** DMA **/ | ||
40 | #if CONFIG_CPU == PP5020 | ||
41 | #define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x3f000000) >> 24) | ||
42 | #elif CONFIG_CPU == PP5002 | ||
43 | #define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x7800000) >> 23) | ||
44 | #elif CONFIG_CPU == PP5024 | ||
45 | #define FIFO_FREE_COUNT 4 /* TODO: make this sensible */ | ||
46 | #endif | ||
47 | |||
48 | /**************************************************************************** | ||
49 | ** Playback DMA transfer | ||
50 | **/ | ||
51 | static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ | ||
52 | |||
53 | /* NOTE: The order of these two variables is important if you use the iPod | ||
54 | assembler optimised fiq handler, so don't change it. */ | ||
55 | unsigned short* p IBSS_ATTR; | ||
56 | size_t p_size IBSS_ATTR; | ||
57 | |||
58 | /* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode | ||
59 | has registers r8-r14 banked, and so does not need to be saved. This routine | ||
60 | uses only these registers, and so will never touch the stack unless it | ||
61 | actually needs to do so when calling pcm_callback_for_more. C version is | ||
62 | still included below for reference. | ||
63 | */ | ||
64 | #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 | ||
65 | void fiq(void) ICODE_ATTR __attribute__((naked)); | ||
66 | void fiq(void) | ||
67 | { | ||
68 | /* r12 contains IISCONFIG address (set in crt0.S to minimise code in actual | ||
69 | * FIQ handler. r11 contains address of p (also set in crt0.S). Most other | ||
70 | * addresses we need are generated by using offsets with these two. | ||
71 | * r12 + 0x40 is IISFIFO_WR, and r12 + 0x0c is IISFIFO_CFG. | ||
72 | * r8 and r9 contains local copies of p_size and p respectively. | ||
73 | * r10 is a working register. | ||
74 | */ | ||
75 | asm volatile ( | ||
76 | #if CONFIG_CPU == PP5002 | ||
77 | "ldr r10, =0xcf001040 \n\t" /* Some magic from iPodLinux */ | ||
78 | "ldr r10, [r10] \n\t" | ||
79 | "ldr r10, [r12, #0x1c]\n\t" | ||
80 | "bic r10, r10, #0x200 \n\t" /* clear interrupt */ | ||
81 | "str r10, [r12, #0x1c]\n\t" | ||
82 | #else | ||
83 | "ldr r10, [r12] \n\t" | ||
84 | "bic r10, r10, #0x2 \n\t" /* clear interrupt */ | ||
85 | "str r10, [r12] \n\t" | ||
86 | #endif | ||
87 | "ldr r8, [r11, #4] \n\t" /* r8 = p_size */ | ||
88 | "ldr r9, [r11] \n\t" /* r9 = p */ | ||
89 | ".loop: \n\t" | ||
90 | "cmp r8, #0 \n\t" /* is p_size 0? */ | ||
91 | "beq .more_data \n\t" /* if so, ask pcmbuf for more data */ | ||
92 | ".fifo_loop: \n\t" | ||
93 | #if CONFIG_CPU == PP5002 | ||
94 | "ldr r10, [r12, #0x1c]\n\t" /* read IISFIFO_CFG to check FIFO status */ | ||
95 | "and r10, r10, #0x7800000\n\t" | ||
96 | "cmp r10, #0x800000 \n\t" | ||
97 | #else | ||
98 | "ldr r10, [r12, #0x0c]\n\t" /* read IISFIFO_CFG to check FIFO status */ | ||
99 | "and r10, r10, #0x3f0000\n\t" | ||
100 | "cmp r10, #0x10000 \n\t" | ||
101 | #endif | ||
102 | "bls .fifo_full \n\t" /* FIFO full, exit */ | ||
103 | "ldr r10, [r9], #4 \n\t" /* load two samples */ | ||
104 | "mov r10, r10, ror #16\n\t" /* put left sample at the top bits */ | ||
105 | "str r10, [r12, #0x40]\n\t" /* write top sample, lower sample ignored */ | ||
106 | "mov r10, r10, lsl #16\n\t" /* shift lower sample up */ | ||
107 | "str r10, [r12, #0x40]\n\t" /* then write it */ | ||
108 | "subs r8, r8, #4 \n\t" /* check if we have more samples */ | ||
109 | "bne .fifo_loop \n\t" /* yes, continue */ | ||
110 | ".more_data: \n\t" | ||
111 | "stmdb sp!, { r0-r3, r12, lr}\n\t" /* stack scratch regs and lr */ | ||
112 | "mov r0, r11 \n\t" /* r0 = &p */ | ||
113 | "add r1, r11, #4 \n\t" /* r1 = &p_size */ | ||
114 | "str r9, [r0] \n\t" /* save internal copies of variables back */ | ||
115 | "str r8, [r1] \n\t" | ||
116 | "ldr r2, =pcm_callback_for_more\n\t" | ||
117 | "ldr r2, [r2] \n\t" /* get callback address */ | ||
118 | "cmp r2, #0 \n\t" /* check for null pointer */ | ||
119 | "movne lr, pc \n\t" /* call pcm_callback_for_more */ | ||
120 | "bxne r2 \n\t" | ||
121 | "ldmia sp!, { r0-r3, r12, lr}\n\t" | ||
122 | "ldr r8, [r11, #4] \n\t" /* reload p_size and p */ | ||
123 | "ldr r9, [r11] \n\t" | ||
124 | "cmp r8, #0 \n\t" /* did we actually get more data? */ | ||
125 | "bne .loop \n\t" /* yes, continue to try feeding FIFO */ | ||
126 | ".dma_stop: \n\t" /* no more data, do dma_stop() and exit */ | ||
127 | "ldr r10, =pcm_playing\n\t" | ||
128 | "strb r8, [r10] \n\t" /* pcm_playing = false (r8=0, look above) */ | ||
129 | "ldr r10, [r12] \n\t" | ||
130 | #if CONFIG_CPU == PP5002 | ||
131 | "bic r10, r10, #0x4\n\t" /* disable playback FIFO */ | ||
132 | "str r10, [r12] \n\t" | ||
133 | "ldr r10, [r12, #0x1c] \n\t" | ||
134 | "bic r10, r10, #0x200 \n\t" /* clear interrupt */ | ||
135 | "str r10, [r12, #0x1c] \n\t" | ||
136 | #else | ||
137 | "bic r10, r10, #0x20000002\n\t" /* disable playback FIFO and IRQ */ | ||
138 | "str r10, [r12] \n\t" | ||
139 | #endif | ||
140 | "mrs r10, cpsr \n\t" | ||
141 | "orr r10, r10, #0x40 \n\t" /* disable FIQ */ | ||
142 | "msr cpsr_c, r10 \n\t" | ||
143 | ".exit: \n\t" | ||
144 | "str r8, [r11, #4] \n\t" | ||
145 | "str r9, [r11] \n\t" | ||
146 | "subs pc, lr, #4 \n\t" /* FIQ specific return sequence */ | ||
147 | ".fifo_full: \n\t" /* enable IRQ and exit */ | ||
148 | #if CONFIG_CPU == PP5002 | ||
149 | "ldr r10, [r12, #0x1c]\n\t" | ||
150 | "orr r10, r10, #0x200 \n\t" /* set interrupt */ | ||
151 | "str r10, [r12, #0x1c]\n\t" | ||
152 | #else | ||
153 | "ldr r10, [r12] \n\t" | ||
154 | "orr r10, r10, #0x2 \n\t" /* set interrupt */ | ||
155 | "str r10, [r12] \n\t" | ||
156 | #endif | ||
157 | "b .exit \n\t" | ||
158 | ); | ||
159 | } | ||
160 | #else /* !(CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002) */ | ||
161 | void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); | ||
162 | void fiq(void) | ||
163 | { | ||
164 | /* Clear interrupt */ | ||
165 | #if CONFIG_CPU == PP5020 | ||
166 | IISCONFIG &= ~0x2; | ||
167 | #elif CONFIG_CPU == PP5002 | ||
168 | inl(0xcf001040); | ||
169 | IISFIFO_CFG &= ~(1<<9); | ||
170 | #endif | ||
171 | |||
172 | do { | ||
173 | while (p_size) { | ||
174 | if (FIFO_FREE_COUNT < 2) { | ||
175 | /* Enable interrupt */ | ||
176 | #if CONFIG_CPU == PP5020 | ||
177 | IISCONFIG |= 0x2; | ||
178 | #elif CONFIG_CPU == PP5002 | ||
179 | IISFIFO_CFG |= (1<<9); | ||
180 | #endif | ||
181 | return; | ||
182 | } | ||
183 | |||
184 | IISFIFO_WR = (*(p++))<<16; | ||
185 | IISFIFO_WR = (*(p++))<<16; | ||
186 | p_size-=4; | ||
187 | } | ||
188 | |||
189 | /* p is empty, get some more data */ | ||
190 | if (pcm_callback_for_more) { | ||
191 | pcm_callback_for_more((unsigned char**)&p,&p_size); | ||
192 | } | ||
193 | } while (p_size); | ||
194 | |||
195 | /* No more data, so disable the FIFO/FIQ */ | ||
196 | pcm_play_dma_stop(); | ||
197 | } | ||
198 | #endif /* CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 */ | ||
199 | |||
200 | void pcm_play_dma_start(const void *addr, size_t size) | ||
201 | { | ||
202 | p=(unsigned short*)addr; | ||
203 | p_size=size; | ||
204 | |||
205 | pcm_playing = true; | ||
206 | |||
207 | #if CONFIG_CPU == PP5020 | ||
208 | /* setup I2S interrupt for FIQ */ | ||
209 | outl(inl(0x6000402c) | I2S_MASK, 0x6000402c); | ||
210 | outl(I2S_MASK, 0x60004024); | ||
211 | #elif CONFIG_CPU == PP5024 | ||
212 | #else | ||
213 | /* setup I2S interrupt for FIQ */ | ||
214 | outl(inl(0xcf00102c) | DMA_OUT_MASK, 0xcf00102c); | ||
215 | outl(DMA_OUT_MASK, 0xcf001024); | ||
216 | #endif | ||
217 | |||
218 | /* Clear the FIQ disable bit in cpsr_c */ | ||
219 | enable_fiq(fiq); | ||
220 | |||
221 | /* Enable playback FIFO */ | ||
222 | #if CONFIG_CPU == PP5020 | ||
223 | IISCONFIG |= 0x20000000; | ||
224 | #elif CONFIG_CPU == PP5002 | ||
225 | IISCONFIG |= 0x4; | ||
226 | #endif | ||
227 | |||
228 | /* Fill the FIFO - we assume there are enough bytes in the pcm buffer to | ||
229 | fill the 32-byte FIFO. */ | ||
230 | while (p_size > 0) { | ||
231 | if (FIFO_FREE_COUNT < 2) { | ||
232 | /* Enable interrupt */ | ||
233 | #if CONFIG_CPU == PP5020 | ||
234 | IISCONFIG |= 0x2; | ||
235 | #elif CONFIG_CPU == PP5002 | ||
236 | IISFIFO_CFG |= (1<<9); | ||
237 | #endif | ||
238 | return; | ||
239 | } | ||
240 | |||
241 | IISFIFO_WR = (*(p++))<<16; | ||
242 | IISFIFO_WR = (*(p++))<<16; | ||
243 | p_size-=4; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | /* Stops the DMA transfer and interrupt */ | ||
248 | void pcm_play_dma_stop(void) | ||
249 | { | ||
250 | pcm_playing = false; | ||
251 | |||
252 | #if CONFIG_CPU == PP5020 | ||
253 | |||
254 | /* Disable playback FIFO */ | ||
255 | IISCONFIG &= ~0x20000000; | ||
256 | |||
257 | /* Disable the interrupt */ | ||
258 | IISCONFIG &= ~0x2; | ||
259 | |||
260 | #elif CONFIG_CPU == PP5002 | ||
261 | |||
262 | /* Disable playback FIFO */ | ||
263 | IISCONFIG &= ~0x4; | ||
264 | |||
265 | /* Disable the interrupt */ | ||
266 | IISFIFO_CFG &= ~(1<<9); | ||
267 | #endif | ||
268 | |||
269 | disable_fiq(); | ||
270 | } | ||
271 | |||
272 | void pcm_play_pause_pause(void) | ||
273 | { | ||
274 | #if CONFIG_CPU == PP5020 | ||
275 | /* Disable the interrupt */ | ||
276 | IISCONFIG &= ~0x2; | ||
277 | /* Disable playback FIFO */ | ||
278 | IISCONFIG &= ~0x20000000; | ||
279 | #elif CONFIG_CPU == PP5002 | ||
280 | /* Disable the interrupt */ | ||
281 | IISFIFO_CFG &= ~(1<<9); | ||
282 | /* Disable playback FIFO */ | ||
283 | IISCONFIG &= ~0x4; | ||
284 | #endif | ||
285 | disable_fiq(); | ||
286 | } | ||
287 | |||
288 | void pcm_play_pause_unpause(void) | ||
289 | { | ||
290 | /* Enable the FIFO and fill it */ | ||
291 | |||
292 | enable_fiq(fiq); | ||
293 | |||
294 | /* Enable playback FIFO */ | ||
295 | #if CONFIG_CPU == PP5020 | ||
296 | IISCONFIG |= 0x20000000; | ||
297 | #elif CONFIG_CPU == PP5002 | ||
298 | IISCONFIG |= 0x4; | ||
299 | #endif | ||
300 | |||
301 | /* Fill the FIFO - we assume there are enough bytes in the | ||
302 | pcm buffer to fill the 32-byte FIFO. */ | ||
303 | while (p_size > 0) { | ||
304 | if (FIFO_FREE_COUNT < 2) { | ||
305 | /* Enable interrupt */ | ||
306 | #if CONFIG_CPU == PP5020 | ||
307 | IISCONFIG |= 0x2; | ||
308 | #elif CONFIG_CPU == PP5002 | ||
309 | IISFIFO_CFG |= (1<<9); | ||
310 | #endif | ||
311 | return; | ||
312 | } | ||
313 | |||
314 | IISFIFO_WR = (*(p++))<<16; | ||
315 | IISFIFO_WR = (*(p++))<<16; | ||
316 | p_size-=4; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | void pcm_set_frequency(unsigned int frequency) | ||
321 | { | ||
322 | (void)frequency; | ||
323 | pcm_freq = HW_SAMPR_DEFAULT; | ||
324 | } | ||
325 | |||
326 | size_t pcm_get_bytes_waiting(void) | ||
327 | { | ||
328 | return p_size; | ||
329 | } | ||
330 | |||
331 | #ifdef HAVE_PP5024_CODEC | ||
332 | void pcm_init(void) | ||
333 | { | ||
334 | } | ||
335 | #else | ||
336 | void pcm_init(void) | ||
337 | { | ||
338 | pcm_playing = false; | ||
339 | pcm_paused = false; | ||
340 | pcm_callback_for_more = NULL; | ||
341 | |||
342 | /* Initialize default register values. */ | ||
343 | audiohw_init(); | ||
344 | |||
345 | /* Power on */ | ||
346 | audiohw_enable_output(true); | ||
347 | |||
348 | /* Unmute the master channel (DAC should be at zero point now). */ | ||
349 | audiohw_mute(false); | ||
350 | |||
351 | /* Call pcm_play_dma_stop to initialize everything. */ | ||
352 | pcm_play_dma_stop(); | ||
353 | } | ||
354 | #endif /* HAVE_PP5024_CODEC */ | ||
355 | |||
356 | |||
357 | /**************************************************************************** | ||
358 | ** Recording DMA transfer | ||
359 | **/ | ||
360 | static short peak_l, peak_r IBSS_ATTR; | ||
361 | |||
362 | void fiq_record(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); | ||
363 | void fiq_record(void) | ||
364 | { | ||
365 | short value; | ||
366 | pcm_more_callback_type2 more_ready; | ||
367 | int status = 0; | ||
368 | |||
369 | /* Clear interrupt */ | ||
370 | #if CONFIG_CPU == PP5020 | ||
371 | IISCONFIG &= ~0x01; | ||
372 | #elif CONFIG_CPU == PP5002 | ||
373 | /* TODO */ | ||
374 | #endif | ||
375 | |||
376 | while (p_size > 0) { | ||
377 | if (FIFO_FREE_COUNT < 2) { | ||
378 | /* enable interrupt */ | ||
379 | #if CONFIG_CPU == PP5020 | ||
380 | IISCONFIG |= 0x01; | ||
381 | #elif CONFIG_CPU == PP5002 | ||
382 | /* TODO */ | ||
383 | #endif | ||
384 | return; | ||
385 | } | ||
386 | value = (unsigned short)(IISFIFO_RD >> 16); | ||
387 | if (value > peak_l) peak_l = value; | ||
388 | else if (-value > peak_l) peak_l = -value; | ||
389 | *(p++) = value; | ||
390 | |||
391 | value = (unsigned short)(IISFIFO_RD >> 16); | ||
392 | if (value > peak_r) peak_r = value; | ||
393 | else if (-value > peak_r) peak_r = -value; | ||
394 | *(p++) = value; | ||
395 | |||
396 | p_size -= 4; | ||
397 | |||
398 | /* If we have filled the current chunk, start a new one */ | ||
399 | if (p_size == 0) { | ||
400 | rec_peak_left = peak_l; | ||
401 | rec_peak_right = peak_r; | ||
402 | peak_l = peak_r = 0; | ||
403 | } | ||
404 | } | ||
405 | |||
406 | more_ready = pcm_callback_more_ready; | ||
407 | |||
408 | if (more_ready != NULL && more_ready(status) >= 0) | ||
409 | return; | ||
410 | |||
411 | /* Finished recording */ | ||
412 | pcm_rec_dma_stop(); | ||
413 | } | ||
414 | |||
415 | /* Continue transferring data in */ | ||
416 | void pcm_record_more(void *start, size_t size) | ||
417 | { | ||
418 | rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */ | ||
419 | p = start; | ||
420 | p_size = size; /* Bytes to transfer */ | ||
421 | #if CONFIG_CPU == PP5020 | ||
422 | IISCONFIG |= 0x01; | ||
423 | #elif CONFIG_CPU == PP5002 | ||
424 | /* TODO */ | ||
425 | #endif | ||
426 | } | ||
427 | |||
428 | void pcm_rec_dma_stop(void) | ||
429 | { | ||
430 | logf("pcm_rec_dma_stop"); | ||
431 | |||
432 | /* disable fifo */ | ||
433 | IISCONFIG &= ~0x10000000; | ||
434 | |||
435 | disable_fiq(); | ||
436 | |||
437 | pcm_recording = false; | ||
438 | } | ||
439 | |||
440 | void pcm_rec_dma_start(void *addr, size_t size) | ||
441 | { | ||
442 | logf("pcm_rec_dma_start"); | ||
443 | |||
444 | pcm_recording = true; | ||
445 | |||
446 | peak_l = peak_r = 0; | ||
447 | p_size = size; | ||
448 | p = addr; | ||
449 | |||
450 | /* setup FIQ */ | ||
451 | outl(inl(0x6000402c) | I2S_MASK, 0x6000402c); | ||
452 | outl(I2S_MASK, 0x60004024); | ||
453 | |||
454 | /* interrupt on full fifo */ | ||
455 | outl(inl(0x70002800) | 0x1, 0x70002800); | ||
456 | |||
457 | /* enable record fifo */ | ||
458 | outl(inl(0x70002800) | 0x10000000, 0x70002800); | ||
459 | |||
460 | enable_fiq(fiq_record); | ||
461 | } | ||
462 | |||
463 | void pcm_close_recording(void) | ||
464 | { | ||
465 | logf("pcm_close_recording"); | ||
466 | |||
467 | pcm_rec_dma_stop(); | ||
468 | |||
469 | #if (CONFIG_CPU == PP5020) | ||
470 | disable_fiq(); | ||
471 | |||
472 | /* disable fifo */ | ||
473 | IISCONFIG &= ~0x10000000; | ||
474 | |||
475 | /* Clear interrupt */ | ||
476 | IISCONFIG &= ~0x01; | ||
477 | #endif | ||
478 | } /* pcm_close_recording */ | ||
479 | |||
480 | void pcm_init_recording(void) | ||
481 | { | ||
482 | logf("pcm_init_recording"); | ||
483 | |||
484 | pcm_recording = false; | ||
485 | pcm_callback_more_ready = NULL; | ||
486 | |||
487 | #if (CONFIG_CPU == PP5020) | ||
488 | #if defined(IPOD_COLOR) || defined (IPOD_4G) | ||
489 | /* The usual magic from IPL - I'm guessing this configures the headphone | ||
490 | socket to be input or output - in this case, input. */ | ||
491 | GPIOI_OUTPUT_VAL &= ~0x40; | ||
492 | GPIOA_OUTPUT_VAL &= ~0x4; | ||
493 | #endif | ||
494 | /* Setup the recording FIQ handler */ | ||
495 | *((unsigned int*)(15*4)) = (unsigned int)&fiq_record; | ||
496 | #endif | ||
497 | |||
498 | pcm_rec_dma_stop(); | ||
499 | } /* pcm_init */ | ||
500 | |||
501 | void pcm_calculate_rec_peaks(int *left, int *right) | ||
502 | { | ||
503 | *left = rec_peak_left; | ||
504 | *right = rec_peak_right; | ||
505 | } | ||
506 | |||
507 | /* | ||
508 | * This function goes directly into the DMA buffer to calculate the left and | ||
509 | * right peak values. To avoid missing peaks it tries to look forward two full | ||
510 | * peek periods (2/HZ sec, 100% overlap), although it's always possible that | ||
511 | * the entire period will not be visible. To reduce CPU load it only looks at | ||
512 | * every third sample, and this can be reduced even further if needed (even | ||
513 | * every tenth sample would still be pretty accurate). | ||
514 | */ | ||
515 | |||
516 | /* Check for a peak every PEAK_STRIDE samples */ | ||
517 | #define PEAK_STRIDE 3 | ||
518 | /* Up to 1/50th of a second of audio for peak calculation */ | ||
519 | /* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */ | ||
520 | #define PEAK_SAMPLES (44100/50) | ||
521 | void pcm_calculate_peaks(int *left, int *right) | ||
522 | { | ||
523 | short *addr; | ||
524 | short *end; | ||
525 | { | ||
526 | size_t samples = p_size / 4; | ||
527 | addr = p; | ||
528 | |||
529 | if (samples > PEAK_SAMPLES) | ||
530 | samples = PEAK_SAMPLES - (PEAK_STRIDE - 1); | ||
531 | else | ||
532 | samples -= MIN(PEAK_STRIDE - 1, samples); | ||
533 | |||
534 | end = &addr[samples * 2]; | ||
535 | } | ||
536 | |||
537 | if (left && right) { | ||
538 | int left_peak = 0, right_peak = 0; | ||
539 | |||
540 | while (addr < end) { | ||
541 | int value; | ||
542 | if ((value = addr [0]) > left_peak) | ||
543 | left_peak = value; | ||
544 | else if (-value > left_peak) | ||
545 | left_peak = -value; | ||
546 | |||
547 | if ((value = addr [PEAK_STRIDE | 1]) > right_peak) | ||
548 | right_peak = value; | ||
549 | else if (-value > right_peak) | ||
550 | right_peak = -value; | ||
551 | |||
552 | addr = &addr[PEAK_STRIDE * 2]; | ||
553 | } | ||
554 | |||
555 | *left = left_peak; | ||
556 | *right = right_peak; | ||
557 | } | ||
558 | else if (left || right) { | ||
559 | int peak_value = 0, value; | ||
560 | |||
561 | if (right) | ||
562 | addr += (PEAK_STRIDE | 1); | ||
563 | |||
564 | while (addr < end) { | ||
565 | if ((value = addr [0]) > peak_value) | ||
566 | peak_value = value; | ||
567 | else if (-value > peak_value) | ||
568 | peak_value = -value; | ||
569 | |||
570 | addr += PEAK_STRIDE * 2; | ||
571 | } | ||
572 | |||
573 | if (left) | ||
574 | *left = peak_value; | ||
575 | else | ||
576 | *right = peak_value; | ||
577 | } | ||
578 | } | ||