summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2006-12-10 14:21:31 +0000
committerMichael Sevakis <jethead71@rockbox.org>2006-12-10 14:21:31 +0000
commiteca9f7fd6023a2b7acde89692812d1ecbd8964e6 (patch)
treef434bcc02db8245b27de67fda4977f19a723815f
parent0a8d88228baf68c3bc515ca4a1add44aac7e5617 (diff)
downloadrockbox-eca9f7fd6023a2b7acde89692812d1ecbd8964e6.tar.gz
rockbox-eca9f7fd6023a2b7acde89692812d1ecbd8964e6.zip
Place all recording functionality on pcmrec thread to serialize all recording operations. Button mash problems should be ruled out of pcm_record.c. Add additional lightweight checks by default and display any warnings that occurred during recording in first line of recording screen when they occur by blinking back and forth from warning display to normal line (Warning: <hex bitmask>). Warnings are cleared when beginning a new recording so write the number down if you see it and file a report. Add heavier checks when PCMREC_PARANOID is defined in the player config header (encoders and pcm_record must be aware of the define since it changes the chunk header format). These checks are mainly concerned with things that may cause skipping but also add unwanted overhead for normal operation. Best used with logf enabled.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11705 a1c6a512-1295-4272-9138-f99709370657
-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);