summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/recorder/recording.c25
-rw-r--r--firmware/export/audio.h17
-rw-r--r--firmware/export/enc_base.h8
-rw-r--r--firmware/export/pcm_record.h32
-rw-r--r--firmware/pcm_record.c683
5 files changed, 499 insertions, 266 deletions
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index 61103a8209..12090ee412 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -751,6 +751,10 @@ bool recording_screen(bool no_source)
751 bool been_in_usb_mode = false; 751 bool been_in_usb_mode = false;
752 int last_audio_stat = -1; 752 int last_audio_stat = -1;
753 int audio_stat; 753 int audio_stat;
754#if CONFIG_CODEC == SWCODEC
755 int warning_counter = 0;
756 #define WARNING_PERIOD 7
757#endif
754#ifdef HAVE_FMRADIO_IN 758#ifdef HAVE_FMRADIO_IN
755 /* Radio is left on if: 759 /* Radio is left on if:
756 * 1) Is was on at the start and the initial source is FM Radio 760 * 1) Is was on at the start and the initial source is FM Radio
@@ -1292,7 +1296,7 @@ bool recording_screen(bool no_source)
1292 default: 1296 default:
1293 default_event_handler(button); 1297 default_event_handler(button);
1294 break; 1298 break;
1295 } 1299 } /* end switch */
1296 1300
1297#ifdef HAVE_AGC 1301#ifdef HAVE_AGC
1298 peak_read = !peak_read; 1302 peak_read = !peak_read;
@@ -1328,6 +1332,20 @@ bool recording_screen(bool no_source)
1328 for(i = 0; i < screen_update; i++) 1332 for(i = 0; i < screen_update; i++)
1329 screens[i].clear_display(); 1333 screens[i].clear_display();
1330 1334
1335#if CONFIG_CODEC == SWCODEC
1336 if ((audio_stat & AUDIO_STATUS_WARNING)
1337 && (warning_counter++ % WARNING_PERIOD) < WARNING_PERIOD/2)
1338 {
1339 /* Switch back and forth displaying warning on first available
1340 line to ensure visibility - the motion should also help
1341 draw attention */
1342 /* Don't use language string unless agreed upon to make this
1343 method permanent - could do something in the statusbar */
1344 snprintf(buf, sizeof(buf), "Warning: %08X",
1345 pcm_rec_get_warnings());
1346 }
1347 else
1348#endif /* CONFIG_CODEC == SWCODEC */
1331 if ((global_settings.rec_sizesplit) && (global_settings.rec_split_method)) 1349 if ((global_settings.rec_sizesplit) && (global_settings.rec_split_method))
1332 { 1350 {
1333 dmb = dsize/1024/1024; 1351 dmb = dsize/1024/1024;
@@ -1381,18 +1399,17 @@ bool recording_screen(bool no_source)
1381 { 1399 {
1382 if (filename_offset[i] > 0) 1400 if (filename_offset[i] > 0)
1383 { 1401 {
1402 *filename = '\0';
1384 if (audio_stat & AUDIO_STATUS_RECORD) 1403 if (audio_stat & AUDIO_STATUS_RECORD)
1385 { 1404 {
1386 strncpy(filename, path_buffer + 1405 strncpy(filename, path_buffer +
1387 strlen(path_buffer) - 12, 13); 1406 strlen(path_buffer) - 12, 13);
1388 filename[12]='\0'; 1407 filename[12]='\0';
1389 } 1408 }
1390 else
1391 strcpy(filename, "");
1392 1409
1393 snprintf(buf, sizeof(buf), "%s %s", 1410 snprintf(buf, sizeof(buf), "%s %s",
1394 str(LANG_SYSFONT_RECORDING_FILENAME), filename); 1411 str(LANG_SYSFONT_RECORDING_FILENAME), filename);
1395 screens[i].puts(0, 2, buf); 1412 screens[i].puts(0, 2, buf);
1396 } 1413 }
1397 } 1414 }
1398 1415
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index 3edbfe155d..6a98d6f4c5 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -39,16 +39,17 @@
39#define audio_play(x) sim_audio_play(x) 39#define audio_play(x) sim_audio_play(x)
40#endif 40#endif
41 41
42#define AUDIO_STATUS_PLAY 1 42#define AUDIO_STATUS_PLAY 0x0001
43#define AUDIO_STATUS_PAUSE 2 43#define AUDIO_STATUS_PAUSE 0x0002
44#define AUDIO_STATUS_RECORD 4 44#define AUDIO_STATUS_RECORD 0x0004
45#define AUDIO_STATUS_PRERECORD 8 45#define AUDIO_STATUS_PRERECORD 0x0008
46#define AUDIO_STATUS_ERROR 16 46#define AUDIO_STATUS_ERROR 0x0010
47#define AUDIO_STATUS_WARNING 0x0020
47 48
48#define AUDIOERR_DISK_FULL 1 49#define AUDIOERR_DISK_FULL 1
49 50
50#define AUDIO_GAIN_LINEIN 0 51#define AUDIO_GAIN_LINEIN 0
51#define AUDIO_GAIN_MIC 1 52#define AUDIO_GAIN_MIC 1
52 53
53 54
54struct audio_debug 55struct audio_debug
diff --git a/firmware/export/enc_base.h b/firmware/export/enc_base.h
index 1be796ec8f..8d1e6fa11e 100644
--- a/firmware/export/enc_base.h
+++ b/firmware/export/enc_base.h
@@ -152,10 +152,18 @@ struct encoder_config
152#define CHUNKF_ERROR 0x80000000 /* An error has occured (passed to/ 152#define CHUNKF_ERROR 0x80000000 /* An error has occured (passed to/
153 from encoder). Use the sign bit to 153 from encoder). Use the sign bit to
154 check (long)flags < 0. */ 154 check (long)flags < 0. */
155#define CHUNKF_ALLFLAGS 0x80000033
155 156
156/* Header at the beginning of every encoder chunk */ 157/* Header at the beginning of every encoder chunk */
158#ifdef PCMREC_PARANOID
159#define ENC_CHUNK_MAGIC H_TO_BE32(('P' << 24) | ('T' << 16) | ('Y' << 8) | 'R')
160#endif
157struct enc_chunk_hdr 161struct enc_chunk_hdr
158{ 162{
163#ifdef PCMREC_PARANOID
164 unsigned long id; /* overflow detection - 'PTYR' - acronym for
165 "PTYR Tells You Right" ;) */
166#endif
159 unsigned long flags; /* in/out: flags used by encoder and file 167 unsigned long flags; /* in/out: flags used by encoder and file
160 writing */ 168 writing */
161 size_t enc_size; /* out: amount of encoder data written to 169 size_t enc_size; /* out: amount of encoder data written to
diff --git a/firmware/export/pcm_record.h b/firmware/export/pcm_record.h
index f6dddb3424..865a37fc70 100644
--- a/firmware/export/pcm_record.h
+++ b/firmware/export/pcm_record.h
@@ -25,6 +25,37 @@
25#define DMA_REC_ERROR_SPDIF (-2) 25#define DMA_REC_ERROR_SPDIF (-2)
26#endif 26#endif
27 27
28/** Warnings **/
29/* pcm (dma) buffer has overflowed */
30#define PCMREC_W_PCM_BUFFER_OVF 0x00000001
31/* encoder output buffer has overflowed */
32#define PCMREC_W_ENC_BUFFER_OVF 0x00000002
33#ifdef PCMREC_PARANOID
34/* dma write position alignment incorrect */
35#define PCMREC_W_DMA_WR_POS_ALIGN 0x00000004
36/* pcm read position changed at some point not under control of recording */
37#define PCMREC_W_PCM_RD_POS_TRASHED 0x00000008
38/* dma write position changed at some point not under control of recording */
39#define PCMREC_W_DMA_WR_POS_TRASHED 0x00000010
40#endif /* PCMREC_PARANOID */
41/** Errors **/
42/* failed to load encoder */
43#define PCMREC_E_LOAD_ENCODER 0x80001000
44/* error originating in encoder */
45#define PCMREC_E_ENCODER 0x80002000
46/* filename queue has desynced from stream markers */
47#define PCMREC_E_FNQ_DESYNC 0x80004000
48#ifdef PCMREC_PARANOID
49/* encoder has written past end of allotted space */
50#define PCMREC_E_CHUNK_OVF 0x80008000
51/* chunk header incorrect */
52#define PCMREC_E_BAD_CHUNK 0x80010000
53/* encoder read position changed outside of recording control */
54#define PCMREC_E_ENC_RD_INDEX_TRASHED 0x80020000
55/* encoder write position changed outside of recording control */
56#define PCMREC_E_ENC_WR_INDEX_TRASHED 0x80040000
57#endif /* PCMREC_PARANOID */
58
28/** 59/**
29 * RAW pcm data recording 60 * RAW pcm data recording
30 * These calls are nescessary only when using the raw pcm apis directly. 61 * These calls are nescessary only when using the raw pcm apis directly.
@@ -54,6 +85,7 @@ void pcm_rec_error_clear(void);
54/* pcm_rec_status is deprecated for general use. audio_status merges the 85/* pcm_rec_status is deprecated for general use. audio_status merges the
55 results for consistency with the hardware codec version */ 86 results for consistency with the hardware codec version */
56unsigned long pcm_rec_status(void); 87unsigned long pcm_rec_status(void);
88unsigned long pcm_rec_get_warnings(void);
57void pcm_rec_init(void); 89void pcm_rec_init(void);
58int pcm_rec_current_bitrate(void); 90int pcm_rec_current_bitrate(void);
59int pcm_rec_encoder_afmt(void); /* AFMT_* value, AFMT_UNKNOWN if none */ 91int pcm_rec_encoder_afmt(void); /* AFMT_* value, AFMT_UNKNOWN if none */
diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c
index 93a6e067b1..34b104d29a 100644
--- a/firmware/pcm_record.c
+++ b/firmware/pcm_record.c
@@ -60,10 +60,11 @@ volatile pcm_more_callback_type2 pcm_callback_more_ready = NULL;
60volatile bool pcm_recording = false; 60volatile bool pcm_recording = false;
61 61
62/** General recording state **/ 62/** General recording state **/
63static bool is_recording; /* We are recording */ 63static bool is_recording; /* We are recording */
64static bool is_paused; /* We have paused */ 64static bool is_paused; /* We have paused */
65static bool is_stopping; /* We are currently stopping */ 65static bool is_stopping; /* We are currently stopping */
66static bool is_error; /* An error has occured */ 66static unsigned long errors; /* An error has occured */
67static unsigned long warnings; /* Warning */
67 68
68/** Stats on encoded data for current file **/ 69/** Stats on encoded data for current file **/
69static size_t num_rec_bytes; /* Num bytes recorded */ 70static size_t num_rec_bytes; /* Num bytes recorded */
@@ -91,6 +92,7 @@ static int rec_frequency; /* current frequency setting */
91static unsigned long sample_rate; /* Sample rate in HZ */ 92static unsigned long sample_rate; /* Sample rate in HZ */
92static int num_channels; /* Current number of channels */ 93static int num_channels; /* Current number of channels */
93static struct encoder_config enc_config; /* Current encoder configuration */ 94static struct encoder_config enc_config; /* Current encoder configuration */
95static unsigned long pre_record_ticks; /* pre-record time in ticks */
94 96
95/**************************************************************************** 97/****************************************************************************
96 use 2 circular buffers: 98 use 2 circular buffers:
@@ -104,16 +106,24 @@ static struct encoder_config enc_config; /* Current encoder configuration */
104 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk 106 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
105 107
106 Functions calls (basic encoder steps): 108 Functions calls (basic encoder steps):
107 1.main: audio_load_encoder(); start the encoder 109 1.main: audio_load_encoder(); start the encoder
108 2.encoder: enc_get_inputs(); get encoder recording settings 110 2.encoder: enc_get_inputs(); get encoder recording settings
109 3.encoder: enc_set_parameters(); set the encoder parameters 111 3.encoder: enc_set_parameters(); set the encoder parameters
110 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data 112 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
111 5.encoder: enc_pcm_buf_near_empty(); if 1: reduce cpu_boost 113 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional)
112 6.encoder: enc_alloc_chunk(); get a ptr to next enc chunk 114 6.encoder: enc_pcm_buf_near_empty(); if !0: reduce cpu_boost
113 7.encoder: <process enc chunk> compress and store data to enc chunk 115 7.encoder: enc_get_chunk(); get a ptr to next enc chunk
114 8.encoder: enc_free_chunk(); inform main about chunk process finished 116 8.encoder: <process enc chunk> compress and store data to enc chunk
115 9.encoder: repeat 4. to 8. 117 9.encoder: enc_finish_chunk(); inform main about chunk processed and
116 A.pcmrec: enc_events_callback(); called for certain events 118 is available to be written to a file.
119 Encoder can place any number of chunks
120 of PCM data in a single output chunk
121 but must stay within its output chunk
122 size
123 A.encoder: repeat 4. to 9.
124 B.pcmrec: enc_events_callback(); called for certain events
125
126 (*) Optional step
117****************************************************************************/ 127****************************************************************************/
118 128
119/** buffer parameters where incoming PCM data is placed **/ 129/** buffer parameters where incoming PCM data is placed **/
@@ -124,10 +134,53 @@ static struct encoder_config enc_config; /* Current encoder configuration */
124#define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset))) 134#define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
125#define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index)) 135#define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
126 136
137#ifdef PCMREC_PARANOID
138static void paranoid_set_code(unsigned long code, int line)
139{
140 logf("%08X at %d", code, line);
141 if ((long)code < 0)
142 errors |= code;
143 else
144 warnings |= code;
145}
146
147#define PARANOID_ENC_INDEX_CHECK(index) \
148 { if (index != index##_last) \
149 paranoid_set_code((&index == &enc_rd_index) ? \
150 PCMREC_E_ENC_RD_INDEX_TRASHED : PCMREC_E_ENC_WR_INDEX_TRASHED, \
151 __LINE__); }
152#define PARANOID_PCM_POS_CHECK(pos) \
153 { if (pos != pos##_last) \
154 paranoid_set_code((&pos == &pcm_rd_pos) ? \
155 PCMREC_W_PCM_RD_POS_TRASHED : PCMREC_W_DMA_WR_POS_TRASHED, \
156 __LINE__); }
157#define PARANOID_SET_LAST(var) \
158 ; var##_last = var
159#define PARANOID_CHUNK_CHECK(chunk) \
160 paranoid_chunk_check(chunk)
161#else
162#define PARANOID_ENC_INDEX_CHECK(index)
163#define PARANOID_PCM_POS_CHECK(pos)
164#define PARANOID_SET_LAST(var)
165#define PARANOID_CHUNK_CHECK(chunk)
166#endif
167
127#define INC_ENC_INDEX(index) \ 168#define INC_ENC_INDEX(index) \
128 { if (++index >= enc_num_chunks) index = 0; } 169 PARANOID_ENC_INDEX_CHECK(index) \
170 { if (++index >= enc_num_chunks) index = 0; } \
171 PARANOID_SET_LAST(index)
129#define DEC_ENC_INDEX(index) \ 172#define DEC_ENC_INDEX(index) \
130 { if (--index < 0) index = enc_num_chunks - 1; } 173 PARANOID_ENC_INDEX_CHECK(index) \
174 { if (--index < 0) index = enc_num_chunks - 1; } \
175 PARANOID_SET_LAST(index)
176#define SET_ENC_INDEX(index, value) \
177 PARANOID_ENC_INDEX_CHECK(index) \
178 index = value \
179 PARANOID_SET_LAST(index)
180#define SET_PCM_POS(pos, value) \
181 PARANOID_PCM_POS_CHECK(pos) \
182 pos = value \
183 PARANOID_SET_LAST(pos)
131 184
132static size_t rec_buffer_size; /* size of available buffer */ 185static size_t rec_buffer_size; /* size of available buffer */
133static unsigned char *pcm_buffer; /* circular recording buffer */ 186static unsigned char *pcm_buffer; /* circular recording buffer */
@@ -135,14 +188,12 @@ static unsigned char *enc_buffer; /* circular encoding buffer */
135static volatile int dma_wr_pos; /* current DMA write pos */ 188static volatile int dma_wr_pos; /* current DMA write pos */
136static int pcm_rd_pos; /* current PCM read pos */ 189static int pcm_rd_pos; /* current PCM read pos */
137static volatile bool dma_lock; /* lock DMA write position */ 190static volatile bool dma_lock; /* lock DMA write position */
138static unsigned long pre_record_ticks;/* pre-record time in ticks */
139static int enc_wr_index; /* encoder chunk write index */ 191static int enc_wr_index; /* encoder chunk write index */
140static int enc_rd_index; /* encoder chunk read index */ 192static int enc_rd_index; /* encoder chunk read index */
141static int enc_num_chunks; /* number of chunks in ringbuffer */ 193static int enc_num_chunks; /* number of chunks in ringbuffer */
142static size_t enc_chunk_size; /* maximum encoder chunk size */ 194static size_t enc_chunk_size; /* maximum encoder chunk size */
143static size_t enc_data_size; /* maximum data size for encoder */
144static unsigned long enc_sample_rate; /* sample rate used by encoder */ 195static unsigned long enc_sample_rate; /* sample rate used by encoder */
145static bool wav_queue_empty; /* all wav chunks processed? */ 196static bool wav_queue_empty; /* all wav chunks processed? */
146 197
147/** file flushing **/ 198/** file flushing **/
148static int write_threshold; /* max chunk limit for data flush */ 199static int write_threshold; /* max chunk limit for data flush */
@@ -159,6 +210,16 @@ static ssize_t fnq_size; /* capacity of queue in bytes */
159static int fnq_rd_pos; /* current read position */ 210static int fnq_rd_pos; /* current read position */
160static int fnq_wr_pos; /* current write position */ 211static int fnq_wr_pos; /* current write position */
161 212
213/** extra debugging info positioned away from other vars **/
214#ifdef PCMREC_PARANOID
215static unsigned long *wrap_id_p; /* magic at end of encoding buffer */
216static volatile int dma_wr_pos_last; /* previous dma write position */
217static int pcm_rd_pos_last; /* previous pcm read position */
218static int enc_rd_index_last; /* previsou encoder read position */
219static int enc_wr_index_last; /* previsou encoder read position */
220#endif
221
222
162/***************************************************************************/ 223/***************************************************************************/
163 224
164static struct event_queue pcmrec_queue; 225static struct event_queue pcmrec_queue;
@@ -169,19 +230,18 @@ static void pcmrec_thread(void);
169 230
170/* Event values which are also single-bit flags */ 231/* Event values which are also single-bit flags */
171#define PCMREC_INIT 0x00000001 /* enable recording */ 232#define PCMREC_INIT 0x00000001 /* enable recording */
172#define PCMREC_CLOSE 0x00000002 233#define PCMREC_CLOSE 0x00000002 /* close recording */
173 234#define PCMREC_OPTIONS 0x00000004 /* set recording options */
174#define PCMREC_START 0x00000004 /* start recording (when stopped) */ 235#define PCMREC_START 0x00000008 /* start recording */
175#define PCMREC_STOP 0x00000008 /* stop the current recording */ 236#define PCMREC_STOP 0x00000010 /* stop the current recording */
176#define PCMREC_PAUSE 0x00000010 /* pause the current recording */ 237#define PCMREC_PAUSE 0x00000020 /* pause the current recording */
177#define PCMREC_RESUME 0x00000020 /* resume the current recording */ 238#define PCMREC_RESUME 0x00000040 /* resume the current recording */
178#define PCMREC_NEW_FILE 0x00000040 /* start new file (when recording) */ 239#define PCMREC_NEW_FILE 0x00000080 /* start new file */
179#define PCMREC_SET_GAIN 0x00000080
180#define PCMREC_FLUSH_NUM 0x00000100 /* flush a number of files out */ 240#define PCMREC_FLUSH_NUM 0x00000100 /* flush a number of files out */
181#define PCMREC_FINISH_STOP 0x00000200 /* finish the stopping recording */ 241#define PCMREC_FINISH_STOP 0x00000200 /* finish the stopping recording */
182 242
183/* mask for signaling events */ 243/* mask for signaling events */
184static volatile long pcm_thread_event_mask; 244static volatile long pcm_thread_event_mask = PCMREC_CLOSE;
185 245
186static void pcm_thread_sync_post(long event, void *data) 246static void pcm_thread_sync_post(long event, void *data)
187{ 247{
@@ -206,21 +266,11 @@ static inline bool pcm_thread_event_state(long signaled, long unsignaled)
206 return ((signaled | unsignaled) & pcm_thread_event_mask) == signaled; 266 return ((signaled | unsignaled) & pcm_thread_event_mask) == signaled;
207} /* pcm_thread_event_state */ 267} /* pcm_thread_event_state */
208 268
209static void pcm_thread_wait_for_stop(void)
210{
211 if (is_stopping)
212 {
213 logf("waiting for stop to finish");
214 while (is_stopping)
215 yield();
216 }
217} /* pcm_thread_wait_for_stop */
218
219/*******************************************************************/ 269/*******************************************************************/
220/* Functions that are not executing in the pcmrec_thread first */ 270/* Functions that are not executing in the pcmrec_thread first */
221/*******************************************************************/ 271/*******************************************************************/
222 272
223/* Callback for when more data is ready */ 273/* Callback for when more data is ready - called in interrupt context */
224static int pcm_rec_have_more(int status) 274static int pcm_rec_have_more(int status)
225{ 275{
226 if (status < 0) 276 if (status < 0)
@@ -237,7 +287,23 @@ static int pcm_rec_have_more(int status)
237 else if (!dma_lock) 287 else if (!dma_lock)
238 { 288 {
239 /* advance write position */ 289 /* advance write position */
240 dma_wr_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK; 290 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
291
292 /* set pcm ovf if read position is inside current write chunk */
293 if ((unsigned)(pcm_rd_pos - next_pos) < PCM_CHUNK_SIZE)
294 warnings |= PCMREC_W_PCM_BUFFER_OVF;
295
296#ifdef PCMREC_PARANOID
297 /* write position must always be on PCM_CHUNK_SIZE boundary -
298 anything else is corruption */
299 if (next_pos & (PCM_CHUNK_SIZE-1))
300 {
301 logf("dma_wr_pos unalgn: %d", next_pos);
302 warnings |= PCMREC_W_DMA_WR_POS_ALIGN;
303 next_pos &= ~PCM_CHUNK_SIZE; /* re-align */
304 }
305#endif
306 SET_PCM_POS(dma_wr_pos, next_pos);
241 } 307 }
242 308
243 pcm_record_more(GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE); 309 pcm_record_more(GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE);
@@ -253,31 +319,47 @@ static void reset_hardware(void)
253} 319}
254 320
255/** pcm_rec_* group **/ 321/** pcm_rec_* group **/
322
323/**
324 * Clear all errors and warnings
325 */
256void pcm_rec_error_clear(void) 326void pcm_rec_error_clear(void)
257{ 327{
258 is_error = false; 328 errors = warnings = 0;
259} /* pcm_rec_error_clear */ 329} /* pcm_rec_error_clear */
260 330
331/**
332 * Check mode, errors and warnings
333 */
261unsigned long pcm_rec_status(void) 334unsigned long pcm_rec_status(void)
262{ 335{
263 unsigned long ret = 0; 336 unsigned long ret = 0;
264 337
265 if (is_recording) 338 if (is_recording)
266 ret |= AUDIO_STATUS_RECORD; 339 ret |= AUDIO_STATUS_RECORD;
340 else if (pre_record_ticks)
341 ret |= AUDIO_STATUS_PRERECORD;
267 342
268 if (is_paused) 343 if (is_paused)
269 ret |= AUDIO_STATUS_PAUSE; 344 ret |= AUDIO_STATUS_PAUSE;
270 345
271 if (is_error) 346 if (errors)
272 ret |= AUDIO_STATUS_ERROR; 347 ret |= AUDIO_STATUS_ERROR;
273 348
274 if (!is_recording && pre_record_ticks && 349 if (warnings)
275 pcm_thread_event_state(PCMREC_INIT, PCMREC_CLOSE)) 350 ret |= AUDIO_STATUS_WARNING;
276 ret |= AUDIO_STATUS_PRERECORD;
277 351
278 return ret; 352 return ret;
279} /* pcm_rec_status */ 353} /* pcm_rec_status */
280 354
355/**
356 * Return warnings that have occured since recording started
357 */
358unsigned long pcm_rec_get_warnings(void)
359{
360 return warnings;
361}
362
281#if 0 363#if 0
282int pcm_rec_current_bitrate(void) 364int pcm_rec_current_bitrate(void)
283{ 365{
@@ -325,171 +407,133 @@ void pcm_rec_init(void)
325 407
326/** audio_* group **/ 408/** audio_* group **/
327 409
410/* NOTE: The following posting functions are really only single-thread safe
411 at the moment since a response to a particular message at a particular
412 position in the queue can't be distinguished */
413
414/**
415 * Initializes recording - call before calling any other recording function
416 */
328void audio_init_recording(unsigned int buffer_offset) 417void audio_init_recording(unsigned int buffer_offset)
329{ 418{
330 (void)buffer_offset; 419 logf("audio_init_recording");
331 pcm_thread_wait_for_stop();
332 pcm_thread_sync_post(PCMREC_INIT, NULL); 420 pcm_thread_sync_post(PCMREC_INIT, NULL);
421 logf("audio_init_recording done");
422 (void)buffer_offset;
333} /* audio_init_recording */ 423} /* audio_init_recording */
334 424
425/**
426 * Closes recording - call audio_stop_recording first
427 */
335void audio_close_recording(void) 428void audio_close_recording(void)
336{ 429{
337 pcm_thread_wait_for_stop(); 430 logf("audio_close_recording");
338 pcm_thread_sync_post(PCMREC_CLOSE, NULL); 431 pcm_thread_sync_post(PCMREC_CLOSE, NULL);
339 reset_hardware(); 432 logf("audio_close_recording done");
340 audio_remove_encoder();
341} /* audio_close_recording */ 433} /* audio_close_recording */
342 434
343unsigned long audio_recorded_time(void)
344{
345 if (!is_recording || enc_sample_rate == 0)
346 return 0;
347
348 /* return actual recorded time a la encoded data even if encoder rate
349 doesn't match the pcm rate */
350 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
351} /* audio_recorded_time */
352
353unsigned long audio_num_recorded_bytes(void)
354{
355 if (!is_recording)
356 return 0;
357
358 return num_rec_bytes;
359} /* audio_num_recorded_bytes */
360
361#ifdef HAVE_SPDIF_IN
362/**
363 * Return SPDIF sample rate index in audio_master_sampr_list. Since we base
364 * our reading on the actual SPDIF sample rate (which might be a bit
365 * inaccurate), we round off to the closest sample rate that is supported by
366 * SPDIF.
367 */
368int audio_get_spdif_sample_rate(void)
369{
370 unsigned long measured_rate = spdif_measure_frequency();
371 /* Find which SPDIF sample rate we're closest to. */
372 return round_value_to_list32(measured_rate, audio_master_sampr_list,
373 SAMPR_NUM_FREQ, false);
374} /* audio_get_spdif_sample_rate */
375#endif /* HAVE_SPDIF_IN */
376
377/** 435/**
378 * Sets recording parameters 436 * Sets recording parameters
379 */ 437 */
380void audio_set_recording_options(struct audio_recording_options *options) 438void audio_set_recording_options(struct audio_recording_options *options)
381{ 439{
382 pcm_thread_wait_for_stop(); 440 logf("audio_set_recording_options");
383 441 pcm_thread_sync_post(PCMREC_OPTIONS, (void *)options);
384 /* stop DMA transfer */ 442 logf("audio_set_recording_options done");
385 dma_lock = true;
386 pcm_stop_recording();
387
388 rec_frequency = options->rec_frequency;
389 rec_source = options->rec_source;
390 num_channels = options->rec_channels == 1 ? 1 : 2;
391 pre_record_ticks = options->rec_prerecord_time * HZ;
392 enc_config = options->enc_config;
393 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
394
395#ifdef HAVE_SPDIF_IN
396 if (rec_source == AUDIO_SRC_SPDIF)
397 {
398 /* must measure SPDIF sample rate before configuring codecs */
399 unsigned long sr = spdif_measure_frequency();
400 /* round to master list for SPDIF rate */
401 int index = round_value_to_list32(sr, audio_master_sampr_list,
402 SAMPR_NUM_FREQ, false);
403 sample_rate = audio_master_sampr_list[index];
404 /* round to HW playback rates for monitoring */
405 index = round_value_to_list32(sr, hw_freq_sampr,
406 HW_NUM_FREQ, false);
407 pcm_set_frequency(hw_freq_sampr[index]);
408 /* encoders with a limited number of rates do their own rounding */
409 }
410 else
411#endif
412 {
413 /* set sample rate from frequency selection */
414 sample_rate = rec_freq_sampr[rec_frequency];
415 pcm_set_frequency(sample_rate);
416 }
417
418 /* set monitoring */
419 audio_set_output_source(rec_source);
420
421 /* apply pcm settings to hardware */
422 pcm_apply_settings(true);
423
424 if (audio_load_encoder(enc_config.afmt))
425 {
426 /* start DMA transfer */
427 dma_lock = pre_record_ticks == 0;
428 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos),
429 PCM_CHUNK_SIZE);
430 }
431 else
432 {
433 logf("set rec opt: enc load failed");
434 is_error = true;
435 }
436} /* audio_set_recording_options */ 443} /* audio_set_recording_options */
437 444
438/** 445/**
439 * Start recording 446 * Start recording if not recording or else split
440 *
441 * Use audio_set_recording_options first to select recording options
442 */ 447 */
443void audio_record(const char *filename) 448void audio_record(const char *filename)
444{ 449{
445 logf("audio_record: %s", filename); 450 logf("audio_record: %s", filename);
446
447 pcm_thread_wait_for_stop();
448 pcm_thread_sync_post(PCMREC_START, (void *)filename); 451 pcm_thread_sync_post(PCMREC_START, (void *)filename);
449
450 logf("audio_record_done"); 452 logf("audio_record_done");
451} /* audio_record */ 453} /* audio_record */
452 454
455/**
456 * Equivalent to audio_record()
457 */
453void audio_new_file(const char *filename) 458void audio_new_file(const char *filename)
454{ 459{
455 logf("audio_new_file: %s", filename); 460 logf("audio_new_file: %s", filename);
456
457 pcm_thread_wait_for_stop();
458 pcm_thread_sync_post(PCMREC_NEW_FILE, (void *)filename); 461 pcm_thread_sync_post(PCMREC_NEW_FILE, (void *)filename);
459
460 logf("audio_new_file done"); 462 logf("audio_new_file done");
461} /* audio_new_file */ 463} /* audio_new_file */
462 464
465/**
466 * Stop current recording if recording
467 */
463void audio_stop_recording(void) 468void audio_stop_recording(void)
464{ 469{
465 logf("audio_stop_recording"); 470 logf("audio_stop_recording");
466
467 pcm_thread_wait_for_stop();
468 pcm_thread_sync_post(PCMREC_STOP, NULL); 471 pcm_thread_sync_post(PCMREC_STOP, NULL);
469
470 logf("audio_stop_recording done"); 472 logf("audio_stop_recording done");
471} /* audio_stop_recording */ 473} /* audio_stop_recording */
472 474
475/**
476 * Pause current recording
477 */
473void audio_pause_recording(void) 478void audio_pause_recording(void)
474{ 479{
475 logf("audio_pause_recording"); 480 logf("audio_pause_recording");
476
477 pcm_thread_wait_for_stop();
478 pcm_thread_sync_post(PCMREC_PAUSE, NULL); 481 pcm_thread_sync_post(PCMREC_PAUSE, NULL);
479
480 logf("audio_pause_recording done"); 482 logf("audio_pause_recording done");
481} /* audio_pause_recording */ 483} /* audio_pause_recording */
482 484
485/**
486 * Resume current recording if paused
487 */
483void audio_resume_recording(void) 488void audio_resume_recording(void)
484{ 489{
485 logf("audio_resume_recording"); 490 logf("audio_resume_recording");
486
487 pcm_thread_wait_for_stop();
488 pcm_thread_sync_post(PCMREC_RESUME, NULL); 491 pcm_thread_sync_post(PCMREC_RESUME, NULL);
489
490 logf("audio_resume_recording done"); 492 logf("audio_resume_recording done");
491} /* audio_resume_recording */ 493} /* audio_resume_recording */
492 494
495/** Information about current state **/
496
497/**
498 * Return current recorded time in ticks (playback eqivalent time)
499 */
500unsigned long audio_recorded_time(void)
501{
502 if (!is_recording || enc_sample_rate == 0)
503 return 0;
504
505 /* return actual recorded time a la encoded data even if encoder rate
506 doesn't match the pcm rate */
507 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
508} /* audio_recorded_time */
509
510/**
511 * Return number of bytes encoded to output
512 */
513unsigned long audio_num_recorded_bytes(void)
514{
515 if (!is_recording)
516 return 0;
517
518 return num_rec_bytes;
519} /* audio_num_recorded_bytes */
520
521#ifdef HAVE_SPDIF_IN
522/**
523 * Return SPDIF sample rate index in audio_master_sampr_list. Since we base
524 * our reading on the actual SPDIF sample rate (which might be a bit
525 * inaccurate), we round off to the closest sample rate that is supported by
526 * SPDIF.
527 */
528int audio_get_spdif_sample_rate(void)
529{
530 unsigned long measured_rate = spdif_measure_frequency();
531 /* Find which SPDIF sample rate we're closest to. */
532 return round_value_to_list32(measured_rate, audio_master_sampr_list,
533 SAMPR_NUM_FREQ, false);
534} /* audio_get_spdif_sample_rate */
535#endif /* HAVE_SPDIF_IN */
536
493/***************************************************************************/ 537/***************************************************************************/
494/* */ 538/* */
495/* Functions that execute in the context of pcmrec_thread */ 539/* Functions that execute in the context of pcmrec_thread */
@@ -580,6 +624,51 @@ static void pcmrec_close_file(int *fd_p)
580 *fd_p = -1; 624 *fd_p = -1;
581} /* pcmrec_close_file */ 625} /* pcmrec_close_file */
582 626
627#ifdef PCMREC_PARANOID
628static void paranoid_chunk_check(const struct enc_chunk_hdr *chunk)
629{
630 /* check integrity of things that must be ok - data or not */
631
632 /* check magic in header */
633 if (chunk->id != ENC_CHUNK_MAGIC)
634 {
635 errors |= PCMREC_E_BAD_CHUNK | PCMREC_E_CHUNK_OVF;
636 logf("bad chunk: %d", chunk - (struct enc_chunk_hdr *)enc_buffer);
637 }
638
639 /* check magic wrap id */
640 if (*wrap_id_p != ENC_CHUNK_MAGIC)
641 {
642 errors |= PCMREC_E_BAD_CHUNK | PCMREC_E_CHUNK_OVF;
643 logf("bad magic at wrap pos");
644 }
645
646 if (chunk->enc_data == NULL) /* has data? */
647 return;
648
649 /* check that data points to something after header */
650 if (chunk->enc_data < ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk))
651 {
652 errors |= PCMREC_E_BAD_CHUNK;
653 logf("chk ptr < hdr end");
654 }
655
656 /* check if data end is within chunk */
657 if (chunk->enc_data + chunk->enc_size >
658 (unsigned char *)chunk + enc_chunk_size)
659 {
660 errors |= PCMREC_E_BAD_CHUNK;
661 logf("chk data > chk end");
662 }
663
664 if ((chunk->flags & ~CHUNKF_ALLFLAGS) != 0)
665 {
666 errors |= PCMREC_E_BAD_CHUNK;
667 logf("chk bad flags %08X", chunk->flags);
668 }
669} /* paranoid_chunk_check */
670#endif /* PCMREC_PARANOID */
671
583/** Data Flushing **/ 672/** Data Flushing **/
584 673
585/** 674/**
@@ -627,20 +716,20 @@ static void pcmrec_start_file(void)
627 { 716 {
628 logf("start file: fnq empty"); 717 logf("start file: fnq empty");
629 *filename = '\0'; 718 *filename = '\0';
630 is_error = true; 719 errors |= PCMREC_E_FNQ_DESYNC;
631 } 720 }
632 else if (is_error) 721 else if (errors != 0)
633 { 722 {
634 logf("start file: is_error already"); 723 logf("start file: error already");
635 } 724 }
636 else if (curr_rec_file >= 0) 725 else if (curr_rec_file >= 0)
637 { 726 {
638 /* Any previous file should have been closed */ 727 /* Any previous file should have been closed */
639 logf("start file: file already open"); 728 logf("start file: file already open");
640 is_error = true; 729 errors |= PCMREC_E_FNQ_DESYNC;
641 } 730 }
642 731
643 if (is_error) 732 if (errors != 0)
644 rec_fdata.chunk->flags |= CHUNKF_ERROR; 733 rec_fdata.chunk->flags |= CHUNKF_ERROR;
645 734
646 /* encoder can set error flag here and should increase 735 /* encoder can set error flag here and should increase
@@ -649,13 +738,13 @@ static void pcmrec_start_file(void)
649 rec_fdata.filename = filename; 738 rec_fdata.filename = filename;
650 enc_events_callback(ENC_START_FILE, &rec_fdata); 739 enc_events_callback(ENC_START_FILE, &rec_fdata);
651 740
652 if (!is_error && (rec_fdata.chunk->flags & CHUNKF_ERROR)) 741 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
653 { 742 {
654 logf("start file: enc error"); 743 logf("start file: enc error");
655 is_error = true; 744 errors |= PCMREC_E_ENCODER;
656 } 745 }
657 746
658 if (is_error) 747 if (errors != 0)
659 { 748 {
660 pcmrec_close_file(&curr_rec_file); 749 pcmrec_close_file(&curr_rec_file);
661 /* Write no more to this file */ 750 /* Write no more to this file */
@@ -674,7 +763,7 @@ static inline void pcmrec_write_chunk(void)
674 size_t enc_size = rec_fdata.new_enc_size; 763 size_t enc_size = rec_fdata.new_enc_size;
675 unsigned long num_pcm = rec_fdata.new_num_pcm; 764 unsigned long num_pcm = rec_fdata.new_num_pcm;
676 765
677 if (is_error) 766 if (errors != 0)
678 rec_fdata.chunk->flags |= CHUNKF_ERROR; 767 rec_fdata.chunk->flags |= CHUNKF_ERROR;
679 768
680 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata); 769 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
@@ -683,11 +772,11 @@ static inline void pcmrec_write_chunk(void)
683 { 772 {
684 pcmrec_update_sizes_inl(enc_size, num_pcm); 773 pcmrec_update_sizes_inl(enc_size, num_pcm);
685 } 774 }
686 else if (!is_error) 775 else if (errors == 0)
687 { 776 {
688 logf("wr chk enc error %d %d", 777 logf("wr chk enc error %d %d",
689 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm); 778 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
690 is_error = true; 779 errors |= PCMREC_E_ENCODER;
691 } 780 }
692} /* pcmrec_write_chunk */ 781} /* pcmrec_write_chunk */
693 782
@@ -701,12 +790,12 @@ static void pcmrec_end_file(void)
701 790
702 enc_events_callback(ENC_END_FILE, &rec_fdata); 791 enc_events_callback(ENC_END_FILE, &rec_fdata);
703 792
704 if (!is_error) 793 if (errors == 0)
705 { 794 {
706 if (rec_fdata.chunk->flags & CHUNKF_ERROR) 795 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
707 { 796 {
708 logf("end file: enc error"); 797 logf("end file: enc error");
709 is_error = true; 798 errors |= PCMREC_E_ENCODER;
710 } 799 }
711 else 800 else
712 { 801 {
@@ -715,7 +804,7 @@ static void pcmrec_end_file(void)
715 } 804 }
716 805
717 /* Force file close if error */ 806 /* Force file close if error */
718 if (is_error) 807 if (errors != 0)
719 pcmrec_close_file(&rec_fdata.rec_file); 808 pcmrec_close_file(&rec_fdata.rec_file);
720 809
721 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE; 810 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
@@ -792,7 +881,7 @@ static void pcmrec_flush(unsigned flush_num)
792 881
793 cpu_boost(true); 882 cpu_boost(true);
794 883
795 for (i=0; i<num_ready; i++) 884 for (i = 0; i < num_ready; i++)
796 { 885 {
797 if (prio == -1 && (num >= panic_threshold || 886 if (prio == -1 && (num >= panic_threshold ||
798 current_tick - start_tick > 10*HZ)) 887 current_tick - start_tick > 10*HZ))
@@ -806,6 +895,8 @@ static void pcmrec_flush(unsigned flush_num)
806 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size; 895 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
807 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm; 896 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
808 897
898 PARANOID_CHUNK_CHECK(rec_fdata.chunk);
899
809 if (rec_fdata.chunk->flags & CHUNKF_START_FILE) 900 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
810 { 901 {
811 pcmrec_start_file(); 902 pcmrec_start_file();
@@ -821,7 +912,7 @@ static void pcmrec_flush(unsigned flush_num)
821 912
822 INC_ENC_INDEX(enc_rd_index); 913 INC_ENC_INDEX(enc_rd_index);
823 914
824 if (is_error) 915 if (errors != 0)
825 break; 916 break;
826 917
827 if (prio == -1) 918 if (prio == -1)
@@ -877,6 +968,9 @@ static void pcmrec_new_stream(const char *filename, /* next file name */
877 968
878 struct enc_chunk_hdr * get_prev_chunk(int index) 969 struct enc_chunk_hdr * get_prev_chunk(int index)
879 { 970 {
971#ifdef PCMREC_PARANOID
972 int index_last = index;
973#endif
880 DEC_ENC_INDEX(index); 974 DEC_ENC_INDEX(index);
881 return GET_ENC_CHUNK(index); 975 return GET_ENC_CHUNK(index);
882 } 976 }
@@ -947,8 +1041,8 @@ static void pcmrec_new_stream(const char *filename, /* next file name */
947 } 1041 }
948 else 1042 else
949 { 1043 {
950 logf("adding filename: %s", filename); 1044 logf("adding filename: %s", filename);
951 fnq_add_fn = pcmrec_fnq_add_filename; 1045 fnq_add_fn = pcmrec_fnq_add_filename;
952 } 1046 }
953 } 1047 }
954 1048
@@ -967,6 +1061,9 @@ static void pcmrec_new_stream(const char *filename, /* next file name */
967 { 1061 {
968 /* get stats on data added to start - sort of a prerecord operation */ 1062 /* get stats on data added to start - sort of a prerecord operation */
969 int i = get_chunk_index(data.chunk); 1063 int i = get_chunk_index(data.chunk);
1064#ifdef PCMREC_PARANOID
1065 int i_last = i;
1066#endif
970 struct enc_chunk_hdr *chunk = data.chunk; 1067 struct enc_chunk_hdr *chunk = data.chunk;
971 1068
972 logf("start data: %d %d", i, enc_wr_index); 1069 logf("start data: %d %d", i, enc_wr_index);
@@ -1008,14 +1105,18 @@ static void pcmrec_init(void)
1008 1105
1009 rec_fdata.rec_file = -1; 1106 rec_fdata.rec_file = -1;
1010 1107
1108 /* warings and errors */
1109 warnings =
1110 errors = 0;
1111
1011 /* pcm FIFO */ 1112 /* pcm FIFO */
1012 dma_lock = true; 1113 dma_lock = true;
1013 pcm_rd_pos = 0; 1114 SET_PCM_POS(pcm_rd_pos, 0);
1014 dma_wr_pos = 0; 1115 SET_PCM_POS(dma_wr_pos, 0);
1015 1116
1016 /* encoder FIFO */ 1117 /* encoder FIFO */
1017 enc_wr_index = 0; 1118 SET_ENC_INDEX(enc_wr_index, 0);
1018 enc_rd_index = 0; 1119 SET_ENC_INDEX(enc_rd_index, 0);
1019 1120
1020 /* filename queue */ 1121 /* filename queue */
1021 fnq_rd_pos = 0; 1122 fnq_rd_pos = 0;
@@ -1029,11 +1130,11 @@ static void pcmrec_init(void)
1029 accum_pcm_samples = 0; 1130 accum_pcm_samples = 0;
1030#endif 1131#endif
1031 1132
1032 pcm_thread_unsignal_event(PCMREC_CLOSE); 1133 pre_record_ticks = 0;
1134
1033 is_recording = false; 1135 is_recording = false;
1034 is_paused = false; 1136 is_paused = false;
1035 is_stopping = false; 1137 is_stopping = false;
1036 is_error = false;
1037 1138
1038 buffer = audio_get_recording_buffer(&rec_buffer_size); 1139 buffer = audio_get_recording_buffer(&rec_buffer_size);
1039 /* Line align pcm_buffer 2^4=16 bytes */ 1140 /* Line align pcm_buffer 2^4=16 bytes */
@@ -1044,6 +1145,7 @@ static void pcmrec_init(void)
1044 rec_buffer_size -= pcm_buffer - buffer; 1145 rec_buffer_size -= pcm_buffer - buffer;
1045 1146
1046 pcm_init_recording(); 1147 pcm_init_recording();
1148 pcm_thread_unsignal_event(PCMREC_CLOSE);
1047 pcm_thread_signal_event(PCMREC_INIT); 1149 pcm_thread_signal_event(PCMREC_INIT);
1048} /* pcmrec_init */ 1150} /* pcmrec_init */
1049 1151
@@ -1051,41 +1153,113 @@ static void pcmrec_init(void)
1051static void pcmrec_close(void) 1153static void pcmrec_close(void)
1052{ 1154{
1053 dma_lock = true; 1155 dma_lock = true;
1156 pre_record_ticks = 0; /* Can't be prerecording any more */
1157 warnings = 0;
1054 pcm_close_recording(); 1158 pcm_close_recording();
1159 reset_hardware();
1160 audio_remove_encoder();
1055 pcm_thread_unsignal_event(PCMREC_INIT); 1161 pcm_thread_unsignal_event(PCMREC_INIT);
1056 pcm_thread_signal_event(PCMREC_CLOSE); 1162 pcm_thread_signal_event(PCMREC_CLOSE);
1057} /* pcmrec_close */ 1163} /* pcmrec_close */
1058 1164
1059/* PCMREC_START */ 1165/* PCMREC_OPTIONS */
1060static void pcmrec_start(const char *filename) 1166static void pcmrec_set_recording_options(struct audio_recording_options *options)
1167{
1168 /* stop DMA transfer */
1169 dma_lock = true;
1170 pcm_stop_recording();
1171
1172 rec_frequency = options->rec_frequency;
1173 rec_source = options->rec_source;
1174 num_channels = options->rec_channels == 1 ? 1 : 2;
1175 pre_record_ticks = options->rec_prerecord_time * HZ;
1176 enc_config = options->enc_config;
1177 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
1178
1179#ifdef HAVE_SPDIF_IN
1180 if (rec_source == AUDIO_SRC_SPDIF)
1181 {
1182 /* must measure SPDIF sample rate before configuring codecs */
1183 unsigned long sr = spdif_measure_frequency();
1184 /* round to master list for SPDIF rate */
1185 int index = round_value_to_list32(sr, audio_master_sampr_list,
1186 SAMPR_NUM_FREQ, false);
1187 sample_rate = audio_master_sampr_list[index];
1188 /* round to HW playback rates for monitoring */
1189 index = round_value_to_list32(sr, hw_freq_sampr,
1190 HW_NUM_FREQ, false);
1191 pcm_set_frequency(hw_freq_sampr[index]);
1192 /* encoders with a limited number of rates do their own rounding */
1193 }
1194 else
1195#endif
1196 {
1197 /* set sample rate from frequency selection */
1198 sample_rate = rec_freq_sampr[rec_frequency];
1199 pcm_set_frequency(sample_rate);
1200 }
1201
1202 /* set monitoring */
1203 audio_set_output_source(rec_source);
1204
1205 /* apply pcm settings to hardware */
1206 pcm_apply_settings(true);
1207
1208 if (audio_load_encoder(enc_config.afmt))
1209 {
1210 /* start DMA transfer */
1211 dma_lock = pre_record_ticks == 0;
1212 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos),
1213 PCM_CHUNK_SIZE);
1214 }
1215 else
1216 {
1217 logf("set rec opt: enc load failed");
1218 errors |= PCMREC_E_LOAD_ENCODER;
1219 }
1220
1221 pcm_thread_signal_event(PCMREC_OPTIONS);
1222} /* pcmrec_set_recording_options */
1223
1224/* PCMREC_START/PCMREC_NEW_FILE - start recording (not gapless)
1225 or split stream (gapless) */
1226static void pcmrec_start(int event, const char *filename)
1061{ 1227{
1062 unsigned long pre_sample_ticks; 1228 unsigned long pre_sample_ticks;
1063 int rd_start; 1229 int rd_start;
1064 1230
1065 logf("pcmrec_start: %s", filename); 1231 logf("pcmrec_start: %s", filename);
1066 1232
1233 /* reset stats */
1234 num_rec_bytes = 0;
1235 num_rec_samples = 0;
1236
1067 if (is_recording) 1237 if (is_recording)
1068 { 1238 {
1069 logf("already recording"); 1239 /* already recording, just split the stream */
1070 goto already_recording; 1240 logf("inserting split");
1241 pcmrec_new_stream(filename,
1242 CHUNKF_START_FILE | CHUNKF_END_FILE,
1243 0);
1244 goto start_done;
1071 } 1245 }
1072 1246
1073 /* reset stats */
1074 num_rec_bytes = 0;
1075 num_rec_samples = 0;
1076#if 0 1247#if 0
1077 accum_rec_bytes = 0; 1248 accum_rec_bytes = 0;
1078 accum_pcm_samples = 0; 1249 accum_pcm_samples = 0;
1079#endif 1250#endif
1080 spinup_time = -1; 1251 spinup_time = -1;
1252 warnings = 0; /* reset warnings */
1081 1253
1082 rd_start = enc_wr_index; 1254 rd_start = enc_wr_index;
1083 pre_sample_ticks = 0; 1255 pre_sample_ticks = 0;
1084 1256
1085 if (pre_record_ticks) 1257 if (pre_record_ticks)
1086 { 1258 {
1087 int i; 1259 int i = rd_start;
1088 1260#ifdef PCMREC_PARANOID
1261 int i_last = i;
1262#endif
1089 /* calculate number of available chunks */ 1263 /* calculate number of available chunks */
1090 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index + 1264 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1091 enc_num_chunks) % enc_num_chunks; 1265 enc_num_chunks) % enc_num_chunks;
@@ -1094,7 +1268,7 @@ static void pcmrec_start(const char *filename)
1094 1268
1095 /* Get exact measure of recorded data as number of samples aren't 1269 /* Get exact measure of recorded data as number of samples aren't
1096 nescessarily going to be the max for each chunk */ 1270 nescessarily going to be the max for each chunk */
1097 for (i = rd_start; avail_pre_chunks-- > 0;) 1271 for (; avail_pre_chunks-- > 0;)
1098 { 1272 {
1099 struct enc_chunk_hdr *chunk; 1273 struct enc_chunk_hdr *chunk;
1100 unsigned long chunk_sample_ticks; 1274 unsigned long chunk_sample_ticks;
@@ -1125,7 +1299,7 @@ static void pcmrec_start(const char *filename)
1125#endif 1299#endif
1126 } 1300 }
1127 1301
1128 enc_rd_index = rd_start; 1302 SET_ENC_INDEX(enc_rd_index, rd_start);
1129 1303
1130 /* filename queue should be empty */ 1304 /* filename queue should be empty */
1131 if (!pcmrec_fnq_is_empty()) 1305 if (!pcmrec_fnq_is_empty())
@@ -1143,8 +1317,8 @@ static void pcmrec_start(const char *filename)
1143 (pre_sample_ticks > 0 ? CHUNKF_PRERECORD : 0), 1317 (pre_sample_ticks > 0 ? CHUNKF_PRERECORD : 0),
1144 enc_rd_index); 1318 enc_rd_index);
1145 1319
1146already_recording: 1320start_done:
1147 pcm_thread_signal_event(PCMREC_START); 1321 pcm_thread_signal_event(event);
1148 logf("pcmrec_start done"); 1322 logf("pcmrec_start done");
1149} /* pcmrec_start */ 1323} /* pcmrec_start */
1150 1324
@@ -1189,7 +1363,7 @@ static void pcmrec_finish_stop(void)
1189 pcmrec_flush(-1); 1363 pcmrec_flush(-1);
1190 1364
1191 /* wait for encoder to finish remaining data */ 1365 /* wait for encoder to finish remaining data */
1192 while (!is_error && !wav_queue_empty) 1366 while (errors == 0 && !wav_queue_empty)
1193 yield(); 1367 yield();
1194 1368
1195 /* end stream at last data */ 1369 /* end stream at last data */
@@ -1212,7 +1386,7 @@ static void pcmrec_finish_stop(void)
1212 } 1386 }
1213 1387
1214 /* be absolutely sure the file is closed */ 1388 /* be absolutely sure the file is closed */
1215 if (is_error) 1389 if (errors != 0)
1216 pcmrec_close_file(&rec_fdata.rec_file); 1390 pcmrec_close_file(&rec_fdata.rec_file);
1217 rec_fdata.rec_file = -1; 1391 rec_fdata.rec_file = -1;
1218 1392
@@ -1265,7 +1439,7 @@ static void pcmrec_resume(void)
1265 goto not_recording_or_not_paused; 1439 goto not_recording_or_not_paused;
1266 } 1440 }
1267 1441
1268 is_paused = false; 1442 is_paused = false;
1269 is_recording = true; 1443 is_recording = true;
1270 dma_lock = false; 1444 dma_lock = false;
1271 1445
@@ -1274,29 +1448,6 @@ not_recording_or_not_paused:
1274 logf("pcmrec_resume done"); 1448 logf("pcmrec_resume done");
1275} /* pcmrec_resume */ 1449} /* pcmrec_resume */
1276 1450
1277/* PCMREC_NEW_FILE */
1278static void pcmrec_new_file(const char *filename)
1279{
1280 logf("pcmrec_new_file: %s", filename);
1281
1282 if (!is_recording)
1283 {
1284 logf("not recording");
1285 goto not_recording;
1286 }
1287
1288 num_rec_bytes = 0;
1289 num_rec_samples = 0;
1290
1291 pcmrec_new_stream(filename,
1292 CHUNKF_START_FILE | CHUNKF_END_FILE,
1293 0);
1294
1295not_recording:
1296 pcm_thread_signal_event(PCMREC_NEW_FILE);
1297 logf("pcmrec_new_file done");
1298} /* pcmrec_new_file */
1299
1300static void pcmrec_thread(void) __attribute__((noreturn)); 1451static void pcmrec_thread(void) __attribute__((noreturn));
1301static void pcmrec_thread(void) 1452static void pcmrec_thread(void)
1302{ 1453{
@@ -1325,7 +1476,7 @@ static void pcmrec_thread(void)
1325 1476
1326 switch (ev.id) 1477 switch (ev.id)
1327 { 1478 {
1328 case PCMREC_INIT: 1479 case PCMREC_INIT:
1329 pcmrec_init(); 1480 pcmrec_init();
1330 break; 1481 break;
1331 1482
@@ -1333,8 +1484,14 @@ static void pcmrec_thread(void)
1333 pcmrec_close(); 1484 pcmrec_close();
1334 break; 1485 break;
1335 1486
1487 case PCMREC_OPTIONS:
1488 pcmrec_set_recording_options(
1489 (struct audio_recording_options *)ev.data);
1490 break;
1491
1336 case PCMREC_START: 1492 case PCMREC_START:
1337 pcmrec_start((const char *)ev.data); 1493 case PCMREC_NEW_FILE:
1494 pcmrec_start(ev.id, (const char *)ev.data);
1338 break; 1495 break;
1339 1496
1340 case PCMREC_STOP: 1497 case PCMREC_STOP:
@@ -1353,10 +1510,6 @@ static void pcmrec_thread(void)
1353 pcmrec_resume(); 1510 pcmrec_resume();
1354 break; 1511 break;
1355 1512
1356 case PCMREC_NEW_FILE:
1357 pcmrec_new_file((const char *)ev.data);
1358 break;
1359
1360 case PCMREC_FLUSH_NUM: 1513 case PCMREC_FLUSH_NUM:
1361 pcmrec_flush((unsigned)ev.data); 1514 pcmrec_flush((unsigned)ev.data);
1362 break; 1515 break;
@@ -1407,13 +1560,12 @@ void enc_set_parameters(struct enc_parameters *params)
1407 enc_sample_rate = params->enc_sample_rate; 1560 enc_sample_rate = params->enc_sample_rate;
1408 logf("enc sampr:%d", enc_sample_rate); 1561 logf("enc sampr:%d", enc_sample_rate);
1409 1562
1410 pcm_rd_pos = dma_wr_pos; 1563 SET_PCM_POS(pcm_rd_pos, dma_wr_pos);
1411 1564
1412 enc_config.afmt = params->afmt; 1565 enc_config.afmt = params->afmt;
1413 /* addition of the header is always implied - chunk size 4-byte aligned */ 1566 /* addition of the header is always implied - chunk size 4-byte aligned */
1414 enc_chunk_size = 1567 enc_chunk_size =
1415 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2); 1568 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1416 enc_data_size = enc_chunk_size - ENC_CHUNK_HDR_SIZE;
1417 enc_events_callback = params->events_callback; 1569 enc_events_callback = params->events_callback;
1418 1570
1419 logf("chunk size:%d", enc_chunk_size); 1571 logf("chunk size:%d", enc_chunk_size);
@@ -1430,7 +1582,11 @@ void enc_set_parameters(struct enc_parameters *params)
1430 logf("resbytes:%d", resbytes); 1582 logf("resbytes:%d", resbytes);
1431 1583
1432 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) - 1584 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1433 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH; 1585 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH
1586#ifdef PCMREC_PARANOID
1587 - sizeof (*wrap_id_p)
1588#endif
1589 ;
1434 1590
1435 enc_num_chunks = bufsize / enc_chunk_size; 1591 enc_num_chunks = bufsize / enc_chunk_size;
1436 logf("num chunks:%d", enc_num_chunks); 1592 logf("num chunks:%d", enc_num_chunks);
@@ -1439,6 +1595,13 @@ void enc_set_parameters(struct enc_parameters *params)
1439 bufsize = enc_num_chunks*enc_chunk_size; 1595 bufsize = enc_num_chunks*enc_chunk_size;
1440 logf("enc size:%d", bufsize); 1596 logf("enc size:%d", bufsize);
1441 1597
1598#ifdef PCMREC_PARANOID
1599 /* add magic at wraparound */
1600 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize);
1601 bufsize += sizeof (*wrap_id_p);
1602 *wrap_id_p = ENC_CHUNK_MAGIC;
1603#endif /* PCMREC_PARANOID */
1604
1442 /* panic boost thread priority at 1 second remaining */ 1605 /* panic boost thread priority at 1 second remaining */
1443 panic_threshold = enc_num_chunks - 1606 panic_threshold = enc_num_chunks -
1444 (4*sample_rate + (enc_chunk_size-1)) / enc_chunk_size; 1607 (4*sample_rate + (enc_chunk_size-1)) / enc_chunk_size;
@@ -1473,15 +1636,24 @@ void enc_set_parameters(struct enc_parameters *params)
1473 logf("pcm:%08X", (unsigned long)pcm_buffer); 1636 logf("pcm:%08X", (unsigned long)pcm_buffer);
1474 logf("enc:%08X", (unsigned long)enc_buffer); 1637 logf("enc:%08X", (unsigned long)enc_buffer);
1475 logf("res:%08X", (unsigned long)params->reserve_buffer); 1638 logf("res:%08X", (unsigned long)params->reserve_buffer);
1639#ifdef PCMREC_PARANOID
1640 logf("wip:%08X", (unsigned long)wrap_id_p);
1641#endif
1476 logf("fnq:%08X", (unsigned long)fn_queue); 1642 logf("fnq:%08X", (unsigned long)fn_queue);
1477 logf("end:%08X", (unsigned long)fn_queue + fnq_size); 1643 logf("end:%08X", (unsigned long)fn_queue + fnq_size);
1478 logf("abe:%08X", (unsigned long)audiobufend); 1644 logf("abe:%08X", (unsigned long)audiobufend);
1479#endif 1645#endif
1480 1646
1481 /* init all chunk headers and reset indexes */ 1647 /* init all chunk headers and reset indexes */
1482 enc_rd_index = 0; 1648 SET_ENC_INDEX(enc_rd_index, 0);
1483 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; ) 1649 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1484 GET_ENC_CHUNK(--enc_wr_index)->flags = 0; 1650 {
1651 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1652#ifdef PCMREC_PARANOID
1653 chunk->id = ENC_CHUNK_MAGIC;
1654#endif
1655 chunk->flags = 0;
1656 }
1485 1657
1486 logf("enc_set_parameters done"); 1658 logf("enc_set_parameters done");
1487} /* enc_set_parameters */ 1659} /* enc_set_parameters */
@@ -1490,6 +1662,15 @@ void enc_set_parameters(struct enc_parameters *params)
1490struct enc_chunk_hdr * enc_get_chunk(void) 1662struct enc_chunk_hdr * enc_get_chunk(void)
1491{ 1663{
1492 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); 1664 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1665
1666#ifdef PCMREC_PARANOID
1667 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
1668 {
1669 errors |= PCMREC_E_CHUNK_OVF;
1670 logf("finish chk ovf: %d", enc_wr_index);
1671 }
1672#endif
1673
1493 chunk->flags &= CHUNKF_START_FILE; 1674 chunk->flags &= CHUNKF_START_FILE;
1494 1675
1495 if (!is_recording) 1676 if (!is_recording)
@@ -1503,42 +1684,31 @@ void enc_finish_chunk(void)
1503{ 1684{
1504 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); 1685 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1505 1686
1506 /* encoder may have set error flag or written too much data */ 1687 if ((long)chunk->flags < 0)
1507 if ((long)chunk->flags < 0 || chunk->enc_size > enc_data_size)
1508 { 1688 {
1509 is_error = true; 1689 /* encoder set error flag */
1510 1690 errors |= PCMREC_E_ENCODER;
1511#ifdef ROCKBOX_HAS_LOGF 1691 logf("finish chk enc error");
1512 if (chunk->enc_size > enc_data_size)
1513 {
1514 /* illegal to scribble over next chunk */
1515 logf("finish chk ovf: %d>%d", chunk->enc_size, enc_data_size);
1516 }
1517 else
1518 {
1519 /* encoder set error flag */
1520 logf("finish chk enc error");
1521 }
1522#endif
1523 } 1692 }
1524 1693
1694 PARANOID_CHUNK_CHECK(chunk);
1695
1525 /* advance enc_wr_index to the next encoder chunk */ 1696 /* advance enc_wr_index to the next encoder chunk */
1526 INC_ENC_INDEX(enc_wr_index); 1697 INC_ENC_INDEX(enc_wr_index);
1527 1698
1528 if (enc_rd_index != enc_wr_index) 1699 if (enc_rd_index != enc_wr_index)
1529 { 1700 {
1530 num_rec_bytes += chunk->enc_size; 1701 num_rec_bytes += chunk->enc_size;
1531#if 0
1532 accum_rec_bytes += chunk->enc_size;
1533#endif
1534 num_rec_samples += chunk->num_pcm; 1702 num_rec_samples += chunk->num_pcm;
1535#if 0 1703#if 0
1704 accum_rec_bytes += chunk->enc_size;
1536 accum_pcm_samples += chunk->num_pcm; 1705 accum_pcm_samples += chunk->num_pcm;
1537#endif 1706#endif
1538 } 1707 }
1539 else if (is_recording) /* buffer full */ 1708 else if (is_recording) /* buffer full */
1540 { 1709 {
1541 /* keep current position */ 1710 /* keep current position - but put up warning flag */
1711 warnings |= PCMREC_W_ENC_BUFFER_OVF;
1542 logf("enc_buffer ovf"); 1712 logf("enc_buffer ovf");
1543 DEC_ENC_INDEX(enc_wr_index); 1713 DEC_ENC_INDEX(enc_wr_index);
1544 } 1714 }
@@ -1572,7 +1742,10 @@ unsigned char * enc_get_pcm_data(size_t size)
1572 if (avail >= size) 1742 if (avail >= size)
1573 { 1743 {
1574 unsigned char *ptr = pcm_buffer + pcm_rd_pos; 1744 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1575 pcm_rd_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK; 1745 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1746
1747 SET_PCM_POS(pcm_rd_pos, next_pos);
1748 pcm_rd_pos = next_pos;
1576 1749
1577 /* ptr must point to continous data at wraparound position */ 1750 /* ptr must point to continous data at wraparound position */
1578 if ((size_t)pcm_rd_pos < size) 1751 if ((size_t)pcm_rd_pos < size)
@@ -1599,12 +1772,14 @@ size_t enc_unget_pcm_data(size_t size)
1599 /* disallow backing up into current DMA write chunk */ 1772 /* disallow backing up into current DMA write chunk */
1600 size_t old_avail = (pcm_rd_pos - dma_wr_pos - PCM_CHUNK_SIZE) 1773 size_t old_avail = (pcm_rd_pos - dma_wr_pos - PCM_CHUNK_SIZE)
1601 & PCM_CHUNK_MASK; 1774 & PCM_CHUNK_MASK;
1775 int next_pos;
1602 1776
1603 /* limit size to amount of old data remaining */ 1777 /* limit size to amount of old data remaining */
1604 if (size > old_avail) 1778 if (size > old_avail)
1605 size = old_avail; 1779 size = old_avail;
1606 1780
1607 pcm_rd_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK; 1781 next_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1782 SET_PCM_POS(pcm_rd_pos, next_pos);
1608 } 1783 }
1609 1784
1610 set_irq_level(level); 1785 set_irq_level(level);