summaryrefslogtreecommitdiff
path: root/apps/recorder
diff options
context:
space:
mode:
authorBjörn Stenberg <bjorn@haxx.se>2008-10-14 11:12:20 +0000
committerBjörn Stenberg <bjorn@haxx.se>2008-10-14 11:12:20 +0000
commit9558c4956d3d603c4d132af88633767810f3ba62 (patch)
treeb0d739f6246a7175de106562a2c755724f977cbe /apps/recorder
parent91b9c6139b3447d7c0f4f2924ef73a9dc323703b (diff)
downloadrockbox-9558c4956d3d603c4d132af88633767810f3ba62.tar.gz
rockbox-9558c4956d3d603c4d132af88633767810f3ba62.zip
Moved pcm_record from firmware to apps. Cleaned up some. Now all code using struct mp3entry is in apps.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18807 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/recorder')
-rw-r--r--apps/recorder/pcm_record.c1788
-rw-r--r--apps/recorder/pcm_record.h66
-rw-r--r--apps/recorder/recording.c1
3 files changed, 1855 insertions, 0 deletions
diff --git a/apps/recorder/pcm_record.c b/apps/recorder/pcm_record.c
new file mode 100644
index 0000000000..b1ea535470
--- /dev/null
+++ b/apps/recorder/pcm_record.c
@@ -0,0 +1,1788 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "pcm_record.h"
22#include "system.h"
23#include "kernel.h"
24#include "logf.h"
25#include "thread.h"
26#include <string.h>
27#include "ata.h"
28#include "usb.h"
29#include "buffer.h"
30#include "general.h"
31#include "audio.h"
32#include "sound.h"
33#include "id3.h"
34#ifdef HAVE_SPDIF_IN
35#include "spdif.h"
36#endif
37
38/***************************************************************************/
39
40extern struct thread_entry *codec_thread_p;
41
42/** General recording state **/
43static bool is_recording; /* We are recording */
44static bool is_paused; /* We have paused */
45static unsigned long errors; /* An error has occured */
46static unsigned long warnings; /* Warning */
47static int flush_interrupts = 0; /* Number of messages queued that
48 should interrupt a flush in
49 progress -
50 for a safety net and a prompt
51 response to stop, split and pause
52 requests -
53 only interrupts a flush initiated
54 by pcmrec_flush(0) */
55
56/* Utility functions for setting/clearing flushing interrupt flag */
57static inline void flush_interrupt(void)
58{
59 flush_interrupts++;
60 logf("flush int: %d", flush_interrupts);
61}
62
63static inline void clear_flush_interrupt(void)
64{
65 if (--flush_interrupts < 0)
66 flush_interrupts = 0;
67}
68
69/** Stats on encoded data for current file **/
70static size_t num_rec_bytes; /* Num bytes recorded */
71static unsigned long num_rec_samples; /* Number of PCM samples recorded */
72
73/** Stats on encoded data for all files from start to stop **/
74#if 0
75static unsigned long long accum_rec_bytes; /* total size written to chunks */
76static unsigned long long accum_pcm_samples; /* total pcm count processed */
77#endif
78
79/* Keeps data about current file and is sent as event data for codec */
80static struct enc_file_event_data rec_fdata IDATA_ATTR =
81{
82 .chunk = NULL,
83 .new_enc_size = 0,
84 .new_num_pcm = 0,
85 .rec_file = -1,
86 .num_pcm_samples = 0
87};
88
89/** These apply to current settings **/
90static int rec_source; /* current rec_source setting */
91static int rec_frequency; /* current frequency setting */
92static unsigned long sample_rate; /* Sample rate in HZ */
93static int num_channels; /* Current number of channels */
94static int rec_mono_mode; /* how mono is created */
95static struct encoder_config enc_config; /* Current encoder configuration */
96static unsigned long pre_record_ticks; /* pre-record time in ticks */
97
98/****************************************************************************
99 use 2 circular buffers:
100 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
101 enc_buffer=encoded audio buffer: storage for encoder output data
102
103 Flow:
104 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer
105 2. if enough pcm data are available the encoder codec does encoding of pcm
106 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
107 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
108
109 Functions calls (basic encoder steps):
110 1.main: audio_load_encoder(); start the encoder
111 2.encoder: enc_get_inputs(); get encoder recording settings
112 3.encoder: enc_set_parameters(); set the encoder parameters
113 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
114 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional)
115 6.encoder: enc_get_chunk(); get a ptr to next enc chunk
116 7.encoder: <process enc chunk> compress and store data to enc chunk
117 8.encoder: enc_finish_chunk(); inform main about chunk processed and
118 is available to be written to a file.
119 Encoder can place any number of chunks
120 of PCM data in a single output chunk
121 but must stay within its output chunk
122 size
123 9.encoder: repeat 4. to 8.
124 A.pcmrec: enc_events_callback(); called for certain events
125
126 (*) Optional step
127****************************************************************************/
128
129/** buffer parameters where incoming PCM data is placed **/
130#define PCM_NUM_CHUNKS 256 /* Power of 2 */
131#define PCM_CHUNK_SIZE 8192 /* Power of 2 */
132#define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
133
134#define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
135#define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
136#define INC_ENC_INDEX(index) \
137 { if (++index >= enc_num_chunks) index = 0; }
138#define DEC_ENC_INDEX(index) \
139 { if (--index < 0) index = enc_num_chunks - 1; }
140
141static size_t rec_buffer_size; /* size of available buffer */
142static unsigned char *pcm_buffer; /* circular recording buffer */
143static unsigned char *enc_buffer; /* circular encoding buffer */
144#ifdef DEBUG
145static unsigned long *wrap_id_p; /* magic at wrap position - a debugging
146 aid to check if the encoder data
147 spilled out of its chunk */
148#endif /* DEBUG */
149static volatile int dma_wr_pos; /* current DMA write pos */
150static int pcm_rd_pos; /* current PCM read pos */
151static int pcm_enc_pos; /* position encoder is processing */
152static volatile bool dma_lock; /* lock DMA write position */
153static int enc_wr_index; /* encoder chunk write index */
154static int enc_rd_index; /* encoder chunk read index */
155static int enc_num_chunks; /* number of chunks in ringbuffer */
156static size_t enc_chunk_size; /* maximum encoder chunk size */
157static unsigned long enc_sample_rate; /* sample rate used by encoder */
158static bool pcmrec_context = false; /* called by pcmrec thread? */
159static bool pcm_buffer_empty; /* all pcm chunks processed? */
160
161/** file flushing **/
162static int low_watermark; /* Low watermark to stop flush */
163static int high_watermark; /* max chunk limit for data flush */
164static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */
165static int last_ata_spinup_time = -1;/* previous spin time used */
166#ifdef HAVE_PRIORITY_SCHEDULING
167static int flood_watermark; /* boost thread priority when here */
168#endif
169
170/* Constants that control watermarks */
171#define LOW_SECONDS 1 /* low watermark time till empty */
172#define MINI_CHUNKS 10 /* chunk count for mini flush */
173#ifdef HAVE_PRIORITY_SCHEDULING
174#define PRIO_SECONDS 10 /* max flush time before priority boost */
175#endif
176#if MEM <= 16
177#define PANIC_SECONDS 5 /* flood watermark time until full */
178#define FLUSH_SECONDS 7 /* flush watermark time until full */
179#else
180#define PANIC_SECONDS 8
181#define FLUSH_SECONDS 10
182#endif /* MEM */
183
184/** encoder events **/
185static void (*enc_events_callback)(enum enc_events event, void *data);
186
187/** Path queue for files to write **/
188#define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */
189#define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */
190static unsigned char *fn_queue; /* pointer to first filename */
191static ssize_t fnq_size; /* capacity of queue in bytes */
192static int fnq_rd_pos; /* current read position */
193static int fnq_wr_pos; /* current write position */
194#define FNQ_NEXT(pos) \
195 ({ int p = (pos) + MAX_PATH; \
196 if (p >= fnq_size) \
197 p = 0; \
198 p; })
199#define FNQ_PREV(pos) \
200 ({ int p = (pos) - MAX_PATH; \
201 if (p < 0) \
202 p = fnq_size - MAX_PATH; \
203 p; })
204
205enum
206{
207 PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by
208 incoming messages - combine
209 with other constants */
210 PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */
211 PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of
212 chunks */
213 PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark
214 reached */
215};
216
217/***************************************************************************/
218
219static struct event_queue pcmrec_queue SHAREDBSS_ATTR;
220static struct queue_sender_list pcmrec_queue_send SHAREDBSS_ATTR;
221static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
222static const char pcmrec_thread_name[] = "pcmrec";
223static struct thread_entry *pcmrec_thread_p;
224
225static void pcmrec_thread(void);
226
227enum
228{
229 PCMREC_NULL = 0,
230 PCMREC_INIT, /* enable recording */
231 PCMREC_CLOSE, /* close recording */
232 PCMREC_OPTIONS, /* set recording options */
233 PCMREC_RECORD, /* record a new file */
234 PCMREC_STOP, /* stop the current recording */
235 PCMREC_PAUSE, /* pause the current recording */
236 PCMREC_RESUME, /* resume the current recording */
237#if 0
238 PCMREC_FLUSH_NUM, /* flush a number of files out */
239#endif
240};
241
242/*******************************************************************/
243/* Functions that are not executing in the pcmrec_thread first */
244/*******************************************************************/
245
246/* Callback for when more data is ready - called in interrupt context */
247static int pcm_rec_have_more(int status)
248{
249 if (status < 0)
250 {
251 /* some error condition */
252 if (status == DMA_REC_ERROR_DMA)
253 {
254 /* Flush recorded data to disk and stop recording */
255 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
256 return -1;
257 }
258 /* else try again next transmission */
259 }
260 else if (!dma_lock)
261 {
262 /* advance write position */
263 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
264
265 /* set pcm ovf if processing start position is inside current
266 write chunk */
267 if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE)
268 warnings |= PCMREC_W_PCM_BUFFER_OVF;
269
270 dma_wr_pos = next_pos;
271 }
272
273 pcm_record_more(GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE);
274 return 0;
275} /* pcm_rec_have_more */
276
277static void reset_hardware(void)
278{
279 /* reset pcm to defaults (playback only) */
280 pcm_set_frequency(HW_SAMPR_DEFAULT);
281 audio_set_output_source(AUDIO_SRC_PLAYBACK);
282 pcm_apply_settings();
283}
284
285/** pcm_rec_* group **/
286
287/**
288 * Clear all errors and warnings
289 */
290void pcm_rec_error_clear(void)
291{
292 errors = warnings = 0;
293} /* pcm_rec_error_clear */
294
295/**
296 * Check mode, errors and warnings
297 */
298unsigned long pcm_rec_status(void)
299{
300 unsigned long ret = 0;
301
302 if (is_recording)
303 ret |= AUDIO_STATUS_RECORD;
304 else if (pre_record_ticks)
305 ret |= AUDIO_STATUS_PRERECORD;
306
307 if (is_paused)
308 ret |= AUDIO_STATUS_PAUSE;
309
310 if (errors)
311 ret |= AUDIO_STATUS_ERROR;
312
313 if (warnings)
314 ret |= AUDIO_STATUS_WARNING;
315
316 return ret;
317} /* pcm_rec_status */
318
319/**
320 * Return warnings that have occured since recording started
321 */
322unsigned long pcm_rec_get_warnings(void)
323{
324 return warnings;
325}
326
327#if 0
328int pcm_rec_current_bitrate(void)
329{
330 if (accum_pcm_samples == 0)
331 return 0;
332
333 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
334} /* pcm_rec_current_bitrate */
335#endif
336
337#if 0
338int pcm_rec_encoder_afmt(void)
339{
340 return enc_config.afmt;
341} /* pcm_rec_encoder_afmt */
342#endif
343
344#if 0
345int pcm_rec_rec_format(void)
346{
347 return afmt_rec_format[enc_config.afmt];
348} /* pcm_rec_rec_format */
349#endif
350
351#ifdef HAVE_SPDIF_IN
352unsigned long pcm_rec_sample_rate(void)
353{
354 /* Which is better ?? */
355#if 0
356 return enc_sample_rate;
357#endif
358 return sample_rate;
359} /* audio_get_sample_rate */
360#endif
361
362/**
363 * Creates pcmrec_thread
364 */
365void pcm_rec_init(void)
366{
367 queue_init(&pcmrec_queue, true);
368 pcmrec_thread_p =
369 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
370 0, pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)
371 IF_COP(, CPU));
372 queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send,
373 pcmrec_thread_p);
374} /* pcm_rec_init */
375
376/** audio_* group **/
377
378/**
379 * Initializes recording - call before calling any other recording function
380 */
381void audio_init_recording(unsigned int buffer_offset)
382{
383 logf("audio_init_recording");
384 queue_send(&pcmrec_queue, PCMREC_INIT, 0);
385 logf("audio_init_recording done");
386 (void)buffer_offset;
387} /* audio_init_recording */
388
389/**
390 * Closes recording - call audio_stop_recording first
391 */
392void audio_close_recording(void)
393{
394 logf("audio_close_recording");
395 queue_send(&pcmrec_queue, PCMREC_CLOSE, 0);
396 logf("audio_close_recording done");
397} /* audio_close_recording */
398
399/**
400 * Sets recording parameters
401 */
402void audio_set_recording_options(struct audio_recording_options *options)
403{
404 logf("audio_set_recording_options");
405 queue_send(&pcmrec_queue, PCMREC_OPTIONS, (intptr_t)options);
406 logf("audio_set_recording_options done");
407} /* audio_set_recording_options */
408
409/**
410 * Start recording if not recording or else split
411 */
412void audio_record(const char *filename)
413{
414 logf("audio_record: %s", filename);
415 flush_interrupt();
416 queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename);
417 logf("audio_record_done");
418} /* audio_record */
419
420/**
421 * audio_record wrapper for API compatibility with HW codec
422 */
423void audio_new_file(const char *filename)
424{
425 audio_record(filename);
426} /* audio_new_file */
427
428/**
429 * Stop current recording if recording
430 */
431void audio_stop_recording(void)
432{
433 logf("audio_stop_recording");
434 flush_interrupt();
435 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
436 logf("audio_stop_recording done");
437} /* audio_stop_recording */
438
439/**
440 * Pause current recording
441 */
442void audio_pause_recording(void)
443{
444 logf("audio_pause_recording");
445 flush_interrupt();
446 queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
447 logf("audio_pause_recording done");
448} /* audio_pause_recording */
449
450/**
451 * Resume current recording if paused
452 */
453void audio_resume_recording(void)
454{
455 logf("audio_resume_recording");
456 queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
457 logf("audio_resume_recording done");
458} /* audio_resume_recording */
459
460/**
461 * Note that microphone is mono, only left value is used
462 * See audiohw_set_recvol() for exact ranges.
463 *
464 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
465 *
466 */
467void audio_set_recording_gain(int left, int right, int type)
468{
469 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
470 audiohw_set_recvol(left, right, type);
471} /* audio_set_recording_gain */
472
473/** Information about current state **/
474
475/**
476 * Return current recorded time in ticks (playback eqivalent time)
477 */
478unsigned long audio_recorded_time(void)
479{
480 if (!is_recording || enc_sample_rate == 0)
481 return 0;
482
483 /* return actual recorded time a la encoded data even if encoder rate
484 doesn't match the pcm rate */
485 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
486} /* audio_recorded_time */
487
488/**
489 * Return number of bytes encoded to output
490 */
491unsigned long audio_num_recorded_bytes(void)
492{
493 if (!is_recording)
494 return 0;
495
496 return num_rec_bytes;
497} /* audio_num_recorded_bytes */
498
499/***************************************************************************/
500/* */
501/* Functions that execute in the context of pcmrec_thread */
502/* */
503/***************************************************************************/
504
505/** Filename Queue **/
506
507/* returns true if the queue is empty */
508static inline bool pcmrec_fnq_is_empty(void)
509{
510 return fnq_rd_pos == fnq_wr_pos;
511} /* pcmrec_fnq_is_empty */
512
513/* empties the filename queue */
514static inline void pcmrec_fnq_set_empty(void)
515{
516 fnq_rd_pos = fnq_wr_pos;
517} /* pcmrec_fnq_set_empty */
518
519/* returns true if the queue is full */
520static bool pcmrec_fnq_is_full(void)
521{
522 ssize_t size = fnq_wr_pos - fnq_rd_pos;
523 if (size < 0)
524 size += fnq_size;
525
526 return size >= fnq_size - MAX_PATH;
527} /* pcmrec_fnq_is_full */
528
529/* queue another filename - will overwrite oldest one if full */
530static bool pcmrec_fnq_add_filename(const char *filename)
531{
532 strncpy(fn_queue + fnq_wr_pos, filename, MAX_PATH);
533 fnq_wr_pos = FNQ_NEXT(fnq_wr_pos);
534
535 if (fnq_rd_pos != fnq_wr_pos)
536 return true;
537
538 /* queue full */
539 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
540 return true;
541} /* pcmrec_fnq_add_filename */
542
543/* replace the last filename added */
544static bool pcmrec_fnq_replace_tail(const char *filename)
545{
546 int pos;
547
548 if (pcmrec_fnq_is_empty())
549 return false;
550
551 pos = FNQ_PREV(fnq_wr_pos);
552
553 strncpy(fn_queue + pos, filename, MAX_PATH);
554
555 return true;
556} /* pcmrec_fnq_replace_tail */
557
558/* pulls the next filename from the queue */
559static bool pcmrec_fnq_get_filename(char *filename)
560{
561 if (pcmrec_fnq_is_empty())
562 return false;
563
564 if (filename)
565 strncpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
566
567 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
568 return true;
569} /* pcmrec_fnq_get_filename */
570
571/* close the file number pointed to by fd_p */
572static void pcmrec_close_file(int *fd_p)
573{
574 if (*fd_p < 0)
575 return; /* preserve error */
576
577 if (close(*fd_p) != 0)
578 errors |= PCMREC_E_IO;
579
580 *fd_p = -1;
581} /* pcmrec_close_file */
582
583/** Data Flushing **/
584
585/**
586 * called after callback to update sizes if codec changed the amount of data
587 * a chunk represents
588 */
589static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
590 unsigned long prev_num_pcm)
591{
592 if (rec_fdata.new_enc_size != prev_enc_size)
593 {
594 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
595 num_rec_bytes += size_diff;
596#if 0
597 accum_rec_bytes += size_diff;
598#endif
599 }
600
601 if (rec_fdata.new_num_pcm != prev_num_pcm)
602 {
603 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
604 num_rec_samples += pcm_diff;
605#if 0
606 accum_pcm_samples += pcm_diff;
607#endif
608 }
609} /* pcmrec_update_sizes_inl */
610
611/* don't need to inline every instance */
612static void pcmrec_update_sizes(size_t prev_enc_size,
613 unsigned long prev_num_pcm)
614{
615 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm);
616} /* pcmrec_update_sizes */
617
618static void pcmrec_start_file(void)
619{
620 size_t enc_size = rec_fdata.new_enc_size;
621 unsigned long num_pcm = rec_fdata.new_num_pcm;
622 int curr_rec_file = rec_fdata.rec_file;
623 char filename[MAX_PATH];
624
625 /* must always pull the filename that matches with this queue */
626 if (!pcmrec_fnq_get_filename(filename))
627 {
628 logf("start file: fnq empty");
629 *filename = '\0';
630 errors |= PCMREC_E_FNQ_DESYNC;
631 }
632 else if (errors != 0)
633 {
634 logf("start file: error already");
635 }
636 else if (curr_rec_file >= 0)
637 {
638 /* Any previous file should have been closed */
639 logf("start file: file already open");
640 errors |= PCMREC_E_FNQ_DESYNC;
641 }
642
643 if (errors != 0)
644 rec_fdata.chunk->flags |= CHUNKF_ERROR;
645
646 /* encoder can set error flag here and should increase
647 enc_new_size and pcm_new_size to reflect additional
648 data written if any */
649 rec_fdata.filename = filename;
650 enc_events_callback(ENC_START_FILE, &rec_fdata);
651
652 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
653 {
654 logf("start file: enc error");
655 errors |= PCMREC_E_ENCODER;
656 }
657
658 if (errors != 0)
659 {
660 pcmrec_close_file(&curr_rec_file);
661 /* Write no more to this file */
662 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
663 }
664 else
665 {
666 pcmrec_update_sizes(enc_size, num_pcm);
667 }
668
669 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE;
670} /* pcmrec_start_file */
671
672static inline void pcmrec_write_chunk(void)
673{
674 size_t enc_size = rec_fdata.new_enc_size;
675 unsigned long num_pcm = rec_fdata.new_num_pcm;
676
677 if (errors != 0)
678 rec_fdata.chunk->flags |= CHUNKF_ERROR;
679
680 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
681
682 if ((long)rec_fdata.chunk->flags >= 0)
683 {
684 pcmrec_update_sizes_inl(enc_size, num_pcm);
685 }
686 else if (errors == 0)
687 {
688 logf("wr chk enc error %lu %lu",
689 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
690 errors |= PCMREC_E_ENCODER;
691 }
692} /* pcmrec_write_chunk */
693
694static void pcmrec_end_file(void)
695{
696 /* all data in output buffer for current file will have been
697 written and encoder can now do any nescessary steps to
698 finalize the written file */
699 size_t enc_size = rec_fdata.new_enc_size;
700 unsigned long num_pcm = rec_fdata.new_num_pcm;
701
702 enc_events_callback(ENC_END_FILE, &rec_fdata);
703
704 if (errors == 0)
705 {
706 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
707 {
708 logf("end file: enc error");
709 errors |= PCMREC_E_ENCODER;
710 }
711 else
712 {
713 pcmrec_update_sizes(enc_size, num_pcm);
714 }
715 }
716
717 /* Force file close if error */
718 if (errors != 0)
719 pcmrec_close_file(&rec_fdata.rec_file);
720
721 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
722} /* pcmrec_end_file */
723
724/**
725 * Update buffer watermarks with spinup time compensation
726 *
727 * All this assumes reasonable data rates, chunk sizes and sufficient
728 * memory for the most part. Some dumb checks are included but perhaps
729 * are pointless since this all will break down at extreme limits that
730 * are currently not applicable to any supported device.
731 */
732static void pcmrec_refresh_watermarks(void)
733{
734 logf("ata spinup: %d", ata_spinup_time);
735
736 /* set the low mark for when flushing stops if automatic */
737 low_watermark = (LOW_SECONDS*4*sample_rate + (enc_chunk_size-1))
738 / enc_chunk_size;
739 logf("low wmk: %d", low_watermark);
740
741#ifdef HAVE_PRIORITY_SCHEDULING
742 /* panic boost thread priority if 2 seconds of ground is lost -
743 this allows encoder to boost with just under a second of
744 pcm data (if not yet full enough to boost itself)
745 and not falsely trip the alarm. */
746 flood_watermark = enc_num_chunks -
747 (PANIC_SECONDS*4*sample_rate + (enc_chunk_size-1))
748 / enc_chunk_size;
749
750 if (flood_watermark < low_watermark)
751 {
752 logf("warning: panic < low");
753 flood_watermark = low_watermark;
754 }
755
756 logf("flood at: %d", flood_watermark);
757#endif
758 spinup_time = last_ata_spinup_time = ata_spinup_time;
759
760 /* write at 8s + st remaining in enc_buffer - range 12s to
761 20s total - default to 3.5s spinup. */
762 if (spinup_time == 0)
763 spinup_time = 35*HZ/10; /* default - cozy */
764 else if (spinup_time < 2*HZ)
765 spinup_time = 2*HZ; /* ludicrous - ramdisk? */
766 else if (spinup_time > 10*HZ)
767 spinup_time = 10*HZ; /* do you have a functioning HD? */
768
769 /* try to start writing with 10s remaining after disk spinup */
770 high_watermark = enc_num_chunks -
771 ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate +
772 (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ);
773
774 if (high_watermark < low_watermark)
775 {
776 high_watermark = low_watermark;
777 low_watermark /= 2;
778 logf("warning: low 'write at'");
779 }
780
781 logf("write at: %d", high_watermark);
782} /* pcmrec_refresh_watermarks */
783
784/**
785 * Process the chunks
786 *
787 * This function is called when queue_get_w_tmo times out.
788 *
789 * Set flush_num to the number of files to flush to disk or to
790 * a PCMREC_FLUSH_* constant.
791 */
792static void pcmrec_flush(unsigned flush_num)
793{
794#ifdef HAVE_PRIORITY_SCHEDULING
795 static unsigned long last_flush_tick; /* tick when function returned */
796 unsigned long start_tick; /* When flush started */
797 unsigned long prio_tick; /* Timeout for auto boost */
798 int prio_pcmrec; /* Current thread priority for pcmrec */
799 int prio_codec; /* Current thread priority for codec */
800#endif
801 int num_ready; /* Number of chunks ready at start */
802 unsigned remaining; /* Number of file starts remaining */
803 unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */
804 bool interruptable; /* Flush can be interupted */
805
806 num_ready = enc_wr_index - enc_rd_index;
807 if (num_ready < 0)
808 num_ready += enc_num_chunks;
809
810 /* save interruptable flag and remove it to get the actual count */
811 interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0;
812 flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE;
813
814 if (flush_num == 0)
815 {
816 if (!is_recording)
817 return;
818
819 if (ata_spinup_time != last_ata_spinup_time)
820 pcmrec_refresh_watermarks();
821
822 /* enough available? no? then leave */
823 if (num_ready < high_watermark)
824 return;
825 } /* endif (flush_num == 0) */
826
827#ifdef HAVE_PRIORITY_SCHEDULING
828 start_tick = current_tick;
829 prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time;
830
831 if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2))
832 {
833 /* if we're getting called too much and this isn't forced,
834 boost stat by expiring timeout in advance */
835 logf("too frequent flush");
836 prio_tick = current_tick - 1;
837 }
838
839 prio_pcmrec = -1;
840 prio_codec = -1; /* GCC is too stoopid to figure out it doesn't
841 need init */
842#endif
843
844 logf("writing:%d(%d):%s%s", num_ready, flush_num,
845 interruptable ? "i" : "",
846 flush_num == PCMREC_FLUSH_MINI ? "m" : "");
847
848 cpu_boost(true);
849
850 remaining = flush_num;
851 chunks_flushed = 0;
852
853 while (num_ready > 0)
854 {
855 /* check current number of encoder chunks */
856 int num = enc_wr_index - enc_rd_index;
857 if (num < 0)
858 num += enc_num_chunks;
859
860 if (num <= low_watermark &&
861 (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0))
862 {
863 logf("low data: %d", num);
864 break; /* data remaining is below threshold */
865 }
866
867 if (interruptable && flush_interrupts > 0)
868 {
869 logf("int at: %d", num);
870 break; /* interrupted */
871 }
872
873#ifdef HAVE_PRIORITY_SCHEDULING
874 if (prio_pcmrec == -1 && (num >= flood_watermark ||
875 TIME_AFTER(current_tick, prio_tick)))
876 {
877 /* losing ground or holding without progress - boost
878 priority until finished */
879 logf("pcmrec: boost (%s)",
880 num >= flood_watermark ? "num" : "time");
881 prio_pcmrec = thread_set_priority(NULL,
882 thread_get_priority(NULL) - 4);
883 prio_codec = thread_set_priority(codec_thread_p,
884 thread_get_priority(codec_thread_p) - 4);
885 }
886#endif
887
888 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index);
889 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
890 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
891
892 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
893 {
894 pcmrec_start_file();
895 if (--remaining == 0)
896 num_ready = 0; /* stop on next loop - must write this
897 chunk if it has data */
898 }
899
900 pcmrec_write_chunk();
901
902 if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
903 pcmrec_end_file();
904
905 INC_ENC_INDEX(enc_rd_index);
906
907 if (errors != 0)
908 {
909 pcmrec_end_file();
910 break;
911 }
912
913 if (flush_num == PCMREC_FLUSH_MINI &&
914 ++chunks_flushed >= MINI_CHUNKS)
915 {
916 logf("mini flush break");
917 break;
918 }
919 /* no yielding; the file apis called in the codecs do that
920 sufficiently */
921 } /* end while */
922
923 /* sync file */
924 if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0)
925 errors |= PCMREC_E_IO;
926
927 cpu_boost(false);
928
929#ifdef HAVE_PRIORITY_SCHEDULING
930 if (prio_pcmrec != -1)
931 {
932 /* return to original priorities */
933 logf("pcmrec: unboost priority");
934 thread_set_priority(NULL, prio_pcmrec);
935 thread_set_priority(codec_thread_p, prio_codec);
936 }
937
938 last_flush_tick = current_tick; /* save tick when we left */
939#endif
940
941 logf("done");
942} /* pcmrec_flush */
943
944/**
945 * Marks a new stream in the buffer and gives the encoder a chance for special
946 * handling of transition from one to the next. The encoder may change the
947 * chunk that ends the old stream by requesting more chunks and similiarly for
948 * the new but must always advance the position though the interface. It can
949 * later reject any data it cares to when writing the file but should mark the
950 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
951 * a NULL data pointer without error as well.
952 */
953static int pcmrec_get_chunk_index(struct enc_chunk_hdr *chunk)
954{
955 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
956} /* pcmrec_get_chunk_index */
957
958static struct enc_chunk_hdr * pcmrec_get_prev_chunk(int index)
959{
960 DEC_ENC_INDEX(index);
961 return GET_ENC_CHUNK(index);
962} /* pcmrec_get_prev_chunk */
963
964static void pcmrec_new_stream(const char *filename, /* next file name */
965 unsigned long flags, /* CHUNKF_* flags */
966 int pre_index) /* index for prerecorded data */
967{
968 logf("pcmrec_new_stream");
969 char path[MAX_PATH]; /* place to copy filename so sender can be released */
970
971 struct enc_buffer_event_data data;
972 bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add
973 new filename */
974 struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of
975 stream */
976 bool did_flush = false; /* did a flush occurr? */
977
978 if (filename)
979 strncpy(path, filename, MAX_PATH);
980 queue_reply(&pcmrec_queue, 0); /* We have all we need */
981
982 data.pre_chunk = NULL;
983 data.chunk = GET_ENC_CHUNK(enc_wr_index);
984
985 /* end chunk */
986 if (flags & CHUNKF_END_FILE)
987 {
988 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
989
990 if (data.chunk->flags & CHUNKF_START_FILE)
991 {
992 /* cannot start and end on same unprocessed chunk */
993 logf("file end on start");
994 flags &= ~CHUNKF_END_FILE;
995 }
996 else if (enc_rd_index == enc_wr_index)
997 {
998 /* all data flushed but file not ended - chunk will be left
999 empty */
1000 logf("end on dead end");
1001 data.chunk->flags = 0;
1002 data.chunk->enc_size = 0;
1003 data.chunk->num_pcm = 0;
1004 data.chunk->enc_data = NULL;
1005 INC_ENC_INDEX(enc_wr_index);
1006 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1007 }
1008 else
1009 {
1010 struct enc_chunk_hdr *last = pcmrec_get_prev_chunk(enc_wr_index);
1011
1012 if (last->flags & CHUNKF_END_FILE)
1013 {
1014 /* end already processed and marked - can't end twice */
1015 logf("file end again");
1016 flags &= ~CHUNKF_END_FILE;
1017 }
1018 }
1019 }
1020
1021 /* start chunk */
1022 if (flags & CHUNKF_START_FILE)
1023 {
1024 bool pre = flags & CHUNKF_PRERECORD;
1025
1026 if (pre)
1027 {
1028 logf("stream prerecord start");
1029 start = data.pre_chunk = GET_ENC_CHUNK(pre_index);
1030 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD;
1031 }
1032 else
1033 {
1034 logf("stream normal start");
1035 start = data.chunk;
1036 start->flags &= CHUNKF_START_FILE;
1037 }
1038
1039 /* if encoder hasn't yet processed the last start - abort the start
1040 of the previous file queued or else it will be empty and invalid */
1041 if (start->flags & CHUNKF_START_FILE)
1042 {
1043 logf("replacing fnq tail: %s", filename);
1044 fnq_add_fn = pcmrec_fnq_replace_tail;
1045 }
1046 else
1047 {
1048 logf("adding filename: %s", filename);
1049 fnq_add_fn = pcmrec_fnq_add_filename;
1050 }
1051 }
1052
1053 data.flags = flags;
1054 pcmrec_context = true; /* switch encoder context */
1055 enc_events_callback(ENC_REC_NEW_STREAM, &data);
1056 pcmrec_context = false; /* switch back */
1057
1058 if (flags & CHUNKF_END_FILE)
1059 {
1060 int i = pcmrec_get_chunk_index(data.chunk);
1061 pcmrec_get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1062 }
1063
1064 if (start)
1065 {
1066 if (!(flags & CHUNKF_PRERECORD))
1067 {
1068 /* get stats on data added to start - sort of a prerecord
1069 operation */
1070 int i = pcmrec_get_chunk_index(data.chunk);
1071 struct enc_chunk_hdr *chunk = data.chunk;
1072
1073 logf("start data: %d %d", i, enc_wr_index);
1074
1075 num_rec_bytes = 0;
1076 num_rec_samples = 0;
1077
1078 while (i != enc_wr_index)
1079 {
1080 num_rec_bytes += chunk->enc_size;
1081 num_rec_samples += chunk->num_pcm;
1082 INC_ENC_INDEX(i);
1083 chunk = GET_ENC_CHUNK(i);
1084 }
1085
1086 start->flags &= ~CHUNKF_START_FILE;
1087 start = data.chunk;
1088 }
1089
1090 start->flags |= CHUNKF_START_FILE;
1091
1092 /* flush all pending files out if full and adding */
1093 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
1094 {
1095 logf("fnq full");
1096 pcmrec_flush(PCMREC_FLUSH_ALL);
1097 did_flush = true;
1098 }
1099
1100 fnq_add_fn(path);
1101 }
1102
1103 /* Make sure to complete any interrupted high watermark */
1104 if (!did_flush)
1105 pcmrec_flush(PCMREC_FLUSH_IF_HIGH);
1106} /* pcmrec_new_stream */
1107
1108/** event handlers for pcmrec thread */
1109
1110/* PCMREC_INIT */
1111static void pcmrec_init(void)
1112{
1113 unsigned char *buffer;
1114
1115 /* warings and errors */
1116 warnings =
1117 errors = 0;
1118
1119 pcmrec_close_file(&rec_fdata.rec_file);
1120 rec_fdata.rec_file = -1;
1121
1122 /* pcm FIFO */
1123 dma_lock = true;
1124 pcm_rd_pos = 0;
1125 dma_wr_pos = 0;
1126 pcm_enc_pos = 0;
1127
1128 /* encoder FIFO */
1129 enc_wr_index = 0;
1130 enc_rd_index = 0;
1131
1132 /* filename queue */
1133 fnq_rd_pos = 0;
1134 fnq_wr_pos = 0;
1135
1136 /* stats */
1137 num_rec_bytes = 0;
1138 num_rec_samples = 0;
1139#if 0
1140 accum_rec_bytes = 0;
1141 accum_pcm_samples = 0;
1142#endif
1143
1144 pre_record_ticks = 0;
1145
1146 is_recording = false;
1147 is_paused = false;
1148
1149 buffer = audio_get_recording_buffer(&rec_buffer_size);
1150
1151 /* Line align pcm_buffer 2^4=16 bytes */
1152 pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 4);
1153 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1154 PCM_MAX_FEED_SIZE, 2);
1155 /* Adjust available buffer for possible align advancement */
1156 rec_buffer_size -= pcm_buffer - buffer;
1157
1158 pcm_init_recording();
1159} /* pcmrec_init */
1160
1161/* PCMREC_CLOSE */
1162static void pcmrec_close(void)
1163{
1164 dma_lock = true;
1165 pre_record_ticks = 0; /* Can't be prerecording any more */
1166 warnings = 0;
1167 pcm_close_recording();
1168 reset_hardware();
1169 audio_remove_encoder();
1170} /* pcmrec_close */
1171
1172/* PCMREC_OPTIONS */
1173static void pcmrec_set_recording_options(
1174 struct audio_recording_options *options)
1175{
1176 /* stop DMA transfer */
1177 dma_lock = true;
1178 pcm_stop_recording();
1179
1180 rec_frequency = options->rec_frequency;
1181 rec_source = options->rec_source;
1182 num_channels = options->rec_channels == 1 ? 1 : 2;
1183 rec_mono_mode = options->rec_mono_mode;
1184 pre_record_ticks = options->rec_prerecord_time * HZ;
1185 enc_config = options->enc_config;
1186 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
1187
1188#ifdef HAVE_SPDIF_IN
1189 if (rec_source == AUDIO_SRC_SPDIF)
1190 {
1191 /* must measure SPDIF sample rate before configuring codecs */
1192 unsigned long sr = spdif_measure_frequency();
1193 /* round to master list for SPDIF rate */
1194 int index = round_value_to_list32(sr, audio_master_sampr_list,
1195 SAMPR_NUM_FREQ, false);
1196 sample_rate = audio_master_sampr_list[index];
1197 /* round to HW playback rates for monitoring */
1198 index = round_value_to_list32(sr, hw_freq_sampr,
1199 HW_NUM_FREQ, false);
1200 pcm_set_frequency(hw_freq_sampr[index]);
1201 /* encoders with a limited number of rates do their own rounding */
1202 }
1203 else
1204#endif
1205 {
1206 /* set sample rate from frequency selection */
1207 sample_rate = rec_freq_sampr[rec_frequency];
1208 pcm_set_frequency(sample_rate);
1209 }
1210
1211 /* set monitoring */
1212 audio_set_output_source(rec_source);
1213
1214 /* apply hardware setting to start monitoring now */
1215 pcm_apply_settings();
1216
1217 queue_reply(&pcmrec_queue, 0); /* Release sender */
1218
1219 if (audio_load_encoder(enc_config.afmt))
1220 {
1221 /* start DMA transfer */
1222 dma_lock = pre_record_ticks == 0;
1223 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos),
1224 PCM_CHUNK_SIZE);
1225 }
1226 else
1227 {
1228 logf("set rec opt: enc load failed");
1229 errors |= PCMREC_E_LOAD_ENCODER;
1230 }
1231} /* pcmrec_set_recording_options */
1232
1233/* PCMREC_RECORD - start recording (not gapless)
1234 or split stream (gapless) */
1235static void pcmrec_record(const char *filename)
1236{
1237 unsigned long pre_sample_ticks;
1238 int rd_start;
1239 unsigned long flags;
1240 int pre_index;
1241
1242 logf("pcmrec_record: %s", filename);
1243
1244 /* reset stats */
1245 num_rec_bytes = 0;
1246 num_rec_samples = 0;
1247
1248 if (!is_recording)
1249 {
1250#if 0
1251 accum_rec_bytes = 0;
1252 accum_pcm_samples = 0;
1253#endif
1254 warnings = 0; /* reset warnings */
1255
1256 rd_start = enc_wr_index;
1257 pre_sample_ticks = 0;
1258
1259 pcmrec_refresh_watermarks();
1260
1261 if (pre_record_ticks)
1262 {
1263 int i = rd_start;
1264 /* calculate number of available chunks */
1265 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1266 enc_num_chunks) % enc_num_chunks;
1267 /* overflow at 974 seconds of prerecording at 44.1kHz */
1268 unsigned long pre_record_sample_ticks =
1269 enc_sample_rate*pre_record_ticks;
1270 int pre_chunks = 0; /* Counter to limit prerecorded time to
1271 prevent flood state at outset */
1272
1273 logf("pre-st: %ld", pre_record_sample_ticks);
1274
1275 /* Get exact measure of recorded data as number of samples aren't
1276 nescessarily going to be the max for each chunk */
1277 for (; avail_pre_chunks-- > 0;)
1278 {
1279 struct enc_chunk_hdr *chunk;
1280 unsigned long chunk_sample_ticks;
1281
1282 DEC_ENC_INDEX(i);
1283
1284 chunk = GET_ENC_CHUNK(i);
1285
1286 /* must have data to be counted */
1287 if (chunk->enc_data == NULL)
1288 continue;
1289
1290 chunk_sample_ticks = chunk->num_pcm*HZ;
1291
1292 rd_start = i;
1293 pre_sample_ticks += chunk_sample_ticks;
1294 num_rec_bytes += chunk->enc_size;
1295 num_rec_samples += chunk->num_pcm;
1296 pre_chunks++;
1297
1298 /* stop here if enough already */
1299 if (pre_chunks >= high_watermark ||
1300 pre_sample_ticks >= pre_record_sample_ticks)
1301 {
1302 logf("pre-chks: %d", pre_chunks);
1303 break;
1304 }
1305 }
1306
1307#if 0
1308 accum_rec_bytes = num_rec_bytes;
1309 accum_pcm_samples = num_rec_samples;
1310#endif
1311 }
1312
1313 enc_rd_index = rd_start;
1314
1315 /* filename queue should be empty */
1316 if (!pcmrec_fnq_is_empty())
1317 {
1318 logf("fnq: not empty!");
1319 pcmrec_fnq_set_empty();
1320 }
1321
1322 flags = CHUNKF_START_FILE;
1323 if (pre_sample_ticks > 0)
1324 flags |= CHUNKF_PRERECORD;
1325
1326 pre_index = enc_rd_index;
1327
1328 dma_lock = false;
1329 is_paused = false;
1330 is_recording = true;
1331 }
1332 else
1333 {
1334 /* already recording, just split the stream */
1335 logf("inserting split");
1336 flags = CHUNKF_START_FILE | CHUNKF_END_FILE;
1337 pre_index = 0;
1338 }
1339
1340 pcmrec_new_stream(filename, flags, pre_index);
1341 logf("pcmrec_record done");
1342} /* pcmrec_record */
1343
1344/* PCMREC_STOP */
1345static void pcmrec_stop(void)
1346{
1347 logf("pcmrec_stop");
1348
1349 if (is_recording)
1350 {
1351 dma_lock = true; /* lock dma write position */
1352
1353 /* flush all available data first to avoid overflow while waiting
1354 for encoding to finish */
1355 pcmrec_flush(PCMREC_FLUSH_ALL);
1356
1357 /* wait for encoder to finish remaining data */
1358 while (errors == 0 && !pcm_buffer_empty)
1359 yield();
1360
1361 /* end stream at last data */
1362 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
1363
1364 /* flush anything else encoder added */
1365 pcmrec_flush(PCMREC_FLUSH_ALL);
1366
1367 /* remove any pending file start not yet processed - should be at
1368 most one at enc_wr_index */
1369 pcmrec_fnq_get_filename(NULL);
1370 /* encoder should abort any chunk it was in midst of processing */
1371 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1372
1373 /* filename queue should be empty */
1374 if (!pcmrec_fnq_is_empty())
1375 {
1376 logf("fnq: not empty!");
1377 pcmrec_fnq_set_empty();
1378 }
1379
1380 /* be absolutely sure the file is closed */
1381 if (errors != 0)
1382 pcmrec_close_file(&rec_fdata.rec_file);
1383 rec_fdata.rec_file = -1;
1384
1385 is_recording = false;
1386 is_paused = false;
1387 dma_lock = pre_record_ticks == 0;
1388 }
1389 else
1390 {
1391 logf("not recording");
1392 }
1393
1394 logf("pcmrec_stop done");
1395} /* pcmrec_stop */
1396
1397/* PCMREC_PAUSE */
1398static void pcmrec_pause(void)
1399{
1400 logf("pcmrec_pause");
1401
1402 if (!is_recording)
1403 {
1404 logf("not recording");
1405 }
1406 else if (is_paused)
1407 {
1408 logf("already paused");
1409 }
1410 else
1411 {
1412 dma_lock = true;
1413 is_paused = true;
1414 }
1415
1416 logf("pcmrec_pause done");
1417} /* pcmrec_pause */
1418
1419/* PCMREC_RESUME */
1420static void pcmrec_resume(void)
1421{
1422 logf("pcmrec_resume");
1423
1424 if (!is_recording)
1425 {
1426 logf("not recording");
1427 }
1428 else if (!is_paused)
1429 {
1430 logf("not paused");
1431 }
1432 else
1433 {
1434 is_paused = false;
1435 is_recording = true;
1436 dma_lock = false;
1437 }
1438
1439 logf("pcmrec_resume done");
1440} /* pcmrec_resume */
1441
1442static void pcmrec_thread(void) __attribute__((noreturn));
1443static void pcmrec_thread(void)
1444{
1445 struct queue_event ev;
1446
1447 logf("thread pcmrec start");
1448
1449 while(1)
1450 {
1451 if (is_recording)
1452 {
1453 /* Poll periodically to flush data */
1454 queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
1455
1456 if (ev.id == SYS_TIMEOUT)
1457 {
1458 /* Messages that interrupt this will complete it */
1459 pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
1460 PCMREC_FLUSH_INTERRUPTABLE);
1461 continue;
1462 }
1463 }
1464 else
1465 {
1466 /* Not doing anything - sit and wait for commands */
1467 queue_wait(&pcmrec_queue, &ev);
1468 }
1469
1470 switch (ev.id)
1471 {
1472 case PCMREC_INIT:
1473 pcmrec_init();
1474 break;
1475
1476 case PCMREC_CLOSE:
1477 pcmrec_close();
1478 break;
1479
1480 case PCMREC_OPTIONS:
1481 pcmrec_set_recording_options(
1482 (struct audio_recording_options *)ev.data);
1483 break;
1484
1485 case PCMREC_RECORD:
1486 clear_flush_interrupt();
1487 pcmrec_record((const char *)ev.data);
1488 break;
1489
1490 case PCMREC_STOP:
1491 clear_flush_interrupt();
1492 pcmrec_stop();
1493 break;
1494
1495 case PCMREC_PAUSE:
1496 clear_flush_interrupt();
1497 pcmrec_pause();
1498 break;
1499
1500 case PCMREC_RESUME:
1501 pcmrec_resume();
1502 break;
1503#if 0
1504 case PCMREC_FLUSH_NUM:
1505 pcmrec_flush((unsigned)ev.data);
1506 break;
1507#endif
1508 case SYS_USB_CONNECTED:
1509 if (is_recording)
1510 break;
1511 pcmrec_close();
1512 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1513 usb_wait_for_disconnect(&pcmrec_queue);
1514 flush_interrupts = 0;
1515 break;
1516 } /* end switch */
1517 } /* end while */
1518} /* pcmrec_thread */
1519
1520/****************************************************************************/
1521/* */
1522/* following functions will be called by the encoder codec */
1523/* in a free-threaded manner */
1524/* */
1525/****************************************************************************/
1526
1527/* pass the encoder settings to the encoder */
1528void enc_get_inputs(struct enc_inputs *inputs)
1529{
1530 inputs->sample_rate = sample_rate;
1531 inputs->num_channels = num_channels;
1532 inputs->rec_mono_mode = rec_mono_mode;
1533 inputs->config = &enc_config;
1534} /* enc_get_inputs */
1535
1536/* set the encoder dimensions (called by encoder codec at initialization and
1537 termination) */
1538void enc_set_parameters(struct enc_parameters *params)
1539{
1540 size_t bufsize, resbytes;
1541
1542 logf("enc_set_parameters");
1543
1544 if (!params)
1545 {
1546 logf("reset");
1547 /* Encoder is terminating */
1548 memset(&enc_config, 0, sizeof (enc_config));
1549 enc_sample_rate = 0;
1550 cancel_cpu_boost(); /* Make sure no boost remains */
1551 return;
1552 }
1553
1554 enc_sample_rate = params->enc_sample_rate;
1555 logf("enc sampr:%lu", enc_sample_rate);
1556
1557 pcm_rd_pos = dma_wr_pos;
1558 pcm_enc_pos = pcm_rd_pos;
1559
1560 enc_config.afmt = params->afmt;
1561 /* addition of the header is always implied - chunk size 4-byte aligned */
1562 enc_chunk_size =
1563 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1564 enc_events_callback = params->events_callback;
1565
1566 logf("chunk size:%lu", enc_chunk_size);
1567
1568 /*** Configure the buffers ***/
1569
1570 /* Layout of recording buffer:
1571 * [ax] = possible alignment x multiple
1572 * [sx] = possible size alignment of x multiple
1573 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1574 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1575 */
1576 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1577 logf("resbytes:%lu", resbytes);
1578
1579 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1580 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH
1581#ifdef DEBUG
1582 - sizeof (*wrap_id_p)
1583#endif
1584 ;
1585
1586 enc_num_chunks = bufsize / enc_chunk_size;
1587 logf("num chunks:%d", enc_num_chunks);
1588
1589 /* get real amount used by encoder chunks */
1590 bufsize = enc_num_chunks*enc_chunk_size;
1591 logf("enc size:%lu", bufsize);
1592
1593#ifdef DEBUG
1594 /* add magic at wraparound for spillover checks */
1595 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize);
1596 bufsize += sizeof (*wrap_id_p);
1597 *wrap_id_p = ENC_CHUNK_MAGIC;
1598#endif
1599
1600 /** set OUT parameters **/
1601 params->enc_buffer = enc_buffer;
1602 params->buf_chunk_size = enc_chunk_size;
1603 params->num_chunks = enc_num_chunks;
1604
1605 /* calculate reserve buffer start and return pointer to encoder */
1606 params->reserve_buffer = NULL;
1607 if (resbytes > 0)
1608 {
1609 params->reserve_buffer = enc_buffer + bufsize;
1610 bufsize += resbytes;
1611 }
1612
1613 /* place filename queue at end of buffer using up whatever remains */
1614 fnq_rd_pos = 0; /* reset */
1615 fnq_wr_pos = 0; /* reset */
1616 fn_queue = enc_buffer + bufsize;
1617 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1618 fnq_size /= MAX_PATH;
1619 if (fnq_size > FNQ_MAX_NUM_PATHS)
1620 fnq_size = FNQ_MAX_NUM_PATHS;
1621 fnq_size *= MAX_PATH;
1622 logf("fnq files:%ld", fnq_size / MAX_PATH);
1623
1624#if defined(DEBUG)
1625 logf("ab :%08lX", (uintptr_t)audiobuf);
1626 logf("pcm:%08lX", (uintptr_t)pcm_buffer);
1627 logf("enc:%08lX", (uintptr_t)enc_buffer);
1628 logf("res:%08lX", (uintptr_t)params->reserve_buffer);
1629 logf("wip:%08lX", (uintptr_t)wrap_id_p);
1630 logf("fnq:%08lX", (uintptr_t)fn_queue);
1631 logf("end:%08lX", (uintptr_t)fn_queue + fnq_size);
1632 logf("abe:%08lX", (uintptr_t)audiobufend);
1633#endif
1634
1635 /* init all chunk headers and reset indexes */
1636 enc_rd_index = 0;
1637 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1638 {
1639 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1640#ifdef DEBUG
1641 chunk->id = ENC_CHUNK_MAGIC;
1642#endif
1643 chunk->flags = 0;
1644 }
1645
1646 logf("enc_set_parameters done");
1647} /* enc_set_parameters */
1648
1649/* return encoder chunk at current write position -
1650 NOTE: can be called by pcmrec thread when splitting streams */
1651struct enc_chunk_hdr * enc_get_chunk(void)
1652{
1653 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1654
1655#ifdef DEBUG
1656 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
1657 {
1658 errors |= PCMREC_E_CHUNK_OVF;
1659 logf("finish chk ovf: %d", enc_wr_index);
1660 }
1661#endif
1662
1663 chunk->flags &= CHUNKF_START_FILE;
1664
1665 if (!is_recording)
1666 chunk->flags |= CHUNKF_PRERECORD;
1667
1668 return chunk;
1669} /* enc_get_chunk */
1670
1671/* releases the current chunk into the available chunks -
1672 NOTE: can be called by pcmrec thread when splitting streams */
1673void enc_finish_chunk(void)
1674{
1675 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1676
1677 if ((long)chunk->flags < 0)
1678 {
1679 /* encoder set error flag */
1680 errors |= PCMREC_E_ENCODER;
1681 logf("finish chk enc error");
1682 }
1683
1684 /* advance enc_wr_index to the next encoder chunk */
1685 INC_ENC_INDEX(enc_wr_index);
1686
1687 if (enc_rd_index != enc_wr_index)
1688 {
1689 num_rec_bytes += chunk->enc_size;
1690 num_rec_samples += chunk->num_pcm;
1691#if 0
1692 accum_rec_bytes += chunk->enc_size;
1693 accum_pcm_samples += chunk->num_pcm;
1694#endif
1695 }
1696 else if (is_recording) /* buffer full */
1697 {
1698 /* keep current position and put up warning flag */
1699 warnings |= PCMREC_W_ENC_BUFFER_OVF;
1700 logf("enc_buffer ovf");
1701 DEC_ENC_INDEX(enc_wr_index);
1702 if (pcmrec_context)
1703 {
1704 /* if stream splitting, keep this out of circulation and
1705 flush a small number, then readd - cannot risk losing
1706 stream markers */
1707 logf("mini flush");
1708 pcmrec_flush(PCMREC_FLUSH_MINI);
1709 INC_ENC_INDEX(enc_wr_index);
1710 }
1711 }
1712 else
1713 {
1714 /* advance enc_rd_index for prerecording */
1715 INC_ENC_INDEX(enc_rd_index);
1716 }
1717} /* enc_finish_chunk */
1718
1719/* passes a pointer to next chunk of unprocessed wav data */
1720/* TODO: this really should give the actual size returned */
1721unsigned char * enc_get_pcm_data(size_t size)
1722{
1723 int wp = dma_wr_pos;
1724 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1725
1726 /* limit the requested pcm data size */
1727 if (size > PCM_MAX_FEED_SIZE)
1728 size = PCM_MAX_FEED_SIZE;
1729
1730 if (avail >= size)
1731 {
1732 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1733 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1734
1735 pcm_enc_pos = pcm_rd_pos;
1736 pcm_rd_pos = next_pos;
1737
1738 /* ptr must point to continous data at wraparound position */
1739 if ((size_t)pcm_rd_pos < size)
1740 {
1741 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1742 pcm_buffer, pcm_rd_pos);
1743 }
1744
1745 if (avail >= (sample_rate << 2))
1746 {
1747 /* Filling up - boost codec */
1748 trigger_cpu_boost();
1749 }
1750
1751 pcm_buffer_empty = false;
1752 return ptr;
1753 }
1754
1755 /* not enough data available - encoder should idle */
1756 pcm_buffer_empty = true;
1757
1758 cancel_cpu_boost();
1759
1760 /* Sleep long enough to allow one frame on average */
1761 sleep(0);
1762
1763 return NULL;
1764} /* enc_get_pcm_data */
1765
1766/* puts some pcm data back in the queue */
1767size_t enc_unget_pcm_data(size_t size)
1768{
1769 int wp = dma_wr_pos;
1770 size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) -
1771 2*PCM_CHUNK_SIZE;
1772
1773 /* allow one interrupt to occur during this call and not have the
1774 new read position inside the DMA destination chunk */
1775 if ((ssize_t)old_avail > 0)
1776 {
1777 /* limit size to amount of old data remaining */
1778 if (size > old_avail)
1779 size = old_avail;
1780
1781 pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1782 pcm_rd_pos = pcm_enc_pos;
1783
1784 return size;
1785 }
1786
1787 return 0;
1788} /* enc_unget_pcm_data */
diff --git a/apps/recorder/pcm_record.h b/apps/recorder/pcm_record.h
new file mode 100644
index 0000000000..f805313fe5
--- /dev/null
+++ b/apps/recorder/pcm_record.h
@@ -0,0 +1,66 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef PCM_RECORD_H
23#define PCM_RECORD_H
24
25#define DMA_REC_ERROR_DMA (-1)
26#ifdef HAVE_SPDIF_REC
27#define DMA_REC_ERROR_SPDIF (-2)
28#endif
29
30/** Warnings **/
31/* pcm (dma) buffer has overflowed */
32#define PCMREC_W_PCM_BUFFER_OVF 0x00000001
33/* encoder output buffer has overflowed */
34#define PCMREC_W_ENC_BUFFER_OVF 0x00000002
35/** Errors **/
36/* failed to load encoder */
37#define PCMREC_E_LOAD_ENCODER 0x80001000
38/* error originating in encoder */
39#define PCMREC_E_ENCODER 0x80002000
40/* filename queue has desynced from stream markers */
41#define PCMREC_E_FNQ_DESYNC 0x80004000
42/* I/O error has occurred */
43#define PCMREC_E_IO 0x80008000
44#ifdef DEBUG
45/* encoder has written past end of allotted space */
46#define PCMREC_E_CHUNK_OVF 0x80010000
47#endif /* DEBUG */
48
49/** General functions for high level codec recording **/
50/* pcm_rec_error_clear is deprecated for general use. audio_error_clear
51 should be used */
52void pcm_rec_error_clear(void);
53/* pcm_rec_status is deprecated for general use. audio_status merges the
54 results for consistency with the hardware codec version */
55unsigned long pcm_rec_status(void);
56unsigned long pcm_rec_get_warnings(void);
57void pcm_rec_init(void);
58int pcm_rec_current_bitrate(void);
59int pcm_rec_encoder_afmt(void); /* AFMT_* value, AFMT_UNKNOWN if none */
60int pcm_rec_rec_format(void); /* Format index or -1 otherwise */
61unsigned long pcm_rec_sample_rate(void);
62int pcm_get_num_unprocessed(void);
63
64/* audio.h contains audio_* recording functions */
65
66#endif /* PCM_RECORD_H */
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index 6f5c7df09a..d29db390c7 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -40,6 +40,7 @@
40#include "spdif.h" 40#include "spdif.h"
41#endif 41#endif
42#endif /* CONFIG_CODEC == SWCODEC */ 42#endif /* CONFIG_CODEC == SWCODEC */
43#include "pcm_record.h"
43#include "recording.h" 44#include "recording.h"
44#include "mp3_playback.h" 45#include "mp3_playback.h"
45#include "mas.h" 46#include "mas.h"