diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2007-10-06 22:27:27 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2007-10-06 22:27:27 +0000 |
commit | 6077e5b7c85c0d6f5963e4aadb215faf2c4d10d2 (patch) | |
tree | a6bc91ee4168e83617e942eeaea46e5523e82420 /firmware/target/arm/pcm-pp.c | |
parent | f6de0d4083a4fcb6da57f271e1f8ccaf715e571d (diff) | |
download | rockbox-6077e5b7c85c0d6f5963e4aadb215faf2c4d10d2.tar.gz rockbox-6077e5b7c85c0d6f5963e4aadb215faf2c4d10d2.zip |
Unify PCM interface just above the hardware driver level for all targets including the sims. Perform lockout of audio callback when changing states. Weird new playback or recording trouble? Check before and after this revision first though things seem quite sound.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15006 a1c6a512-1295-4272-9138-f99709370657
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 | } | ||