diff options
Diffstat (limited to 'firmware/target/arm/pcm-pp.c')
-rw-r--r-- | firmware/target/arm/pcm-pp.c | 846 |
1 files changed, 336 insertions, 510 deletions
diff --git a/firmware/target/arm/pcm-pp.c b/firmware/target/arm/pcm-pp.c index f38757ec6c..50e5b272aa 100644 --- a/firmware/target/arm/pcm-pp.c +++ b/firmware/target/arm/pcm-pp.c | |||
@@ -23,329 +23,286 @@ | |||
23 | #include "audio.h" | 23 | #include "audio.h" |
24 | #include "sound.h" | 24 | #include "sound.h" |
25 | 25 | ||
26 | /* peaks */ | 26 | /** DMA **/ |
27 | #ifdef HAVE_RECORDING | 27 | |
28 | static unsigned long *rec_peak_addr; | 28 | #if defined(HAVE_AS3514) |
29 | static int rec_peak_left, rec_peak_right; | 29 | /* E200 uses 16bit FIFO - all others should be able to as well - |
30 | i2s-pp.c has to set the right size as well */ | ||
31 | #define SAMPLE_SIZE 16 | ||
32 | #else | ||
33 | #define SAMPLE_SIZE 32 | ||
30 | #endif | 34 | #endif |
31 | 35 | ||
32 | /** DMA **/ | 36 | struct dma_data |
33 | #ifdef CPU_PP502x | 37 | { |
34 | #define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x3f000000) >> 24) | 38 | /* NOTE: The order of size and p is important if you use assembler |
35 | #elif CONFIG_CPU == PP5002 | 39 | optimised fiq handler, so don't change it. */ |
36 | #define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x7800000) >> 23) | 40 | #if SAMPLE_SIZE == 16 |
41 | uint32_t *p; | ||
42 | #elif SAMPLE_SIZE == 32 | ||
43 | uint16_t *p; | ||
37 | #endif | 44 | #endif |
45 | size_t size; | ||
46 | #if NUM_CORES > 1 | ||
47 | unsigned core; | ||
48 | #endif | ||
49 | int locked; | ||
50 | int state; | ||
51 | } __attribute__((packed)); | ||
52 | |||
53 | extern void *fiq_function; | ||
54 | |||
55 | /* Dispatch to the proper handler and leave the main vector table alone */ | ||
56 | void fiq_handler(void) ICODE_ATTR __attribute__((naked)); | ||
57 | void fiq_handler(void) | ||
58 | { | ||
59 | asm volatile ( | ||
60 | "ldr pc, [pc, #-4] \n" | ||
61 | "fiq_function: \n" | ||
62 | ".word 0 \n" | ||
63 | ); | ||
64 | } | ||
65 | |||
66 | /* TODO: Get simultaneous recording and playback to work. Just needs some tweaking */ | ||
38 | 67 | ||
39 | /**************************************************************************** | 68 | /**************************************************************************** |
40 | ** Playback DMA transfer | 69 | ** Playback DMA transfer |
41 | **/ | 70 | **/ |
42 | static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ | 71 | struct dma_data dma_play_data NOCACHEBSS_ATTR = |
72 | { | ||
73 | /* Initialize to a locked, stopped state */ | ||
74 | .p = NULL, | ||
75 | .size = 0, | ||
76 | #if NUM_CORES > 1 | ||
77 | .core = 0x00, | ||
78 | #endif | ||
79 | .locked = 0, | ||
80 | .state = 0 | ||
81 | }; | ||
43 | 82 | ||
44 | /* NOTE: The order of these two variables is important if you use the iPod | 83 | static unsigned long pcm_freq NOCACHEDATA_ATTR = HW_SAMPR_DEFAULT; /* 44.1 is default */ |
45 | assembler optimised fiq handler, so don't change it. */ | 84 | |
46 | unsigned short* p IBSS_ATTR; | 85 | void pcm_set_frequency(unsigned int frequency) |
47 | size_t p_size IBSS_ATTR; | 86 | { |
87 | (void)frequency; | ||
88 | pcm_freq = HW_SAMPR_DEFAULT; | ||
89 | } | ||
90 | |||
91 | void pcm_apply_settings(void) | ||
92 | { | ||
93 | pcm_curr_sampr = pcm_freq; | ||
94 | } | ||
48 | 95 | ||
49 | /* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode | 96 | /* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode |
50 | has registers r8-r14 banked, and so does not need to be saved. This routine | 97 | has registers r8-r14 banked, and so does not need to be saved. This routine |
51 | uses only these registers, and so will never touch the stack unless it | 98 | uses only these registers, and so will never touch the stack unless it |
52 | actually needs to do so when calling pcm_callback_for_more. C version is | 99 | actually needs to do so when calling pcm_callback_for_more. C version is |
53 | still included below for reference. | 100 | still included below for reference and testing. |
54 | */ | 101 | */ |
55 | #if 1 | 102 | #if 1 |
56 | void fiq(void) ICODE_ATTR __attribute__((naked)); | 103 | void fiq_playback(void) ICODE_ATTR __attribute__((naked)); |
57 | void fiq(void) | 104 | void fiq_playback(void) |
58 | { | 105 | { |
59 | /* r12 contains IISCONFIG address (set in crt0.S to minimise code in actual | 106 | /* r10 contains IISCONFIG address (set in crt0.S to minimise code in actual |
60 | * FIQ handler. r11 contains address of p (also set in crt0.S). Most other | 107 | * FIQ handler. r11 contains address of p (also set in crt0.S). Most other |
61 | * addresses we need are generated by using offsets with these two. | 108 | * addresses we need are generated by using offsets with these two. |
62 | * r12 + 0x40 is IISFIFO_WR, and r12 + 0x0c is IISFIFO_CFG. | 109 | * r10 + 0x40 is IISFIFO_WR, and r10 + 0x0c is IISFIFO_CFG. |
63 | * r8 and r9 contains local copies of p_size and p respectively. | 110 | * r8 and r9 contains local copies of p and size respectively. |
64 | * r10 is a working register. | 111 | * r12 is a working register. |
65 | */ | 112 | */ |
66 | asm volatile ( | 113 | asm volatile ( |
67 | #if CONFIG_CPU == PP5002 | 114 | #if CONFIG_CPU == PP5002 |
68 | "ldr r10, =0xcf001040 \n\t" /* Some magic from iPodLinux */ | 115 | "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */ |
69 | "ldr r10, [r10] \n\t" | 116 | "ldr r12, [r12] \n" |
70 | "ldr r10, [r12, #0x1c]\n\t" | ||
71 | "bic r10, r10, #0x200 \n\t" /* clear interrupt */ | ||
72 | "str r10, [r12, #0x1c]\n\t" | ||
73 | #else | ||
74 | "ldr r10, [r12] \n\t" | ||
75 | "bic r10, r10, #0x2 \n\t" /* clear interrupt */ | ||
76 | "str r10, [r12] \n\t" | ||
77 | #endif | ||
78 | "ldr r8, [r11, #4] \n\t" /* r8 = p_size */ | ||
79 | "ldr r9, [r11] \n\t" /* r9 = p */ | ||
80 | ".loop: \n\t" | ||
81 | "cmp r8, #0 \n\t" /* is p_size 0? */ | ||
82 | "beq .more_data \n\t" /* if so, ask pcmbuf for more data */ | ||
83 | ".fifo_loop: \n\t" | ||
84 | #if CONFIG_CPU == PP5002 | ||
85 | "ldr r10, [r12, #0x1c]\n\t" /* read IISFIFO_CFG to check FIFO status */ | ||
86 | "and r10, r10, #0x7800000\n\t" | ||
87 | "cmp r10, #0x800000 \n\t" | ||
88 | #else | ||
89 | "ldr r10, [r12, #0x0c]\n\t" /* read IISFIFO_CFG to check FIFO status */ | ||
90 | "and r10, r10, #0x3f0000\n\t" | ||
91 | "cmp r10, #0x10000 \n\t" | ||
92 | #endif | ||
93 | "bls .fifo_full \n\t" /* FIFO full, exit */ | ||
94 | "ldr r10, [r9], #4 \n\t" /* load two samples */ | ||
95 | #ifdef HAVE_AS3514 | ||
96 | "str r10, [r12, #0x40]\n\t" /* write them */ | ||
97 | #else | ||
98 | "mov r10, r10, ror #16\n\t" /* put left sample at the top bits */ | ||
99 | "str r10, [r12, #0x40]\n\t" /* write top sample, lower sample ignored */ | ||
100 | "mov r10, r10, lsl #16\n\t" /* shift lower sample up */ | ||
101 | "str r10, [r12, #0x40]\n\t" /* then write it */ | ||
102 | #endif | ||
103 | "subs r8, r8, #4 \n\t" /* check if we have more samples */ | ||
104 | "bne .fifo_loop \n\t" /* yes, continue */ | ||
105 | ".more_data: \n\t" | ||
106 | "stmdb sp!, { r0-r3, r12, lr}\n\t" /* stack scratch regs and lr */ | ||
107 | "mov r0, r11 \n\t" /* r0 = &p */ | ||
108 | "add r1, r11, #4 \n\t" /* r1 = &p_size */ | ||
109 | "str r9, [r0] \n\t" /* save internal copies of variables back */ | ||
110 | "str r8, [r1] \n\t" | ||
111 | "ldr r2, =pcm_callback_for_more\n\t" | ||
112 | "ldr r2, [r2] \n\t" /* get callback address */ | ||
113 | "cmp r2, #0 \n\t" /* check for null pointer */ | ||
114 | "movne lr, pc \n\t" /* call pcm_callback_for_more */ | ||
115 | "bxne r2 \n\t" | ||
116 | "ldmia sp!, { r0-r3, r12, lr}\n\t" | ||
117 | "ldr r8, [r11, #4] \n\t" /* reload p_size and p */ | ||
118 | "ldr r9, [r11] \n\t" | ||
119 | "cmp r8, #0 \n\t" /* did we actually get more data? */ | ||
120 | "bne .loop \n\t" /* yes, continue to try feeding FIFO */ | ||
121 | ".dma_stop: \n\t" /* no more data, do dma_stop() and exit */ | ||
122 | "ldr r10, =pcm_playing\n\t" | ||
123 | "strb r8, [r10] \n\t" /* pcm_playing = false (r8=0, look above) */ | ||
124 | "ldr r10, =pcm_paused \n\t" | ||
125 | "strb r8, [r10] \n\t" /* pcm_paused = false (r8=0, look above) */ | ||
126 | "ldr r10, [r12] \n\t" | ||
127 | #if CONFIG_CPU == PP5002 | ||
128 | "bic r10, r10, #0x4\n\t" /* disable playback FIFO */ | ||
129 | "str r10, [r12] \n\t" | ||
130 | "ldr r10, [r12, #0x1c] \n\t" | ||
131 | "bic r10, r10, #0x200 \n\t" /* clear interrupt */ | ||
132 | "str r10, [r12, #0x1c] \n\t" | ||
133 | #else | ||
134 | "bic r10, r10, #0x20000002\n\t" /* disable playback FIFO and IRQ */ | ||
135 | "str r10, [r12] \n\t" | ||
136 | #endif | 117 | #endif |
137 | "mrs r10, cpsr \n\t" | 118 | "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */ |
138 | "orr r10, r10, #0x40 \n\t" /* disable FIQ */ | 119 | "cmp r9, #0 \n" /* is size 0? */ |
139 | "msr cpsr_c, r10 \n\t" | 120 | "beq .more_data \n" /* if so, ask pcmbuf for more data */ |
140 | ".exit: \n\t" | 121 | ".fifo_loop: \n" |
141 | "str r8, [r11, #4] \n\t" | 122 | "ldr r12, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */ |
142 | "str r9, [r11] \n\t" | 123 | "ands r12, r12, %[mask] \n" |
143 | "subs pc, lr, #4 \n\t" /* FIQ specific return sequence */ | 124 | "beq .exit \n" /* FIFO full, exit */ |
144 | ".fifo_full: \n\t" /* enable IRQ and exit */ | 125 | "ldr r12, [r8], #4 \n" /* load two samples */ |
145 | #if CONFIG_CPU == PP5002 | 126 | #if SAMPLE_SIZE == 16 |
146 | "ldr r10, [r12, #0x1c]\n\t" | 127 | "str r12, [r10, %[wr]] \n" /* write them */ |
147 | "orr r10, r10, #0x200 \n\t" /* set interrupt */ | 128 | #elif SAMPLE_SIZE == 32 |
148 | "str r10, [r12, #0x1c]\n\t" | 129 | "mov r12, r12, ror #16 \n" /* put left sample at the top bits */ |
149 | #else | 130 | "str r12, [r10, %[wr]] \n" /* write top sample, lower sample ignored */ |
150 | "ldr r10, [r12] \n\t" | 131 | "mov r12, r12, lsl #16 \n" /* shift lower sample up */ |
151 | "orr r10, r10, #0x2 \n\t" /* set interrupt */ | 132 | "str r12, [r10, %[wr]] \n" /* then write it */ |
152 | "str r10, [r12] \n\t" | ||
153 | #endif | 133 | #endif |
154 | "b .exit \n\t" | 134 | "subs r9, r9, #4 \n" /* check if we have more samples */ |
135 | "bne .fifo_loop \n" /* yes, continue */ | ||
136 | ".more_data: \n" | ||
137 | "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */ | ||
138 | "ldr r2, =pcm_callback_for_more \n" | ||
139 | "ldr r2, [r2] \n" /* get callback address */ | ||
140 | "cmp r2, #0 \n" /* check for null pointer */ | ||
141 | "stmneia r11, { r8-r9 } \n" /* save internal copies of variables back */ | ||
142 | "movne r0, r11 \n" /* r0 = &p */ | ||
143 | "addne r1, r11, #4 \n" /* r1 = &size */ | ||
144 | "movne lr, pc \n" /* call pcm_callback_for_more */ | ||
145 | "bxne r2 \n" | ||
146 | "ldmia r11, { r8-r9 } \n" /* reload p and size */ | ||
147 | "cmp r9, #0 \n" /* did we actually get more data? */ | ||
148 | "ldmnefd sp!, { r0-r3, lr } \n" | ||
149 | "bne .fifo_loop \n" /* yes, continue to try feeding FIFO */ | ||
150 | "ldr r12, =pcm_play_dma_stop \n" | ||
151 | "mov lr, pc \n" | ||
152 | "bx r12 \n" | ||
153 | "ldr r12, =pcm_play_dma_stopped_callback \n" | ||
154 | "mov lr, pc \n" | ||
155 | "bx r12 \n" | ||
156 | "ldmfd sp!, { r0-r3, lr } \n" | ||
157 | ".exit: \n" /* (r8=0 if stopping, look above) */ | ||
158 | "stmia r11, { r8-r9 } \n" /* save p and size */ | ||
159 | "subs pc, lr, #4 \n" /* FIQ specific return sequence */ | ||
160 | ".ltorg \n" | ||
161 | : /* These must only be integers! No regs */ | ||
162 | : [mask]"i"(IIS_TX_FREE_MASK & (IIS_TX_FREE_MASK-1)), | ||
163 | [cfg]"i"((int)&IISFIFO_CFG - (int)&IISCONFIG), | ||
164 | [wr]"i"((int)&IISFIFO_WR - (int)&IISCONFIG) | ||
155 | ); | 165 | ); |
156 | } | 166 | } |
157 | #else /* C version for reference */ | 167 | #else /* C version for reference */ |
158 | void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); | 168 | void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR; |
159 | void fiq(void) | 169 | /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ |
170 | void fiq_playback(void) | ||
160 | { | 171 | { |
161 | /* Clear interrupt */ | 172 | register pcm_more_callback_type get_more; |
162 | #ifdef CPU_PP502x | 173 | |
163 | IISCONFIG &= ~(1 << 1); | 174 | #if CONFIG_CPU == PP5002 |
164 | #elif CONFIG_CPU == PP5002 | ||
165 | inl(0xcf001040); | 175 | inl(0xcf001040); |
166 | IISFIFO_CFG &= ~(1<<9); | ||
167 | #endif | 176 | #endif |
168 | 177 | ||
169 | do { | 178 | do { |
170 | while (p_size) { | 179 | while (dma_play_data.size > 0) { |
171 | //if (FIFO_FREE_COUNT < 2) { | 180 | if (IIS_TX_FREE_COUNT < 2) { |
172 | if (((IISFIFO_CFG & (0x1f << 16)) >> 16) < 2) { | ||
173 | /* Enable interrupt */ | ||
174 | #ifdef CPU_PP502x | ||
175 | IISCONFIG |= (1 << 1); | ||
176 | #elif CONFIG_CPU == PP5002 | ||
177 | IISFIFO_CFG |= (1<<9); | ||
178 | #endif | ||
179 | return; | 181 | return; |
180 | } | 182 | } |
181 | 183 | #if SAMPLE_SIZE == 16 | |
182 | #ifdef HAVE_AS3514 | 184 | IISFIFO_WR = *dma_play_data.p++; |
183 | IISFIFO_WR = *(int32_t *)p; | 185 | #elif SAMPLE_SIZE == 32 |
184 | p += 2; | 186 | IISFIFO_WR = *dma_play_data.p++ << 16; |
185 | #else | 187 | IISFIFO_WR = *dma_play_data.p++ << 16; |
186 | IISFIFO_WR = (*(p++))<<16; | ||
187 | IISFIFO_WR = (*(p++))<<16; | ||
188 | #endif | 188 | #endif |
189 | p_size-=4; | 189 | dma_play_data.size -= 4; |
190 | } | 190 | } |
191 | 191 | ||
192 | /* p is empty, get some more data */ | 192 | /* p is empty, get some more data */ |
193 | if (pcm_callback_for_more) { | 193 | get_more = pcm_callback_for_more; |
194 | pcm_callback_for_more((unsigned char**)&p,&p_size); | 194 | if (get_more) { |
195 | get_more((unsigned char**)&dma_play_data.p, | ||
196 | &dma_play_data.size); | ||
195 | } | 197 | } |
196 | } while (p_size); | 198 | } while (dma_play_data.size); |
197 | 199 | ||
198 | /* No more data, so disable the FIFO/FIQ */ | 200 | /* No more data, so disable the FIFO/interrupt */ |
199 | pcm_play_dma_stop(); | 201 | pcm_play_dma_stop(); |
202 | pcm_play_dma_stopped_callback(); | ||
200 | } | 203 | } |
201 | #endif /* ASM / C selection */ | 204 | #endif /* ASM / C selection */ |
202 | 205 | ||
203 | void pcm_play_dma_start(const void *addr, size_t size) | 206 | /* For the locks, FIQ must be disabled because the handler manipulates |
207 | IISCONFIG and the operation is not atomic - dual core support | ||
208 | will require other measures */ | ||
209 | void pcm_play_lock(void) | ||
204 | { | 210 | { |
205 | p=(unsigned short*)addr; | 211 | int status = set_fiq_status(FIQ_DISABLED); |
206 | p_size=size; | ||
207 | |||
208 | pcm_playing = true; | ||
209 | |||
210 | #ifdef CPU_PP502x | ||
211 | CPU_INT_PRIORITY |= I2S_MASK; /* FIQ priority for I2S */ | ||
212 | CPU_INT_EN = I2S_MASK; /* Enable I2S interrupt */ | ||
213 | #else | ||
214 | /* setup I2S interrupt for FIQ */ | ||
215 | outl(inl(0xcf00102c) | DMA_OUT_MASK, 0xcf00102c); | ||
216 | outl(DMA_OUT_MASK, 0xcf001024); | ||
217 | #endif | ||
218 | 212 | ||
219 | /* Clear the FIQ disable bit in cpsr_c */ | 213 | if (++dma_play_data.locked == 1) { |
220 | set_fiq_handler(fiq); | 214 | IIS_IRQTX_REG &= ~IIS_IRQTX; |
221 | enable_fiq(); | 215 | } |
222 | 216 | ||
223 | #if defined(CPU_PP502x) | 217 | set_fiq_status(status); |
224 | /* Enable playback FIFO */ | 218 | } |
225 | IISCONFIG |= (1 << 29); | ||
226 | #elif CONFIG_CPU == PP5002 | ||
227 | IISCONFIG |= 0x4; | ||
228 | #endif | ||
229 | 219 | ||
230 | /* Fill the FIFO - we assume there are enough bytes in the pcm buffer to | 220 | void pcm_play_unlock(void) |
231 | fill the 32-byte FIFO. */ | 221 | { |
232 | while (p_size > 0) { | 222 | int status = set_fiq_status(FIQ_DISABLED); |
233 | if (FIFO_FREE_COUNT < 2) { | ||
234 | /* Enable interrupt */ | ||
235 | #ifdef CPU_PP502x | ||
236 | IISCONFIG |= (1 << 1); | ||
237 | #elif CONFIG_CPU == PP5002 | ||
238 | IISFIFO_CFG |= (1<<9); | ||
239 | #endif | ||
240 | return; | ||
241 | } | ||
242 | 223 | ||
243 | #ifdef HAVE_AS3514 | 224 | if (--dma_play_data.locked == 0 && dma_play_data.state != 0) { |
244 | IISFIFO_WR = *(int32_t *)p; | 225 | IIS_IRQTX_REG |= IIS_IRQTX; |
245 | p += 2; | ||
246 | #else | ||
247 | IISFIFO_WR = (*(p++))<<16; | ||
248 | IISFIFO_WR = (*(p++))<<16; | ||
249 | #endif | ||
250 | p_size-=4; | ||
251 | } | 226 | } |
227 | |||
228 | set_fiq_status(status); | ||
252 | } | 229 | } |
253 | 230 | ||
254 | /* Stops the DMA transfer and interrupt */ | 231 | static void play_start_pcm(void) |
255 | void pcm_play_dma_stop(void) | ||
256 | { | 232 | { |
257 | pcm_playing = false; | 233 | fiq_function = fiq_playback; |
258 | if (!audio_status()) | 234 | pcm_apply_settings(); |
259 | pcm_paused = false; | ||
260 | 235 | ||
261 | #if CONFIG_CPU == PP5020 | 236 | IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */ |
262 | /* Disable TX interrupt */ | 237 | dma_play_data.state = 1; |
263 | IISCONFIG &= ~(1 << 1); | 238 | |
264 | #elif defined(CPU_PP502x) | 239 | /* Fill the FIFO or start when data is used up */ |
265 | /* Disable playback FIFO and interrupt */ | 240 | while (1) { |
266 | IISCONFIG &= ~((1 << 29) | (1 << 1)); | 241 | if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) { |
267 | #elif CONFIG_CPU == PP5002 | 242 | IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */ |
268 | /* Disable playback FIFO */ | 243 | return; |
269 | IISCONFIG &= ~0x4; | 244 | } |
270 | |||
271 | /* Disable the interrupt */ | ||
272 | IISFIFO_CFG &= ~(1<<9); | ||
273 | #endif | ||
274 | 245 | ||
275 | disable_fiq(); | 246 | #if SAMPLE_SIZE == 16 |
247 | IISFIFO_WR = *dma_play_data.p++; | ||
248 | #elif SAMPLE_SIZE == 32 | ||
249 | IISFIFO_WR = *dma_play_data.p++ << 16; | ||
250 | IISFIFO_WR = *dma_play_data.p++ << 16; | ||
251 | #endif | ||
252 | dma_play_data.size -= 4; | ||
253 | } | ||
276 | } | 254 | } |
277 | 255 | ||
278 | void pcm_play_pause_pause(void) | 256 | static void play_stop_pcm(void) |
279 | { | 257 | { |
280 | #if CONFIG_CPU == PP5020 | ||
281 | /* Disable TX interrupt */ | 258 | /* Disable TX interrupt */ |
282 | IISCONFIG &= ~(1 << 1); | 259 | IIS_IRQTX_REG &= ~IIS_IRQTX; |
283 | #elif defined(CPU_PP502x) | 260 | dma_play_data.state = 0; |
284 | /* Disable playback FIFO and interrupt */ | ||
285 | IISCONFIG &= ~((1 << 29) | (1 << 1)); | ||
286 | #elif CONFIG_CPU == PP5002 | ||
287 | /* Disable the interrupt */ | ||
288 | IISFIFO_CFG &= ~(1<<9); | ||
289 | /* Disable playback FIFO */ | ||
290 | IISCONFIG &= ~0x4; | ||
291 | #endif | ||
292 | disable_fiq(); | ||
293 | } | 261 | } |
294 | 262 | ||
295 | void pcm_play_pause_unpause(void) | 263 | void pcm_play_dma_start(const void *addr, size_t size) |
296 | { | 264 | { |
297 | /* Enable the FIFO and fill it */ | 265 | dma_play_data.p = (void *)addr; |
298 | 266 | dma_play_data.size = size; | |
299 | set_fiq_handler(fiq); | ||
300 | enable_fiq(); | ||
301 | 267 | ||
302 | #if defined(CPU_PP502x) | 268 | #if NUM_CORES > 1 |
303 | /* Enable playback FIFO */ | 269 | /* This will become more important later - and different ! */ |
304 | IISCONFIG |= (1 << 29); | 270 | dma_play_data.core = processor_id(); /* save initiating core */ |
305 | #elif CONFIG_CPU == PP5002 | ||
306 | IISCONFIG |= 0x4; | ||
307 | #endif | 271 | #endif |
308 | 272 | ||
309 | /* Fill the FIFO - we assume there are enough bytes in the | 273 | CPU_INT_PRIORITY |= IIS_MASK; /* FIQ priority for I2S */ |
310 | pcm buffer to fill the 32-byte FIFO. */ | 274 | CPU_INT_EN = IIS_MASK; |
311 | while (p_size > 0) { | ||
312 | if (FIFO_FREE_COUNT < 2) { | ||
313 | /* Enable interrupt */ | ||
314 | #ifdef CPU_PP502x | ||
315 | IISCONFIG |= (1 << 1); | ||
316 | #elif CONFIG_CPU == PP5002 | ||
317 | IISFIFO_CFG |= (1<<9); | ||
318 | #endif | ||
319 | return; | ||
320 | } | ||
321 | 275 | ||
322 | #ifdef HAVE_AS3514 | 276 | play_start_pcm(); |
323 | IISFIFO_WR = *(int32_t *)p; | 277 | } |
324 | p += 2; | 278 | |
325 | #else | 279 | /* Stops the DMA transfer and interrupt */ |
326 | IISFIFO_WR = (*(p++))<<16; | 280 | void pcm_play_dma_stop(void) |
327 | IISFIFO_WR = (*(p++))<<16; | 281 | { |
282 | play_stop_pcm(); | ||
283 | dma_play_data.size = 0; | ||
284 | #if NUM_CORES > 1 | ||
285 | dma_play_data.core = 0; /* no core in control */ | ||
328 | #endif | 286 | #endif |
329 | p_size-=4; | ||
330 | } | ||
331 | } | 287 | } |
332 | 288 | ||
333 | void pcm_set_frequency(unsigned int frequency) | 289 | void pcm_play_dma_pause(bool pause) |
334 | { | 290 | { |
335 | (void)frequency; | 291 | if (pause) { |
336 | pcm_freq = HW_SAMPR_DEFAULT; | 292 | play_stop_pcm(); |
293 | } else { | ||
294 | play_start_pcm(); | ||
295 | } | ||
337 | } | 296 | } |
338 | 297 | ||
339 | size_t pcm_get_bytes_waiting(void) | 298 | size_t pcm_get_bytes_waiting(void) |
340 | { | 299 | { |
341 | return p_size; | 300 | return dma_play_data.size & ~3; |
342 | } | 301 | } |
343 | 302 | ||
344 | void pcm_init(void) | 303 | void pcm_play_dma_init(void) |
345 | { | 304 | { |
346 | pcm_playing = false; | 305 | pcm_set_frequency(SAMPR_44); |
347 | pcm_paused = false; | ||
348 | pcm_callback_for_more = NULL; | ||
349 | 306 | ||
350 | /* Initialize default register values. */ | 307 | /* Initialize default register values. */ |
351 | audiohw_init(); | 308 | audiohw_init(); |
@@ -357,91 +314,129 @@ void pcm_init(void) | |||
357 | audiohw_mute(false); | 314 | audiohw_mute(false); |
358 | #endif | 315 | #endif |
359 | 316 | ||
360 | /* Call pcm_play_dma_stop to initialize everything. */ | 317 | dma_play_data.size = 0; |
361 | pcm_play_dma_stop(); | 318 | #if NUM_CORES > 1 |
362 | 319 | dma_play_data.core = 0; /* no core in control */ | |
363 | #if CONFIG_CPU == PP5020 | ||
364 | /* This processor doesn't like this disabled */ | ||
365 | IISCONFIG |= (1 << 29); | ||
366 | #endif | 320 | #endif |
321 | |||
322 | IISCONFIG |= IIS_TXFIFOEN; | ||
367 | } | 323 | } |
368 | 324 | ||
369 | void pcm_postinit(void) | 325 | void pcm_postinit(void) |
370 | { | 326 | { |
371 | audiohw_postinit(); | 327 | audiohw_postinit(); |
328 | pcm_apply_settings(); | ||
329 | } | ||
330 | |||
331 | const void * pcm_play_dma_get_peak_buffer(int *count) | ||
332 | { | ||
333 | unsigned long addr = (unsigned long)dma_play_data.p; | ||
334 | size_t cnt = dma_play_data.size; | ||
335 | *count = cnt >> 2; | ||
336 | return (void *)((addr + 2) & ~3); | ||
372 | } | 337 | } |
373 | 338 | ||
374 | /**************************************************************************** | 339 | /**************************************************************************** |
375 | ** Recording DMA transfer | 340 | ** Recording DMA transfer |
376 | **/ | 341 | **/ |
377 | #ifdef HAVE_RECORDING | 342 | #ifdef HAVE_RECORDING |
343 | /* PCM recording interrupt routine lockout */ | ||
344 | static struct dma_data dma_rec_data NOCACHEBSS_ATTR = | ||
345 | { | ||
346 | /* Initialize to a locked, stopped state */ | ||
347 | .p = NULL, | ||
348 | .size = 0, | ||
349 | #if NUM_CORES > 1 | ||
350 | .core = 0x00, | ||
351 | #endif | ||
352 | .locked = 0, | ||
353 | .state = 0 | ||
354 | }; | ||
355 | |||
356 | /* For the locks, FIQ must be disabled because the handler manipulates | ||
357 | IISCONFIG and the operation is not atomic - dual core support | ||
358 | will require other measures */ | ||
359 | void pcm_rec_lock(void) | ||
360 | { | ||
361 | int status = set_fiq_status(FIQ_DISABLED); | ||
378 | 362 | ||
379 | #ifdef HAVE_AS3514 | 363 | if (++dma_rec_data.locked == 1) |
380 | void fiq_record(void) ICODE_ATTR __attribute__((naked)); | 364 | IIS_IRQRX_REG &= ~IIS_IRQRX; |
381 | void fiq_record(void) | 365 | |
366 | set_fiq_status(status); | ||
367 | } | ||
368 | |||
369 | void pcm_rec_unlock(void) | ||
382 | { | 370 | { |
383 | register pcm_more_callback_type2 more_ready; | 371 | int status = set_fiq_status(FIQ_DISABLED); |
384 | register int32_t value1, value2; | 372 | |
373 | if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0) | ||
374 | IIS_IRQRX_REG |= IIS_IRQRX; | ||
385 | 375 | ||
386 | asm volatile ("stmfd sp!, {r0-r7, ip, lr} \n"); /* Store context */ | 376 | set_fiq_status(status); |
377 | } | ||
378 | |||
379 | /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ | ||
380 | void fiq_record(void) ICODE_ATTR __attribute__((interrupt ("FIQ"))); | ||
387 | 381 | ||
388 | IISCONFIG &= ~(1 << 0); | 382 | #if defined(SANSA_C200) || defined(SANSA_E200) |
383 | void fiq_record(void) | ||
384 | { | ||
385 | register pcm_more_callback_type2 more_ready; | ||
386 | register int32_t value; | ||
389 | 387 | ||
390 | if (audio_channels == 2) { | 388 | if (audio_channels == 2) { |
391 | /* RX is stereo */ | 389 | /* RX is stereo */ |
392 | while (p_size > 0) { | 390 | while (dma_rec_data.size > 0) { |
393 | if (FIFO_FREE_COUNT < 2) { | 391 | if (IIS_RX_FULL_COUNT < 2) { |
394 | /* enable interrupt */ | 392 | return; |
395 | IISCONFIG |= (1 << 0); | ||
396 | goto fiq_record_exit; | ||
397 | } | 393 | } |
398 | 394 | ||
399 | /* Discard every other sample since ADC clock is 1/2 LRCK */ | 395 | /* Discard every other sample since ADC clock is 1/2 LRCK */ |
400 | value1 = IISFIFO_RD; | 396 | value = IISFIFO_RD; |
401 | value2 = IISFIFO_RD; | 397 | IISFIFO_RD; |
402 | 398 | ||
403 | *(int32_t *)p = value1; | 399 | *dma_rec_data.p++ = value; |
404 | p += 2; | 400 | dma_rec_data.size -= 4; |
405 | p_size -= 4; | ||
406 | 401 | ||
407 | /* TODO: Figure out how to do IIS loopback */ | 402 | /* TODO: Figure out how to do IIS loopback */ |
408 | if (audio_output_source != AUDIO_SRC_PLAYBACK) { | 403 | if (audio_output_source != AUDIO_SRC_PLAYBACK) { |
409 | if ((IISFIFO_CFG & (0x3f << 16)) >= (16 << 16)) { | 404 | if (IIS_TX_FREE_COUNT >= 16) { |
410 | /* Resync the output FIFO - it ran dry */ | 405 | /* Resync the output FIFO - it ran dry */ |
411 | IISFIFO_WR = 0; | 406 | IISFIFO_WR = 0; |
412 | IISFIFO_WR = 0; | 407 | IISFIFO_WR = 0; |
413 | } | 408 | } |
414 | IISFIFO_WR = value1; | 409 | IISFIFO_WR = value; |
415 | IISFIFO_WR = value1; | 410 | IISFIFO_WR = value; |
416 | } | 411 | } |
417 | } | 412 | } |
418 | } | 413 | } |
419 | else { | 414 | else { |
420 | /* RX is left channel mono */ | 415 | /* RX is left channel mono */ |
421 | while (p_size > 0) { | 416 | while (dma_rec_data.size > 0) { |
422 | if (FIFO_FREE_COUNT < 2) { | 417 | if (IIS_RX_FULL_COUNT < 2) { |
423 | /* enable interrupt */ | 418 | return; |
424 | IISCONFIG |= (1 << 0); | ||
425 | goto fiq_record_exit; | ||
426 | } | 419 | } |
427 | 420 | ||
428 | /* Discard every other sample since ADC clock is 1/2 LRCK */ | 421 | /* Discard every other sample since ADC clock is 1/2 LRCK */ |
429 | value1 = IISFIFO_RD; | 422 | value = IISFIFO_RD; |
430 | value2 = IISFIFO_RD; | 423 | IISFIFO_RD; |
431 | *p++ = value1; | 424 | |
432 | *p++ = value1; | 425 | value = (uint16_t)value | (value << 16); |
433 | p_size -= 4; | 426 | |
427 | *dma_rec_data.p++ = value; | ||
428 | dma_rec_data.size -= 4; | ||
434 | 429 | ||
435 | if (audio_output_source != AUDIO_SRC_PLAYBACK) { | 430 | if (audio_output_source != AUDIO_SRC_PLAYBACK) { |
436 | if ((IISFIFO_CFG & (0x3f << 16)) >= (16 << 16)) { | 431 | if (IIS_TX_FREE_COUNT >= 16) { |
437 | /* Resync the output FIFO - it ran dry */ | 432 | /* Resync the output FIFO - it ran dry */ |
438 | IISFIFO_WR = 0; | 433 | IISFIFO_WR = 0; |
439 | IISFIFO_WR = 0; | 434 | IISFIFO_WR = 0; |
440 | } | 435 | } |
441 | 436 | ||
442 | value1 = *((int32_t *)p - 1); | 437 | value = *((int32_t *)dma_rec_data.p - 1); |
443 | IISFIFO_WR = value1; | 438 | IISFIFO_WR = value; |
444 | IISFIFO_WR = value1; | 439 | IISFIFO_WR = value; |
445 | } | 440 | } |
446 | } | 441 | } |
447 | } | 442 | } |
@@ -451,281 +446,112 @@ void fiq_record(void) | |||
451 | if (more_ready == NULL || more_ready(0) < 0) { | 446 | if (more_ready == NULL || more_ready(0) < 0) { |
452 | /* Finished recording */ | 447 | /* Finished recording */ |
453 | pcm_rec_dma_stop(); | 448 | pcm_rec_dma_stop(); |
449 | pcm_rec_dma_stopped_callback(); | ||
454 | } | 450 | } |
455 | |||
456 | fiq_record_exit: | ||
457 | asm volatile("ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */ | ||
458 | "subs pc, lr, #4 \n"); /* Return from FIQ */ | ||
459 | } | 451 | } |
460 | 452 | ||
461 | #else | 453 | #else |
462 | static short peak_l, peak_r IBSS_ATTR; | ||
463 | |||
464 | /* Temporary to stop playback crashing after record */ | ||
465 | void fiq_record(void) ICODE_ATTR __attribute__((naked)); | ||
466 | void fiq_record(void) | 454 | void fiq_record(void) |
467 | { | 455 | { |
468 | asm volatile ("stmfd sp!, {r0-r7, r11, ip, lr} \n"); /* Store context */ | ||
469 | |||
470 | register short value; | ||
471 | register pcm_more_callback_type2 more_ready; | 456 | register pcm_more_callback_type2 more_ready; |
472 | register int status = 0; | ||
473 | 457 | ||
474 | /* Clear interrupt */ | 458 | while (dma_rec_data.size > 0) { |
475 | #ifdef CPU_PP502x | 459 | if (IIS_RX_FULL_COUNT < 2) { |
476 | IISCONFIG &= ~(1 << 0); | 460 | return; |
477 | #elif CONFIG_CPU == PP5002 | ||
478 | /* TODO */ | ||
479 | #endif | ||
480 | |||
481 | while (p_size > 0) { | ||
482 | if (FIFO_FREE_COUNT < 2) { | ||
483 | /* enable interrupt */ | ||
484 | #ifdef CPU_PP502x | ||
485 | IISCONFIG |= (1 << 0); | ||
486 | #elif CONFIG_CPU == PP5002 | ||
487 | /* TODO */ | ||
488 | #endif | ||
489 | goto fiq_record_exit; | ||
490 | } | 461 | } |
491 | 462 | ||
492 | value = (unsigned short)(IISFIFO_RD >> 16); | 463 | #if SAMPLE_SIZE == 16 |
493 | if (value > peak_l) peak_l = value; | 464 | *dma_rec_data.p++ = IISFIFO_RD; |
494 | else if (-value > peak_l) peak_l = -value; | 465 | #elif SAMPLE_SIZE == 32 |
495 | *(p++) = value; | 466 | *dma_rec_data.p++ = IISFIFO_RD >> 16; |
496 | 467 | *dma_rec_data.p++ = IISFIFO_RD >> 16; | |
497 | value = (unsigned short)(IISFIFO_RD >> 16); | 468 | #endif |
498 | if (value > peak_r) peak_r = value; | 469 | dma_rec_data.size -= 4; |
499 | else if (-value > peak_r) peak_r = -value; | ||
500 | *(p++) = value; | ||
501 | |||
502 | p_size -= 4; | ||
503 | |||
504 | /* If we have filled the current chunk, start a new one */ | ||
505 | if (p_size == 0) { | ||
506 | rec_peak_left = peak_l; | ||
507 | rec_peak_right = peak_r; | ||
508 | peak_l = peak_r = 0; | ||
509 | } | ||
510 | } | 470 | } |
511 | 471 | ||
512 | more_ready = pcm_callback_more_ready; | 472 | more_ready = pcm_callback_more_ready; |
513 | 473 | ||
514 | if (more_ready != NULL && more_ready(status) >= 0) | 474 | if (more_ready == NULL || more_ready(0) < 0) { |
515 | goto fiq_record_exit; | 475 | /* Finished recording */ |
516 | 476 | pcm_rec_dma_stop(); | |
517 | /* Finished recording */ | 477 | pcm_rec_dma_stopped_callback(); |
518 | pcm_rec_dma_stop(); | 478 | } |
519 | |||
520 | fiq_record_exit: | ||
521 | asm volatile("ldmfd sp!, {r0-r7, r11, ip, lr} \n" /* Restore context */ | ||
522 | "subs pc, lr, #4 \n"); /* Return from FIQ */ | ||
523 | } | 479 | } |
524 | 480 | ||
525 | #endif /* HAVE_AS3514 */ | 481 | #endif /* SANSA_E200 */ |
526 | 482 | ||
527 | /* Continue transferring data in */ | 483 | /* Continue transferring data in */ |
528 | void pcm_record_more(void *start, size_t size) | 484 | void pcm_record_more(void *start, size_t size) |
529 | { | 485 | { |
530 | rec_peak_addr = start; /* Start peaking at dest */ | 486 | pcm_rec_peak_addr = start; /* Start peaking at dest */ |
531 | p = start; /* Start of RX buffer */ | 487 | dma_rec_data.p = start; /* Start of RX buffer */ |
532 | p_size = size; /* Bytes to transfer */ | 488 | dma_rec_data.size = size; /* Bytes to transfer */ |
533 | #ifdef CPU_PP502x | ||
534 | IISCONFIG |= (1 << 0); | ||
535 | #elif CONFIG_CPU == PP5002 | ||
536 | /* TODO */ | ||
537 | #endif | ||
538 | } | 489 | } |
539 | 490 | ||
540 | void pcm_rec_dma_stop(void) | 491 | void pcm_rec_dma_stop(void) |
541 | { | 492 | { |
542 | logf("pcm_rec_dma_stop"); | 493 | /* disable interrupt */ |
543 | 494 | IIS_IRQRX_REG &= ~IIS_IRQRX; | |
544 | disable_fiq(); | ||
545 | 495 | ||
546 | /* clear interrupt, disable fifo */ | 496 | dma_rec_data.state = 0; |
547 | IISCONFIG &= ~((1 << 28) | (1 << 0)); | 497 | dma_rec_data.size = 0; |
548 | 498 | #if NUM_CORES > 1 | |
549 | /* clear rx fifo */ | 499 | dma_rec_data.core = 0x00; |
550 | IISFIFO_CFG |= (1 << 12); | 500 | #endif |
551 | 501 | ||
552 | pcm_recording = false; | 502 | /* disable fifo */ |
503 | IISCONFIG &= ~IIS_RXFIFOEN; | ||
504 | IISFIFO_CFG |= IIS_RXCLR; | ||
553 | } | 505 | } |
554 | 506 | ||
555 | void pcm_rec_dma_start(void *addr, size_t size) | 507 | void pcm_rec_dma_start(void *addr, size_t size) |
556 | { | 508 | { |
557 | logf("pcm_rec_dma_start"); | 509 | pcm_rec_dma_stop(); |
558 | |||
559 | pcm_recording = true; | ||
560 | 510 | ||
561 | #ifndef HAVE_AS3514 | 511 | pcm_rec_peak_addr = addr; |
562 | peak_l = peak_r = 0; | 512 | dma_rec_data.p = addr; |
513 | dma_rec_data.size = size; | ||
514 | #if NUM_CORES > 1 | ||
515 | /* This will become more important later - and different ! */ | ||
516 | dma_rec_data.core = processor_id(); /* save initiating core */ | ||
563 | #endif | 517 | #endif |
518 | /* setup FIQ handler */ | ||
519 | fiq_function = fiq_record; | ||
564 | 520 | ||
565 | p_size = size; | 521 | /* interrupt on full fifo, enable record fifo interrupt */ |
566 | p = addr; | 522 | dma_rec_data.state = 1; |
567 | |||
568 | /* setup FIQ */ | ||
569 | CPU_INT_PRIORITY |= I2S_MASK; | ||
570 | CPU_INT_EN = I2S_MASK; | ||
571 | 523 | ||
572 | /* interrupt on full fifo, enable record fifo */ | 524 | /* enable RX FIFO */ |
573 | IISCONFIG |= (1 << 28) | (1 << 0); | 525 | IISCONFIG |= IIS_RXFIFOEN; |
574 | 526 | ||
575 | set_fiq_handler(fiq_record); | 527 | /* enable IIS interrupt as FIQ */ |
576 | enable_fiq(); | 528 | CPU_INT_PRIORITY |= IIS_MASK; |
529 | CPU_INT_EN = IIS_MASK; | ||
577 | } | 530 | } |
578 | 531 | ||
579 | void pcm_close_recording(void) | 532 | void pcm_rec_dma_close(void) |
580 | { | 533 | { |
581 | logf("pcm_close_recording"); | ||
582 | pcm_rec_dma_stop(); | 534 | pcm_rec_dma_stop(); |
583 | } /* pcm_close_recording */ | 535 | } /* pcm_close_recording */ |
584 | 536 | ||
585 | void pcm_init_recording(void) | 537 | void pcm_rec_dma_init(void) |
586 | { | 538 | { |
587 | logf("pcm_init_recording"); | ||
588 | |||
589 | pcm_recording = false; | ||
590 | pcm_callback_more_ready = NULL; | ||
591 | |||
592 | #ifdef CPU_PP502x | ||
593 | #if defined(IPOD_COLOR) || defined (IPOD_4G) | 539 | #if defined(IPOD_COLOR) || defined (IPOD_4G) |
594 | /* The usual magic from IPL - I'm guessing this configures the headphone | 540 | /* The usual magic from IPL - I'm guessing this configures the headphone |
595 | socket to be input or output - in this case, input. */ | 541 | socket to be input or output - in this case, input. */ |
596 | GPIOI_OUTPUT_VAL &= ~0x40; | 542 | GPIOI_OUTPUT_VAL &= ~0x40; |
597 | GPIOA_OUTPUT_VAL &= ~0x4; | 543 | GPIOA_OUTPUT_VAL &= ~0x4; |
598 | #endif | 544 | #endif |
599 | /* Setup the recording FIQ handler */ | ||
600 | set_fiq_handler(fiq_record); | ||
601 | #endif | ||
602 | 545 | ||
603 | pcm_rec_dma_stop(); | 546 | pcm_rec_dma_stop(); |
604 | } /* pcm_init */ | 547 | } /* pcm_init */ |
605 | 548 | ||
606 | void pcm_calculate_rec_peaks(int *left, int *right) | 549 | const void * pcm_rec_dma_get_peak_buffer(int *count) |
607 | { | 550 | { |
608 | #ifdef HAVE_AS3514 | 551 | unsigned long addr = (unsigned long)pcm_rec_peak_addr; |
609 | if (pcm_recording) | 552 | unsigned long end = (unsigned long)dma_rec_data.p; |
610 | { | 553 | *count = (end >> 2) - (addr >> 2); |
611 | unsigned long *start = rec_peak_addr; | 554 | return (void *)(addr & ~3); |
612 | unsigned long *end = (unsigned long *)p; | 555 | } /* pcm_rec_dma_get_peak_buffer */ |
613 | |||
614 | if (start < end) | ||
615 | { | ||
616 | unsigned long *addr = start; | ||
617 | long peak_l = 0, peak_r = 0; | ||
618 | long peaksq_l = 0, peaksq_r = 0; | ||
619 | |||
620 | do | ||
621 | { | ||
622 | long value = *addr; | ||
623 | long ch, chsq; | ||
624 | |||
625 | ch = (int16_t)value; | ||
626 | chsq = ch*ch; | ||
627 | if (chsq > peaksq_l) | ||
628 | peak_l = ch, peaksq_l = chsq; | ||
629 | |||
630 | ch = value >> 16; | ||
631 | chsq = ch*ch; | ||
632 | if (chsq > peaksq_r) | ||
633 | peak_r = ch, peaksq_r = chsq; | ||
634 | |||
635 | addr += 4; | ||
636 | } | ||
637 | while (addr < end); | ||
638 | |||
639 | if (start == rec_peak_addr) | ||
640 | rec_peak_addr = end; | ||
641 | |||
642 | rec_peak_left = abs(peak_l); | ||
643 | rec_peak_right = abs(peak_r); | ||
644 | } | ||
645 | } | ||
646 | else | ||
647 | { | ||
648 | rec_peak_left = rec_peak_right = 0; | ||
649 | } | ||
650 | #endif /* HAVE_AS3514 */ | ||
651 | |||
652 | if (left) | ||
653 | *left = rec_peak_left; | ||
654 | 556 | ||
655 | if (right) | ||
656 | *right = rec_peak_right; | ||
657 | } | ||
658 | #endif /* HAVE_RECORDING */ | 557 | #endif /* HAVE_RECORDING */ |
659 | |||
660 | /* | ||
661 | * This function goes directly into the DMA buffer to calculate the left and | ||
662 | * right peak values. To avoid missing peaks it tries to look forward two full | ||
663 | * peek periods (2/HZ sec, 100% overlap), although it's always possible that | ||
664 | * the entire period will not be visible. To reduce CPU load it only looks at | ||
665 | * every third sample, and this can be reduced even further if needed (even | ||
666 | * every tenth sample would still be pretty accurate). | ||
667 | */ | ||
668 | |||
669 | /* Check for a peak every PEAK_STRIDE samples */ | ||
670 | #define PEAK_STRIDE 3 | ||
671 | /* Up to 1/50th of a second of audio for peak calculation */ | ||
672 | /* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */ | ||
673 | #define PEAK_SAMPLES (44100/50) | ||
674 | void pcm_calculate_peaks(int *left, int *right) | ||
675 | { | ||
676 | short *addr; | ||
677 | short *end; | ||
678 | { | ||
679 | size_t samples = p_size / 4; | ||
680 | addr = p; | ||
681 | |||
682 | if (samples > PEAK_SAMPLES) | ||
683 | samples = PEAK_SAMPLES - (PEAK_STRIDE - 1); | ||
684 | else | ||
685 | samples -= MIN(PEAK_STRIDE - 1, samples); | ||
686 | |||
687 | end = &addr[samples * 2]; | ||
688 | } | ||
689 | |||
690 | if (left && right) { | ||
691 | int left_peak = 0, right_peak = 0; | ||
692 | |||
693 | while (addr < end) { | ||
694 | int value; | ||
695 | if ((value = addr [0]) > left_peak) | ||
696 | left_peak = value; | ||
697 | else if (-value > left_peak) | ||
698 | left_peak = -value; | ||
699 | |||
700 | if ((value = addr [PEAK_STRIDE | 1]) > right_peak) | ||
701 | right_peak = value; | ||
702 | else if (-value > right_peak) | ||
703 | right_peak = -value; | ||
704 | |||
705 | addr = &addr[PEAK_STRIDE * 2]; | ||
706 | } | ||
707 | |||
708 | *left = left_peak; | ||
709 | *right = right_peak; | ||
710 | } | ||
711 | else if (left || right) { | ||
712 | int peak_value = 0, value; | ||
713 | |||
714 | if (right) | ||
715 | addr += (PEAK_STRIDE | 1); | ||
716 | |||
717 | while (addr < end) { | ||
718 | if ((value = addr [0]) > peak_value) | ||
719 | peak_value = value; | ||
720 | else if (-value > peak_value) | ||
721 | peak_value = -value; | ||
722 | |||
723 | addr += PEAK_STRIDE * 2; | ||
724 | } | ||
725 | |||
726 | if (left) | ||
727 | *left = peak_value; | ||
728 | else | ||
729 | *right = peak_value; | ||
730 | } | ||
731 | } | ||