diff options
-rw-r--r-- | apps/recorder/recording.c | 25 | ||||
-rw-r--r-- | firmware/export/audio.h | 17 | ||||
-rw-r--r-- | firmware/export/enc_base.h | 8 | ||||
-rw-r--r-- | firmware/export/pcm_record.h | 32 | ||||
-rw-r--r-- | firmware/pcm_record.c | 683 |
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 | ||
54 | struct audio_debug | 55 | struct 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 | ||
157 | struct enc_chunk_hdr | 161 | struct 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 */ |
56 | unsigned long pcm_rec_status(void); | 87 | unsigned long pcm_rec_status(void); |
88 | unsigned long pcm_rec_get_warnings(void); | ||
57 | void pcm_rec_init(void); | 89 | void pcm_rec_init(void); |
58 | int pcm_rec_current_bitrate(void); | 90 | int pcm_rec_current_bitrate(void); |
59 | int pcm_rec_encoder_afmt(void); /* AFMT_* value, AFMT_UNKNOWN if none */ | 91 | int 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; | |||
60 | volatile bool pcm_recording = false; | 60 | volatile bool pcm_recording = false; |
61 | 61 | ||
62 | /** General recording state **/ | 62 | /** General recording state **/ |
63 | static bool is_recording; /* We are recording */ | 63 | static bool is_recording; /* We are recording */ |
64 | static bool is_paused; /* We have paused */ | 64 | static bool is_paused; /* We have paused */ |
65 | static bool is_stopping; /* We are currently stopping */ | 65 | static bool is_stopping; /* We are currently stopping */ |
66 | static bool is_error; /* An error has occured */ | 66 | static unsigned long errors; /* An error has occured */ |
67 | static unsigned long warnings; /* Warning */ | ||
67 | 68 | ||
68 | /** Stats on encoded data for current file **/ | 69 | /** Stats on encoded data for current file **/ |
69 | static size_t num_rec_bytes; /* Num bytes recorded */ | 70 | static size_t num_rec_bytes; /* Num bytes recorded */ |
@@ -91,6 +92,7 @@ static int rec_frequency; /* current frequency setting */ | |||
91 | static unsigned long sample_rate; /* Sample rate in HZ */ | 92 | static unsigned long sample_rate; /* Sample rate in HZ */ |
92 | static int num_channels; /* Current number of channels */ | 93 | static int num_channels; /* Current number of channels */ |
93 | static struct encoder_config enc_config; /* Current encoder configuration */ | 94 | static struct encoder_config enc_config; /* Current encoder configuration */ |
95 | static 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 | ||
138 | static 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 | ||
132 | static size_t rec_buffer_size; /* size of available buffer */ | 185 | static size_t rec_buffer_size; /* size of available buffer */ |
133 | static unsigned char *pcm_buffer; /* circular recording buffer */ | 186 | static unsigned char *pcm_buffer; /* circular recording buffer */ |
@@ -135,14 +188,12 @@ static unsigned char *enc_buffer; /* circular encoding buffer */ | |||
135 | static volatile int dma_wr_pos; /* current DMA write pos */ | 188 | static volatile int dma_wr_pos; /* current DMA write pos */ |
136 | static int pcm_rd_pos; /* current PCM read pos */ | 189 | static int pcm_rd_pos; /* current PCM read pos */ |
137 | static volatile bool dma_lock; /* lock DMA write position */ | 190 | static volatile bool dma_lock; /* lock DMA write position */ |
138 | static unsigned long pre_record_ticks;/* pre-record time in ticks */ | ||
139 | static int enc_wr_index; /* encoder chunk write index */ | 191 | static int enc_wr_index; /* encoder chunk write index */ |
140 | static int enc_rd_index; /* encoder chunk read index */ | 192 | static int enc_rd_index; /* encoder chunk read index */ |
141 | static int enc_num_chunks; /* number of chunks in ringbuffer */ | 193 | static int enc_num_chunks; /* number of chunks in ringbuffer */ |
142 | static size_t enc_chunk_size; /* maximum encoder chunk size */ | 194 | static size_t enc_chunk_size; /* maximum encoder chunk size */ |
143 | static size_t enc_data_size; /* maximum data size for encoder */ | ||
144 | static unsigned long enc_sample_rate; /* sample rate used by encoder */ | 195 | static unsigned long enc_sample_rate; /* sample rate used by encoder */ |
145 | static bool wav_queue_empty; /* all wav chunks processed? */ | 196 | static bool wav_queue_empty; /* all wav chunks processed? */ |
146 | 197 | ||
147 | /** file flushing **/ | 198 | /** file flushing **/ |
148 | static int write_threshold; /* max chunk limit for data flush */ | 199 | static int write_threshold; /* max chunk limit for data flush */ |
@@ -159,6 +210,16 @@ static ssize_t fnq_size; /* capacity of queue in bytes */ | |||
159 | static int fnq_rd_pos; /* current read position */ | 210 | static int fnq_rd_pos; /* current read position */ |
160 | static int fnq_wr_pos; /* current write position */ | 211 | static int fnq_wr_pos; /* current write position */ |
161 | 212 | ||
213 | /** extra debugging info positioned away from other vars **/ | ||
214 | #ifdef PCMREC_PARANOID | ||
215 | static unsigned long *wrap_id_p; /* magic at end of encoding buffer */ | ||
216 | static volatile int dma_wr_pos_last; /* previous dma write position */ | ||
217 | static int pcm_rd_pos_last; /* previous pcm read position */ | ||
218 | static int enc_rd_index_last; /* previsou encoder read position */ | ||
219 | static int enc_wr_index_last; /* previsou encoder read position */ | ||
220 | #endif | ||
221 | |||
222 | |||
162 | /***************************************************************************/ | 223 | /***************************************************************************/ |
163 | 224 | ||
164 | static struct event_queue pcmrec_queue; | 225 | static 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 */ |
184 | static volatile long pcm_thread_event_mask; | 244 | static volatile long pcm_thread_event_mask = PCMREC_CLOSE; |
185 | 245 | ||
186 | static void pcm_thread_sync_post(long event, void *data) | 246 | static 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 | ||
209 | static 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 */ |
224 | static int pcm_rec_have_more(int status) | 274 | static 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 | */ | ||
256 | void pcm_rec_error_clear(void) | 326 | void 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 | */ | ||
261 | unsigned long pcm_rec_status(void) | 334 | unsigned 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 | */ | ||
358 | unsigned long pcm_rec_get_warnings(void) | ||
359 | { | ||
360 | return warnings; | ||
361 | } | ||
362 | |||
281 | #if 0 | 363 | #if 0 |
282 | int pcm_rec_current_bitrate(void) | 364 | int 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 | */ | ||
328 | void audio_init_recording(unsigned int buffer_offset) | 417 | void 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 | */ | ||
335 | void audio_close_recording(void) | 428 | void 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 | ||
343 | unsigned 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 | |||
353 | unsigned 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 | */ | ||
368 | int 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 | */ |
380 | void audio_set_recording_options(struct audio_recording_options *options) | 438 | void 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 | */ |
443 | void audio_record(const char *filename) | 448 | void 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 | */ | ||
453 | void audio_new_file(const char *filename) | 458 | void 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 | */ | ||
463 | void audio_stop_recording(void) | 468 | void 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 | */ | ||
473 | void audio_pause_recording(void) | 478 | void 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 | */ | ||
483 | void audio_resume_recording(void) | 488 | void 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 | */ | ||
500 | unsigned 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 | */ | ||
513 | unsigned 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 | */ | ||
528 | int 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 | ||
628 | static 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) | |||
1051 | static void pcmrec_close(void) | 1153 | static 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 */ |
1060 | static void pcmrec_start(const char *filename) | 1166 | static 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) */ | ||
1226 | static 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 | ||
1146 | already_recording: | 1320 | start_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 */ | ||
1278 | static 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 | |||
1295 | not_recording: | ||
1296 | pcm_thread_signal_event(PCMREC_NEW_FILE); | ||
1297 | logf("pcmrec_new_file done"); | ||
1298 | } /* pcmrec_new_file */ | ||
1299 | |||
1300 | static void pcmrec_thread(void) __attribute__((noreturn)); | 1451 | static void pcmrec_thread(void) __attribute__((noreturn)); |
1301 | static void pcmrec_thread(void) | 1452 | static 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) | |||
1490 | struct enc_chunk_hdr * enc_get_chunk(void) | 1662 | struct 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); |