summaryrefslogtreecommitdiff
path: root/apps/recorder/pcm_record.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/recorder/pcm_record.c')
-rw-r--r--apps/recorder/pcm_record.c2924
1 files changed, 1571 insertions, 1353 deletions
diff --git a/apps/recorder/pcm_record.c b/apps/recorder/pcm_record.c
index 6b9adda8bd..a3fe2097b1 100644
--- a/apps/recorder/pcm_record.c
+++ b/apps/recorder/pcm_record.c
@@ -7,7 +7,10 @@
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing 10 * Copyright (C) 2005 Linus Nielsen Feltzing
11 * Copyright (C) 2006 Antonius Hellmann
12 * Copyright (C) 2006-2013 Michael Sevakis
13 *
11 * 14 *
12 * This program is free software; you can redistribute it and/or 15 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 16 * modify it under the terms of the GNU General Public License
@@ -18,17 +21,14 @@
18 * KIND, either express or implied. 21 * KIND, either express or implied.
19 * 22 *
20 ****************************************************************************/ 23 ****************************************************************************/
21
22#include "config.h" 24#include "config.h"
23#include "gcc_extensions.h"
24#include "pcm_record.h"
25#include "system.h" 25#include "system.h"
26#include "kernel.h" 26#include "kernel.h"
27#include "pcm_record.h"
28#include "codecs.h"
27#include "logf.h" 29#include "logf.h"
28#include "thread.h" 30#include "thread.h"
29#include "string-extra.h"
30#include "storage.h" 31#include "storage.h"
31#include "usb.h"
32#include "general.h" 32#include "general.h"
33#include "codec_thread.h" 33#include "codec_thread.h"
34#include "audio.h" 34#include "audio.h"
@@ -40,1757 +40,1975 @@
40#endif 40#endif
41#include "audio_thread.h" 41#include "audio_thread.h"
42 42
43/***************************************************************************/ 43/* Macros to enable logf for queues
44 logging on SYS_TIMEOUT can be disabled */
45#ifdef SIMULATOR
46/* Define this for logf output of all queuing except SYS_TIMEOUT */
47#define PCMREC_LOGQUEUES
48/* Define this to logf SYS_TIMEOUT messages */
49/*#define PCMREC_LOGQUEUES_SYS_TIMEOUT*/
50#endif /* SIMULATOR */
51
52#ifdef PCMREC_LOGQUEUES
53#define LOGFQUEUE logf
54#else
55#define LOGFQUEUE(...)
56#endif
44 57
45extern struct event_queue audio_queue; 58#ifdef PCMREC_LOGQUEUES_SYS_TIMEOUT
59#define LOGFQUEUE_SYS_TIMEOUT logf
60#else
61#define LOGFQUEUE_SYS_TIMEOUT(...)
62#endif
63
64/** Target-related configuration **/
65
66/**
67 * PCM_NUM_CHUNKS: Number of PCM chunks
68 * PCM_CHUNK_SAMP: Number of samples in a PCM chunk
69 * PCM_BOOST_SECONDS: PCM level at which to boost CPU
70 * PANIC_SECONDS: Flood watermark time until full
71 * FLUSH_SECONDS: Flush watermark time until full
72 * STREAM_BUF_SIZE: Size of stream write buffer
73 * PRIO_SECONDS: Max flush time before prio boost
74 *
75 * Total PCM buffer size should be mem aligned
76 *
77 * Fractions should be left without parentheses so the multiplier is
78 * multiplied by the numerator first.
79 */
80#if MEMORYSIZE <= 2
81#define PCM_NUM_CHUNKS 56
82#define PCM_CHUNK_SAMP 1024
83#define PCM_BOOST_SECONDS 1/2
84#define PANIC_SECONDS 1/2
85#define FLUSH_SECONDS 1
86#define FLUSH_MON_INTERVAL 1/6
87#define STREAM_BUF_SIZE 32768
88#elif MEMORYSIZE <= 16
89#define PANIC_SECONDS 5
90#define FLUSH_SECONDS 7
91#else /* MEMORYSIZE > 16 */
92#define PANIC_SECONDS 8
93#define FLUSH_SECONDS 10
94#endif /* MEMORYSIZE */
95
96/* Default values if not overridden above */
97#ifndef PCM_NUM_CHUNKS
98#define PCM_NUM_CHUNKS 256
99#endif
100#ifndef PCM_CHUNK_SAMP
101#define PCM_CHUNK_SAMP 2048
102#endif
103#ifndef PCM_BOOST_SECONDS
104#define PCM_BOOST_SECONDS 1
105#endif
106#ifndef FLUSH_MON_INTERVAL
107#define FLUSH_MON_INTERVAL 1/4
108#endif
109#ifndef STREAM_BUF_SIZE
110#define STREAM_BUF_SIZE 65536
111#endif
112#ifndef PRIO_SECONDS
113#define PRIO_SECONDS 10
114#endif
115
116/* FAT limit for filesize. Recording will accept no further data from the
117 * codec if this limit is reached in order to preserve its own data
118 * integrity. A split should have made by the higher-ups long before this
119 * point.
120 *
121 * Leave a generous 64k margin for metadata being added to file. */
122#define MAX_NUM_REC_BYTES ((size_t)0x7fff0000u)
123
124/***************************************************************************/
125extern struct codec_api ci; /* in codec_thread.c */
126extern struct event_queue audio_queue; /* in audio_thread.c */
127extern unsigned int audio_thread_id; /* in audio_thread.c */
46 128
47/** General recording state **/ 129/** General recording state **/
48static bool is_recording; /* We are recording */
49static bool is_paused; /* We have paused */
50static unsigned long errors; /* An error has occured */
51static unsigned long warnings; /* Warning */
52static int flush_interrupts = 0; /* Number of messages queued that
53 should interrupt a flush in
54 progress -
55 for a safety net and a prompt
56 response to stop, split and pause
57 requests -
58 only interrupts a flush initiated
59 by pcmrec_flush(0) */
60 130
61/* Utility functions for setting/clearing flushing interrupt flag */ 131/* Recording action being performed */
62static inline void flush_interrupt(void) 132static enum record_status
63{ 133{
64 flush_interrupts++; 134 RECORD_STOPPED = 0,
65 logf("flush int: %d", flush_interrupts); 135 RECORD_PRERECORDING = AUDIO_STATUS_PRERECORD,
66} 136 RECORD_RECORDING = AUDIO_STATUS_RECORD,
67 137 RECORD_PAUSED = (AUDIO_STATUS_RECORD | AUDIO_STATUS_PAUSE),
68static inline void clear_flush_interrupt(void) 138} record_status = RECORD_STOPPED;
139
140/* State of engine operations */
141static enum record_state
69{ 142{
70 if (--flush_interrupts < 0) 143 REC_STATE_IDLE, /* Stopped or prerecording */
71 flush_interrupts = 0; 144 REC_STATE_MONITOR, /* Monitoring buffer status */
72} 145 REC_STATE_FLUSH, /* Flushing buffer */
146} record_state = REC_STATE_IDLE;
73 147
74/** Stats on encoded data for current file **/ 148static uint32_t errors; /* An error has occured (bitmask) */
75static size_t num_rec_bytes; /* Num bytes recorded */ 149static uint32_t warnings; /* Non-fatal warnings (bitmask) */
76static unsigned long num_rec_samples; /* Number of PCM samples recorded */
77 150
78/** Stats on encoded data for all files from start to stop **/ 151static uint32_t rec_errors; /* Mirror of errors but private to
79#if 0 152 * avoid race with controlling
80static unsigned long long accum_rec_bytes; /* total size written to chunks */ 153 * thread. Engine uses this
81static unsigned long long accum_pcm_samples; /* total pcm count processed */ 154 * internally. */
82#endif
83 155
84/* Keeps data about current file and is sent as event data for codec */ 156/** Stats on encoded data for current file **/
85static struct enc_file_event_data rec_fdata IDATA_ATTR = 157static int rec_fd = -1; /* Currently open file descriptor */
86{ 158static size_t num_rec_bytes; /* Number of bytes recorded */
87 .chunk = NULL, 159static uint64_t num_rec_samples; /* Number of PCM samples recorded */
88 .new_enc_size = 0, 160static uint64_t encbuf_rec_count; /* Count of slots written to buffer
89 .new_num_pcm = 0, 161 for current file */
90 .rec_file = -1,
91 .num_pcm_samples = 0
92};
93 162
94/** These apply to current settings **/ 163/** These apply to current settings **/
95static int rec_source; /* current rec_source setting */ 164static int rec_source; /* Current rec_source setting */
96static int rec_frequency; /* current frequency setting */ 165static unsigned long sample_rate; /* Samplerate setting in HZ */
97static unsigned long sample_rate; /* Sample rate in HZ */
98static int num_channels; /* Current number of channels */ 166static int num_channels; /* Current number of channels */
99static int rec_mono_mode; /* how mono is created */
100static struct encoder_config enc_config; /* Current encoder configuration */ 167static struct encoder_config enc_config; /* Current encoder configuration */
101static unsigned long pre_record_ticks; /* pre-record time in ticks */ 168static unsigned int pre_record_seconds; /* Pre-record time in seconds */
102 169
103/**************************************************************************** 170/****************************************************************************
104 use 2 circular buffers: 171 Use 2 circular buffers:
105 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data 172 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
106 enc_buffer=encoded audio buffer: storage for encoder output data 173 enc_buffer=encoded audio buffer: storage for encoder output data
107 174
108 Flow: 175 Flow:
109 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer 176 1. When entering recording_screen DMA feeds the ringbuffer pcm_buffer
110 2. if enough pcm data are available the encoder codec does encoding of pcm 177 2. If enough pcm data are available the encoder codec does encoding of pcm
111 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread 178 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
112 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk 179 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
113 180
114 Functions calls (basic encoder steps): 181 Functions calls (basic encoder steps):
115 1.main: audio_load_encoder(); start the encoder 182 1.audio: codec_load(); load the encoder
116 2.encoder: enc_get_inputs(); get encoder recording settings 183 2.encoder: enc_init_parameters(); set the encoder parameters (at load)
117 3.encoder: enc_set_parameters(); set the encoder parameters 184 3.audio: enc_callback(); configure encoder recording settings
118 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data 185 4.audio: codec_go(); start encoding the new stream
119 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional) 186 5.encoder: enc_encbuf_get_buffer(); obtain an output buffer of size n
120 6.encoder: enc_get_chunk(); get a ptr to next enc chunk 187 6.encoder: enc_pcmbuf_read(); read n bytes of unprocessed pcm data
121 7.encoder: <process enc chunk> compress and store data to enc chunk 188 7.encoder: enc_encbuf_finish_buffer(); add the obtained buffer to output
122 8.encoder: enc_finish_chunk(); inform main about chunk processed and 189 8.encoder: enc_pcmbuf_advance(); advance pcm by n samples
123 is available to be written to a file. 190 9.encoder: while more PCM available, repeat 5. to 9.
124 Encoder can place any number of chunks 191 10.audio: codec_finish_stream(); finish the output for current stream
125 of PCM data in a single output chunk 192
126 but must stay within its output chunk 193 Function calls (basic stream flushing steps through enc_callback()):
127 size 194 1.audio: flush_stream_start(); stream flush destination is opening
128 9.encoder: repeat 4. to 8. 195 2.audio: flush_stream_data(); flush encoded audio to stream
129 A.pcmrec: enc_events_callback(); called for certain events 196 3.audio: while encoded data available, repeat 2.
130 197 4.audio: flush_stream_end(); stream flush destination is closing
131 (*) Optional step 198
132****************************************************************************/ 199****************************************************************************/
133 200
134/** buffer parameters where incoming PCM data is placed **/ 201/** Buffer parameters where incoming PCM data is placed **/
135#if MEMORYSIZE <= 2 202#define PCM_DEPTH_BYTES (sizeof (int16_t))
136#define PCM_NUM_CHUNKS 16 /* Power of 2 */ 203#define PCM_SAMP_SIZE (2*PCM_DEPTH_BYTES)
137#else 204#define PCM_CHUNK_SIZE (PCM_CHUNK_SAMP*PCM_SAMP_SIZE)
138#define PCM_NUM_CHUNKS 256 /* Power of 2 */ 205#define PCM_BUF_SIZE (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE)
139#endif 206
140#define PCM_CHUNK_SIZE 8192 /* Power of 2 */ 207/* Convert byte sizes into buffer slot counts */
141#define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1) 208#define CHUNK_SIZE_COUNT(size) \
142 209 (((size) + ENC_HDR_SIZE - 1) / ENC_HDR_SIZE)
143#define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset))) 210#define CHUNK_FILE_COUNT(size) \
144#define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index)) 211 ({ typeof (size) __size = (size); \
145#define INC_ENC_INDEX(index) \ 212 CHUNK_SIZE_COUNT(MIN(__size, MAX_PATH) + ENC_HDR_SIZE); })
146 { if (++index >= enc_num_chunks) index = 0; } 213#define CHUNK_FILE_COUNT_PATH(path) \
147#define DEC_ENC_INDEX(index) \ 214 CHUNK_FILE_COUNT(strlen(path) + 1)
148 { if (--index < 0) index = enc_num_chunks - 1; } 215#define CHUNK_DATA_COUNT(size) \
149 216 CHUNK_SIZE_COUNT((size) + sizeof (struct enc_chunk_data))
150static size_t rec_buffer_size; /* size of available buffer */ 217
151static unsigned char *pcm_buffer; /* circular recording buffer */ 218/* Min margin to write stream split headers without overwrap risk */
152static unsigned char *enc_buffer; /* circular encoding buffer */ 219#define ENCBUF_MIN_SPLIT_MARGIN \
153#ifdef DEBUG 220 (2*(1 + CHUNK_FILE_COUNT(MAX_PATH)) - 1)
154static unsigned long *wrap_id_p; /* magic at wrap position - a debugging 221
155 aid to check if the encoder data 222static void *rec_buffer; /* Root rec buffer pointer */
156 spilled out of its chunk */ 223static size_t rec_buffer_size; /* Root rec buffer size */
157#endif /* DEBUG */ 224
158static volatile int dma_wr_pos; /* current DMA write pos */ 225static void *pcm_buffer; /* Circular buffer for PCM samples */
159static int pcm_rd_pos; /* current PCM read pos */ 226static volatile bool pcm_pause; /* Freeze DMA write position */
160static int pcm_enc_pos; /* position encoder is processing */ 227static volatile size_t pcm_widx; /* Current DMA write position */
161static volatile bool dma_lock; /* lock DMA write position */ 228static volatile size_t pcm_ridx; /* Current PCM read position */
162static int enc_wr_index; /* encoder chunk write index */ 229
163static int enc_rd_index; /* encoder chunk read index */ 230static union enc_chunk_hdr *enc_buffer; /* Circular encoding buffer */
164static int enc_num_chunks; /* number of chunks in ringbuffer */ 231static size_t enc_widx; /* Encoder chunk write index */
165static size_t enc_chunk_size; /* maximum encoder chunk size */ 232static size_t enc_ridx; /* Encoder chunk read index */
166static unsigned long enc_sample_rate; /* sample rate used by encoder */ 233static size_t enc_buflen; /* Length of buffer in slots */
167static bool pcmrec_context = false; /* called by pcmrec thread? */ 234
168static bool pcm_buffer_empty; /* all pcm chunks processed? */ 235static unsigned char *stream_buffer; /* Stream-to-disk write buffer */
169 236static ssize_t stream_buf_used; /* Stream write buffer occupancy */
170/** file flushing **/ 237
171static int low_watermark; /* Low watermark to stop flush */ 238static struct enc_chunk_file *fname_buf;/* Buffer with next file to create */
172static int high_watermark; /* max chunk limit for data flush */ 239
173static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */ 240static unsigned long enc_sample_rate; /* Samplerate used by encoder */
174static int last_storage_spinup_time = -1;/* previous spin time used */ 241static bool pcm_buffer_empty; /* All PCM chunks processed? */
175#ifdef HAVE_PRIORITY_SCHEDULING 242
176static int flood_watermark; /* boost thread priority when here */ 243static typeof (memcpy) *pcm_copyfn; /* PCM memcpy or copy_buffer_mono */
244static enc_callback_t enc_cb; /* Encoder's recording callback */
245
246/** File flushing **/
247static unsigned long encbuf_datarate; /* Rate of data per second */
248#if (CONFIG_STORAGE & STORAGE_ATA)
249static int spinup_time; /* Last spinup time */
177#endif 250#endif
251static size_t high_watermark; /* Max limit for data flush */
178 252
179/* Constants that control watermarks */
180#define MINI_CHUNKS 10 /* chunk count for mini flush */
181#ifdef HAVE_PRIORITY_SCHEDULING 253#ifdef HAVE_PRIORITY_SCHEDULING
182#define PRIO_SECONDS 10 /* max flush time before priority boost */ 254static size_t flood_watermark; /* Max limit for thread prio boost */
255static bool prio_boosted;
183#endif 256#endif
184#if MEMORYSIZE <= 2
185/* fractions must be integer fractions of 4 because they are evaluated with
186 * X*4*XXX_SECONDS, that way we avoid float calculation */
187#define LOW_SECONDS 1/4 /* low watermark time till empty */
188#define PANIC_SECONDS 1/2 /* flood watermark time until full */
189#define FLUSH_SECONDS 1 /* flush watermark time until full */
190#elif MEMORYSIZE <= 16
191#define LOW_SECONDS 1 /* low watermark time till empty */
192#define PANIC_SECONDS 5 /* flood watermark time until full */
193#define FLUSH_SECONDS 7 /* flush watermark time until full */
194#else
195#define LOW_SECONDS 1 /* low watermark time till empty */
196#define PANIC_SECONDS 8
197#define FLUSH_SECONDS 10
198#endif /* MEMORYSIZE */
199 257
200/** encoder events **/ 258/** Stream marking **/
201static void (*enc_events_callback)(enum enc_events event, void *data); 259enum mark_stream_action
202 260{
203/** Path queue for files to write **/ 261 MARK_STREAM_END = 0x1, /* Mark end current stream */
204#define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */ 262 MARK_STREAM_START = 0x2, /* Mark start of new stream */
205#define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */ 263 MARK_STREAM_SPLIT = 0x3, /* Insert split; orr of above values */
206static unsigned char *fn_queue; /* pointer to first filename */ 264 MARK_STREAM_PRE = 0x4, /* Do prerecord data tally */
207static ssize_t fnq_size; /* capacity of queue in bytes */ 265 MARK_STREAM_START_PRE = MARK_STREAM_PRE | MARK_STREAM_START,
208static int fnq_rd_pos; /* current read position */
209static int fnq_wr_pos; /* current write position */
210#define FNQ_NEXT(pos) \
211 ({ int p = (pos) + MAX_PATH; \
212 if (p >= fnq_size) \
213 p = 0; \
214 p; })
215#define FNQ_PREV(pos) \
216 ({ int p = (pos) - MAX_PATH; \
217 if (p < 0) \
218 p = fnq_size - MAX_PATH; \
219 p; })
220
221enum
222{
223 PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by
224 incoming messages - combine
225 with other constants */
226 PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */
227 PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of
228 chunks */
229 PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark
230 reached */
231}; 266};
232 267
268
233/***************************************************************************/ 269/***************************************************************************/
234 270
235enum 271/* Buffer pointer (p) to PCM sample memory address */
272static inline void * pcmbuf_ptr(size_t p)
236{ 273{
237 PCMREC_NULL = 0, 274 return pcm_buffer + p;
238 PCMREC_INIT, /* enable recording */ 275}
239 PCMREC_CLOSE, /* close recording */
240 PCMREC_OPTIONS, /* set recording options */
241 PCMREC_RECORD, /* record a new file */
242 PCMREC_STOP, /* stop the current recording */
243 PCMREC_PAUSE, /* pause the current recording */
244 PCMREC_RESUME, /* resume the current recording */
245};
246
247/*******************************************************************/
248/* Functions that are not executing in the audio thread first */
249/*******************************************************************/
250 276
251static void pcmrec_raise_error_status(unsigned long e) 277/* Buffer pointer (p) plus value (v), wrapped if necessary */
278static size_t pcmbuf_add(size_t p, size_t v)
252{ 279{
253 pcm_rec_lock(); /* DMA sets this too */ 280 size_t res = p + v;
254 errors |= e; 281
255 pcm_rec_unlock(); 282 if (res >= PCM_BUF_SIZE)
283 res -= PCM_BUF_SIZE;
284
285 return res;
256} 286}
257 287
258static void pcmrec_raise_warning_status(unsigned long w) 288/* Size of data in PCM buffer */
289size_t pcmbuf_used(void)
259{ 290{
260 warnings |= w; 291 size_t p1 = pcm_ridx;
292 size_t p2 = pcm_widx;
293
294 if (p1 > p2)
295 p2 += PCM_BUF_SIZE;
296
297 return p2 - p1;
261} 298}
262 299
263/* Callback for when more data is ready - called in interrupt context */ 300/* Buffer pointer (p) to memory address of header */
264static void pcm_rec_have_more(void **start, size_t *size) 301static inline union enc_chunk_hdr * encbuf_ptr(size_t p)
265{ 302{
266 if (!dma_lock) 303 return enc_buffer + p;
267 { 304}
268 /* advance write position */
269 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
270 305
271 /* set pcm ovf if processing start position is inside current 306/* Buffer pointer (p) plus value (v), wrapped if necessary */
272 write chunk */ 307static size_t encbuf_add(size_t p, size_t v)
273 if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE) 308{
274 pcmrec_raise_warning_status(PCMREC_W_PCM_BUFFER_OVF); 309 size_t res = p + v;
275 310
276 dma_wr_pos = next_pos; 311 if (res >= enc_buflen)
277 } 312 res -= enc_buflen;
278 313
279 *start = GET_PCM_CHUNK(dma_wr_pos); 314 return res;
280 *size = PCM_CHUNK_SIZE; 315}
281} /* pcm_rec_have_more */
282 316
283static enum pcm_dma_status pcm_rec_status_callback(enum pcm_dma_status status) 317/* Number of free buffer slots */
318static size_t encbuf_free(void)
284{ 319{
285 if (status < PCM_DMAST_OK) 320 size_t p1 = enc_ridx;
286 { 321 size_t p2 = enc_widx;
287 /* some error condition */
288 if (status == PCM_DMAST_ERR_DMA)
289 {
290 /* Flush recorded data to disk and stop recording */
291 errors |= PCMREC_E_DMA;
292 return status;
293 }
294 /* else try again next transmission - frame is invalid */
295 }
296 322
297 return PCM_DMAST_OK; 323 if (p2 >= p1)
298} /* pcm_rec_status_callback */ 324 p1 += enc_buflen;
299 325
300static void reset_hardware(void) 326 return p1 - p2;
327}
328
329/* Number of used buffer slots */
330static size_t encbuf_used(void)
301{ 331{
302 /* reset pcm to defaults */ 332 size_t p1 = enc_ridx;
303 pcm_set_frequency(HW_SAMPR_RESET | SAMPR_TYPE_REC); 333 size_t p2 = enc_widx;
304 audio_set_output_source(AUDIO_SRC_PLAYBACK); 334
305 pcm_apply_settings(); 335 if (p1 > p2)
336 p2 += enc_buflen;
337
338 return p2 - p1;
306} 339}
307 340
308/** pcm_rec_* group **/ 341/* Is the encoder buffer empty? */
342static bool encbuf_empty(void)
343{
344 return enc_ridx == enc_widx;
345}
309 346
310/** 347/* Buffer pointer (p) plus size (v), written to enc_widx, new widx
311 * Clear all errors and warnings 348 * zero-initialized */
312 */ 349static void encbuf_widx_advance(size_t widx, size_t v)
313void pcm_rec_error_clear(void)
314{ 350{
315 errors = warnings = 0; 351 widx = encbuf_add(widx, v);
316} /* pcm_rec_error_clear */ 352 encbuf_ptr(widx)->zero = 0;
353 enc_widx = widx;
354}
317 355
318/** 356/* Buffer pointer (p) plus size of chunk at (p), wrapped to (0) if
319 * Check mode, errors and warnings 357 * necessary.
320 */ 358 *
321unsigned int pcm_rec_status(void) 359 * pout points to variable to receive increment result
360 *
361 * Returns NULL if it was a wrap marker */
362static void * encbuf_read_ptr_incr(size_t p, size_t *pout)
322{ 363{
323 unsigned int ret = 0; 364 union enc_chunk_hdr *hdr = encbuf_ptr(p);
365 size_t v;
324 366
325 if (is_recording) 367 switch (hdr->type)
326 ret |= AUDIO_STATUS_RECORD; 368 {
327 else if (pre_record_ticks) 369 case CHUNK_T_DATA:
328 ret |= AUDIO_STATUS_PRERECORD; 370 v = CHUNK_DATA_COUNT(hdr->size);
371 break;
372 case CHUNK_T_STREAM_START:
373 v = hdr->size;
374 break;
375 case CHUNK_T_STREAM_END:
376 default:
377 v = 1;
378 break;
379 case CHUNK_T_WRAP:
380 /* Wrap markers are not returned but caller may have to know that
381 the index was changed since it impacts available space */
382 *pout = 0;
383 return NULL;
384 }
329 385
330 if (is_paused) 386 *pout = encbuf_add(p, v);
331 ret |= AUDIO_STATUS_PAUSE; 387 return hdr;
388}
332 389
333 if (errors) 390/* Buffer pointer (p) of contiguous free space (v), wrapped to (0) if
334 ret |= AUDIO_STATUS_ERROR; 391 * necessary.
392 *
393 * pout points to variable to receive possible-adjusted p
394 *
395 * Returns header at (p) or wrapped header at (0) if wrap was
396 * required in order to provide contiguous space. Header is zero-
397 * initialized.
398 *
399 * Marks the wrap point if a wrap is required to make the allocation. */
400static void * encbuf_get_write_ptr(size_t p, size_t v, size_t *pout)
401{
402 union enc_chunk_hdr *hdr = encbuf_ptr(p);
335 403
336 if (warnings) 404 if (p + v > enc_buflen)
337 ret |= AUDIO_STATUS_WARNING; 405 {
406 hdr->type = CHUNK_T_WRAP; /* All other fields ignored */
407 p = 0;
408 hdr = encbuf_ptr(0);
409 }
338 410
339 return ret; 411 *pout = p;
340} /* pcm_rec_status */ 412 hdr->zero = 0;
413 return hdr;
414}
341 415
342/** 416/* Post a flush request to audio thread, if none is currently queued */
343 * Return warnings that have occured since recording started 417static void encbuf_request_flush(void)
344 */
345unsigned long pcm_rec_get_warnings(void)
346{ 418{
347 return warnings; 419 if (!queue_peek_ex(&audio_queue, NULL, 0,
420 &(const long [2]){ Q_AUDIO_RECORD_FLUSH,
421 Q_AUDIO_RECORD_FLUSH }))
422 queue_post(&audio_queue, Q_AUDIO_RECORD_FLUSH, 0);
348} 423}
349 424
350#if 0 425/* Set the error bits in (e): no lock */
351int pcm_rec_current_bitrate(void) 426static inline void set_error_bits(uint32_t e)
352{ 427{
353 if (accum_pcm_samples == 0) 428 errors |= e;
354 return 0; 429 rec_errors |= e;
355 430}
356 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
357} /* pcm_rec_current_bitrate */
358#endif
359 431
360#if 0 432/* Clear the error bits in (e): no lock */
361int pcm_rec_encoder_afmt(void) 433static inline void clear_error_bits(uint32_t e)
362{ 434{
363 return enc_config.afmt; 435 errors &= ~e;
364} /* pcm_rec_encoder_afmt */ 436}
365#endif
366 437
367#if 0 438/* Set the error bits in (e) */
368int pcm_rec_rec_format(void) 439static void raise_error_status(uint32_t e)
369{ 440{
370 return afmt_rec_format[enc_config.afmt]; 441 pcm_rec_lock();
371} /* pcm_rec_rec_format */ 442 set_error_bits(e);
372#endif 443 pcm_rec_unlock();
444}
373 445
374#ifdef HAVE_SPDIF_IN 446/* Clear the error bits in (e) */
375unsigned long pcm_rec_sample_rate(void) 447static void clear_error_status(uint32_t e)
376{ 448{
377 /* Which is better ?? */ 449 pcm_rec_lock();
378#if 0 450 clear_error_bits(e);
379 return enc_sample_rate; 451 pcm_rec_unlock();
380#endif 452}
381 return sample_rate;
382} /* audio_get_sample_rate */
383#endif
384
385/** audio_* group **/
386 453
387/** 454/* Set the warning bits in (w): no lock */
388 * Initializes recording - call before calling any other recording function 455static inline void set_warning_bits(uint32_t w)
389 */
390void audio_init_recording(void)
391{ 456{
392 logf("audio_init_recording"); 457 warnings |= w;
393 queue_send(&audio_queue, Q_AUDIO_INIT_RECORDING, 1); 458}
394 logf("audio_init_recording done");
395} /* audio_init_recording */
396 459
397/** 460/* Clear the warning bits in (w): no lock */
398 * Closes recording - call audio_stop_recording first 461static inline void clear_warning_bits(uint32_t w)
399 */
400void audio_close_recording(void)
401{ 462{
402 logf("audio_close_recording"); 463 warnings &= ~w;
403 queue_send(&audio_queue, Q_AUDIO_CLOSE_RECORDING, 0); 464}
404 logf("audio_close_recording done");
405} /* audio_close_recording */
406 465
407/** 466/* Set the warning bits in (w) */
408 * Sets recording parameters 467static void raise_warning_status(uint32_t w)
409 */
410void audio_set_recording_options(struct audio_recording_options *options)
411{ 468{
412 logf("audio_set_recording_options"); 469 pcm_rec_lock();
413 queue_send(&audio_queue, Q_AUDIO_RECORDING_OPTIONS, (intptr_t)options); 470 set_warning_bits(w);
414 logf("audio_set_recording_options done"); 471 pcm_rec_unlock();
415} /* audio_set_recording_options */ 472}
416 473
417/** 474/* Clear the warning bits in (w) */
418 * Start recording if not recording or else split 475static void clear_warning_status(uint32_t w)
419 */
420void audio_record(const char *filename)
421{ 476{
422 logf("audio_record: %s", filename); 477 pcm_rec_lock();
423 flush_interrupt(); 478 clear_warning_bits(w);
424 queue_send(&audio_queue, Q_AUDIO_RECORD, (intptr_t)filename); 479 pcm_rec_unlock();
425 logf("audio_record_done"); 480}
426} /* audio_record */
427 481
428/** 482/* Callback for when more data is ready - called by DMA ISR */
429 * audio_record wrapper for API compatibility with HW codec 483static void pcm_rec_have_more(void **start, size_t *size)
430 */
431void audio_new_file(const char *filename)
432{ 484{
433 audio_record(filename); 485 size_t next_idx = pcm_widx;
434} /* audio_new_file */
435 486
436/** 487 if (!pcm_pause)
437 * Stop current recording if recording 488 {
438 */ 489 /* One empty chunk must remain after widx is advanced */
439void audio_stop_recording(void) 490 if (pcmbuf_used() <= PCM_BUF_SIZE - 2*PCM_CHUNK_SIZE)
440{ 491 next_idx = pcmbuf_add(next_idx, PCM_CHUNK_SIZE);
441 logf("audio_stop_recording"); 492 else
442 flush_interrupt(); 493 set_warning_bits(PCMREC_W_PCM_BUFFER_OVF);
443 queue_post(&audio_queue, Q_AUDIO_STOP, 0); 494 }
444 logf("audio_stop_recording done");
445} /* audio_stop_recording */
446 495
447/** 496 *start = pcmbuf_ptr(next_idx);
448 * Pause current recording 497 *size = PCM_CHUNK_SIZE;
449 */
450void audio_pause_recording(void)
451{
452 logf("audio_pause_recording");
453 flush_interrupt();
454 queue_post(&audio_queue, Q_AUDIO_PAUSE, 0);
455 logf("audio_pause_recording done");
456} /* audio_pause_recording */
457 498
458/** 499 pcm_widx = next_idx;
459 * Resume current recording if paused 500}
460 */
461void audio_resume_recording(void)
462{
463 logf("audio_resume_recording");
464 queue_post(&audio_queue, Q_AUDIO_RESUME, 0);
465 logf("audio_resume_recording done");
466} /* audio_resume_recording */
467 501
468/** 502static enum pcm_dma_status pcm_rec_status_callback(enum pcm_dma_status status)
469 * Note that microphone is mono, only left value is used
470 * See audiohw_set_recvol() for exact ranges.
471 *
472 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
473 *
474 */
475void audio_set_recording_gain(int left, int right, int type)
476{ 503{
477 //logf("rcmrec: t=%d l=%d r=%d", type, left, right); 504 if (status < PCM_DMAST_OK)
478 audiohw_set_recvol(left, right, type); 505 {
479} /* audio_set_recording_gain */ 506 /* Some error condition */
507 if (status == PCM_DMAST_ERR_DMA)
508 {
509 set_error_bits(PCMREC_E_DMA);
510 return status;
511 }
512 else
513 {
514 /* Try again next transmission - frame is invalid */
515 set_warning_bits(PCMREC_W_DMA);
516 }
517 }
480 518
481/** Information about current state **/ 519 return PCM_DMAST_OK;
520}
482 521
483/** 522/* Start DMA transfer */
484 * Return current recorded time in ticks (playback eqivalent time) 523static void pcm_start_recording(void)
485 */
486unsigned long audio_recorded_time(void)
487{ 524{
488 if (!is_recording || enc_sample_rate == 0) 525 pcm_record_data(pcm_rec_have_more, pcm_rec_status_callback,
489 return 0; 526 pcmbuf_ptr(pcm_widx), PCM_CHUNK_SIZE);
527}
490 528
491 /* return actual recorded time a la encoded data even if encoder rate 529/* Initialize the various recording buffers */
492 doesn't match the pcm rate */ 530static void init_rec_buffers(void)
493 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate); 531{
494} /* audio_recorded_time */ 532 /* Layout of recording buffer: |PCMBUF|STREAMBUF|FILENAME|ENCBUF| */
533 void *buf = rec_buffer;
534 size_t size = rec_buffer_size;
535
536 /* PCMBUF */
537 pcm_buffer = CACHEALIGN_UP(buf); /* Line align */
538 size -= pcm_buffer + PCM_BUF_SIZE - buf;
539 buf = pcm_buffer + PCM_BUF_SIZE;
540
541 /* STREAMBUF */
542 stream_buffer = buf; /* Also line-aligned */
543 buf += STREAM_BUF_SIZE;
544 size -= STREAM_BUF_SIZE;
545
546 /* FILENAME */
547 fname_buf = buf;
548 buf += CHUNK_FILE_COUNT(MAX_PATH)*ENC_HDR_SIZE;
549 size -= CHUNK_FILE_COUNT(MAX_PATH)*ENC_HDR_SIZE;
550 fname_buf->hdr.zero = 0;
551
552 /* ENCBUF */
553 enc_buffer = buf;
554 enc_buflen = size;
555 ALIGN_BUFFER(enc_buffer, enc_buflen, ENC_HDR_SIZE);
556 enc_buflen = CHUNK_SIZE_COUNT(enc_buflen);
557}
495 558
496/** 559/* Reset the circular buffers */
497 * Return number of bytes encoded to output 560static void reset_fifos(bool hard)
498 */
499unsigned long audio_num_recorded_bytes(void)
500{ 561{
501 if (!is_recording) 562 /* PCM FIFO */
502 return 0; 563 pcm_pause = true;
503 564
504 return num_rec_bytes; 565 if (hard)
505} /* audio_num_recorded_bytes */ 566 pcm_widx = 0; /* Don't just empty but reset it */
506 567
507/***************************************************************************/ 568 pcm_ridx = pcm_widx;
508/* */
509/* Functions that execute in the context of audio thread */
510/* */
511/***************************************************************************/
512 569
513static void pcmrec_init_state(void) 570 /* Encoder FIFO */
514{ 571 encbuf_widx_advance(0, 0);
515 flush_interrupts = 0; 572 enc_ridx = 0;
516 573
517 /* warings and errors */ 574 /* No overflow-related warnings now */
518 warnings = 575 clear_warning_status(PCMREC_W_PCM_BUFFER_OVF | PCMREC_W_ENC_BUFFER_OVF);
519 errors = 0; 576}
520 577
521 /* pcm FIFO */ 578/* Initialize file statistics */
522 dma_lock = true; 579static void reset_rec_stats(void)
523 pcm_rd_pos = 0; 580{
524 dma_wr_pos = 0; 581 num_rec_bytes = 0;
525 pcm_enc_pos = 0; 582 num_rec_samples = 0;
583 encbuf_rec_count = 0;
584 clear_warning_status(PCMREC_W_FILE_SIZE);
585}
526 586
527 /* encoder FIFO */ 587/* Boost or unboost recording threads' priorities */
528 enc_wr_index = 0; 588static void do_prio_boost(bool boost)
529 enc_rd_index = 0; 589{
590#ifdef HAVE_PRIORITY_SCHEDULING
591 prio_boosted = boost;
530 592
531 /* filename queue */ 593 int prio = PRIORITY_RECORDING;
532 fnq_rd_pos = 0;
533 fnq_wr_pos = 0;
534 594
535 /* stats */ 595 if (boost)
536 num_rec_bytes = 0; 596 prio -= 4;
537 num_rec_samples = 0; 597
538#if 0 598 codec_thread_set_priority(prio);
539 accum_rec_bytes = 0; 599 thread_set_priority(audio_thread_id, prio);
540 accum_pcm_samples = 0;
541#endif 600#endif
601 (void)boost;
602}
603
604/* Reset all relevant state */
605static void init_state(void)
606{
607 reset_fifos(true);
608 reset_rec_stats();
609 do_prio_boost(false);
610 cancel_cpu_boost();
611 record_state = REC_STATE_IDLE;
612 record_status = RECORD_STOPPED;
613}
542 614
543 pre_record_ticks = 0; 615/* Set hardware samplerate and save it */
616static void update_samplerate_config(unsigned long sampr)
617{
618 /* PCM samplerate is either the same as the setting or the nearest
619 one hardware supports if using S/PDIF */
620 unsigned long pcm_sampr = sampr;
544 621
545 is_recording = false; 622#ifdef HAVE_SPDIF_IN
546 is_paused = false; 623 if (rec_source == AUDIO_SRC_SPDIF)
547} /* pcmrec_init_state */ 624 {
625 int index = round_value_to_list32(sampr, hw_freq_sampr,
626 HW_NUM_FREQ, false);
627 pcm_sampr = hw_freq_sampr[index];
628 }
629#endif /* HAVE_SPDIF_IN */
548 630
549/** Filename Queue **/ 631 pcm_set_frequency(pcm_sampr | SAMPR_TYPE_REC);
632 sample_rate = sampr;
633}
550 634
551/* returns true if the queue is empty */ 635/* Calculate the average data rate */
552static inline bool pcmrec_fnq_is_empty(void) 636static unsigned long get_encbuf_datarate(void)
553{ 637{
554 return fnq_rd_pos == fnq_wr_pos; 638 /* If not yet calculable, start with uncompressed PCM byterate */
555} /* pcmrec_fnq_is_empty */ 639 if (num_rec_samples && sample_rate && encbuf_rec_count)
640 {
641 return (encbuf_rec_count*sample_rate + num_rec_samples - 1)
642 / num_rec_samples;
643 }
644 else
645 {
646 return CHUNK_SIZE_COUNT(sample_rate*num_channels*PCM_DEPTH_BYTES);
647 }
648}
556 649
557/* empties the filename queue */ 650/* Returns true if the watermarks should be updated due to data rate
558static inline void pcmrec_fnq_set_empty(void) 651 change */
652static bool monitor_encbuf_datarate(void)
559{ 653{
560 fnq_rd_pos = fnq_wr_pos; 654 unsigned long rate = get_encbuf_datarate();
561} /* pcmrec_fnq_set_empty */ 655 long diff = rate - encbuf_datarate;
656 /* Off by more than 1/2 FLUSH_MON_INTERVAL? */
657 return 2*(unsigned long)abs(diff) > encbuf_datarate*FLUSH_MON_INTERVAL;
658}
562 659
563/* returns true if the queue is full */ 660/* Get adjusted spinup time */
564static bool pcmrec_fnq_is_full(void) 661static int get_spinup_time(void)
565{ 662{
566 ssize_t size = fnq_wr_pos - fnq_rd_pos; 663 int spin = storage_spinup_time();
567 if (size < 0)
568 size += fnq_size;
569 664
570 return size >= fnq_size - MAX_PATH; 665#if (CONFIG_STORAGE & STORAGE_ATA)
571} /* pcmrec_fnq_is_full */ 666 /* Write at FLUSH_SECONDS + st remaining in enc_buffer - range fs+2s to
667 fs+10s total - default to 3.5s spinup. */
668 if (spin == 0)
669 spin = 35*HZ/10; /* default - cozy */
670 else if (spin < 2*HZ)
671 spin = 2*HZ; /* ludicrous - ramdisk? */
672 else if (spin > 10*HZ)
673 spin = 10*HZ; /* do you have a functioning HD? */
674#endif /* (CONFIG_STORAGE & STORAGE_ATA) */
675
676 return spin;
677}
572 678
573/* queue another filename - will overwrite oldest one if full */ 679/* Returns true if the watermarks should be updated due to spinup time
574static bool pcmrec_fnq_add_filename(const char *filename) 680 change */
681static inline bool monitor_spinup_time(void)
575{ 682{
576 strlcpy(fn_queue + fnq_wr_pos, filename, MAX_PATH); 683#if (CONFIG_STORAGE & STORAGE_ATA)
577 fnq_wr_pos = FNQ_NEXT(fnq_wr_pos); 684 return get_spinup_time() != spinup_time;
685#else
686 return false;
687#endif
688}
578 689
579 if (fnq_rd_pos != fnq_wr_pos) 690/* Update buffer watermarks with spinup time compensation */
580 return true; 691static void refresh_watermarks(void)
692{
693 int spin = get_spinup_time();
694#if (CONFIG_STORAGE & STORAGE_ATA)
695 logf("ata spinup: %d", spin);
696 spinup_time = spin;
697#endif
581 698
582 /* queue full */ 699 unsigned long rate = get_encbuf_datarate();
583 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos); 700 logf("byterate: %lu", rate * ENC_HDR_SIZE);
584 return true; 701 encbuf_datarate = rate;
585} /* pcmrec_fnq_add_filename */
586 702
587/* replace the last filename added */ 703 /* Try to start writing with FLUSH_SECONDS remaining after disk spinup */
588static bool pcmrec_fnq_replace_tail(const char *filename) 704 high_watermark = (uint64_t)rate*(FLUSH_SECONDS*HZ + spin) / HZ;
589{
590 int pos;
591 705
592 if (pcmrec_fnq_is_empty()) 706 if (high_watermark > enc_buflen)
593 return false; 707 high_watermark = enc_buflen;
594 708
595 pos = FNQ_PREV(fnq_wr_pos); 709 high_watermark = enc_buflen - high_watermark;
596 710
597 strlcpy(fn_queue + pos, filename, MAX_PATH); 711 logf("high wm: %lu", (unsigned long)high_watermark);
598 712
599 return true; 713#ifdef HAVE_PRIORITY_SCHEDULING
600} /* pcmrec_fnq_replace_tail */ 714 /* Boost thread priority if enough ground is lost since flushing started
715 or is taking an unreasonably long time */
716 flood_watermark = rate*PANIC_SECONDS;
601 717
602/* pulls the next filename from the queue */ 718 if (flood_watermark > enc_buflen)
603static bool pcmrec_fnq_get_filename(char *filename) 719 flood_watermark = enc_buflen;
604{
605 if (pcmrec_fnq_is_empty())
606 return false;
607 720
608 if (filename) 721 flood_watermark = enc_buflen - flood_watermark;
609 strlcpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
610 722
611 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos); 723 logf("flood wm: %lu", (unsigned long)flood_watermark);
612 return true; 724#endif /* HAVE_PRIORITY_SCHEDULING */
613} /* pcmrec_fnq_get_filename */ 725}
614 726
615/* close the file number pointed to by fd_p */ 727/* Tell encoder the stream parameters and get information back */
616static void pcmrec_close_file(int *fd_p) 728static bool configure_encoder_stream(void)
617{ 729{
618 if (*fd_p < 0) 730 struct enc_inputs inputs;
619 return; /* preserve error */ 731 inputs.sample_rate = sample_rate;
732 inputs.num_channels = num_channels;
733 inputs.config = &enc_config;
620 734
621 if (close(*fd_p) != 0) 735 /* encoder can change these - init with defaults */
622 pcmrec_raise_error_status(PCMREC_E_IO); 736 inputs.enc_sample_rate = sample_rate;
623 737
624 *fd_p = -1; 738 if (enc_cb(ENC_CB_INPUTS, &inputs) < 0)
625} /* pcmrec_close_file */ 739 {
740 raise_error_status(PCMREC_E_ENC_SETUP);
741 return false;
742 }
626 743
627/** Data Flushing **/ 744 enc_sample_rate = inputs.enc_sample_rate;
628 745
629/** 746 if (enc_sample_rate != sample_rate)
630 * called after callback to update sizes if codec changed the amount of data
631 * a chunk represents
632 */
633static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
634 unsigned long prev_num_pcm)
635{
636 if (rec_fdata.new_enc_size != prev_enc_size)
637 { 747 {
638 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size; 748 /* Codec doesn't want to/can't use the setting and has chosen a
639 num_rec_bytes += size_diff; 749 different sample rate */
640#if 0 750 raise_warning_status(PCMREC_W_SAMPR_MISMATCH);
641 accum_rec_bytes += size_diff; 751 logf("enc sampr:%lu", enc_sample_rate);
642#endif
643 } 752 }
644 753 else
645 if (rec_fdata.new_num_pcm != prev_num_pcm)
646 { 754 {
647 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm; 755 clear_warning_status(PCMREC_W_SAMPR_MISMATCH);
648 num_rec_samples += pcm_diff;
649#if 0
650 accum_pcm_samples += pcm_diff;
651#endif
652 } 756 }
653} /* pcmrec_update_sizes_inl */
654 757
655/* don't need to inline every instance */ 758 refresh_watermarks();
656static void pcmrec_update_sizes(size_t prev_enc_size, 759 return true;
657 unsigned long prev_num_pcm) 760}
761
762#ifdef HAVE_SPDIF_IN
763/* Return the S/PDIF sample rate closest to a value in the master list */
764static unsigned long get_spdif_samplerate(void)
658{ 765{
659 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm); 766 unsigned long sr = spdif_measure_frequency();
660} /* pcmrec_update_sizes */ 767 int index = round_value_to_list32(sr, audio_master_sampr_list,
768 SAMPR_NUM_FREQ, false);
769 return audio_master_sampr_list[index];
770}
661 771
662static void pcmrec_start_file(void) 772/* Check the S/PDIF rate and compare to current setting. Apply the new
773 * rate if it changed. */
774static void check_spdif_samplerate(void)
663{ 775{
664 size_t enc_size = rec_fdata.new_enc_size; 776 unsigned long sampr = get_spdif_samplerate();
665 unsigned long num_pcm = rec_fdata.new_num_pcm;
666 int curr_rec_file = rec_fdata.rec_file;
667 char filename[MAX_PATH];
668 777
669 /* must always pull the filename that matches with this queue */ 778 if (sampr == sample_rate)
670 if (!pcmrec_fnq_get_filename(filename)) 779 return;
671 { 780
672 logf("start file: fnq empty"); 781 codec_stop();
673 *filename = '\0'; 782 pcm_stop_recording();
674 pcmrec_raise_error_status(PCMREC_E_FNQ_DESYNC); 783 reset_fifos(true);
675 } 784 reset_rec_stats();
676 else if (errors != 0) 785 update_samplerate_config(sampr);
677 { 786 pcm_apply_settings();
678 logf("start file: error already");
679 }
680 else if (curr_rec_file >= 0)
681 {
682 /* Any previous file should have been closed */
683 logf("start file: file already open");
684 pcmrec_raise_error_status(PCMREC_E_FNQ_DESYNC);
685 }
686 787
687 if (errors != 0) 788 if (!configure_encoder_stream() || rec_errors)
688 rec_fdata.chunk->flags |= CHUNKF_ERROR; 789 return;
689 790
690 /* encoder can set error flag here and should increase 791 pcm_start_recording();
691 enc_new_size and pcm_new_size to reflect additional
692 data written if any */
693 rec_fdata.filename = filename;
694 enc_events_callback(ENC_START_FILE, &rec_fdata);
695 792
696 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR)) 793 if (record_status == RECORD_PRERECORDING)
697 { 794 {
698 logf("start file: enc error"); 795 codec_go();
699 pcmrec_raise_error_status(PCMREC_E_ENCODER); 796 pcm_pause = false;
700 } 797 }
798}
799#endif /* HAVE_SPDIF_IN */
701 800
702 if (errors != 0) 801/* Discard the stream buffer contents */
802static inline void stream_discard_buf(void)
803{
804 stream_buf_used = 0;
805}
806
807/* Flush stream buffer to disk */
808static bool stream_flush_buf(void)
809{
810 if (stream_buf_used == 0)
811 return true;
812
813 ssize_t rc = write(rec_fd, stream_buffer, stream_buf_used);
814
815 if (LIKELY(rc == stream_buf_used))
703 { 816 {
704 pcmrec_close_file(&curr_rec_file); 817 stream_discard_buf();
705 /* Write no more to this file */ 818 return true;
706 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
707 } 819 }
708 else 820
821 if (rc > 0)
709 { 822 {
710 pcmrec_update_sizes(enc_size, num_pcm); 823 /* Some was written; keep in sync */
824 stream_buf_used -= rc;
825 memmove(stream_buffer, stream_buffer + rc, stream_buf_used);
711 } 826 }
712 827
713 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE; 828 return false;
714} /* pcmrec_start_file */ 829}
715 830
716static inline void pcmrec_write_chunk(void) 831/* Close the output file */
832static void close_rec_file(void)
717{ 833{
718 size_t enc_size = rec_fdata.new_enc_size; 834 if (rec_fd < 0)
719 unsigned long num_pcm = rec_fdata.new_num_pcm; 835 return;
836
837 bool ok = stream_flush_buf();
720 838
721 if (errors != 0) 839 if (close(rec_fd) != 0 || !ok)
722 rec_fdata.chunk->flags |= CHUNKF_ERROR; 840 raise_error_status(PCMREC_E_IO);
723 841
724 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata); 842 rec_fd = -1;
843}
725 844
726 if ((long)rec_fdata.chunk->flags >= 0) 845/* Creates or opens the current path */
846static bool open_rec_file(bool create)
847{
848 if (rec_fd >= 0)
727 { 849 {
728 pcmrec_update_sizes_inl(enc_size, num_pcm); 850 /* Any previous file should have been closed */
851 logf("open file: file already open");
852 close_rec_file();
729 } 853 }
730 else if (errors == 0) 854
855 stream_discard_buf();
856 int oflags = create ? O_CREAT|O_TRUNC : 0;
857 rec_fd = open(fname_buf->path, O_RDWR|oflags, 0666);
858
859 if (rec_fd < 0)
731 { 860 {
732 logf("wr chk enc error %lu %lu", 861 raise_error_status(PCMREC_E_IO);
733 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm); 862 return false;
734 pcmrec_raise_error_status(PCMREC_E_ENCODER);
735 } 863 }
736} /* pcmrec_write_chunk */
737 864
738static void pcmrec_end_file(void) 865 return true;
866}
867
868/* Copy with mono conversion - output 1/2 size of input */
869static void * ICODE_ATTR
870copy_buffer_mono_lr(void *dst, const void *src, size_t src_size)
739{ 871{
740 /* all data in output buffer for current file will have been 872 int16_t *d = dst;
741 written and encoder can now do any nescessary steps to 873 int16_t const *s = src;
742 finalize the written file */
743 size_t enc_size = rec_fdata.new_enc_size;
744 unsigned long num_pcm = rec_fdata.new_num_pcm;
745 874
746 enc_events_callback(ENC_END_FILE, &rec_fdata); 875 /* mono = (L + R) / 2 */
876 do
877 *d++ = ((int32_t){ *s++ } + *s++ + 1) >> 1;
878 while (src_size -= PCM_SAMP_SIZE);
747 879
748 if (errors == 0) 880 return dst;
749 { 881}
750 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
751 {
752 logf("end file: enc error");
753 pcmrec_raise_error_status(PCMREC_E_ENCODER);
754 }
755 else
756 {
757 pcmrec_update_sizes(enc_size, num_pcm);
758 }
759 }
760 882
761 /* Force file close if error */ 883/* Copy with mono conversion - output 1/2 size of input */
762 if (errors != 0) 884static void * ICODE_ATTR
763 pcmrec_close_file(&rec_fdata.rec_file); 885copy_buffer_mono_l(void *dst, const void *src, size_t src_size)
886{
887 int16_t *d = dst;
888 int16_t const *s = (int16_t *)src - 2;
764 889
765 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE; 890 /* mono = L */
766} /* pcmrec_end_file */ 891 do
892 *d++ = *(s += 2);
893 while (src_size -= PCM_SAMP_SIZE);
767 894
768/** 895 return dst;
769 * Update buffer watermarks with spinup time compensation 896}
770 * 897
771 * All this assumes reasonable data rates, chunk sizes and sufficient 898/* Copy with mono conversion - output 1/2 size of input */
772 * memory for the most part. Some dumb checks are included but perhaps 899static void * ICODE_ATTR
773 * are pointless since this all will break down at extreme limits that 900copy_buffer_mono_r(void *dst, const void *src, size_t src_size)
774 * are currently not applicable to any supported device.
775 */
776static void pcmrec_refresh_watermarks(void)
777{ 901{
778 logf("ata spinup: %d", storage_spinup_time()); 902 int16_t *d = dst;
903 int16_t const *s = (int16_t *)src - 1;
779 904
780 /* set the low mark for when flushing stops if automatic */ 905 /* mono = R */
781 /* don't change the order in this expression, LOW_SECONDS can be an 906 do
782 * integer fraction of 4 */ 907 *d++ = *(s += 2);
783 low_watermark = (sample_rate*4*LOW_SECONDS + (enc_chunk_size-1)) 908 while (src_size -= PCM_SAMP_SIZE);
784 / enc_chunk_size;
785 logf("low wmk: %d", low_watermark);
786 909
787#ifdef HAVE_PRIORITY_SCHEDULING 910 return dst;
788 /* panic boost thread priority if 2 seconds of ground is lost - 911}
789 this allows encoder to boost with just under a second of
790 pcm data (if not yet full enough to boost itself)
791 and not falsely trip the alarm. */
792 /* don't change the order in this expression, PANIC_SECONDS can be an
793 * integer fraction of 4 */
794 flood_watermark = enc_num_chunks -
795 (sample_rate*4*PANIC_SECONDS + (enc_chunk_size-1))
796 / enc_chunk_size;
797
798 if (flood_watermark < low_watermark)
799 {
800 logf("warning: panic < low");
801 flood_watermark = low_watermark;
802 }
803 912
804 logf("flood at: %d", flood_watermark); 913
805#endif 914/** pcm_rec_* group **/
806 spinup_time = last_storage_spinup_time = storage_spinup_time(); 915
807#if (CONFIG_STORAGE & STORAGE_ATA) 916/* Clear all errors and warnings */
808 /* write at 8s + st remaining in enc_buffer - range 12s to 917void pcm_rec_error_clear(void)
809 20s total - default to 3.5s spinup. */ 918{
810 if (spinup_time == 0) 919 clear_error_status(PCMREC_E_ALL);
811 spinup_time = 35*HZ/10; /* default - cozy */ 920 clear_warning_status(PCMREC_W_ALL);
812 else if (spinup_time < 2*HZ) 921}
813 spinup_time = 2*HZ; /* ludicrous - ramdisk? */ 922
814 else if (spinup_time > 10*HZ) 923/* Check mode, errors and warnings */
815 spinup_time = 10*HZ; /* do you have a functioning HD? */ 924unsigned int pcm_rec_status(void)
925{
926 unsigned int ret = record_status;
927
928 if (errors)
929 ret |= AUDIO_STATUS_ERROR;
930
931 if (warnings)
932 ret |= AUDIO_STATUS_WARNING;
933
934 return ret;
935}
936
937/* Return warnings that have occured since recording started */
938uint32_t pcm_rec_get_warnings(void)
939{
940 return warnings;
941}
942
943#ifdef HAVE_SPDIF_IN
944/* Return the currently-configured sample rate */
945unsigned long pcm_rec_sample_rate(void)
946{
947 return sample_rate;
948}
816#endif 949#endif
817 950
818 /* try to start writing with 10s remaining after disk spinup */
819 high_watermark = enc_num_chunks -
820 ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate +
821 (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ);
822 951
823 if (high_watermark < low_watermark) 952/** audio_* group **/
824 { 953
825 logf("warning: low 'write at' (%d)", high_watermark); 954/* Initializes recording - call before calling any other recording function */
826 high_watermark = low_watermark; 955void audio_init_recording(void)
827 low_watermark /= 2; 956{
828 } 957 LOGFQUEUE("audio >| pcmrec Q_AUDIO_INIT_RECORDING");
958 audio_queue_send(Q_AUDIO_INIT_RECORDING, 1);
959}
829 960
830 logf("write at: %d", high_watermark); 961/* Closes recording - call audio_stop_recording first or risk data loss */
831} /* pcmrec_refresh_watermarks */ 962void audio_close_recording(void)
963{
964 LOGFQUEUE("audio >| pcmrec Q_AUDIO_CLOSE_RECORDING");
965 audio_queue_send(Q_AUDIO_CLOSE_RECORDING, 0);
966}
832 967
833/** 968/* Sets recording parameters */
834 * Process the chunks 969void audio_set_recording_options(struct audio_recording_options *options)
835 *
836 * This function is called when queue_get_w_tmo times out.
837 *
838 * Set flush_num to the number of files to flush to disk or to
839 * a PCMREC_FLUSH_* constant.
840 */
841static void pcmrec_flush(unsigned flush_num)
842{ 970{
843#ifdef HAVE_PRIORITY_SCHEDULING 971 LOGFQUEUE("audio >| pcmrec Q_AUDIO_RECORDING_OPTIONS");
844 static unsigned long last_flush_tick; /* tick when function returned */ 972 audio_queue_send(Q_AUDIO_RECORDING_OPTIONS, (intptr_t)options);
845 unsigned long start_tick; /* When flush started */ 973}
846 unsigned long prio_tick; /* Timeout for auto boost */ 974
847 int prio_pcmrec; /* Current thread priority for pcmrec */ 975/* Start recording if not recording or else split */
848 int prio_codec; /* Current thread priority for codec */ 976void audio_record(const char *filename)
977{
978 LOGFQUEUE("audio >| pcmrec Q_AUDIO_RECORD: %s", filename);
979 audio_queue_send(Q_AUDIO_RECORD, (intptr_t)filename);
980}
981
982/* audio_record alias for API compatibility with HW codec */
983void audio_new_file(const char *filename)
984 __attribute__((alias("audio_record")));
985
986/* Stop current recording if recording */
987void audio_stop_recording(void)
988{
989 LOGFQUEUE("audio > pcmrec Q_AUDIO_RECORD_STOP");
990 audio_queue_post(Q_AUDIO_RECORD_STOP, 0);
991}
992
993/* Pause current recording */
994void audio_pause_recording(void)
995{
996 LOGFQUEUE("audio > pcmrec Q_AUDIO_RECORD_PAUSE");
997 audio_queue_post(Q_AUDIO_RECORD_PAUSE, 0);
998}
999
1000/* Resume current recording if paused */
1001void audio_resume_recording(void)
1002{
1003 LOGFQUEUE("audio > pcmrec Q_AUDIO_RECORD_RESUME");
1004 audio_queue_post(Q_AUDIO_RECORD_RESUME, 0);
1005}
1006
1007/* Set the input source gain. For mono sources, only left gain is used */
1008void audio_set_recording_gain(int left, int right, int type)
1009{
1010#if 0
1011 logf("pcmrec: t=%d l=%d r=%d", type, left, right);
849#endif 1012#endif
850 int num_ready; /* Number of chunks ready at start */ 1013 audiohw_set_recvol(left, right, type);
851 unsigned remaining; /* Number of file starts remaining */ 1014}
852 unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */
853 bool interruptable; /* Flush can be interupted */
854 1015
855 num_ready = enc_wr_index - enc_rd_index;
856 if (num_ready < 0)
857 num_ready += enc_num_chunks;
858 1016
859 /* save interruptable flag and remove it to get the actual count */ 1017/** Information about current state **/
860 interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0; 1018
861 flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE; 1019/* Return sample clock in HZ */
1020static unsigned long get_samples_time(void)
1021{
1022 if (enc_sample_rate == 0)
1023 return 0;
1024
1025 return (unsigned long)(HZ*num_rec_samples / enc_sample_rate);
1026}
862 1027
863 if (flush_num == 0) 1028/* Return current prerecorded time in ticks (playback equivalent time) */
1029unsigned long audio_prerecorded_time(void)
1030{
1031 if (record_status != RECORD_PRERECORDING)
1032 return 0;
1033
1034 unsigned long t = get_samples_time();
1035 return MIN(t, pre_record_seconds*HZ);
1036}
1037
1038/* Return current recorded time in ticks (playback equivalent time) */
1039unsigned long audio_recorded_time(void)
1040{
1041 if (record_state == REC_STATE_IDLE)
1042 return 0;
1043
1044 return get_samples_time();
1045}
1046
1047/* Return number of bytes encoded to output */
1048unsigned long audio_num_recorded_bytes(void)
1049{
1050 if (record_state == REC_STATE_IDLE)
1051 return 0;
1052
1053 return num_rec_bytes;
1054}
1055
1056
1057/** Data Flushing **/
1058
1059/* Stream start chunk with path was encountered */
1060static void flush_stream_start(struct enc_chunk_file *file)
1061{
1062 /* Save filename; don't open file here which avoids creating files
1063 with no audio content. Splitting while paused can create those
1064 in large numbers. */
1065 fname_buf->hdr = file->hdr;
1066 /* Correct size if this was wrap-padded */
1067 fname_buf->hdr.size = CHUNK_FILE_COUNT(
1068 strlcpy(fname_buf->path, file->path, MAX_PATH) + 1);
1069}
1070
1071/* Data chunk was encountered */
1072static bool flush_stream_data(struct enc_chunk_data *data)
1073{
1074 if (fname_buf->hdr.zero)
864 { 1075 {
865 if (!is_recording) 1076 /* First data chunk; create the file */
866 return; 1077 if (open_rec_file(true))
1078 {
1079 /* Inherit some flags from initial data chunk */
1080 fname_buf->hdr.err = data->hdr.err;
1081 fname_buf->hdr.pre = data->hdr.pre;
1082 fname_buf->hdr.aux0 = data->hdr.aux0;
867 1083
868 if (storage_spinup_time() != last_storage_spinup_time) 1084 if (enc_cb(ENC_CB_STREAM, fname_buf) < 0)
869 pcmrec_refresh_watermarks(); 1085 raise_error_status(PCMREC_E_ENCODER_STREAM);
1086 }
870 1087
871 /* enough available? no? then leave */ 1088 fname_buf->hdr.zero = 0;
872 if (num_ready < high_watermark)
873 return;
874 } /* endif (flush_num == 0) */
875 1089
876#ifdef HAVE_PRIORITY_SCHEDULING 1090 if (rec_errors)
877 start_tick = current_tick; 1091 return false;
878 prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time; 1092 }
1093
1094 if (rec_fd < 0)
1095 return true; /* Just keep discarding */
879 1096
880 if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2)) 1097 if (enc_cb(ENC_CB_STREAM, data) < 0)
881 { 1098 {
882 /* if we're getting called too much and this isn't forced, 1099 raise_error_status(PCMREC_E_ENCODER_STREAM);
883 boost stat by expiring timeout in advance */ 1100 return false;
884 logf("too frequent flush");
885 prio_tick = current_tick - 1;
886 } 1101 }
887 1102
888 prio_pcmrec = -1; 1103 return true;
889 prio_codec = -1; /* GCC is too stoopid to figure out it doesn't 1104}
890 need init */
891#endif
892 1105
893 logf("writing:%d(%d):%s%s", num_ready, flush_num, 1106/* Stream end chunk was encountered */
894 interruptable ? "i" : "", 1107static bool flush_stream_end(union enc_chunk_hdr *hdr)
895 flush_num == PCMREC_FLUSH_MINI ? "m" : ""); 1108{
1109 if (rec_fd < 0)
1110 return true;
896 1111
897 cpu_boost(true); 1112 if (enc_cb(ENC_CB_STREAM, hdr) < 0)
1113 {
1114 raise_error_status(PCMREC_E_ENCODER_STREAM);
1115 return false;
1116 }
898 1117
899 remaining = flush_num; 1118 close_rec_file();
900 chunks_flushed = 0; 1119 return true;
1120}
901 1121
902 while (num_ready > 0) 1122/* Discard remainder of stream in encoder buffer */
1123static void discard_stream(void)
1124{
1125 /* Discard everything up until the next non-data chunk */
1126 while (!encbuf_empty())
903 { 1127 {
904 /* check current number of encoder chunks */ 1128 size_t ridx;
905 int num = enc_wr_index - enc_rd_index; 1129 union enc_chunk_hdr *hdr = encbuf_read_ptr_incr(enc_ridx, &ridx);
906 if (num < 0)
907 num += enc_num_chunks;
908 1130
909 if (num <= low_watermark && 1131 if (hdr && hdr->type != CHUNK_T_DATA)
910 (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0))
911 { 1132 {
912 logf("low data: %d", num); 1133 if (hdr->type != CHUNK_T_STREAM_START)
913 break; /* data remaining is below threshold */ 1134 enc_ridx = ridx;
1135 break;
914 } 1136 }
915 1137
916 if (interruptable && flush_interrupts > 0) 1138 enc_ridx = ridx;
917 { 1139 }
918 logf("int at: %d", num);
919 break; /* interrupted */
920 }
921 1140
1141 /* Try to finish header by closing and reopening the file. A seek or
1142 other operation will likely fail because buffers will need to be
1143 flushed (here and in file code). That will likely fail but a close
1144 will just close the fd and discard everything. We reopen with what
1145 actually made it to disk. Modifying existing file contents will
1146 more than likely succeed even on a full disk. The result might not
1147 be entirely correct as far as the headers' sizes and counts unless
1148 the codec can correct that but the sample format information
1149 should be. */
1150 if (rec_fd >= 0 && open_rec_file(false))
1151 {
1152 /* Synthesize a special end chunk here */
1153 union enc_chunk_hdr end;
1154 end.zero = 0;
1155 end.err = 1; /* Codec should try to correct anything that's off */
1156 end.type = CHUNK_T_STREAM_END;
1157 if (!flush_stream_end(&end))
1158 close_rec_file();
1159 }
1160}
1161
1162/* Flush a chunk to disk
1163 *
1164 * Transitions state from REC_STATE_MONITOR to REC_STATE_FLUSH when buffer
1165 * is filling. 'margin' is fullness threshold that transitions to flush state.
1166 *
1167 * Call with REC_STATE_IDLE to indicate a forced flush which flushes buffer
1168 * to less than 'margin'.
1169 */
1170static enum record_state flush_chunk(enum record_state state, size_t margin)
1171{
922#ifdef HAVE_PRIORITY_SCHEDULING 1172#ifdef HAVE_PRIORITY_SCHEDULING
923 if (prio_pcmrec == -1 && (num >= flood_watermark || 1173 static unsigned long prio_tick; /* Timeout for auto boost */
924 TIME_AFTER(current_tick, prio_tick)))
925 {
926 /* losing ground or holding without progress - boost
927 priority until finished */
928 logf("pcmrec: boost (%s)",
929 num >= flood_watermark ? "num" : "time");
930 prio_pcmrec = thread_set_priority(thread_self(),
931 thread_get_priority(thread_self()) - 4);
932 prio_codec = codec_thread_set_priority(
933 codec_thread_get_priority() - 4);
934 }
935#endif 1174#endif
936 1175
937 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index); 1176 size_t used = encbuf_used();
938 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
939 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
940 1177
941 if (rec_fdata.chunk->flags & CHUNKF_START_FILE) 1178 switch (state)
942 { 1179 {
943 pcmrec_start_file(); 1180 case REC_STATE_MONITOR:
944 if (--remaining == 0) 1181 if (monitor_encbuf_datarate() || monitor_spinup_time())
945 num_ready = 0; /* stop on next loop - must write this 1182 refresh_watermarks();
946 chunk if it has data */
947 }
948 1183
949 pcmrec_write_chunk(); 1184 if (used < margin)
1185 return REC_STATE_MONITOR;
950 1186
951 if (rec_fdata.chunk->flags & CHUNKF_END_FILE) 1187 state = REC_STATE_FLUSH;
952 pcmrec_end_file(); 1188 trigger_cpu_boost();
953 1189
954 INC_ENC_INDEX(enc_rd_index); 1190#ifdef HAVE_PRIORITY_SCHEDULING
1191 prio_tick = current_tick + PRIO_SECONDS*HZ;
1192#if (CONFIG_STORAGE & STORAGE_ATA)
1193 prio_tick += spinup_time;
1194#endif
1195#endif /* HAVE_PRIORITY_SCHEDULING */
955 1196
956 if (errors != 0) 1197 /* Fall-through */
957 { 1198 case REC_STATE_IDLE: /* As a hint for "forced" */
958 pcmrec_end_file(); 1199 if (used < margin)
959 break; 1200 break;
960 }
961 1201
962 if (flush_num == PCMREC_FLUSH_MINI && 1202 /* Fall-through */
963 ++chunks_flushed >= MINI_CHUNKS) 1203 case REC_STATE_FLUSH:
1204#ifdef HAVE_PRIORITY_SCHEDULING
1205 if (!prio_boosted && state != REC_STATE_IDLE &&
1206 (used >= flood_watermark || TIME_AFTER(current_tick, prio_tick)))
1207 do_prio_boost(true);
1208#endif /* HAVE_PRIORITY_SCHEDULING */
1209
1210 while (used)
964 { 1211 {
965 logf("mini flush break"); 1212 union enc_chunk_hdr *hdr = encbuf_ptr(enc_ridx);
966 break; 1213 size_t count = 0;
967 }
968 /* no yielding; the file apis called in the codecs do that
969 sufficiently */
970 } /* end while */
971 1214
972 /* sync file */ 1215 switch (hdr->type)
973 if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0) 1216 {
974 pcmrec_raise_error_status(PCMREC_E_IO); 1217 case CHUNK_T_DATA:
1218 if (flush_stream_data(ENC_DATA_HDR(hdr)))
1219 count = CHUNK_DATA_COUNT(hdr->size);
1220 break;
975 1221
976 cpu_boost(false); 1222 case CHUNK_T_STREAM_START:
1223 /* Doesn't do stream writes */
1224 flush_stream_start(ENC_FILE_HDR(hdr));
1225 count = hdr->size;
1226 break;
977 1227
978#ifdef HAVE_PRIORITY_SCHEDULING 1228 case CHUNK_T_STREAM_END:
979 if (prio_pcmrec != -1) 1229 if (flush_stream_end(hdr))
980 { 1230 count = 1;
981 /* return to original priorities */ 1231 break;
982 logf("pcmrec: unboost priority");
983 thread_set_priority(thread_self(), prio_pcmrec);
984 codec_thread_set_priority(prio_codec);
985 }
986 1232
987 last_flush_tick = current_tick; /* save tick when we left */ 1233 case CHUNK_T_WRAP:
988#endif 1234 enc_ridx = 0;
1235 used = encbuf_used();
1236 continue;
1237 }
989 1238
990 logf("done"); 1239 if (count)
991} /* pcmrec_flush */ 1240 enc_ridx = encbuf_add(enc_ridx, count);
1241 else
1242 discard_stream();
992 1243
993/** 1244 break;
994 * Marks a new stream in the buffer and gives the encoder a chance for special 1245 }
995 * handling of transition from one to the next. The encoder may change the
996 * chunk that ends the old stream by requesting more chunks and similiarly for
997 * the new but must always advance the position though the interface. It can
998 * later reject any data it cares to when writing the file but should mark the
999 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
1000 * a NULL data pointer without error as well.
1001 */
1002static int pcmrec_get_chunk_index(struct enc_chunk_hdr *chunk)
1003{
1004 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
1005} /* pcmrec_get_chunk_index */
1006 1246
1007static struct enc_chunk_hdr * pcmrec_get_prev_chunk(int index) 1247 if (!encbuf_empty())
1008{ 1248 return state;
1009 DEC_ENC_INDEX(index);
1010 return GET_ENC_CHUNK(index);
1011} /* pcmrec_get_prev_chunk */
1012 1249
1013static void pcmrec_new_stream(const char *filename, /* next file name */ 1250 break;
1014 unsigned long flags, /* CHUNKF_* flags */ 1251 }
1015 int pre_index) /* index for prerecorded data */
1016{
1017 logf("pcmrec_new_stream");
1018 char path[MAX_PATH]; /* place to copy filename so sender can be released */
1019 1252
1020 struct enc_buffer_event_data data; 1253 if (encbuf_empty())
1021 bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add 1254 {
1022 new filename */ 1255 do_prio_boost(false);
1023 struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of 1256 cancel_cpu_boost();
1024 stream */ 1257 }
1025 bool did_flush = false; /* did a flush occurr? */
1026 1258
1027 if (filename) 1259 return REC_STATE_MONITOR;
1028 strlcpy(path, filename, MAX_PATH); 1260}
1029 queue_reply(&audio_queue, 0); /* We have all we need */
1030 1261
1031 data.pre_chunk = NULL; 1262/* Monitor buffer and finish stream, freeing-up space at the same time */
1032 data.chunk = GET_ENC_CHUNK(enc_wr_index); 1263static void finish_stream(bool stopping)
1264{
1265 size_t threshold = stopping ? 1 : enc_buflen - ENCBUF_MIN_SPLIT_MARGIN;
1266 enum record_state state = REC_STATE_MONITOR;
1267 size_t need = 1;
1033 1268
1034 /* end chunk */ 1269 while (1)
1035 if (flags & CHUNKF_END_FILE)
1036 { 1270 {
1037 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE; 1271 switch (state)
1038
1039 if (data.chunk->flags & CHUNKF_START_FILE)
1040 {
1041 /* cannot start and end on same unprocessed chunk */
1042 logf("file end on start");
1043 flags &= ~CHUNKF_END_FILE;
1044 }
1045 else if (enc_rd_index == enc_wr_index)
1046 {
1047 /* all data flushed but file not ended - chunk will be left
1048 empty */
1049 logf("end on dead end");
1050 data.chunk->flags = 0;
1051 data.chunk->enc_size = 0;
1052 data.chunk->num_pcm = 0;
1053 data.chunk->enc_data = NULL;
1054 INC_ENC_INDEX(enc_wr_index);
1055 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1056 }
1057 else
1058 { 1272 {
1059 struct enc_chunk_hdr *last = pcmrec_get_prev_chunk(enc_wr_index); 1273 case REC_STATE_IDLE:
1274 state = flush_chunk(state, threshold);
1275 continue;
1276
1277 default:
1278 if (!need)
1279 break;
1060 1280
1061 if (last->flags & CHUNKF_END_FILE) 1281 if (!stopping || pcm_buffer_empty)
1062 { 1282 {
1063 /* end already processed and marked - can't end twice */ 1283 need = codec_finish_stream();
1064 logf("file end again"); 1284
1065 flags &= ~CHUNKF_END_FILE; 1285 if (need)
1286 {
1287 need = 2*CHUNK_DATA_COUNT(need) - 1;
1288
1289 if (need >= enc_buflen)
1290 {
1291 need = 0;
1292 codec_stop();
1293 threshold = 1;
1294 }
1295 else if (threshold > enc_buflen - need)
1296 {
1297 threshold = enc_buflen - need;
1298 }
1299 }
1066 } 1300 }
1301
1302 if (!need || encbuf_used() >= threshold)
1303 state = REC_STATE_IDLE; /* Start flush */
1304 else
1305 sleep(HZ/10); /* Don't flood with pings */
1306
1307 continue;
1067 } 1308 }
1309
1310 break;
1068 } 1311 }
1312}
1069 1313
1070 /* start chunk */ 1314/* Start a new stream, transistion to a new one or end the current one */
1071 if (flags & CHUNKF_START_FILE) 1315static void mark_stream(const char *path, enum mark_stream_action action)
1316{
1317 if (action & MARK_STREAM_END)
1072 { 1318 {
1073 bool pre = flags & CHUNKF_PRERECORD; 1319 size_t widx;
1320 union enc_chunk_hdr *hdr = encbuf_get_write_ptr(enc_widx, 1, &widx);
1321 hdr->type = CHUNK_T_STREAM_END;
1322 encbuf_widx_advance(widx, 1);
1323 }
1074 1324
1075 if (pre) 1325 if (action & MARK_STREAM_START)
1076 { 1326 {
1077 logf("stream prerecord start"); 1327 size_t count = CHUNK_FILE_COUNT_PATH(path);
1078 start = data.pre_chunk = GET_ENC_CHUNK(pre_index); 1328 struct enc_chunk_file *file;
1079 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD; 1329 size_t widx;
1080 }
1081 else
1082 {
1083 logf("stream normal start");
1084 start = data.chunk;
1085 start->flags &= CHUNKF_START_FILE;
1086 }
1087 1330
1088 /* if encoder hasn't yet processed the last start - abort the start 1331 if (action & MARK_STREAM_PRE)
1089 of the previous file queued or else it will be empty and invalid */
1090 if (start->flags & CHUNKF_START_FILE)
1091 { 1332 {
1092 logf("replacing fnq tail: %s", filename); 1333 /* Prerecord: START marker goes first or before existing data */
1093 fnq_add_fn = pcmrec_fnq_replace_tail; 1334 if (enc_ridx < count)
1335 {
1336 /* Adjust to occupy end of buffer and pad accordingly */
1337 count += enc_ridx;
1338 enc_ridx += enc_buflen;
1339 }
1340
1341 enc_ridx -= count;
1342
1343 /* Won't adjust p since enc_ridx is already set as non-wrapping */
1344 file = encbuf_get_write_ptr(enc_ridx, count, &widx);
1094 } 1345 }
1095 else 1346 else
1096 { 1347 {
1097 logf("adding filename: %s", filename); 1348 /* The usual: START marker goes first or after existing data */
1098 fnq_add_fn = pcmrec_fnq_add_filename; 1349 file = encbuf_get_write_ptr(enc_widx, count, &widx);
1350 encbuf_widx_advance(widx, count);
1099 } 1351 }
1100 }
1101 1352
1102 data.flags = flags; 1353 file->hdr.type = CHUNK_T_STREAM_START;
1103 pcmrec_context = true; /* switch encoder context */ 1354 file->hdr.size = count;
1104 enc_events_callback(ENC_REC_NEW_STREAM, &data); 1355 strlcpy(file->path, path, MAX_PATH);
1105 pcmrec_context = false; /* switch back */
1106
1107 if (flags & CHUNKF_END_FILE)
1108 {
1109 int i = pcmrec_get_chunk_index(data.chunk);
1110 pcmrec_get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1111 } 1356 }
1357}
1112 1358
1113 if (start) 1359/* Tally-up and keep the required amount of prerecord data.
1114 { 1360 * Updates record stats accordingly. */
1115 if (!(flags & CHUNKF_PRERECORD)) 1361static void tally_prerecord_data(void)
1116 { 1362{
1117 /* get stats on data added to start - sort of a prerecord 1363 unsigned long count = 0;
1118 operation */ 1364 size_t bytes = 0;
1119 int i = pcmrec_get_chunk_index(data.chunk); 1365 unsigned long samples = 0;
1120 struct enc_chunk_hdr *chunk = data.chunk;
1121 1366
1122 logf("start data: %d %d", i, enc_wr_index); 1367 /* Find out how much is there */
1368 for (size_t idx = enc_ridx; idx != enc_widx;)
1369 {
1370 struct enc_chunk_data *data = encbuf_read_ptr_incr(idx, &idx);
1123 1371
1124 num_rec_bytes = 0; 1372 if (!data)
1125 num_rec_samples = 0; 1373 continue;
1126 1374
1127 while (i != enc_wr_index) 1375 count += CHUNK_DATA_COUNT(data->hdr.size);
1128 { 1376 bytes += data->hdr.size;
1129 num_rec_bytes += chunk->enc_size; 1377 samples += data->pcm_count;
1130 num_rec_samples += chunk->num_pcm; 1378 }
1131 INC_ENC_INDEX(i);
1132 chunk = GET_ENC_CHUNK(i);
1133 }
1134 1379
1135 start->flags &= ~CHUNKF_START_FILE; 1380 /* Have too much? Discard oldest data. */
1136 start = data.chunk; 1381 unsigned long pre_samples = enc_sample_rate*pre_record_seconds;
1137 }
1138 1382
1139 start->flags |= CHUNKF_START_FILE; 1383 while (samples > pre_samples)
1384 {
1385 struct enc_chunk_data *data =
1386 encbuf_read_ptr_incr(enc_ridx, &enc_ridx);
1140 1387
1141 /* flush all pending files out if full and adding */ 1388 if (!data)
1142 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full()) 1389 continue;
1143 {
1144 logf("fnq full");
1145 pcmrec_flush(PCMREC_FLUSH_ALL);
1146 did_flush = true;
1147 }
1148 1390
1149 fnq_add_fn(path); 1391 count -= CHUNK_DATA_COUNT(data->hdr.size);
1392 bytes -= data->hdr.size;
1393 samples -= data->pcm_count;
1150 } 1394 }
1151 1395
1152 /* Make sure to complete any interrupted high watermark */ 1396 encbuf_rec_count = count;
1153 if (!did_flush) 1397 num_rec_bytes = bytes;
1154 pcmrec_flush(PCMREC_FLUSH_IF_HIGH); 1398 num_rec_samples = samples;
1155} /* pcmrec_new_stream */ 1399}
1156 1400
1157 1401
1158/** event handlers for pcmrec thread */ 1402/** Event handlers for recording thread **/
1159 1403
1160/* PCMREC_INIT */ 1404/* Q_AUDIO_INIT_RECORDING */
1161static void pcmrec_init(void) 1405static void on_init_recording(void)
1162{ 1406{
1163 send_event(RECORDING_EVENT_START, NULL); 1407 send_event(RECORDING_EVENT_START, NULL);
1164 pcmrec_close_file(&rec_fdata.rec_file); 1408 rec_buffer = audio_get_buffer(true, &rec_buffer_size);
1165 1409 init_rec_buffers();
1166 pcmrec_init_state(); 1410 init_state();
1167
1168 unsigned char *buffer = audio_get_buffer(true, &rec_buffer_size);
1169
1170 /* Line align pcm_buffer 2^5=32 bytes */
1171 pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 5);
1172 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1173 PCM_MAX_FEED_SIZE, 2);
1174 /* Adjust available buffer for possible align advancement */
1175 rec_buffer_size -= pcm_buffer - buffer;
1176
1177 pcm_init_recording(); 1411 pcm_init_recording();
1178} /* pcmrec_init */ 1412}
1179 1413
1180/* PCMREC_CLOSE */ 1414/* Q_AUDIO_CLOSE_RECORDING */
1181static void pcmrec_close(void) 1415static void on_close_recording(void)
1182{ 1416{
1183 dma_lock = true; 1417 /* Simply shut down the recording system. Whatever wasn't saved is
1184 pre_record_ticks = 0; /* Can't be prerecording any more */ 1418 lost. */
1185 warnings = 0;
1186 codec_unload(); 1419 codec_unload();
1187 pcm_close_recording(); 1420 pcm_close_recording();
1188 reset_hardware(); 1421 close_rec_file();
1422 init_state();
1423
1424 rec_errors = 0;
1425 pcm_rec_error_clear();
1426
1427 /* Reset PCM to defaults */
1428 pcm_set_frequency(HW_SAMPR_RESET | SAMPR_TYPE_REC);
1429 audio_set_output_source(AUDIO_SRC_PLAYBACK);
1430 pcm_apply_settings();
1431
1189 send_event(RECORDING_EVENT_STOP, NULL); 1432 send_event(RECORDING_EVENT_STOP, NULL);
1190} /* pcmrec_close */ 1433}
1191 1434
1192/* PCMREC_OPTIONS */ 1435/* Q_AUDIO_RECORDING_OPTIONS */
1193static void pcmrec_set_recording_options( 1436static void on_recording_options(struct audio_recording_options *options)
1194 struct audio_recording_options *options)
1195{ 1437{
1196 /* stop everything */ 1438 if (!options)
1197 dma_lock = true; 1439 {
1198 codec_unload(); 1440 logf("options: option NULL!");
1441 return;
1442 }
1443
1444 if (record_state != REC_STATE_IDLE)
1445 {
1446 /* This would ruin things */
1447 logf("options: still recording!");
1448 return;
1449 }
1450
1451 /* Stop everything else that might be running */
1199 pcm_stop_recording(); 1452 pcm_stop_recording();
1200 pcmrec_init_state();
1201 1453
1202 rec_frequency = options->rec_frequency; 1454 int afmt = rec_format_afmt[options->enc_config.rec_format];
1455 bool enc_load = true;
1456
1457 if (codec_loaded() != AFMT_UNKNOWN)
1458 {
1459 if (get_audio_base_codec_type(enc_config.afmt) !=
1460 get_audio_base_codec_type(afmt))
1461 {
1462 /* New format, new encoder; unload this one */
1463 codec_unload();
1464 }
1465 else
1466 {
1467 /* Keep current encoder */
1468 codec_stop();
1469 enc_load = false;
1470 }
1471 }
1472
1473 init_state();
1474
1475 /* Read recording options, remember the ones used elsewhere */
1476 unsigned frequency = options->rec_frequency;
1203 rec_source = options->rec_source; 1477 rec_source = options->rec_source;
1204 num_channels = options->rec_channels == 1 ? 1 : 2; 1478 num_channels = options->rec_channels == 1 ? 1 : 2;
1205 rec_mono_mode = options->rec_mono_mode; 1479 unsigned mono_mode = options->rec_mono_mode;
1206 pre_record_ticks = options->rec_prerecord_time * HZ; 1480 pre_record_seconds = options->rec_prerecord_time;
1207 enc_config = options->enc_config; 1481 enc_config = options->enc_config;
1208 enc_config.afmt = rec_format_afmt[enc_config.rec_format]; 1482 enc_config.afmt = afmt;
1209 1483
1210#ifdef HAVE_SPDIF_IN 1484 queue_reply(&audio_queue, 0); /* Let caller go */
1211 if (rec_source == AUDIO_SRC_SPDIF) 1485
1486 /* Pick appropriate PCM copy routine */
1487 pcm_copyfn = memcpy;
1488
1489 if (num_channels == 1)
1212 { 1490 {
1213 /* must measure SPDIF sample rate before configuring codecs */ 1491 static typeof (memcpy) * const copy_buffer_mono[] =
1214 unsigned long sr = spdif_measure_frequency(); 1492 {
1215 /* round to master list for SPDIF rate */ 1493 copy_buffer_mono_lr,
1216 int index = round_value_to_list32(sr, audio_master_sampr_list, 1494 copy_buffer_mono_l,
1217 SAMPR_NUM_FREQ, false); 1495 copy_buffer_mono_r
1218 sample_rate = audio_master_sampr_list[index]; 1496 };
1219 /* round to HW playback rates for monitoring */ 1497
1220 index = round_value_to_list32(sr, hw_freq_sampr, 1498 if (mono_mode >= ARRAYLEN(copy_buffer_mono))
1221 HW_NUM_FREQ, false); 1499 mono_mode = 0;
1222 pcm_set_frequency(hw_freq_sampr[index] | SAMPR_TYPE_REC); 1500
1223 /* encoders with a limited number of rates do their own rounding */ 1501 pcm_copyfn = copy_buffer_mono[mono_mode];
1224 } 1502 }
1503
1504 /* Get the hardware samplerate to be used */
1505 unsigned long sampr;
1506
1507#ifdef HAVE_SPDIF_IN
1508 if (rec_source == AUDIO_SRC_SPDIF)
1509 sampr = get_spdif_samplerate(); /* Determined by source */
1225 else 1510 else
1226#endif 1511#endif /* HAVE_SPDIF_IN */
1227 { 1512 sampr = rec_freq_sampr[frequency];
1228 /* set sample rate from frequency selection */ 1513
1229 sample_rate = rec_freq_sampr[rec_frequency]; 1514 update_samplerate_config(sampr);
1230 pcm_set_frequency(sample_rate | SAMPR_TYPE_REC);
1231 }
1232 1515
1233 /* set monitoring */ 1516 /* Set monitoring */
1234 audio_set_output_source(rec_source); 1517 audio_set_output_source(rec_source);
1235 1518
1236 /* apply hardware setting to start monitoring now */ 1519 /* Apply hardware setting to start monitoring now */
1237 pcm_apply_settings(); 1520 pcm_apply_settings();
1238 1521
1239 queue_reply(&audio_queue, 0); /* Release sender */ 1522 if (!enc_load || codec_load(-1, afmt | CODEC_TYPE_ENCODER))
1240
1241 if (codec_load(-1, enc_config.afmt | CODEC_TYPE_ENCODER))
1242 { 1523 {
1524 enc_cb = codec_get_enc_callback();
1243 1525
1244 /* run immediately */ 1526 if (!enc_cb || !configure_encoder_stream())
1245 codec_go(); 1527 {
1528 codec_unload();
1529 return;
1530 }
1246 1531
1247 /* start DMA transfer */ 1532 if (pre_record_seconds != 0)
1248 dma_lock = pre_record_ticks == 0; 1533 {
1249 pcm_record_data(pcm_rec_have_more, pcm_rec_status_callback, 1534 record_status = RECORD_PRERECORDING;
1250 GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE); 1535 codec_go();
1536 pcm_pause = false;
1537 }
1538
1539 pcm_start_recording();
1251 } 1540 }
1252 else 1541 else
1253 { 1542 {
1254 logf("set rec opt: enc load failed"); 1543 logf("set rec opt: enc load failed");
1255 pcmrec_raise_error_status(PCMREC_E_LOAD_ENCODER); 1544 raise_error_status(PCMREC_E_LOAD_ENCODER);
1256 } 1545 }
1257} /* pcmrec_set_recording_options */ 1546}
1258 1547
1259/* PCMREC_RECORD - start recording (not gapless) 1548/* Q_AUDIO_RECORD - start recording (not gapless)
1260 or split stream (gapless) */ 1549 or split stream (gapless) */
1261static void pcmrec_record(const char *filename) 1550static void on_record(const char *filename)
1262{ 1551{
1263 unsigned long pre_sample_ticks; 1552 if (rec_errors)
1264 int rd_start;
1265 unsigned long flags;
1266 int pre_index;
1267
1268 logf("pcmrec_record: %s", filename);
1269
1270 /* reset stats */
1271 num_rec_bytes = 0;
1272 num_rec_samples = 0;
1273
1274 if (!is_recording)
1275 { 1553 {
1276#if 0 1554 logf("on_record: errors not cleared");
1277 accum_rec_bytes = 0; 1555 return;
1278 accum_pcm_samples = 0; 1556 }
1279#endif
1280 warnings = 0; /* reset warnings */
1281
1282 rd_start = enc_wr_index;
1283 pre_sample_ticks = 0;
1284
1285 pcmrec_refresh_watermarks();
1286
1287 if (pre_record_ticks)
1288 {
1289 int i = rd_start;
1290 /* calculate number of available chunks */
1291 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1292 enc_num_chunks) % enc_num_chunks;
1293 /* overflow at 974 seconds of prerecording at 44.1kHz */
1294 unsigned long pre_record_sample_ticks =
1295 enc_sample_rate*pre_record_ticks;
1296 int pre_chunks = 0; /* Counter to limit prerecorded time to
1297 prevent flood state at outset */
1298
1299 logf("pre-st: %ld", pre_record_sample_ticks);
1300
1301 /* Get exact measure of recorded data as number of samples aren't
1302 nescessarily going to be the max for each chunk */
1303 for (; avail_pre_chunks-- > 0;)
1304 {
1305 struct enc_chunk_hdr *chunk;
1306 unsigned long chunk_sample_ticks;
1307
1308 DEC_ENC_INDEX(i);
1309 1557
1310 chunk = GET_ENC_CHUNK(i); 1558 if (!filename)
1559 {
1560 logf("on_record: No filename");
1561 return;
1562 }
1311 1563
1312 /* must have data to be counted */ 1564 if (codec_loaded() == AFMT_UNKNOWN)
1313 if (chunk->enc_data == NULL) 1565 {
1314 continue; 1566 logf("on_record: Recording options not set");
1567 return;
1568 }
1315 1569
1316 chunk_sample_ticks = chunk->num_pcm*HZ; 1570 logf("on_record: new file '%s'", filename);
1317 1571
1318 rd_start = i; 1572 /* Copy path and let caller go */
1319 pre_sample_ticks += chunk_sample_ticks; 1573 char path[MAX_PATH];
1320 num_rec_bytes += chunk->enc_size; 1574 strlcpy(path, filename, MAX_PATH);
1321 num_rec_samples += chunk->num_pcm;
1322 pre_chunks++;
1323 1575
1324 /* stop here if enough already */ 1576 queue_reply(&audio_queue, 0);
1325 if (pre_chunks >= high_watermark ||
1326 pre_sample_ticks >= pre_record_sample_ticks)
1327 {
1328 logf("pre-chks: %d", pre_chunks);
1329 break;
1330 }
1331 }
1332 1577
1333#if 0 1578 enum mark_stream_action mark_action;
1334 accum_rec_bytes = num_rec_bytes;
1335 accum_pcm_samples = num_rec_samples;
1336#endif
1337 }
1338 1579
1339 enc_rd_index = rd_start; 1580 if (record_state == REC_STATE_IDLE)
1581 {
1582 mark_action = MARK_STREAM_START;
1340 1583
1341 /* filename queue should be empty */ 1584 if (pre_record_seconds)
1342 if (!pcmrec_fnq_is_empty())
1343 { 1585 {
1344 logf("fnq: not empty!"); 1586 codec_pause();
1345 pcmrec_fnq_set_empty(); 1587 tally_prerecord_data();
1588 mark_action = MARK_STREAM_START_PRE;
1346 } 1589 }
1347 1590
1348 flags = CHUNKF_START_FILE; 1591 clear_warning_status(PCMREC_W_ALL &
1349 if (pre_sample_ticks > 0) 1592 ~(PCMREC_W_SAMPR_MISMATCH|PCMREC_W_DMA));
1350 flags |= CHUNKF_PRERECORD; 1593 record_state = REC_STATE_MONITOR;
1351 1594 record_status = RECORD_RECORDING;
1352 pre_index = enc_rd_index;
1353
1354 dma_lock = false;
1355 is_paused = false;
1356 is_recording = true;
1357 } 1595 }
1358 else 1596 else
1359 { 1597 {
1360 /* already recording, just split the stream */ 1598 /* Already recording, just split the stream */
1361 logf("inserting split"); 1599 logf("inserting split");
1362 flags = CHUNKF_START_FILE | CHUNKF_END_FILE; 1600 mark_action = MARK_STREAM_SPLIT;
1363 pre_index = 0; 1601 finish_stream(false);
1602 reset_rec_stats();
1364 } 1603 }
1365 1604
1366 pcmrec_new_stream(filename, flags, pre_index); 1605 if (rec_errors)
1367 logf("pcmrec_record done");
1368} /* pcmrec_record */
1369
1370/* PCMREC_STOP */
1371static void pcmrec_stop(void)
1372{
1373 logf("pcmrec_stop");
1374
1375 if (is_recording)
1376 { 1606 {
1377 dma_lock = true; /* lock dma write position */ 1607 pcm_pause = true;
1608 codec_stop();
1609 reset_fifos(false);
1610 return;
1611 }
1378 1612
1379 /* flush all available data first to avoid overflow while waiting 1613 mark_stream(path, mark_action);
1380 for encoding to finish */
1381 pcmrec_flush(PCMREC_FLUSH_ALL);
1382 1614
1383 /* wait for encoder to finish remaining data */ 1615 codec_go();
1384 while (errors == 0 && !pcm_buffer_empty) 1616 pcm_pause = record_status != RECORD_RECORDING;
1385 yield(); 1617}
1386 1618
1387 /* end stream at last data */ 1619/* Q_AUDIO_RECORD_STOP */
1388 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0); 1620static void on_record_stop(void)
1621{
1622 if (record_state == REC_STATE_IDLE)
1623 return;
1389 1624
1390 /* flush anything else encoder added */ 1625 /* Drain encoder and PCM buffers */
1391 pcmrec_flush(PCMREC_FLUSH_ALL); 1626 pcm_pause = true;
1627 finish_stream(true);
1392 1628
1393 /* remove any pending file start not yet processed - should be at 1629 /* End stream at last data and flush end marker */
1394 most one at enc_wr_index */ 1630 mark_stream(NULL, MARK_STREAM_END);
1395 pcmrec_fnq_get_filename(NULL); 1631 while (flush_chunk(REC_STATE_IDLE, 1) == REC_STATE_IDLE);
1396 /* encoder should abort any chunk it was in midst of processing */
1397 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1398 1632
1399 /* filename queue should be empty */ 1633 reset_fifos(false);
1400 if (!pcmrec_fnq_is_empty())
1401 {
1402 logf("fnq: not empty!");
1403 pcmrec_fnq_set_empty();
1404 }
1405 1634
1406 /* be absolutely sure the file is closed */ 1635 bool prerecord = pre_record_seconds != 0;
1407 if (errors != 0)
1408 pcmrec_close_file(&rec_fdata.rec_file);
1409 rec_fdata.rec_file = -1;
1410 1636
1411 is_recording = false; 1637 if (rec_errors)
1412 is_paused = false;
1413 dma_lock = pre_record_ticks == 0;
1414 }
1415 else
1416 { 1638 {
1417 logf("not recording"); 1639 codec_stop();
1640 prerecord = false;
1418 } 1641 }
1419 1642
1420 logf("pcmrec_stop done"); 1643 close_rec_file();
1421} /* pcmrec_stop */ 1644 rec_errors = 0;
1422 1645
1423/* PCMREC_PAUSE */ 1646 record_state = REC_STATE_IDLE;
1424static void pcmrec_pause(void) 1647 record_status = prerecord ? RECORD_PRERECORDING : RECORD_STOPPED;
1425{ 1648 reset_rec_stats();
1426 logf("pcmrec_pause");
1427 1649
1428 if (!is_recording) 1650 if (prerecord)
1429 {
1430 logf("not recording");
1431 }
1432 else if (is_paused)
1433 {
1434 logf("already paused");
1435 }
1436 else
1437 { 1651 {
1438 dma_lock = true; 1652 codec_go();
1439 is_paused = true; 1653 pcm_pause = false;
1440 } 1654 }
1655}
1441 1656
1442 logf("pcmrec_pause done"); 1657/* Q_AUDIO_RECORD_PAUSE */
1443} /* pcmrec_pause */ 1658static void on_record_pause(void)
1444
1445/* PCMREC_RESUME */
1446static void pcmrec_resume(void)
1447{ 1659{
1448 logf("pcmrec_resume"); 1660 if (record_status != RECORD_RECORDING)
1661 return;
1449 1662
1450 if (!is_recording) 1663 pcm_pause = true;
1451 { 1664 record_status = RECORD_PAUSED;
1452 logf("not recording"); 1665}
1453 } 1666
1454 else if (!is_paused) 1667/* Q_AUDIO_RECORD_RESUME */
1455 { 1668static void on_record_resume(void)
1456 logf("not paused"); 1669{
1457 } 1670 if (record_status != RECORD_PAUSED)
1458 else 1671 return;
1459 {
1460 is_paused = false;
1461 is_recording = true;
1462 dma_lock = false;
1463 }
1464 1672
1465 logf("pcmrec_resume done"); 1673 record_status = RECORD_RECORDING;
1466} /* pcmrec_resume */ 1674 pcm_pause = !!rec_errors;
1675}
1467 1676
1468/* Called by audio thread when recording is initialized */ 1677/* Called by audio thread when recording is initialized */
1469void audio_recording_handler(struct queue_event *ev) 1678void audio_recording_handler(struct queue_event *ev)
1470{ 1679{
1471 logf("audio recording start"); 1680#ifdef HAVE_PRIORITY_SCHEDULING
1681 /* Get current priorities since they get changed */
1682 int old_prio = thread_get_priority(audio_thread_id);
1683 int old_cod_prio = codec_thread_get_priority();
1684#endif
1685
1686 LOGFQUEUE("record < Q_AUDIO_INIT_RECORDING");
1687 on_init_recording();
1472 1688
1473 while (1) 1689 while (1)
1474 { 1690 {
1691 int watermark = high_watermark;
1692
1475 switch (ev->id) 1693 switch (ev->id)
1476 { 1694 {
1477 case Q_AUDIO_INIT_RECORDING:
1478 pcmrec_init();
1479 break;
1480
1481 case SYS_USB_CONNECTED:
1482 if (is_recording)
1483 break;
1484 /* Fall-through */
1485 case Q_AUDIO_CLOSE_RECORDING: 1695 case Q_AUDIO_CLOSE_RECORDING:
1486 pcmrec_close(); 1696 LOGFQUEUE("record < Q_AUDIO_CLOSE_RECORDING");
1487 return; /* no more recording */ 1697 goto recording_done;
1488 1698
1489 case Q_AUDIO_RECORDING_OPTIONS: 1699 case Q_AUDIO_RECORDING_OPTIONS:
1490 pcmrec_set_recording_options( 1700 LOGFQUEUE("record < Q_AUDIO_RECORDING_OPTIONS");
1491 (struct audio_recording_options *)ev->data); 1701 on_recording_options((struct audio_recording_options *)ev->data);
1492 break; 1702 break;
1493 1703
1494 case Q_AUDIO_RECORD: 1704 case Q_AUDIO_RECORD:
1495 clear_flush_interrupt(); 1705 LOGFQUEUE("record < Q_AUDIO_RECORD: %s", (const char *)ev->data);
1496 pcmrec_record((const char *)ev->data); 1706 on_record((const char *)ev->data);
1497 break; 1707 break;
1498 1708
1499 case Q_AUDIO_STOP: 1709 case Q_AUDIO_RECORD_STOP:
1500 clear_flush_interrupt(); 1710 LOGFQUEUE("record < Q_AUDIO_RECORD_STOP");
1501 pcmrec_stop(); 1711 on_record_stop();
1502 break; 1712 break;
1503 1713
1504 case Q_AUDIO_PAUSE: 1714 case Q_AUDIO_RECORD_PAUSE:
1505 clear_flush_interrupt(); 1715 LOGFQUEUE("record < Q_AUDIO_RECORD_PAUSE");
1506 pcmrec_pause(); 1716 on_record_pause();
1507 break; 1717 break;
1508 1718
1509 case Q_AUDIO_RESUME: 1719 case Q_AUDIO_RECORD_RESUME:
1510 pcmrec_resume(); 1720 LOGFQUEUE("record < Q_AUDIO_RECORD_RESUME");
1721 on_record_resume();
1511 break; 1722 break;
1512 1723
1513 case SYS_TIMEOUT: 1724 case Q_AUDIO_RECORD_FLUSH:
1514 /* Messages that interrupt this will complete it */ 1725 watermark = 1;
1515 pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
1516 PCMREC_FLUSH_INTERRUPTABLE);
1517
1518 if (errors & PCMREC_E_DMA)
1519 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
1520 break; 1726 break;
1521 } /* end switch */
1522 1727
1523 queue_wait_w_tmo(&audio_queue, ev, 1728 case SYS_USB_CONNECTED:
1524 is_recording ? HZ/5 : TIMEOUT_BLOCK); 1729 LOGFQUEUE("record < SYS_USB_CONNECTED");
1525 } /* end while */ 1730 if (record_state != REC_STATE_IDLE)
1526} /* audio_recording_handler */ 1731 {
1527 1732 LOGFQUEUE(" still recording");
1528/****************************************************************************/ 1733 break;
1529/* */ 1734 }
1530/* following functions will be called by the encoder codec */
1531/* in a free-threaded manner */
1532/* */
1533/****************************************************************************/
1534 1735
1535/* pass the encoder settings to the encoder */ 1736 goto recording_done;
1536void enc_get_inputs(struct enc_inputs *inputs) 1737 } /* switch */
1537{
1538 inputs->sample_rate = sample_rate;
1539 inputs->num_channels = num_channels;
1540 inputs->rec_mono_mode = rec_mono_mode;
1541 inputs->config = &enc_config;
1542} /* enc_get_inputs */
1543 1738
1544/* set the encoder dimensions (called by encoder codec at initialization and 1739 int timeout;
1545 termination) */
1546void enc_set_parameters(struct enc_parameters *params)
1547{
1548 size_t bufsize, resbytes;
1549 1740
1550 logf("enc_set_parameters"); 1741 switch (record_state)
1742 {
1743 case REC_STATE_FLUSH:
1744 case REC_STATE_MONITOR:
1745 do
1746 record_state = flush_chunk(record_state, watermark);
1747 while (record_state == REC_STATE_FLUSH &&
1748 queue_empty(&audio_queue));
1749
1750 timeout = record_state == REC_STATE_FLUSH ?
1751 HZ*0 : HZ*FLUSH_MON_INTERVAL;
1752 break;
1753 case REC_STATE_IDLE:
1754#ifdef HAVE_SPDIF_IN
1755 if (rec_source == AUDIO_SRC_SPDIF)
1756 {
1757 check_spdif_samplerate();
1758 timeout = HZ/2;
1759 break;
1760 }
1761#endif /* HAVE_SPDIF_IN */
1762 default:
1763 timeout = TIMEOUT_BLOCK;
1764 break;
1765 }
1551 1766
1552 if (!params) 1767 queue_wait_w_tmo(&audio_queue, ev, timeout);
1553 { 1768 } /* while */
1554 logf("reset");
1555 /* Encoder is terminating */
1556 memset(&enc_config, 0, sizeof (enc_config));
1557 enc_sample_rate = 0;
1558 cancel_cpu_boost(); /* Make sure no boost remains */
1559 return;
1560 }
1561 1769
1562 enc_sample_rate = params->enc_sample_rate; 1770recording_done:
1563 logf("enc sampr:%lu", enc_sample_rate); 1771 on_close_recording();
1772#ifdef HAVE_PRIORITY_SCHEDULING
1773 /* Restore normal thread priorities */
1774 thread_set_priority(audio_thread_id, old_prio);
1775 codec_thread_set_priority(old_cod_prio);
1776#endif
1777}
1564 1778
1565 pcm_rd_pos = dma_wr_pos;
1566 pcm_enc_pos = pcm_rd_pos;
1567 1779
1568 enc_config.afmt = params->afmt; 1780/** Encoder callbacks **/
1569 /* addition of the header is always implied - chunk size 4-byte aligned */
1570 enc_chunk_size =
1571 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1572 enc_events_callback = params->events_callback;
1573 1781
1574 logf("chunk size:%lu", enc_chunk_size); 1782/* Read a block of unprocessed PCM data, with mono conversion if
1783 * num_channels == 1
1784 *
1785 * NOTE: Request must be less than the PCM buffer length in samples in order
1786 * to progress.
1787 * (ie. count <= PCM_NUM_CHUNKS*PCM_CHUNK_SAMP)
1788 */
1789static int enc_pcmbuf_read(void *buffer, int count)
1790{
1791 size_t avail = pcmbuf_used();
1792 size_t size = count*PCM_SAMP_SIZE;
1575 1793
1576 /*** Configure the buffers ***/ 1794 if (count > 0 && avail >= size)
1795 {
1796 size_t endidx = pcm_ridx + size;
1577 1797
1578 /* Layout of recording buffer: 1798 if (endidx > PCM_BUF_SIZE)
1579 * [ax] = possible alignment x multiple 1799 {
1580 * [sx] = possible size alignment of x multiple 1800 size_t wrap = endidx - PCM_BUF_SIZE;
1581 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|-> 1801 size_t offset = size -= wrap;
1582 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1583 */
1584 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1585 logf("resbytes:%lu", resbytes);
1586 1802
1587 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) - 1803 if (num_channels == 1)
1588 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH 1804 offset /= 2; /* src offset -> dst offset */
1589#ifdef DEBUG
1590 - sizeof (*wrap_id_p)
1591#endif
1592 ;
1593 1805
1594 enc_num_chunks = bufsize / enc_chunk_size; 1806 pcm_copyfn(buffer + offset, pcmbuf_ptr(0), wrap);
1595 logf("num chunks:%d", enc_num_chunks); 1807 }
1596 1808
1597 /* get real amount used by encoder chunks */ 1809 pcm_copyfn(buffer, pcmbuf_ptr(pcm_ridx), size);
1598 bufsize = enc_num_chunks*enc_chunk_size;
1599 logf("enc size:%lu", bufsize);
1600 1810
1601#ifdef DEBUG 1811 if (avail >= sample_rate*PCM_SAMP_SIZE*PCM_BOOST_SECONDS ||
1602 /* add magic at wraparound for spillover checks */ 1812 avail >= PCM_BUF_SIZE*1/2)
1603 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize); 1813 {
1604 bufsize += sizeof (*wrap_id_p); 1814 /* Filling up - boost threshold data available or more or 1/2 full
1605 *wrap_id_p = ENC_CHUNK_MAGIC; 1815 or more - boost codec */
1606#endif 1816 trigger_cpu_boost();
1817 }
1607 1818
1608 /** set OUT parameters **/ 1819 pcm_buffer_empty = false;
1609 params->enc_buffer = enc_buffer;
1610 params->buf_chunk_size = enc_chunk_size;
1611 params->num_chunks = enc_num_chunks;
1612 1820
1613 /* calculate reserve buffer start and return pointer to encoder */ 1821 return count;
1614 params->reserve_buffer = NULL;
1615 if (resbytes > 0)
1616 {
1617 params->reserve_buffer = enc_buffer + bufsize;
1618 bufsize += resbytes;
1619 } 1822 }
1620 1823
1621 /* place filename queue at end of buffer using up whatever remains */ 1824 /* Not enough data available - encoder should idle */
1622 fnq_rd_pos = 0; /* reset */ 1825 pcm_buffer_empty = true;
1623 fnq_wr_pos = 0; /* reset */
1624 fn_queue = enc_buffer + bufsize;
1625 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1626 fnq_size /= MAX_PATH;
1627 if (fnq_size > FNQ_MAX_NUM_PATHS)
1628 fnq_size = FNQ_MAX_NUM_PATHS;
1629 fnq_size *= MAX_PATH;
1630 logf("fnq files:%ld", fnq_size / MAX_PATH);
1631
1632#if defined(DEBUG)
1633 logf("pcm:%08lX", (uintptr_t)pcm_buffer);
1634 logf("enc:%08lX", (uintptr_t)enc_buffer);
1635 logf("res:%08lX", (uintptr_t)params->reserve_buffer);
1636 logf("wip:%08lX", (uintptr_t)wrap_id_p);
1637 logf("fnq:%08lX", (uintptr_t)fn_queue);
1638 logf("end:%08lX", (uintptr_t)fn_queue + fnq_size);
1639#endif
1640 1826
1641 /* init all chunk headers and reset indexes */ 1827 cancel_cpu_boost();
1642 enc_rd_index = 0; 1828
1643 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; ) 1829 /* Sleep a little bit */
1644 { 1830 sleep(0);
1645 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1646#ifdef DEBUG
1647 chunk->id = ENC_CHUNK_MAGIC;
1648#endif
1649 chunk->flags = 0;
1650 }
1651 1831
1652 logf("enc_set_parameters done"); 1832 return 0;
1653} /* enc_set_parameters */ 1833}
1654 1834
1655/* return encoder chunk at current write position - 1835/* Advance PCM buffer by count samples */
1656 NOTE: can be called by pcmrec thread when splitting streams */ 1836static int enc_pcmbuf_advance(int count)
1657struct enc_chunk_hdr * enc_get_chunk(void)
1658{ 1837{
1659 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); 1838 if (count <= 0)
1839 return 0;
1660 1840
1661#ifdef DEBUG 1841 size_t avail = pcmbuf_used();
1662 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC) 1842 size_t size = count*PCM_SAMP_SIZE;
1843
1844 if (avail < size)
1663 { 1845 {
1664 pcmrec_raise_error_status(PCMREC_E_CHUNK_OVF); 1846 size = avail;
1665 logf("finish chk ovf: %d", enc_wr_index); 1847 count = size / PCM_SAMP_SIZE;
1666 } 1848 }
1667#endif
1668
1669 chunk->flags &= CHUNKF_START_FILE;
1670 1849
1671 if (!is_recording) 1850 pcm_ridx = pcmbuf_add(pcm_ridx, size);
1672 chunk->flags |= CHUNKF_PRERECORD;
1673 1851
1674 return chunk; 1852 return count;
1675} /* enc_get_chunk */ 1853}
1676 1854
1677/* releases the current chunk into the available chunks - 1855/* Return encoder chunk at current write position, wrapping to 0 if
1678 NOTE: can be called by pcmrec thread when splitting streams */ 1856 * requested size demands it.
1679void enc_finish_chunk(void) 1857 *
1858 * NOTE: No request should be more than 1/2 the buffer length, all elements
1859 * included, or progress will not be guaranteed.
1860 * (ie. CHUNK_DATA_COUNT(need) <= enc_buflen / 2)
1861 */
1862static struct enc_chunk_data * enc_encbuf_get_buffer(size_t need)
1680{ 1863{
1681 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); 1864 /* Convert to buffer slot count, including the header */
1865 need = CHUNK_DATA_COUNT(need);
1682 1866
1683 if ((long)chunk->flags < 0) 1867 enum record_state state = record_state;
1868 size_t avail = encbuf_free();
1869
1870 /* Must have the split margin as well but it does not have to be
1871 continuous with the request */
1872 while (avail <= need + ENCBUF_MIN_SPLIT_MARGIN ||
1873 (enc_widx + need > enc_buflen &&
1874 enc_ridx <= need + ENCBUF_MIN_SPLIT_MARGIN))
1684 { 1875 {
1685 /* encoder set error flag */ 1876 if (UNLIKELY(state == REC_STATE_IDLE))
1686 pcmrec_raise_error_status(PCMREC_E_ENCODER); 1877 {
1687 logf("finish chk enc error"); 1878 /* Prerecording - delete some old data */
1688 } 1879 size_t ridx;
1880 struct enc_chunk_data *data =
1881 encbuf_read_ptr_incr(enc_ridx, &ridx);
1689 1882
1690 /* advance enc_wr_index to the next encoder chunk */ 1883 if (data)
1691 INC_ENC_INDEX(enc_wr_index); 1884 {
1885 encbuf_rec_count -= CHUNK_DATA_COUNT(data->hdr.size);
1886 num_rec_bytes -= data->hdr.size;
1887 num_rec_samples -= data->pcm_count;
1888 }
1692 1889
1693 if (enc_rd_index != enc_wr_index) 1890 enc_ridx = ridx;
1694 { 1891 avail = encbuf_free();
1695 num_rec_bytes += chunk->enc_size; 1892 continue;
1696 num_rec_samples += chunk->num_pcm; 1893 }
1697#if 0 1894 else if (avail == enc_buflen)
1698 accum_rec_bytes += chunk->enc_size;
1699 accum_pcm_samples += chunk->num_pcm;
1700#endif
1701 }
1702 else if (is_recording) /* buffer full */
1703 {
1704 /* keep current position and put up warning flag */
1705 pcmrec_raise_warning_status(PCMREC_W_ENC_BUFFER_OVF);
1706 logf("enc_buffer ovf");
1707 DEC_ENC_INDEX(enc_wr_index);
1708 if (pcmrec_context)
1709 { 1895 {
1710 /* if stream splitting, keep this out of circulation and 1896 /* Empty but request larger than any possible space */
1711 flush a small number, then readd - cannot risk losing 1897 raise_warning_status(PCMREC_W_ENC_BUFFER_OVF);
1712 stream markers */
1713 logf("mini flush");
1714 pcmrec_flush(PCMREC_FLUSH_MINI);
1715 INC_ENC_INDEX(enc_wr_index);
1716 } 1898 }
1899 else if (state != REC_STATE_FLUSH && encbuf_used() < high_watermark)
1900 {
1901 /* Not yet even at high watermark but what's needed won't fit */
1902 encbuf_request_flush();
1903 }
1904
1905 sleep(0);
1906 return NULL;
1717 } 1907 }
1718 else
1719 {
1720 /* advance enc_rd_index for prerecording */
1721 INC_ENC_INDEX(enc_rd_index);
1722 }
1723} /* enc_finish_chunk */
1724 1908
1725/* passes a pointer to next chunk of unprocessed wav data */ 1909 struct enc_chunk_data *data =
1726/* TODO: this really should give the actual size returned */ 1910 encbuf_get_write_ptr(enc_widx, need, &enc_widx);
1727unsigned char * enc_get_pcm_data(size_t size)
1728{
1729 int wp = dma_wr_pos;
1730 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1731 1911
1732 /* limit the requested pcm data size */ 1912 if (state == REC_STATE_IDLE)
1733 if (size > PCM_MAX_FEED_SIZE) 1913 data->hdr.pre = 1;
1734 size = PCM_MAX_FEED_SIZE;
1735 1914
1736 if (avail >= size) 1915 return data;
1916}
1917
1918/* Releases the current buffer into the available chunks */
1919static void enc_encbuf_finish_buffer(void)
1920{
1921 struct enc_chunk_data *data = ENC_DATA_HDR(encbuf_ptr(enc_widx));
1922
1923 if (data->hdr.err)
1737 { 1924 {
1738 unsigned char *ptr = pcm_buffer + pcm_rd_pos; 1925 /* Encoder set error flag */
1739 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK; 1926 raise_error_status(PCMREC_E_ENCODER);
1927 return;
1928 }
1740 1929
1741 pcm_enc_pos = pcm_rd_pos; 1930 size_t data_size = data->hdr.size;
1742 pcm_rd_pos = next_pos;
1743 1931
1744 /* ptr must point to continous data at wraparound position */ 1932 if (data_size == 0)
1745 if ((size_t)pcm_rd_pos < size) 1933 return; /* Claims nothing was written */
1746 {
1747 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1748 pcm_buffer, pcm_rd_pos);
1749 }
1750 1934
1751 if (avail >= (sample_rate << 2) || 1935 size_t count = CHUNK_DATA_COUNT(data_size);
1752 avail >= 3*(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE) / 4) 1936 size_t avail = encbuf_free();
1753 {
1754 /* Filling up - 1s data available or more or 3/4 full or more -
1755 boost codec */
1756 trigger_cpu_boost();
1757 }
1758 1937
1759 pcm_buffer_empty = false; 1938 if (avail <= count || enc_widx + count > enc_buflen)
1760 return ptr; 1939 {
1940 /* Claims it wrote too much? */
1941 raise_warning_status(PCMREC_W_ENC_BUFFER_OVF);
1942 return;
1761 } 1943 }
1762 1944
1763 /* not enough data available - encoder should idle */ 1945 if (num_rec_bytes + data_size > MAX_NUM_REC_BYTES)
1764 pcm_buffer_empty = true; 1946 {
1947 /* Would exceed filesize limit; should have split sooner.
1948 This chunk will be dropped. :'( */
1949 raise_warning_status(PCMREC_W_FILE_SIZE);
1950 return;
1951 }
1765 1952
1766 cancel_cpu_boost(); 1953 encbuf_widx_advance(enc_widx, count);
1767 1954
1768 /* Sleep long enough to allow one frame on average */ 1955 encbuf_rec_count += count;
1769 sleep(0); 1956 num_rec_bytes += data_size;
1957 num_rec_samples += data->pcm_count;
1958}
1770 1959
1771 return NULL; 1960/* Read from the output stream */
1772} /* enc_get_pcm_data */ 1961static ssize_t enc_stream_read(void *buf, size_t count)
1962{
1963 if (!stream_flush_buf())
1964 return -1;
1965
1966 return read(rec_fd, buf, count);
1967}
1773 1968
1774/* puts some pcm data back in the queue */ 1969/* Seek the output steam */
1775size_t enc_unget_pcm_data(size_t size) 1970static off_t enc_stream_lseek(off_t offset, int whence)
1776{ 1971{
1777 int wp = dma_wr_pos; 1972 if (!stream_flush_buf())
1778 size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) - 1973 return -1;
1779 2*PCM_CHUNK_SIZE;
1780 1974
1781 /* allow one interrupt to occur during this call and not have the 1975 return lseek(rec_fd, offset, whence);
1782 new read position inside the DMA destination chunk */ 1976}
1783 if ((ssize_t)old_avail > 0) 1977
1978/* Write to the output stream */
1979static ssize_t enc_stream_write(const void *buf, size_t count)
1980{
1981 if (UNLIKELY(count >= STREAM_BUF_SIZE))
1784 { 1982 {
1785 /* limit size to amount of old data remaining */ 1983 /* Too big to buffer */
1786 if (size > old_avail) 1984 if (stream_flush_buf())
1787 size = old_avail; 1985 return write(rec_fd, buf, count);
1986 }
1788 1987
1789 pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK; 1988 if (!count)
1790 pcm_rd_pos = pcm_enc_pos; 1989 return 0;
1791 1990
1792 return size; 1991 if (stream_buf_used + count > STREAM_BUF_SIZE)
1992 {
1993 if (!stream_flush_buf() && stream_buf_used + count > STREAM_BUF_SIZE)
1994 count = STREAM_BUF_SIZE - stream_buf_used;
1793 } 1995 }
1794 1996
1795 return 0; 1997 memcpy(stream_buffer + stream_buf_used, buf, count);
1796} /* enc_unget_pcm_data */ 1998 stream_buf_used += count;
1999
2000 return count;
2001}
2002
2003/* One-time init at startup */
2004void INIT_ATTR recording_init(void)
2005{
2006 /* Init API */
2007 ci.enc_pcmbuf_read = enc_pcmbuf_read;
2008 ci.enc_pcmbuf_advance = enc_pcmbuf_advance;
2009 ci.enc_encbuf_get_buffer = enc_encbuf_get_buffer;
2010 ci.enc_encbuf_finish_buffer = enc_encbuf_finish_buffer;
2011 ci.enc_stream_read = enc_stream_read;
2012 ci.enc_stream_lseek = enc_stream_lseek;
2013 ci.enc_stream_write = enc_stream_write;
2014}