diff options
Diffstat (limited to 'firmware/pcm_record.c')
-rw-r--r-- | firmware/pcm_record.c | 717 |
1 files changed, 469 insertions, 248 deletions
diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c index 8b46a09ed3..b167d6a562 100644 --- a/firmware/pcm_record.c +++ b/firmware/pcm_record.c | |||
@@ -30,11 +30,7 @@ | |||
30 | 30 | ||
31 | #include "cpu.h" | 31 | #include "cpu.h" |
32 | #include "i2c.h" | 32 | #include "i2c.h" |
33 | #if defined(HAVE_UDA1380) | ||
34 | #include "uda1380.h" | 33 | #include "uda1380.h" |
35 | #elif defined(HAVE_TLV320) | ||
36 | #include "tlv320.h" | ||
37 | #endif | ||
38 | #include "system.h" | 34 | #include "system.h" |
39 | #include "usb.h" | 35 | #include "usb.h" |
40 | 36 | ||
@@ -56,47 +52,56 @@ | |||
56 | static volatile bool is_recording; /* We are recording */ | 52 | static volatile bool is_recording; /* We are recording */ |
57 | static volatile bool is_stopping; /* Are we going to stop */ | 53 | static volatile bool is_stopping; /* Are we going to stop */ |
58 | static volatile bool is_paused; /* We have paused */ | 54 | static volatile bool is_paused; /* We have paused */ |
55 | static volatile bool is_error; /* An error has occured */ | ||
59 | 56 | ||
60 | static volatile int num_rec_bytes; | 57 | static volatile unsigned long num_rec_bytes; /* Num bytes recorded */ |
58 | static volatile unsigned long num_file_bytes; /* Num bytes written to current file */ | ||
61 | static volatile int int_count; /* Number of DMA completed interrupts */ | 59 | static volatile int int_count; /* Number of DMA completed interrupts */ |
62 | static volatile int error_count; /* Number of DMA errors */ | 60 | static volatile int error_count; /* Number of DMA errors */ |
63 | 61 | ||
64 | static unsigned long record_start_time; /* Value of current_tick when recording was started */ | 62 | static unsigned long record_start_time; /* Value of current_tick when recording was started */ |
65 | static unsigned long pause_start_time; /* Value of current_tick when pause was started */ | 63 | static unsigned long pause_start_time; /* Value of current_tick when pause was started */ |
66 | 64 | ||
67 | static int rec_gain, rec_volume; | ||
68 | static bool show_waveform; | 65 | static bool show_waveform; |
69 | static int init_done = 0; | 66 | |
70 | static int wav_file; | 67 | static int wav_file; |
71 | static char recording_filename[MAX_PATH]; | 68 | static char recording_filename[MAX_PATH]; |
72 | 69 | ||
70 | static bool init_done, close_done, record_done, stop_done, pause_done, resume_done, new_file_done; | ||
71 | |||
73 | /***************************************************************************/ | 72 | /***************************************************************************/ |
74 | 73 | ||
75 | /* | 74 | /* |
76 | Some estimates: | 75 | Some estimates: |
77 | 44100 HZ * 4 = 176400 bytes/s | 76 | Normal recording rate: 44100 HZ * 4 = 176 KB/s |
78 | Refresh LCD 10 HZ = 176400 / 10 = 17640 bytes ~=~ 1024*16 bytes | 77 | Total buffer size: 32 MB / 176 KB/s = 181s before writing to disk |
78 | CHUNK_SIZE: 65536 | ||
79 | Chunks/s: 176 KB / 65536 = ~3 chunks / s | ||
80 | |||
81 | WRITE_THRESHOLD: 30 | ||
82 | - Should gives us < 10s to start writing to disk before we run out | ||
83 | of buffer space.. | ||
79 | 84 | ||
80 | If NUM_BUFFERS is 80 we can hold ~8 sec of data in memory | ||
81 | ALL_BUFFER_SIZE will be 1024*16 * 80 = 1310720 bytes | ||
82 | */ | 85 | */ |
83 | 86 | ||
84 | #define NUM_BUFFERS 80 | 87 | #define CHUNK_SIZE 65536 /* Multiple of 4 */ |
85 | #define EACH_BUFFER_SIZE (1024*16) /* Multiple of 4. Use small value to get responsive waveform */ | 88 | #define WRITE_THRESHOLD 30 /* Write when this many chunks (or less) until buffer full */ |
86 | #define ALL_BUFFERS_SIZE (NUM_BUFFERS * EACH_BUFFER_SIZE) | 89 | |
90 | #define GET_CHUNK(x) (short*)(&rec_buffer[CHUNK_SIZE*(x)]) | ||
87 | 91 | ||
88 | #define WRITE_THRESHOLD 40 /* Minimum number of buffers before write to file */ | 92 | static unsigned char *rec_buffer; /* Circular recording buffer */ |
93 | static int num_chunks; /* Number of chunks available in rec_buffer */ | ||
89 | 94 | ||
90 | static unsigned char *rec_buffers[NUM_BUFFERS]; | ||
91 | 95 | ||
92 | /* | 96 | /* |
93 | Overrun occures when DMA needs to write a new buffer and write_index == read_index | 97 | Overrun occures when DMA needs to write a new chunk and write_index == read_index |
94 | Solution to this is to optimize pcmrec_callback, use cpu_boost somewhere or increase | 98 | Solution to this is to optimize pcmrec_callback, use cpu_boost or save to disk |
95 | the total buffer size (or WRITE_THRESHOLD) | 99 | more often. |
96 | */ | 100 | */ |
97 | 101 | ||
98 | static int write_index; /* Which buffer the DMA is currently recording */ | 102 | static volatile int write_index; /* Current chunk the DMA is writing to */ |
99 | static int read_index; /* The oldest buffer that the pcmrec_callback has not read */ | 103 | static volatile int read_index; /* Oldest chunk that is not written to disk */ |
104 | static volatile int read2_index; /* Latest chunk that has not been converted to little endian */ | ||
100 | 105 | ||
101 | /***************************************************************************/ | 106 | /***************************************************************************/ |
102 | 107 | ||
@@ -105,10 +110,13 @@ static long pcmrec_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(lon | |||
105 | static const char pcmrec_thread_name[] = "pcmrec"; | 110 | static const char pcmrec_thread_name[] = "pcmrec"; |
106 | 111 | ||
107 | static void pcmrec_thread(void); | 112 | static void pcmrec_thread(void); |
113 | static void pcmrec_dma_start(void); | ||
114 | static void pcmrec_dma_stop(void); | ||
108 | 115 | ||
109 | /* Event IDs */ | 116 | /* Event IDs */ |
110 | #define PCMREC_OPEN 1 /* Enable recording */ | 117 | #define PCMREC_INIT 1 /* Enable recording */ |
111 | #define PCMREC_CLOSE 2 /* Disable recording */ | 118 | #define PCMREC_CLOSE 2 |
119 | |||
112 | #define PCMREC_START 3 /* Start a new recording */ | 120 | #define PCMREC_START 3 /* Start a new recording */ |
113 | #define PCMREC_STOP 4 /* Stop the current recording */ | 121 | #define PCMREC_STOP 4 /* Stop the current recording */ |
114 | #define PCMREC_PAUSE 10 | 122 | #define PCMREC_PAUSE 10 |
@@ -122,71 +130,58 @@ static void pcmrec_thread(void); | |||
122 | /* Functions that are not executing in the pcmrec_thread first */ | 130 | /* Functions that are not executing in the pcmrec_thread first */ |
123 | /*******************************************************************/ | 131 | /*******************************************************************/ |
124 | 132 | ||
125 | void pcm_init_recording(void) | 133 | /* Creates pcmrec_thread */ |
134 | void pcm_rec_init(void) | ||
126 | { | 135 | { |
127 | int_count = 0; | ||
128 | error_count = 0; | ||
129 | |||
130 | show_waveform = 0; | ||
131 | is_recording = 0; | ||
132 | is_stopping = 0; | ||
133 | num_rec_bytes = 0; | ||
134 | wav_file = -1; | ||
135 | read_index = 0; | ||
136 | write_index = 0; | ||
137 | |||
138 | queue_init(&pcmrec_queue); | 136 | queue_init(&pcmrec_queue); |
139 | create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), pcmrec_thread_name); | 137 | create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), pcmrec_thread_name); |
140 | } | 138 | } |
141 | 139 | ||
142 | void pcm_open_recording(void) | ||
143 | { | ||
144 | init_done = 0; | ||
145 | |||
146 | logf("pcm_open_rec"); | ||
147 | |||
148 | queue_post(&pcmrec_queue, PCMREC_OPEN, 0); | ||
149 | 140 | ||
150 | while (init_done) | 141 | /* Initializes recording: |
151 | { | 142 | * - Set up the UDA1380 for recording |
152 | sleep(HZ >> 8); | 143 | * - Prepare for DMA transfers |
153 | } | 144 | */ |
154 | 145 | ||
155 | logf("pcm_open_rec done"); | 146 | void audio_init_recording(void) |
147 | { | ||
148 | init_done = false; | ||
149 | queue_post(&pcmrec_queue, PCMREC_INIT, 0); | ||
150 | |||
151 | while(!init_done) | ||
152 | sleep_thread(); | ||
153 | wake_up_thread(); | ||
156 | } | 154 | } |
157 | 155 | ||
158 | void pcm_close_recording(void) | 156 | void audio_close_recording(void) |
159 | { | 157 | { |
160 | /* todo: synchronize completion with pcmrec thread */ | 158 | close_done = false; |
161 | queue_post(&pcmrec_queue, PCMREC_CLOSE, 0); | 159 | queue_post(&pcmrec_queue, PCMREC_CLOSE, 0); |
160 | |||
161 | while(!close_done) | ||
162 | sleep_thread(); | ||
163 | wake_up_thread(); | ||
162 | } | 164 | } |
163 | 165 | ||
164 | 166 | unsigned long pcm_rec_status(void) | |
165 | |||
166 | unsigned long pcm_status(void) | ||
167 | { | 167 | { |
168 | unsigned long ret = 0; | 168 | unsigned long ret = 0; |
169 | 169 | ||
170 | if (is_recording) | 170 | if (is_recording) |
171 | ret |= AUDIO_STATUS_RECORD; | 171 | ret |= AUDIO_STATUS_RECORD; |
172 | if (is_paused) | ||
173 | ret |= AUDIO_STATUS_PAUSE; | ||
174 | if (is_error) | ||
175 | ret |= AUDIO_STATUS_ERROR; | ||
172 | 176 | ||
173 | return ret; | 177 | return ret; |
174 | } | 178 | } |
175 | 179 | ||
176 | 180 | unsigned long audio_recorded_time(void) | |
177 | |||
178 | void pcm_new_file(const char *filename) | ||
179 | { | ||
180 | /* todo */ | ||
181 | filename = filename; | ||
182 | |||
183 | } | ||
184 | |||
185 | unsigned long pcm_recorded_time(void) | ||
186 | { | 181 | { |
187 | if (is_recording) | 182 | if (is_recording) |
188 | { | 183 | { |
189 | if(is_paused) | 184 | if (is_paused) |
190 | return pause_start_time - record_start_time; | 185 | return pause_start_time - record_start_time; |
191 | else | 186 | else |
192 | return current_tick - record_start_time; | 187 | return current_tick - record_start_time; |
@@ -195,92 +190,168 @@ unsigned long pcm_recorded_time(void) | |||
195 | return 0; | 190 | return 0; |
196 | } | 191 | } |
197 | 192 | ||
198 | unsigned long pcm_num_recorded_bytes(void) | 193 | unsigned long audio_num_recorded_bytes(void) |
199 | { | 194 | { |
200 | |||
201 | if (is_recording) | 195 | if (is_recording) |
202 | { | ||
203 | return num_rec_bytes; | 196 | return num_rec_bytes; |
204 | } | ||
205 | else | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | void pcm_pause_recording(void) | ||
210 | { | ||
211 | /* todo */ | ||
212 | } | ||
213 | 197 | ||
214 | void pcm_resume_recording(void) | 198 | return 0; |
215 | { | ||
216 | /* todo */ | ||
217 | } | 199 | } |
218 | 200 | ||
219 | 201 | ||
220 | /** | 202 | /** |
221 | * Sets the audio source | 203 | * Sets the audio source |
222 | * | 204 | * |
223 | * Side effect: This functions starts feeding the CPU with audio data over the I2S bus | 205 | * This functions starts feeding the CPU with audio data over the I2S bus |
224 | * | 206 | * |
225 | * @param source 0=line-in, 1=mic | 207 | * @param source 0=mic, 1=line-in, (todo: 2=spdif) |
226 | */ | 208 | */ |
227 | void pcm_set_recording_options(int source, bool enable_waveform) | 209 | void audio_set_recording_options(int frequency, int quality, |
210 | int source, int channel_mode, | ||
211 | bool editable, int prerecord_time, | ||
212 | bool monitor) | ||
228 | { | 213 | { |
229 | #if defined(HAVE_UDA1380) | 214 | /* TODO: */ |
230 | uda1380_enable_recording(source); | 215 | (void)frequency; |
231 | #elif defined(HAVE_TLV320) | 216 | (void)quality; |
232 | tlv320_enable_recording(source); | 217 | (void)channel_mode; |
233 | #endif | 218 | (void)editable; |
234 | show_waveform = enable_waveform; | 219 | (void)prerecord_time; |
220 | |||
221 | //logf("pcmrec: src=%d", source); | ||
222 | |||
223 | switch (source) | ||
224 | { | ||
225 | /* mic */ | ||
226 | case 0: | ||
227 | uda1380_enable_recording(true); | ||
228 | break; | ||
229 | |||
230 | /* line-in */ | ||
231 | case 1: | ||
232 | uda1380_enable_recording(false); | ||
233 | break; | ||
234 | } | ||
235 | |||
236 | uda1380_set_monitor(monitor); | ||
235 | } | 237 | } |
236 | 238 | ||
237 | 239 | ||
238 | /** | 240 | /** |
241 | * Note that microphone is mono, only left value is used | ||
242 | * See uda1380_set_recvol() for exact ranges. | ||
239 | * | 243 | * |
240 | * @param gain line-in and microphone gain (0-15) | 244 | * @param type 0=line-in (radio), 1=mic, 2=ADC |
241 | * @param volume ADC volume (0-255) | 245 | * |
242 | */ | 246 | */ |
243 | void pcm_set_recording_gain(int gain, int volume) | 247 | void audio_set_recording_gain(int left, int right, int type) |
244 | { | 248 | { |
245 | rec_gain = gain; | 249 | //logf("rcmrec: t=%d l=%d r=%d", type, left, right); |
246 | rec_volume = volume; | 250 | uda1380_set_recvol(left, right, type); |
247 | |||
248 | queue_post(&pcmrec_queue, PCMREC_SET_GAIN, 0); | ||
249 | } | 251 | } |
250 | 252 | ||
253 | |||
251 | /** | 254 | /** |
252 | * Start recording | 255 | * Start recording |
253 | * | 256 | * |
254 | * Use pcm_set_recording_options before calling record | 257 | * Use audio_set_recording_options first to select recording options |
255 | */ | 258 | */ |
256 | void pcm_record(const char *filename) | 259 | void audio_record(const char *filename) |
257 | { | 260 | { |
261 | if (is_recording) | ||
262 | { | ||
263 | logf("record while recording"); | ||
264 | return; | ||
265 | } | ||
266 | |||
258 | strncpy(recording_filename, filename, MAX_PATH - 1); | 267 | strncpy(recording_filename, filename, MAX_PATH - 1); |
259 | recording_filename[MAX_PATH - 1] = 0; | 268 | recording_filename[MAX_PATH - 1] = 0; |
260 | 269 | ||
270 | record_done = false; | ||
261 | queue_post(&pcmrec_queue, PCMREC_START, 0); | 271 | queue_post(&pcmrec_queue, PCMREC_START, 0); |
272 | |||
273 | while(!record_done) | ||
274 | sleep_thread(); | ||
275 | wake_up_thread(); | ||
276 | } | ||
277 | |||
278 | |||
279 | void audio_new_file(const char *filename) | ||
280 | { | ||
281 | logf("pcm_new_file"); | ||
282 | |||
283 | new_file_done = false; | ||
284 | |||
285 | strncpy(recording_filename, filename, MAX_PATH - 1); | ||
286 | recording_filename[MAX_PATH - 1] = 0; | ||
287 | |||
288 | queue_post(&pcmrec_queue, PCMREC_NEW_FILE, 0); | ||
289 | |||
290 | while(!new_file_done) | ||
291 | sleep_thread(); | ||
292 | wake_up_thread(); | ||
293 | |||
294 | logf("pcm_new_file done"); | ||
262 | } | 295 | } |
263 | 296 | ||
264 | /** | 297 | /** |
265 | * | 298 | * |
266 | */ | 299 | */ |
267 | void pcm_stop_recording(void) | 300 | void audio_stop_recording(void) |
268 | { | 301 | { |
269 | if (is_recording) | 302 | if (!is_recording) |
270 | is_stopping = 1; | 303 | return; |
271 | 304 | ||
305 | logf("pcm_stop"); | ||
306 | |||
307 | stop_done = false; | ||
272 | queue_post(&pcmrec_queue, PCMREC_STOP, 0); | 308 | queue_post(&pcmrec_queue, PCMREC_STOP, 0); |
273 | 309 | ||
274 | logf("pcm_stop_recording"); | 310 | while(!stop_done) |
311 | sleep_thread(); | ||
312 | wake_up_thread(); | ||
313 | |||
314 | logf("pcm_stop done"); | ||
315 | } | ||
275 | 316 | ||
276 | while (is_stopping) | 317 | void audio_pause_recording(void) |
318 | { | ||
319 | if (!is_recording) | ||
320 | { | ||
321 | logf("pause when not recording"); | ||
322 | return; | ||
323 | } | ||
324 | if (is_paused) | ||
277 | { | 325 | { |
278 | sleep(HZ >> 4); | 326 | logf("pause when paused"); |
327 | return; | ||
279 | } | 328 | } |
329 | |||
330 | pause_done = false; | ||
331 | queue_post(&pcmrec_queue, PCMREC_PAUSE, 0); | ||
280 | 332 | ||
281 | logf("pcm_stop_recording done"); | 333 | while(!pause_done) |
334 | sleep_thread(); | ||
335 | wake_up_thread(); | ||
282 | } | 336 | } |
283 | 337 | ||
338 | void audio_resume_recording(void) | ||
339 | { | ||
340 | if (!is_paused) | ||
341 | { | ||
342 | logf("resume when not paused"); | ||
343 | return; | ||
344 | } | ||
345 | |||
346 | resume_done = false; | ||
347 | queue_post(&pcmrec_queue, PCMREC_RESUME, 0); | ||
348 | |||
349 | while(!resume_done) | ||
350 | sleep_thread(); | ||
351 | wake_up_thread(); | ||
352 | } | ||
353 | |||
354 | |||
284 | 355 | ||
285 | /***************************************************************************/ | 356 | /***************************************************************************/ |
286 | /* Functions that executes in the context of pcmrec_thread */ | 357 | /* Functions that executes in the context of pcmrec_thread */ |
@@ -288,100 +359,116 @@ void pcm_stop_recording(void) | |||
288 | 359 | ||
289 | 360 | ||
290 | /** | 361 | /** |
291 | * Process the buffers using read_index and write_index. | 362 | * Process the chunks using read_index and write_index. |
292 | * | 363 | * |
293 | * DMA1 handler posts to pcmrec_queue so that pcmrec_thread calls this | 364 | * DMA1 handler posts to pcmrec_queue and pcmrec_thread calls this |
294 | * function. Also pcmrec_stop will call this function when the recording | 365 | * function. |
295 | * is stopping, and that call will have flush = true. | 366 | * |
367 | * Other function can also call this function with flush = true when | ||
368 | * they want to save everything recorded sofar to disk. | ||
296 | * | 369 | * |
297 | */ | 370 | */ |
298 | 371 | ||
299 | void pcmrec_callback(bool flush) __attribute__ ((section (".icode"))); | 372 | static void pcmrec_callback(bool flush) __attribute__ ((section (".icode"))); |
300 | void pcmrec_callback(bool flush) | 373 | static void pcmrec_callback(bool flush) |
301 | { | 374 | { |
302 | int num_ready; | 375 | int num_ready, num_free, num_new; |
376 | unsigned short *ptr; | ||
377 | int i, j, w; | ||
378 | |||
379 | w = write_index; | ||
380 | |||
381 | num_new = w - read2_index; | ||
382 | if (num_new < 0) | ||
383 | num_new += num_chunks; | ||
384 | |||
385 | for (i=0; i<num_new; i++) | ||
386 | { | ||
387 | /* Convert the samples to little-endian so we only have to write later | ||
388 | (Less hd-spinning time) | ||
389 | */ | ||
390 | ptr = GET_CHUNK(read2_index); | ||
391 | for (j=0; j<CHUNK_SIZE/2; j++) | ||
392 | { | ||
393 | /* TODO: might be a good place to add the peak-meter.. */ | ||
394 | |||
395 | *ptr = htole16(*ptr); | ||
396 | ptr++; | ||
397 | } | ||
398 | |||
399 | num_rec_bytes += CHUNK_SIZE; | ||
400 | |||
401 | read2_index++; | ||
402 | if (read2_index >= num_chunks) | ||
403 | read2_index = 0; | ||
404 | } | ||
303 | 405 | ||
304 | num_ready = write_index - read_index; | 406 | num_ready = w - read_index; |
305 | if (num_ready < 0) | 407 | if (num_ready < 0) |
306 | num_ready += NUM_BUFFERS; | 408 | num_ready += num_chunks; |
307 | |||
308 | /* we can consume up to num_ready buffers */ | ||
309 | 409 | ||
310 | #ifdef HAVE_REMOTE_LCD | 410 | if (num_ready >= num_chunks) |
311 | /* Draw waveform on remote LCD */ | ||
312 | if (show_waveform && num_ready>0) | ||
313 | { | 411 | { |
314 | short *buf; | 412 | logf("num_ready overflow?"); |
315 | long x,y,offset; | 413 | num_ready = num_chunks-1; |
316 | int show_index; | 414 | } |
317 | |||
318 | /* Just display the last buffer (most recent one) */ | ||
319 | show_index = read_index + num_ready - 1; | ||
320 | buf = (short*)rec_buffers[show_index]; | ||
321 | |||
322 | lcd_remote_clear_display(); | ||
323 | |||
324 | offset = 0; | ||
325 | for (x=0; x<LCD_REMOTE_WIDTH-1; x++) | ||
326 | { | ||
327 | y = buf[offset] * (LCD_REMOTE_HEIGHT / 2) *5; /* The 5 is just 'zooming' */ | ||
328 | y = y >> 15; /* Divide with SHRT_MAX */ | ||
329 | y += LCD_REMOTE_HEIGHT/2; | ||
330 | 415 | ||
331 | if (y < 2) y=2; | 416 | num_free = num_chunks - num_ready; |
332 | if (y >= LCD_REMOTE_HEIGHT-2) y = LCD_REMOTE_HEIGHT-2; | ||
333 | 417 | ||
334 | lcd_remote_drawpixel(x,y); | 418 | if (wav_file == -1 || (!is_recording && !flush)) |
419 | { | ||
420 | /* In this case we should consume the buffers to avoid */ | ||
421 | /* getting 'dma1 overrun' */ | ||
335 | 422 | ||
336 | offset += (EACH_BUFFER_SIZE/2) / LCD_REMOTE_WIDTH; | 423 | read_index+=num_ready; |
337 | } | 424 | if (read_index >= num_chunks) |
425 | read_index -= num_chunks; | ||
338 | 426 | ||
339 | lcd_remote_update(); | 427 | return; |
340 | } | 428 | } |
341 | 429 | ||
342 | #endif | 430 | if (num_free <= WRITE_THRESHOLD || flush) |
343 | |||
344 | /* Note: This might be a good place to call the 'codec' later */ | ||
345 | |||
346 | /* Check that we have the minimum amount of data to save or */ | ||
347 | /* that if it's closing time which mean we have to save.. */ | ||
348 | if (wav_file != -1) | ||
349 | { | 431 | { |
350 | if (num_ready >= WRITE_THRESHOLD || flush) | 432 | logf("writing: %d (%d)", num_ready, flush); |
433 | |||
434 | for (i=0; i<num_ready; i++) | ||
351 | { | 435 | { |
352 | unsigned short *ptr = (unsigned short*)rec_buffers[read_index]; | 436 | if (write(wav_file, GET_CHUNK(read_index), CHUNK_SIZE) != CHUNK_SIZE) |
353 | int i; | ||
354 | |||
355 | for (i=0; i<EACH_BUFFER_SIZE * num_ready / 2; i++) | ||
356 | { | 437 | { |
357 | *ptr = htole16(*ptr); | 438 | logf("pcmrec: write err"); |
358 | ptr++; | 439 | pcmrec_dma_stop(); |
440 | return; | ||
359 | } | 441 | } |
360 | 442 | ||
361 | write(wav_file, rec_buffers[read_index], EACH_BUFFER_SIZE * num_ready); | 443 | num_file_bytes += CHUNK_SIZE; |
362 | 444 | ||
363 | read_index+=num_ready; | 445 | read_index++; |
364 | if (read_index >= NUM_BUFFERS) | 446 | if (read_index >= num_chunks) |
365 | read_index -= NUM_BUFFERS; | 447 | read_index = 0; |
366 | } | 448 | } |
367 | 449 | ||
368 | } else | 450 | logf("done"); |
369 | { | ||
370 | /* In this case we must consume the buffers otherwise we will */ | ||
371 | /* get 'dma1 overrun' pretty fast */ | ||
372 | |||
373 | read_index+=num_ready; | ||
374 | if (read_index >= NUM_BUFFERS) | ||
375 | read_index -= NUM_BUFFERS; | ||
376 | } | 451 | } |
377 | } | 452 | } |
378 | 453 | ||
454 | /* Abort dma transfer */ | ||
455 | static void pcmrec_dma_stop(void) | ||
456 | { | ||
457 | DCR1 = 0; | ||
458 | |||
459 | is_error = true; | ||
460 | is_recording = false; | ||
461 | |||
462 | error_count++; | ||
463 | |||
464 | logf("dma1 stopped"); | ||
465 | } | ||
379 | 466 | ||
380 | void pcmrec_dma_start(void) | 467 | static void pcmrec_dma_start(void) |
381 | { | 468 | { |
382 | DAR1 = (unsigned long)rec_buffers[write_index++]; /* Destination address */ | 469 | DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */ |
383 | SAR1 = (unsigned long)&PDIR2; /* Source address */ | 470 | SAR1 = (unsigned long)&PDIR2; /* Source address */ |
384 | BCR1 = EACH_BUFFER_SIZE; /* Bytes to transfer */ | 471 | BCR1 = CHUNK_SIZE; /* Bytes to transfer */ |
385 | 472 | ||
386 | /* Start the DMA transfer.. */ | 473 | /* Start the DMA transfer.. */ |
387 | DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START; | 474 | DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START; |
@@ -404,36 +491,37 @@ void DMA1(void) | |||
404 | { | 491 | { |
405 | DCR1 = 0; /* Stop DMA transfer */ | 492 | DCR1 = 0; /* Stop DMA transfer */ |
406 | error_count++; | 493 | error_count++; |
407 | is_recording = 0; | 494 | is_recording = false; |
408 | 495 | ||
409 | logf("dma1 err 0x%x", res); | 496 | logf("dma1 err 0x%x", res); |
497 | |||
498 | /* Flush recorded data to disk and stop recording */ | ||
499 | queue_post(&pcmrec_queue, PCMREC_STOP, NULL); | ||
410 | 500 | ||
411 | } else | 501 | } else |
412 | { | 502 | { |
413 | num_rec_bytes += EACH_BUFFER_SIZE; | ||
414 | |||
415 | write_index++; | 503 | write_index++; |
416 | if (write_index >= NUM_BUFFERS) | 504 | if (write_index >= num_chunks) |
417 | write_index = 0; | 505 | write_index = 0; |
418 | 506 | ||
419 | if (is_stopping || !is_recording) | 507 | if (is_stopping || !is_recording) |
420 | { | 508 | { |
421 | DCR1 = 0; /* Stop DMA transfer */ | 509 | DCR1 = 0; /* Stop DMA transfer */ |
422 | is_recording = 0; | 510 | is_stopping = false; |
423 | 511 | ||
424 | logf("dma1 stopping"); | 512 | logf("dma1 stopping"); |
425 | 513 | ||
426 | } else if (write_index == read_index) | 514 | } else if (write_index == read_index) |
427 | { | 515 | { |
428 | DCR1 = 0; /* Stop DMA transfer */ | 516 | DCR1 = 0; /* Stop DMA transfer */ |
429 | is_recording = 0; | 517 | is_recording = false; |
430 | 518 | ||
431 | logf("dma1 overrun"); | 519 | logf("dma1 overrun"); |
432 | 520 | ||
433 | } else | 521 | } else |
434 | { | 522 | { |
435 | DAR1 = (unsigned long)rec_buffers[write_index]; /* Destination address */ | 523 | DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */ |
436 | BCR1 = EACH_BUFFER_SIZE; | 524 | BCR1 = CHUNK_SIZE; |
437 | 525 | ||
438 | queue_post(&pcmrec_queue, PCMREC_GOT_DATA, NULL); | 526 | queue_post(&pcmrec_queue, PCMREC_GOT_DATA, NULL); |
439 | 527 | ||
@@ -443,6 +531,8 @@ void DMA1(void) | |||
443 | IPR |= (1<<15); /* Clear pending interrupt request */ | 531 | IPR |= (1<<15); /* Clear pending interrupt request */ |
444 | } | 532 | } |
445 | 533 | ||
534 | /* Create WAVE file and write header */ | ||
535 | /* Sets returns 0 if success, -1 on failure */ | ||
446 | static int start_wave(void) | 536 | static int start_wave(void) |
447 | { | 537 | { |
448 | unsigned char header[44] = | 538 | unsigned char header[44] = |
@@ -456,7 +546,8 @@ static int start_wave(void) | |||
456 | if (wav_file < 0) | 546 | if (wav_file < 0) |
457 | { | 547 | { |
458 | wav_file = -1; | 548 | wav_file = -1; |
459 | logf("create failed: %d", wav_file); | 549 | logf("rec: create failed: %d", wav_file); |
550 | is_error = true; | ||
460 | return -1; | 551 | return -1; |
461 | } | 552 | } |
462 | 553 | ||
@@ -464,8 +555,9 @@ static int start_wave(void) | |||
464 | { | 555 | { |
465 | close(wav_file); | 556 | close(wav_file); |
466 | wav_file = -1; | 557 | wav_file = -1; |
467 | logf("write failed"); | 558 | logf("rec: write failed"); |
468 | return -2; | 559 | is_error = true; |
560 | return -1; | ||
469 | } | 561 | } |
470 | 562 | ||
471 | return 0; | 563 | return 0; |
@@ -476,16 +568,19 @@ static void close_wave(void) | |||
476 | { | 568 | { |
477 | long l; | 569 | long l; |
478 | 570 | ||
479 | l = htole32(num_rec_bytes + 36); | 571 | if (wav_file != -1) |
480 | lseek(wav_file, 4, SEEK_SET); | 572 | { |
481 | write(wav_file, &l, 4); | 573 | l = htole32(num_file_bytes + 36); |
482 | 574 | lseek(wav_file, 4, SEEK_SET); | |
483 | l = htole32(num_rec_bytes); | 575 | write(wav_file, &l, 4); |
484 | lseek(wav_file, 40, SEEK_SET); | 576 | |
485 | write(wav_file, &l, 4); | 577 | l = htole32(num_file_bytes); |
486 | 578 | lseek(wav_file, 40, SEEK_SET); | |
487 | close(wav_file); | 579 | write(wav_file, &l, 4); |
488 | wav_file = -1; | 580 | |
581 | close(wav_file); | ||
582 | wav_file = -1; | ||
583 | } | ||
489 | } | 584 | } |
490 | 585 | ||
491 | static void pcmrec_start(void) | 586 | static void pcmrec_start(void) |
@@ -493,74 +588,206 @@ static void pcmrec_start(void) | |||
493 | logf("pcmrec_start"); | 588 | logf("pcmrec_start"); |
494 | 589 | ||
495 | if (is_recording) | 590 | if (is_recording) |
591 | { | ||
592 | logf("already recording"); | ||
593 | record_done = true; | ||
496 | return; | 594 | return; |
497 | 595 | } | |
596 | |||
498 | if (wav_file != -1) | 597 | if (wav_file != -1) |
499 | close(wav_file); | 598 | close(wav_file); |
500 | 599 | ||
501 | logf("rec: %s", recording_filename); | 600 | if (start_wave() != 0) |
502 | 601 | { | |
503 | start_wave(); /* todo: send signal to pcm_record if we have failed */ | 602 | /* failed to create the file */ |
504 | 603 | record_done = true; | |
604 | return; | ||
605 | } | ||
606 | |||
505 | num_rec_bytes = 0; | 607 | num_rec_bytes = 0; |
506 | 608 | num_file_bytes = 0; | |
507 | /* Store the current time */ | ||
508 | record_start_time = current_tick; | 609 | record_start_time = current_tick; |
509 | 610 | pause_start_time = 0; | |
611 | |||
510 | write_index = 0; | 612 | write_index = 0; |
511 | read_index = 0; | 613 | read_index = 0; |
614 | read2_index = 0; | ||
512 | 615 | ||
513 | is_stopping = 0; | 616 | is_stopping = false; |
514 | is_paused = 0; | 617 | is_paused = false; |
515 | is_recording = 1; | 618 | is_recording = true; |
516 | 619 | ||
517 | pcmrec_dma_start(); | 620 | pcmrec_dma_start(); |
518 | 621 | ||
622 | record_done = true; | ||
519 | } | 623 | } |
520 | 624 | ||
521 | static void pcmrec_stop(void) | 625 | static void pcmrec_stop(void) |
522 | { | 626 | { |
523 | /* wait for recording to finish */ | 627 | logf("pcmrec_stop"); |
628 | |||
629 | if (!is_recording) | ||
630 | { | ||
631 | stop_done = true; | ||
632 | return; | ||
633 | } | ||
634 | |||
635 | if (!is_paused) | ||
636 | { | ||
637 | /* wait for recording to finish */ | ||
638 | is_stopping = true; | ||
639 | |||
640 | while (is_stopping && is_recording) | ||
641 | sleep_thread(); | ||
642 | wake_up_thread(); | ||
643 | |||
644 | is_stopping = false; | ||
645 | } | ||
646 | |||
647 | is_recording = false; | ||
648 | |||
649 | /* Flush buffers to file */ | ||
650 | pcmrec_callback(true); | ||
524 | 651 | ||
525 | /* todo: Abort current DMA transfer using DCR1.. */ | 652 | close_wave(); |
526 | 653 | ||
527 | logf("pcmrec_stop"); | 654 | stop_done = true; |
655 | |||
656 | logf("pcmrec_stop done"); | ||
657 | } | ||
528 | 658 | ||
529 | while (is_recording) | 659 | static void pcmrec_new_file(void) |
660 | { | ||
661 | logf("pcmrec_new_file"); | ||
662 | |||
663 | if (!is_recording) | ||
530 | { | 664 | { |
531 | sleep(HZ >> 4); | 665 | logf("not recording"); |
666 | new_file_done = true; | ||
667 | return; | ||
532 | } | 668 | } |
669 | |||
670 | /* Since pcmrec_callback() blocks until the data has been written, | ||
671 | here is a good approximation when recording to the new file starts | ||
672 | */ | ||
673 | record_start_time = current_tick; | ||
674 | num_rec_bytes = 0; | ||
675 | |||
676 | if (is_paused) | ||
677 | pause_start_time = record_start_time; | ||
678 | |||
679 | /* Flush what we got in buffers to file */ | ||
680 | pcmrec_callback(true); | ||
681 | |||
682 | close_wave(); | ||
533 | 683 | ||
534 | logf("pcmrec_stop done"); | 684 | num_file_bytes = 0; |
685 | |||
686 | /* start the new file */ | ||
687 | if (start_wave() != 0) | ||
688 | { | ||
689 | logf("new_file failed"); | ||
690 | pcmrec_stop(); | ||
691 | } | ||
535 | 692 | ||
536 | /* Write unfinished buffers to file */ | 693 | new_file_done = true; |
694 | logf("pcmrec_new_file done"); | ||
695 | } | ||
696 | |||
697 | static void pcmrec_pause(void) | ||
698 | { | ||
699 | logf("pcmrec_pause"); | ||
700 | |||
701 | if (!is_recording) | ||
702 | { | ||
703 | logf("pause: not recording"); | ||
704 | pause_done = true; | ||
705 | return; | ||
706 | } | ||
707 | |||
708 | /* Abort DMA transfer and flush to file? */ | ||
709 | |||
710 | is_stopping = true; | ||
711 | |||
712 | while (is_stopping && is_recording) | ||
713 | sleep_thread(); | ||
714 | wake_up_thread(); | ||
715 | |||
716 | pause_start_time = current_tick; | ||
717 | is_paused = true; | ||
718 | |||
719 | /* Flush what we got in buffers to file */ | ||
537 | pcmrec_callback(true); | 720 | pcmrec_callback(true); |
721 | |||
722 | pause_done = true; | ||
723 | |||
724 | logf("pcmrec_pause done"); | ||
725 | } | ||
538 | 726 | ||
539 | close_wave(); | ||
540 | 727 | ||
541 | is_stopping = 0; | 728 | static void pcmrec_resume(void) |
729 | { | ||
730 | logf("pcmrec_resume"); | ||
731 | |||
732 | if (!is_paused) | ||
733 | { | ||
734 | logf("resume: not paused"); | ||
735 | resume_done = true; | ||
736 | return; | ||
737 | } | ||
738 | |||
739 | is_paused = false; | ||
740 | is_recording = true; | ||
741 | |||
742 | /* Compensate for the time we have been paused */ | ||
743 | if (pause_start_time) | ||
744 | { | ||
745 | record_start_time += current_tick - pause_start_time; | ||
746 | pause_start_time = 0; | ||
747 | } | ||
748 | |||
749 | pcmrec_dma_start(); | ||
750 | |||
751 | resume_done = true; | ||
752 | |||
753 | logf("pcmrec_resume done"); | ||
542 | } | 754 | } |
543 | 755 | ||
544 | static void pcmrec_open(void) | 756 | |
757 | /** | ||
758 | * audio_init_recording calls this function using PCMREC_INIT | ||
759 | * | ||
760 | */ | ||
761 | static void pcmrec_init(void) | ||
545 | { | 762 | { |
546 | unsigned long buffer_start; | 763 | unsigned long buffer_size; |
547 | int i; | ||
548 | 764 | ||
549 | show_waveform = 0; | 765 | show_waveform = 0; |
550 | is_recording = 0; | ||
551 | is_stopping = 0; | ||
552 | num_rec_bytes = 0; | ||
553 | wav_file = -1; | 766 | wav_file = -1; |
554 | read_index = 0; | 767 | read_index = 0; |
768 | read2_index = 0; | ||
555 | write_index = 0; | 769 | write_index = 0; |
556 | 770 | ||
557 | buffer_start = (unsigned long)(&audiobuf[(audiobufend - audiobuf) - (ALL_BUFFERS_SIZE + 16)]); | 771 | num_rec_bytes = 0; |
558 | buffer_start &= ~3; | 772 | num_file_bytes = 0; |
559 | 773 | record_start_time = 0; | |
560 | for (i=0; i<NUM_BUFFERS; i++) | 774 | pause_start_time = 0; |
561 | { | 775 | |
562 | rec_buffers[i] = (unsigned char*)(buffer_start + EACH_BUFFER_SIZE * i); | 776 | is_recording = false; |
563 | } | 777 | is_stopping = false; |
778 | is_paused = false; | ||
779 | is_error = false; | ||
780 | |||
781 | rec_buffer = (unsigned char*)(((unsigned long)audiobuf) & ~3); | ||
782 | buffer_size = (long)audiobufend - (long)audiobuf - 16; | ||
783 | |||
784 | //buffer_size = 1024*1024*5; | ||
785 | |||
786 | logf("buf size: %d kb", buffer_size/1024); | ||
787 | |||
788 | num_chunks = buffer_size / CHUNK_SIZE; | ||
789 | |||
790 | logf("num_chunks: %d", num_chunks); | ||
564 | 791 | ||
565 | IIS1CONFIG = 0x800; /* Stop any playback */ | 792 | IIS1CONFIG = 0x800; /* Stop any playback */ |
566 | AUDIOGLOB |= 0x180; /* IIS1 fifo auto sync = on, PDIR2 auto sync = on */ | 793 | AUDIOGLOB |= 0x180; /* IIS1 fifo auto sync = on, PDIR2 auto sync = on */ |
@@ -578,16 +805,13 @@ static void pcmrec_open(void) | |||
578 | 805 | ||
579 | static void pcmrec_close(void) | 806 | static void pcmrec_close(void) |
580 | { | 807 | { |
581 | #if defined(HAVE_UDA1380) | ||
582 | uda1380_disable_recording(); | 808 | uda1380_disable_recording(); |
583 | #elif defined(HAVE_TLV320) | ||
584 | tlv320_disable_recording(); | ||
585 | #endif | ||
586 | 809 | ||
587 | DMAROUTE = (DMAROUTE & 0xffff00ff); | 810 | DMAROUTE = (DMAROUTE & 0xffff00ff); |
588 | ICR7 = 0x00; /* Disable interrupt */ | 811 | ICR7 = 0x00; /* Disable interrupt */ |
589 | IMR |= (1<<15); /* bit 15 is DMA1 */ | 812 | IMR |= (1<<15); /* bit 15 is DMA1 */ |
590 | 813 | ||
814 | close_done = true; | ||
591 | } | 815 | } |
592 | 816 | ||
593 | static void pcmrec_thread(void) | 817 | static void pcmrec_thread(void) |
@@ -596,14 +820,17 @@ static void pcmrec_thread(void) | |||
596 | 820 | ||
597 | logf("thread pcmrec start"); | 821 | logf("thread pcmrec start"); |
598 | 822 | ||
823 | int_count = 0; | ||
824 | error_count = 0; | ||
825 | |||
599 | while (1) | 826 | while (1) |
600 | { | 827 | { |
601 | queue_wait(&pcmrec_queue, &ev); | 828 | queue_wait(&pcmrec_queue, &ev); |
602 | 829 | ||
603 | switch (ev.id) | 830 | switch (ev.id) |
604 | { | 831 | { |
605 | case PCMREC_OPEN: | 832 | case PCMREC_INIT: |
606 | pcmrec_open(); | 833 | pcmrec_init(); |
607 | break; | 834 | break; |
608 | 835 | ||
609 | case PCMREC_CLOSE: | 836 | case PCMREC_CLOSE: |
@@ -619,25 +846,18 @@ static void pcmrec_thread(void) | |||
619 | break; | 846 | break; |
620 | 847 | ||
621 | case PCMREC_PAUSE: | 848 | case PCMREC_PAUSE: |
622 | /* todo */ | 849 | pcmrec_pause(); |
623 | break; | 850 | break; |
624 | 851 | ||
625 | case PCMREC_RESUME: | 852 | case PCMREC_RESUME: |
626 | /* todo */ | 853 | pcmrec_resume(); |
627 | break; | 854 | break; |
628 | 855 | ||
629 | case PCMREC_NEW_FILE: | 856 | case PCMREC_NEW_FILE: |
630 | /* todo */ | 857 | pcmrec_new_file(); |
631 | break; | ||
632 | |||
633 | case PCMREC_SET_GAIN: | ||
634 | #if defined(HAVE_UDA1380) | ||
635 | uda1380_set_recvol(rec_gain, rec_gain, rec_volume); | ||
636 | #elif defined(HAVE_TLV320) | ||
637 | /* ToDo */ | ||
638 | #endif | ||
639 | break; | 858 | break; |
640 | 859 | ||
860 | /* Notification by DMA interrupt */ | ||
641 | case PCMREC_GOT_DATA: | 861 | case PCMREC_GOT_DATA: |
642 | pcmrec_callback(false); | 862 | pcmrec_callback(false); |
643 | break; | 863 | break; |
@@ -655,7 +875,8 @@ static void pcmrec_thread(void) | |||
655 | logf("thread pcmrec done"); | 875 | logf("thread pcmrec done"); |
656 | } | 876 | } |
657 | 877 | ||
658 | void pcmrec_set_mux(int source) | 878 | /* Select VINL & VINR source: 0=Line-in, 1=FM Radio */ |
879 | void pcm_rec_mux(int source) | ||
659 | { | 880 | { |
660 | if(source == 0) | 881 | if(source == 0) |
661 | and_l(~0x00800000, &GPIO_OUT); /* Line In */ | 882 | and_l(~0x00800000, &GPIO_OUT); /* Line In */ |