diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/pcm_record.c | 651 |
1 files changed, 405 insertions, 246 deletions
diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c index 98473a3870..85ab7caafc 100644 --- a/firmware/pcm_record.c +++ b/firmware/pcm_record.c | |||
@@ -46,6 +46,7 @@ | |||
46 | 46 | ||
47 | /** These items may be implemented target specifically or need to | 47 | /** These items may be implemented target specifically or need to |
48 | be shared semi-privately **/ | 48 | be shared semi-privately **/ |
49 | extern struct thread_entry *codec_thread_p; | ||
49 | 50 | ||
50 | /* the registered callback function for when more data is available */ | 51 | /* the registered callback function for when more data is available */ |
51 | volatile pcm_more_callback_type2 pcm_callback_more_ready = NULL; | 52 | volatile pcm_more_callback_type2 pcm_callback_more_ready = NULL; |
@@ -57,6 +58,14 @@ static bool is_recording; /* We are recording */ | |||
57 | static bool is_paused; /* We have paused */ | 58 | static bool is_paused; /* We have paused */ |
58 | static unsigned long errors; /* An error has occured */ | 59 | static unsigned long errors; /* An error has occured */ |
59 | static unsigned long warnings; /* Warning */ | 60 | static unsigned long warnings; /* Warning */ |
61 | static int flush_interrupts = 0; /* Number of messages queued that | ||
62 | should interrupt a flush in | ||
63 | progress - | ||
64 | for a safety net and a prompt | ||
65 | response to stop, split and pause | ||
66 | requests - | ||
67 | only interrupts a flush initiated | ||
68 | by pcmrec_flush(0) */ | ||
60 | 69 | ||
61 | /** Stats on encoded data for current file **/ | 70 | /** Stats on encoded data for current file **/ |
62 | static size_t num_rec_bytes; /* Num bytes recorded */ | 71 | static size_t num_rec_bytes; /* Num bytes recorded */ |
@@ -179,31 +188,62 @@ static unsigned char *pcm_buffer; /* circular recording buffer */ | |||
179 | static unsigned char *enc_buffer; /* circular encoding buffer */ | 188 | static unsigned char *enc_buffer; /* circular encoding buffer */ |
180 | static volatile int dma_wr_pos; /* current DMA write pos */ | 189 | static volatile int dma_wr_pos; /* current DMA write pos */ |
181 | static int pcm_rd_pos; /* current PCM read pos */ | 190 | static int pcm_rd_pos; /* current PCM read pos */ |
191 | static int pcm_enc_pos; /* position encoder is processing */ | ||
182 | static volatile bool dma_lock; /* lock DMA write position */ | 192 | static volatile bool dma_lock; /* lock DMA write position */ |
183 | static int enc_wr_index; /* encoder chunk write index */ | 193 | static int enc_wr_index; /* encoder chunk write index */ |
184 | static int enc_rd_index; /* encoder chunk read index */ | 194 | static int enc_rd_index; /* encoder chunk read index */ |
185 | static int enc_num_chunks; /* number of chunks in ringbuffer */ | 195 | static int enc_num_chunks; /* number of chunks in ringbuffer */ |
186 | static size_t enc_chunk_size; /* maximum encoder chunk size */ | 196 | static size_t enc_chunk_size; /* maximum encoder chunk size */ |
187 | static unsigned long enc_sample_rate; /* sample rate used by encoder */ | 197 | static unsigned long enc_sample_rate; /* sample rate used by encoder */ |
188 | static bool wav_queue_empty; /* all wav chunks processed? */ | 198 | static bool pcmrec_context = false; /* called by pcmrec thread? */ |
199 | static bool pcm_buffer_empty; /* all pcm chunks processed? */ | ||
189 | 200 | ||
190 | /** file flushing **/ | 201 | /** file flushing **/ |
191 | static int write_threshold; /* max chunk limit for data flush */ | 202 | static int low_watermark; /* Low watermark to stop flush */ |
192 | static int spinup_time = -1;/* last ata_spinup_time */ | 203 | static int high_watermark; /* max chunk limit for data flush */ |
204 | static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */ | ||
205 | static int last_ata_spinup_time = -1;/* previous spin time used */ | ||
193 | #ifdef HAVE_PRIORITY_SCHEDULING | 206 | #ifdef HAVE_PRIORITY_SCHEDULING |
194 | static int panic_threshold; /* boost thread prio when here */ | 207 | static int flood_watermark; /* boost thread priority when here */ |
195 | #endif | 208 | #endif |
196 | 209 | ||
210 | /* Constants that control watermarks */ | ||
211 | #define LOW_SECONDS 1 /* low watermark time till empty */ | ||
212 | #define MINI_CHUNKS 10 /* chunk count for mini flush */ | ||
213 | #ifdef HAVE_PRIORITY_SCHEDULING | ||
214 | #define PRIO_SECONDS 10 /* max flush time before priority boost */ | ||
215 | #endif | ||
216 | #if MEM <= 16 | ||
217 | #define PANIC_SECONDS 5 /* flood watermark time until full */ | ||
218 | #define FLUSH_SECONDS 7 /* flush watermark time until full */ | ||
219 | #else | ||
220 | #define PANIC_SECONDS 8 | ||
221 | #define FLUSH_SECONDS 10 | ||
222 | #endif /* MEM */ | ||
223 | |||
197 | /** encoder events **/ | 224 | /** encoder events **/ |
198 | static void (*enc_events_callback)(enum enc_events event, void *data); | 225 | static void (*enc_events_callback)(enum enc_events event, void *data); |
199 | 226 | ||
200 | /** Path queue for files to write **/ | 227 | /** Path queue for files to write **/ |
201 | #define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */ | 228 | #define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */ |
229 | #define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */ | ||
202 | static unsigned char *fn_queue; /* pointer to first filename */ | 230 | static unsigned char *fn_queue; /* pointer to first filename */ |
203 | static ssize_t fnq_size; /* capacity of queue in bytes */ | 231 | static ssize_t fnq_size; /* capacity of queue in bytes */ |
204 | static int fnq_rd_pos; /* current read position */ | 232 | static int fnq_rd_pos; /* current read position */ |
205 | static int fnq_wr_pos; /* current write position */ | 233 | static int fnq_wr_pos; /* current write position */ |
206 | 234 | ||
235 | enum | ||
236 | { | ||
237 | PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by | ||
238 | incoming messages - combine | ||
239 | with other constants */ | ||
240 | PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */ | ||
241 | PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of | ||
242 | chunks */ | ||
243 | PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark | ||
244 | reached */ | ||
245 | }; | ||
246 | |||
207 | /** extra debugging info positioned away from other vars **/ | 247 | /** extra debugging info positioned away from other vars **/ |
208 | #ifdef PCMREC_PARANOID | 248 | #ifdef PCMREC_PARANOID |
209 | static unsigned long *wrap_id_p; /* magic at end of encoding buffer */ | 249 | static unsigned long *wrap_id_p; /* magic at end of encoding buffer */ |
@@ -220,6 +260,7 @@ static struct event_queue pcmrec_queue; | |||
220 | static struct queue_sender_list pcmrec_queue_send; | 260 | static struct queue_sender_list pcmrec_queue_send; |
221 | static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)]; | 261 | static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)]; |
222 | static const char pcmrec_thread_name[] = "pcmrec"; | 262 | static const char pcmrec_thread_name[] = "pcmrec"; |
263 | static struct thread_entry *pcmrec_thread_p; | ||
223 | 264 | ||
224 | static void pcmrec_thread(void); | 265 | static void pcmrec_thread(void); |
225 | 266 | ||
@@ -261,8 +302,9 @@ static int pcm_rec_have_more(int status) | |||
261 | /* advance write position */ | 302 | /* advance write position */ |
262 | int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK; | 303 | int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK; |
263 | 304 | ||
264 | /* set pcm ovf if read position is inside current write chunk */ | 305 | /* set pcm ovf if processing start position is inside current |
265 | if ((unsigned)(pcm_rd_pos - next_pos) < PCM_CHUNK_SIZE) | 306 | write chunk */ |
307 | if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE) | ||
266 | warnings |= PCMREC_W_PCM_BUFFER_OVF; | 308 | warnings |= PCMREC_W_PCM_BUFFER_OVF; |
267 | 309 | ||
268 | #ifdef PCMREC_PARANOID | 310 | #ifdef PCMREC_PARANOID |
@@ -374,16 +416,13 @@ void pcm_rec_init(void) | |||
374 | { | 416 | { |
375 | queue_init(&pcmrec_queue, true); | 417 | queue_init(&pcmrec_queue, true); |
376 | queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send); | 418 | queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send); |
377 | create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), | 419 | pcmrec_thread_p = |
378 | pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)); | 420 | create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), |
421 | pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)); | ||
379 | } /* pcm_rec_init */ | 422 | } /* pcm_rec_init */ |
380 | 423 | ||
381 | /** audio_* group **/ | 424 | /** audio_* group **/ |
382 | 425 | ||
383 | /* NOTE: The following posting functions are really only single-thread safe | ||
384 | at the moment since a response to a particular message at a particular | ||
385 | position in the queue can't be distinguished */ | ||
386 | |||
387 | /** | 426 | /** |
388 | * Initializes recording - call before calling any other recording function | 427 | * Initializes recording - call before calling any other recording function |
389 | */ | 428 | */ |
@@ -421,6 +460,8 @@ void audio_set_recording_options(struct audio_recording_options *options) | |||
421 | void audio_record(const char *filename) | 460 | void audio_record(const char *filename) |
422 | { | 461 | { |
423 | logf("audio_record: %s", filename); | 462 | logf("audio_record: %s", filename); |
463 | flush_interrupts++; | ||
464 | logf("flush int: %d", flush_interrupts); | ||
424 | queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename); | 465 | queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename); |
425 | logf("audio_record_done"); | 466 | logf("audio_record_done"); |
426 | } /* audio_record */ | 467 | } /* audio_record */ |
@@ -431,6 +472,8 @@ void audio_record(const char *filename) | |||
431 | void audio_stop_recording(void) | 472 | void audio_stop_recording(void) |
432 | { | 473 | { |
433 | logf("audio_stop_recording"); | 474 | logf("audio_stop_recording"); |
475 | flush_interrupts++; | ||
476 | logf("flush int: %d", flush_interrupts); | ||
434 | queue_send(&pcmrec_queue, PCMREC_STOP, 0); | 477 | queue_send(&pcmrec_queue, PCMREC_STOP, 0); |
435 | logf("audio_stop_recording done"); | 478 | logf("audio_stop_recording done"); |
436 | } /* audio_stop_recording */ | 479 | } /* audio_stop_recording */ |
@@ -441,6 +484,8 @@ void audio_stop_recording(void) | |||
441 | void audio_pause_recording(void) | 484 | void audio_pause_recording(void) |
442 | { | 485 | { |
443 | logf("audio_pause_recording"); | 486 | logf("audio_pause_recording"); |
487 | flush_interrupts++; | ||
488 | logf("flush int: %d", flush_interrupts); | ||
444 | queue_send(&pcmrec_queue, PCMREC_PAUSE, 0); | 489 | queue_send(&pcmrec_queue, PCMREC_PAUSE, 0); |
445 | logf("audio_pause_recording done"); | 490 | logf("audio_pause_recording done"); |
446 | } /* audio_pause_recording */ | 491 | } /* audio_pause_recording */ |
@@ -787,95 +832,166 @@ static void pcmrec_end_file(void) | |||
787 | } /* pcmrec_end_file */ | 832 | } /* pcmrec_end_file */ |
788 | 833 | ||
789 | /** | 834 | /** |
835 | * Update buffer watermarks with spinup time compensation | ||
836 | * | ||
837 | * All this assumes reasonable data rates, chunk sizes and sufficient | ||
838 | * memory for the most part. Some dumb checks are included but perhaps | ||
839 | * are pointless since this all will break down at extreme limits that | ||
840 | * are currently not applicable to any supported device. | ||
841 | */ | ||
842 | static void pcmrec_refresh_watermarks(void) | ||
843 | { | ||
844 | logf("ata spinup: %d", ata_spinup_time); | ||
845 | |||
846 | /* set the low mark for when flushing stops if automatic */ | ||
847 | low_watermark = (LOW_SECONDS*4*sample_rate + (enc_chunk_size-1)) | ||
848 | / enc_chunk_size; | ||
849 | logf("low wmk: %d", low_watermark); | ||
850 | |||
851 | #ifdef HAVE_PRIORITY_SCHEDULING | ||
852 | /* panic boost thread priority if 2 seconds of ground is lost - | ||
853 | this allows encoder to boost with just under a second of | ||
854 | pcm data (if not yet full enough to boost itself) | ||
855 | and not falsely trip the alarm. */ | ||
856 | flood_watermark = enc_num_chunks - | ||
857 | (PANIC_SECONDS*4*sample_rate + (enc_chunk_size-1)) | ||
858 | / enc_chunk_size; | ||
859 | |||
860 | if (flood_watermark < low_watermark) | ||
861 | { | ||
862 | logf("warning: panic < low"); | ||
863 | flood_watermark = low_watermark; | ||
864 | } | ||
865 | |||
866 | logf("flood at: %d", flood_watermark); | ||
867 | #endif | ||
868 | spinup_time = last_ata_spinup_time = ata_spinup_time; | ||
869 | |||
870 | /* write at 8s + st remaining in enc_buffer - range 12s to | ||
871 | 20s total - default to 3.5s spinup. */ | ||
872 | if (spinup_time == 0) | ||
873 | spinup_time = 35*HZ/10; /* default - cozy */ | ||
874 | else if (spinup_time < 2*HZ) | ||
875 | spinup_time = 2*HZ; /* ludicrous - ramdisk? */ | ||
876 | else if (spinup_time > 10*HZ) | ||
877 | spinup_time = 10*HZ; /* do you have a functioning HD? */ | ||
878 | |||
879 | /* try to start writing with 10s remaining after disk spinup */ | ||
880 | high_watermark = enc_num_chunks - | ||
881 | ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate + | ||
882 | (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ); | ||
883 | |||
884 | if (high_watermark < low_watermark) | ||
885 | { | ||
886 | high_watermark = low_watermark; | ||
887 | low_watermark /= 2; | ||
888 | logf("warning: low 'write at'"); | ||
889 | } | ||
890 | |||
891 | logf("write at: %d", high_watermark); | ||
892 | } /* pcmrec_refresh_watermarks */ | ||
893 | |||
894 | /** | ||
790 | * Process the chunks | 895 | * Process the chunks |
791 | * | 896 | * |
792 | * This function is called when queue_get_w_tmo times out. | 897 | * This function is called when queue_get_w_tmo times out. |
793 | * | 898 | * |
794 | * Set flush_num to the number of files to flush to disk. | 899 | * Set flush_num to the number of files to flush to disk or to |
795 | * flush_num = -1 to flush all available chunks to disk. | 900 | * a PCMREC_FLUSH_* constant. |
796 | * flush_num = 0 normal write thresholding | ||
797 | * flush_num = 1 or greater - all available chunks of current file plus | ||
798 | * flush_num file starts if first chunk has been processed. | ||
799 | * | ||
800 | */ | 901 | */ |
801 | static void pcmrec_flush(unsigned flush_num) | 902 | static void pcmrec_flush(unsigned flush_num) |
802 | { | 903 | { |
803 | #ifdef HAVE_PRIORITY_SCHEDULING | 904 | #ifdef HAVE_PRIORITY_SCHEDULING |
804 | static unsigned long last_flush_tick = 0; | 905 | static unsigned long last_flush_tick; /* tick when function returned */ |
805 | unsigned long start_tick; | 906 | unsigned long start_tick; /* When flush started */ |
806 | int num; | 907 | unsigned long prio_tick; /* Timeout for auto boost */ |
807 | int prio; | 908 | int prio_pcmrec; /* Current thread priority for pcmrec */ |
909 | int prio_codec; /* Current thread priority for codec */ | ||
808 | #endif | 910 | #endif |
809 | int num_ready; | 911 | int num_ready; /* Number of chunks ready at start */ |
810 | int i; | 912 | unsigned remaining; /* Number of file starts remaining */ |
913 | unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */ | ||
914 | bool interruptable; /* Flush can be interupted */ | ||
811 | 915 | ||
812 | num_ready = enc_wr_index - enc_rd_index; | 916 | num_ready = enc_wr_index - enc_rd_index; |
813 | if (num_ready < 0) | 917 | if (num_ready < 0) |
814 | num_ready += enc_num_chunks; | 918 | num_ready += enc_num_chunks; |
815 | 919 | ||
816 | #ifdef HAVE_PRIORITY_SCHEDULING | 920 | /* save interruptable flag and remove it to get the actual count */ |
817 | num = num_ready; | 921 | interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0; |
818 | #endif | 922 | flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE; |
819 | 923 | ||
820 | if (flush_num == 0) | 924 | if (flush_num == 0) |
821 | { | 925 | { |
822 | if (!is_recording) | 926 | if (!is_recording) |
823 | return; | 927 | return; |
824 | 928 | ||
825 | if (ata_spinup_time != spinup_time) | 929 | if (ata_spinup_time != last_ata_spinup_time) |
826 | { | 930 | pcmrec_refresh_watermarks(); |
827 | /* spinup time has changed, calculate new write threshold */ | ||
828 | logf("new t spinup : %d", ata_spinup_time); | ||
829 | unsigned long st = spinup_time = ata_spinup_time; | ||
830 | |||
831 | /* write at 5s + st remaining in enc_buffer */ | ||
832 | if (st < 2*HZ) | ||
833 | st = 2*HZ; /* my drive is usually < 250 ticks :) */ | ||
834 | else if (st > 10*HZ) | ||
835 | st = 10*HZ; | ||
836 | |||
837 | write_threshold = enc_num_chunks - | ||
838 | (int)(((5ull*HZ + st)*4ull*sample_rate + (enc_chunk_size-1)) / | ||
839 | (enc_chunk_size*HZ)); | ||
840 | |||
841 | if (write_threshold < 0) | ||
842 | write_threshold = 0; | ||
843 | #ifdef HAVE_PRIORITY_SCHEDULING | ||
844 | else if (write_threshold > panic_threshold) | ||
845 | write_threshold = panic_threshold; | ||
846 | #endif | ||
847 | logf("new wr thresh: %d", write_threshold); | ||
848 | } | ||
849 | 931 | ||
850 | if (num_ready < write_threshold) | 932 | /* enough available? no? then leave */ |
933 | if (num_ready < high_watermark) | ||
851 | return; | 934 | return; |
935 | } /* endif (flush_num == 0) */ | ||
852 | 936 | ||
853 | #ifdef HAVE_PRIORITY_SCHEDULING | 937 | #ifdef HAVE_PRIORITY_SCHEDULING |
938 | start_tick = current_tick; | ||
939 | prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time; | ||
940 | |||
941 | if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2)) | ||
942 | { | ||
854 | /* if we're getting called too much and this isn't forced, | 943 | /* if we're getting called too much and this isn't forced, |
855 | boost stat */ | 944 | boost stat by expiring timeout in advance */ |
856 | if (current_tick - last_flush_tick < HZ/2) | 945 | logf("too frequent flush"); |
857 | num = panic_threshold; | 946 | prio_tick = current_tick - 1; |
858 | #endif | ||
859 | } | 947 | } |
860 | 948 | ||
861 | #ifdef HAVE_PRIORITY_SCHEDULING | 949 | prio_pcmrec = -1; |
862 | start_tick = current_tick; | 950 | prio_codec = -1; /* GCC is too stoopid to figure out it doesn't |
863 | prio = -1; | 951 | need init */ |
864 | #endif | 952 | #endif |
865 | 953 | ||
866 | logf("writing: %d (%d)", num_ready, flush_num); | 954 | logf("writing:%d(%d):%s%s", num_ready, flush_num, |
955 | interruptable ? "i" : "", | ||
956 | flush_num == PCMREC_FLUSH_MINI ? "m" : ""); | ||
867 | 957 | ||
868 | cpu_boost(true); | 958 | cpu_boost(true); |
869 | 959 | ||
870 | for (i = 0; i < num_ready; i++) | 960 | remaining = flush_num; |
961 | chunks_flushed = 0; | ||
962 | |||
963 | while (num_ready > 0) | ||
871 | { | 964 | { |
965 | /* check current number of encoder chunks */ | ||
966 | int num = enc_wr_index - enc_rd_index; | ||
967 | if (num < 0) | ||
968 | num += enc_num_chunks; | ||
969 | |||
970 | if (num <= low_watermark && | ||
971 | (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0)) | ||
972 | { | ||
973 | logf("low data: %d", num); | ||
974 | break; /* data remaining is below threshold */ | ||
975 | } | ||
976 | |||
977 | if (interruptable && flush_interrupts > 0) | ||
978 | { | ||
979 | logf("int at: %d", num); | ||
980 | break; /* interrupted */ | ||
981 | } | ||
982 | |||
872 | #ifdef HAVE_PRIORITY_SCHEDULING | 983 | #ifdef HAVE_PRIORITY_SCHEDULING |
873 | if (prio == -1 && (num >= panic_threshold || | 984 | if (prio_pcmrec == -1 && (num >= flood_watermark || |
874 | current_tick - start_tick > 10*HZ)) | 985 | TIME_AFTER(current_tick, prio_tick))) |
875 | { | 986 | { |
876 | /* losing ground - boost priority until finished */ | 987 | /* losing ground or holding without progress - boost |
877 | logf("pcmrec: boost priority"); | 988 | priority until finished */ |
878 | prio = thread_set_priority(NULL, thread_get_priority(NULL)-1); | 989 | logf("pcmrec: boost (%s)", |
990 | num >= flood_watermark ? "num" : "time"); | ||
991 | prio_pcmrec = thread_set_priority(NULL, | ||
992 | thread_get_priority(NULL) - 1); | ||
993 | prio_codec = thread_set_priority(codec_thread_p, | ||
994 | thread_get_priority(codec_thread_p) - 1); | ||
879 | } | 995 | } |
880 | #endif | 996 | #endif |
881 | 997 | ||
@@ -888,8 +1004,8 @@ static void pcmrec_flush(unsigned flush_num) | |||
888 | if (rec_fdata.chunk->flags & CHUNKF_START_FILE) | 1004 | if (rec_fdata.chunk->flags & CHUNKF_START_FILE) |
889 | { | 1005 | { |
890 | pcmrec_start_file(); | 1006 | pcmrec_start_file(); |
891 | if (--flush_num == 0) | 1007 | if (--remaining == 0) |
892 | i = num_ready; /* stop on next loop - must write this | 1008 | num_ready = 0; /* stop on next loop - must write this |
893 | chunk if it has data */ | 1009 | chunk if it has data */ |
894 | } | 1010 | } |
895 | 1011 | ||
@@ -903,16 +1019,15 @@ static void pcmrec_flush(unsigned flush_num) | |||
903 | if (errors != 0) | 1019 | if (errors != 0) |
904 | break; | 1020 | break; |
905 | 1021 | ||
906 | #ifdef HAVE_PRIORITY_SCHEDULING | 1022 | if (flush_num == PCMREC_FLUSH_MINI && |
907 | if (prio == -1) | 1023 | ++chunks_flushed >= MINI_CHUNKS) |
908 | { | 1024 | { |
909 | num = enc_wr_index - enc_rd_index; | 1025 | logf("mini flush break"); |
910 | if (num < 0) | 1026 | break; |
911 | num += enc_num_chunks; | ||
912 | } | 1027 | } |
913 | #endif | 1028 | /* no yielding; the file apis called in the codecs do that |
914 | /* no yielding, the file apis called in the codecs do that */ | 1029 | sufficiently */ |
915 | } /* end for */ | 1030 | } /* end while */ |
916 | 1031 | ||
917 | /* sync file */ | 1032 | /* sync file */ |
918 | if (rec_fdata.rec_file >= 0) | 1033 | if (rec_fdata.rec_file >= 0) |
@@ -921,11 +1036,12 @@ static void pcmrec_flush(unsigned flush_num) | |||
921 | cpu_boost(false); | 1036 | cpu_boost(false); |
922 | 1037 | ||
923 | #ifdef HAVE_PRIORITY_SCHEDULING | 1038 | #ifdef HAVE_PRIORITY_SCHEDULING |
924 | if (prio != -1) | 1039 | if (prio_pcmrec != -1) |
925 | { | 1040 | { |
926 | /* return to original priority */ | 1041 | /* return to original priorities */ |
927 | logf("pcmrec: unboost priority"); | 1042 | logf("pcmrec: unboost priority"); |
928 | thread_set_priority(NULL, prio); | 1043 | thread_set_priority(NULL, prio_pcmrec); |
1044 | thread_set_priority(codec_thread_p, prio_codec); | ||
929 | } | 1045 | } |
930 | 1046 | ||
931 | last_flush_tick = current_tick; /* save tick when we left */ | 1047 | last_flush_tick = current_tick; /* save tick when we left */ |
@@ -948,10 +1064,14 @@ static void pcmrec_new_stream(const char *filename, /* next file name */ | |||
948 | int pre_index) /* index for prerecorded data */ | 1064 | int pre_index) /* index for prerecorded data */ |
949 | { | 1065 | { |
950 | logf("pcmrec_new_stream"); | 1066 | logf("pcmrec_new_stream"); |
1067 | char path[MAX_PATH]; /* place to copy filename so sender can be released */ | ||
951 | 1068 | ||
952 | struct enc_buffer_event_data data; | 1069 | struct enc_buffer_event_data data; |
953 | bool (*fnq_add_fn)(const char *) = NULL; | 1070 | bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add |
954 | struct enc_chunk_hdr *start = NULL; | 1071 | new filename */ |
1072 | struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of | ||
1073 | stream */ | ||
1074 | bool did_flush = false; /* did a flush occurr? */ | ||
955 | 1075 | ||
956 | int get_chunk_index(struct enc_chunk_hdr *chunk) | 1076 | int get_chunk_index(struct enc_chunk_hdr *chunk) |
957 | { | 1077 | { |
@@ -967,6 +1087,10 @@ static void pcmrec_new_stream(const char *filename, /* next file name */ | |||
967 | return GET_ENC_CHUNK(index); | 1087 | return GET_ENC_CHUNK(index); |
968 | } | 1088 | } |
969 | 1089 | ||
1090 | if (filename) | ||
1091 | strncpy(path, filename, MAX_PATH); | ||
1092 | queue_reply(&pcmrec_queue, 0); /* We have all we need */ | ||
1093 | |||
970 | data.pre_chunk = NULL; | 1094 | data.pre_chunk = NULL; |
971 | data.chunk = GET_ENC_CHUNK(enc_wr_index); | 1095 | data.chunk = GET_ENC_CHUNK(enc_wr_index); |
972 | 1096 | ||
@@ -1039,7 +1163,9 @@ static void pcmrec_new_stream(const char *filename, /* next file name */ | |||
1039 | } | 1163 | } |
1040 | 1164 | ||
1041 | data.flags = flags; | 1165 | data.flags = flags; |
1166 | pcmrec_context = true; /* switch encoder context */ | ||
1042 | enc_events_callback(ENC_REC_NEW_STREAM, &data); | 1167 | enc_events_callback(ENC_REC_NEW_STREAM, &data); |
1168 | pcmrec_context = false; /* switch back */ | ||
1043 | 1169 | ||
1044 | if (flags & CHUNKF_END_FILE) | 1170 | if (flags & CHUNKF_END_FILE) |
1045 | { | 1171 | { |
@@ -1049,11 +1175,10 @@ static void pcmrec_new_stream(const char *filename, /* next file name */ | |||
1049 | 1175 | ||
1050 | if (start) | 1176 | if (start) |
1051 | { | 1177 | { |
1052 | char buf[MAX_PATH]; /* place to copy in case we're full */ | ||
1053 | |||
1054 | if (!(flags & CHUNKF_PRERECORD)) | 1178 | if (!(flags & CHUNKF_PRERECORD)) |
1055 | { | 1179 | { |
1056 | /* get stats on data added to start - sort of a prerecord operation */ | 1180 | /* get stats on data added to start - sort of a prerecord |
1181 | operation */ | ||
1057 | int i = get_chunk_index(data.chunk); | 1182 | int i = get_chunk_index(data.chunk); |
1058 | #ifdef PCMREC_PARANOID | 1183 | #ifdef PCMREC_PARANOID |
1059 | int i_last = i; | 1184 | int i_last = i; |
@@ -1083,16 +1208,16 @@ static void pcmrec_new_stream(const char *filename, /* next file name */ | |||
1083 | if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full()) | 1208 | if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full()) |
1084 | { | 1209 | { |
1085 | logf("fnq full"); | 1210 | logf("fnq full"); |
1086 | /* make a local copy of filename and let sender go as this | 1211 | pcmrec_flush(PCMREC_FLUSH_ALL); |
1087 | flush will hang the screen for a bit otherwise */ | 1212 | did_flush = true; |
1088 | strncpy(buf, filename, MAX_PATH); | ||
1089 | filename = buf; | ||
1090 | queue_reply(&pcmrec_queue, 0); | ||
1091 | pcmrec_flush(-1); | ||
1092 | } | 1213 | } |
1093 | 1214 | ||
1094 | fnq_add_fn(filename); | 1215 | fnq_add_fn(path); |
1095 | } | 1216 | } |
1217 | |||
1218 | /* Make sure to complete any interrupted high watermark */ | ||
1219 | if (!did_flush) | ||
1220 | pcmrec_flush(PCMREC_FLUSH_IF_HIGH); | ||
1096 | } /* pcmrec_new_stream */ | 1221 | } /* pcmrec_new_stream */ |
1097 | 1222 | ||
1098 | /** event handlers for pcmrec thread */ | 1223 | /** event handlers for pcmrec thread */ |
@@ -1102,6 +1227,7 @@ static void pcmrec_init(void) | |||
1102 | { | 1227 | { |
1103 | unsigned char *buffer; | 1228 | unsigned char *buffer; |
1104 | 1229 | ||
1230 | pcmrec_close_file(&rec_fdata.rec_file); | ||
1105 | rec_fdata.rec_file = -1; | 1231 | rec_fdata.rec_file = -1; |
1106 | 1232 | ||
1107 | /* warings and errors */ | 1233 | /* warings and errors */ |
@@ -1112,6 +1238,7 @@ static void pcmrec_init(void) | |||
1112 | dma_lock = true; | 1238 | dma_lock = true; |
1113 | SET_PCM_POS(pcm_rd_pos, 0); | 1239 | SET_PCM_POS(pcm_rd_pos, 0); |
1114 | SET_PCM_POS(dma_wr_pos, 0); | 1240 | SET_PCM_POS(dma_wr_pos, 0); |
1241 | pcm_enc_pos = 0; | ||
1115 | 1242 | ||
1116 | /* encoder FIFO */ | 1243 | /* encoder FIFO */ |
1117 | SET_ENC_INDEX(enc_wr_index, 0); | 1244 | SET_ENC_INDEX(enc_wr_index, 0); |
@@ -1137,7 +1264,7 @@ static void pcmrec_init(void) | |||
1137 | buffer = audio_get_recording_buffer(&rec_buffer_size); | 1264 | buffer = audio_get_recording_buffer(&rec_buffer_size); |
1138 | 1265 | ||
1139 | /* Line align pcm_buffer 2^4=16 bytes */ | 1266 | /* Line align pcm_buffer 2^4=16 bytes */ |
1140 | pcm_buffer = (unsigned char *)ALIGN_UP_P2((unsigned long)buffer, 4); | 1267 | pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 4); |
1141 | enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE + | 1268 | enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE + |
1142 | PCM_MAX_FEED_SIZE, 2); | 1269 | PCM_MAX_FEED_SIZE, 2); |
1143 | /* Adjust available buffer for possible align advancement */ | 1270 | /* Adjust available buffer for possible align advancement */ |
@@ -1158,7 +1285,8 @@ static void pcmrec_close(void) | |||
1158 | } /* pcmrec_close */ | 1285 | } /* pcmrec_close */ |
1159 | 1286 | ||
1160 | /* PCMREC_OPTIONS */ | 1287 | /* PCMREC_OPTIONS */ |
1161 | static void pcmrec_set_recording_options(struct audio_recording_options *options) | 1288 | static void pcmrec_set_recording_options( |
1289 | struct audio_recording_options *options) | ||
1162 | { | 1290 | { |
1163 | /* stop DMA transfer */ | 1291 | /* stop DMA transfer */ |
1164 | dma_lock = true; | 1292 | dma_lock = true; |
@@ -1222,97 +1350,111 @@ static void pcmrec_record(const char *filename) | |||
1222 | { | 1350 | { |
1223 | unsigned long pre_sample_ticks; | 1351 | unsigned long pre_sample_ticks; |
1224 | int rd_start; | 1352 | int rd_start; |
1353 | unsigned long flags; | ||
1354 | int pre_index; | ||
1225 | 1355 | ||
1226 | logf("pcmrec_record: %s", filename); | 1356 | logf("pcmrec_record: %s", filename); |
1227 | 1357 | ||
1228 | /* reset stats */ | 1358 | /* reset stats */ |
1229 | num_rec_bytes = 0; | 1359 | num_rec_bytes = 0; |
1230 | num_rec_samples = 0; | 1360 | num_rec_samples = 0; |
1231 | 1361 | ||
1232 | if (is_recording) | 1362 | if (!is_recording) |
1233 | { | 1363 | { |
1234 | /* already recording, just split the stream */ | ||
1235 | logf("inserting split"); | ||
1236 | pcmrec_new_stream(filename, | ||
1237 | CHUNKF_START_FILE | CHUNKF_END_FILE, | ||
1238 | 0); | ||
1239 | goto record_done; | ||
1240 | } | ||
1241 | |||
1242 | #if 0 | 1364 | #if 0 |
1243 | accum_rec_bytes = 0; | 1365 | accum_rec_bytes = 0; |
1244 | accum_pcm_samples = 0; | 1366 | accum_pcm_samples = 0; |
1245 | #endif | 1367 | #endif |
1246 | spinup_time = -1; | 1368 | warnings = 0; /* reset warnings */ |
1247 | warnings = 0; /* reset warnings */ | ||
1248 | 1369 | ||
1249 | rd_start = enc_wr_index; | 1370 | rd_start = enc_wr_index; |
1250 | pre_sample_ticks = 0; | 1371 | pre_sample_ticks = 0; |
1251 | 1372 | ||
1252 | if (pre_record_ticks) | 1373 | pcmrec_refresh_watermarks(); |
1253 | { | 1374 | |
1254 | int i = rd_start; | 1375 | if (pre_record_ticks) |
1376 | { | ||
1377 | int i = rd_start; | ||
1255 | #ifdef PCMREC_PARANOID | 1378 | #ifdef PCMREC_PARANOID |
1256 | int i_last = i; | 1379 | int i_last = i; |
1257 | #endif | 1380 | #endif |
1258 | /* calculate number of available chunks */ | 1381 | /* calculate number of available chunks */ |
1259 | unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index + | 1382 | unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index + |
1260 | enc_num_chunks) % enc_num_chunks; | 1383 | enc_num_chunks) % enc_num_chunks; |
1261 | /* overflow at 974 seconds of prerecording at 44.1kHz */ | 1384 | /* overflow at 974 seconds of prerecording at 44.1kHz */ |
1262 | unsigned long pre_record_sample_ticks = enc_sample_rate*pre_record_ticks; | 1385 | unsigned long pre_record_sample_ticks = |
1263 | 1386 | enc_sample_rate*pre_record_ticks; | |
1264 | /* Get exact measure of recorded data as number of samples aren't | 1387 | int pre_chunks = 0; /* Counter to limit prerecorded time to |
1265 | nescessarily going to be the max for each chunk */ | 1388 | prevent flood state at outset */ |
1266 | for (; avail_pre_chunks-- > 0;) | 1389 | |
1267 | { | 1390 | logf("pre-st: %ld", pre_record_sample_ticks); |
1268 | struct enc_chunk_hdr *chunk; | 1391 | |
1269 | unsigned long chunk_sample_ticks; | 1392 | /* Get exact measure of recorded data as number of samples aren't |
1393 | nescessarily going to be the max for each chunk */ | ||
1394 | for (; avail_pre_chunks-- > 0;) | ||
1395 | { | ||
1396 | struct enc_chunk_hdr *chunk; | ||
1397 | unsigned long chunk_sample_ticks; | ||
1270 | 1398 | ||
1271 | DEC_ENC_INDEX(i); | 1399 | DEC_ENC_INDEX(i); |
1272 | 1400 | ||
1273 | chunk = GET_ENC_CHUNK(i); | 1401 | chunk = GET_ENC_CHUNK(i); |
1274 | 1402 | ||
1275 | /* must have data to be counted */ | 1403 | /* must have data to be counted */ |
1276 | if (chunk->enc_data == NULL) | 1404 | if (chunk->enc_data == NULL) |
1277 | continue; | 1405 | continue; |
1278 | 1406 | ||
1279 | chunk_sample_ticks = chunk->num_pcm*HZ; | 1407 | chunk_sample_ticks = chunk->num_pcm*HZ; |
1280 | 1408 | ||
1281 | rd_start = i; | 1409 | rd_start = i; |
1282 | pre_sample_ticks += chunk_sample_ticks; | 1410 | pre_sample_ticks += chunk_sample_ticks; |
1283 | num_rec_bytes += chunk->enc_size; | 1411 | num_rec_bytes += chunk->enc_size; |
1284 | num_rec_samples += chunk->num_pcm; | 1412 | num_rec_samples += chunk->num_pcm; |
1413 | pre_chunks++; | ||
1285 | 1414 | ||
1286 | /* stop here if enough already */ | 1415 | /* stop here if enough already */ |
1287 | if (pre_sample_ticks >= pre_record_sample_ticks) | 1416 | if (pre_chunks >= high_watermark || |
1288 | break; | 1417 | pre_sample_ticks >= pre_record_sample_ticks) |
1289 | } | 1418 | { |
1419 | logf("pre-chks: %d", pre_chunks); | ||
1420 | break; | ||
1421 | } | ||
1422 | } | ||
1290 | 1423 | ||
1291 | #if 0 | 1424 | #if 0 |
1292 | accum_rec_bytes = num_rec_bytes; | 1425 | accum_rec_bytes = num_rec_bytes; |
1293 | accum_pcm_samples = num_rec_samples; | 1426 | accum_pcm_samples = num_rec_samples; |
1294 | #endif | 1427 | #endif |
1295 | } | 1428 | } |
1429 | |||
1430 | SET_ENC_INDEX(enc_rd_index, rd_start); | ||
1431 | |||
1432 | /* filename queue should be empty */ | ||
1433 | if (!pcmrec_fnq_is_empty()) | ||
1434 | { | ||
1435 | logf("fnq: not empty!"); | ||
1436 | pcmrec_fnq_set_empty(); | ||
1437 | } | ||
1438 | |||
1439 | flags = CHUNKF_START_FILE; | ||
1440 | if (pre_sample_ticks > 0) | ||
1441 | flags |= CHUNKF_PRERECORD; | ||
1296 | 1442 | ||
1297 | SET_ENC_INDEX(enc_rd_index, rd_start); | 1443 | pre_index = enc_rd_index; |
1298 | 1444 | ||
1299 | /* filename queue should be empty */ | 1445 | dma_lock = false; |
1300 | if (!pcmrec_fnq_is_empty()) | 1446 | is_paused = false; |
1447 | is_recording = true; | ||
1448 | } | ||
1449 | else | ||
1301 | { | 1450 | { |
1302 | logf("fnq: not empty!"); | 1451 | /* already recording, just split the stream */ |
1303 | pcmrec_fnq_set_empty(); | 1452 | logf("inserting split"); |
1453 | flags = CHUNKF_START_FILE | CHUNKF_END_FILE; | ||
1454 | pre_index = 0; | ||
1304 | } | 1455 | } |
1305 | |||
1306 | dma_lock = false; | ||
1307 | is_paused = false; | ||
1308 | is_recording = true; | ||
1309 | 1456 | ||
1310 | pcmrec_new_stream(filename, | 1457 | pcmrec_new_stream(filename, flags, pre_index); |
1311 | CHUNKF_START_FILE | | ||
1312 | (pre_sample_ticks > 0 ? CHUNKF_PRERECORD : 0), | ||
1313 | enc_rd_index); | ||
1314 | |||
1315 | record_done: | ||
1316 | logf("pcmrec_record done"); | 1458 | logf("pcmrec_record done"); |
1317 | } /* pcmrec_record */ | 1459 | } /* pcmrec_record */ |
1318 | 1460 | ||
@@ -1321,52 +1463,52 @@ static void pcmrec_stop(void) | |||
1321 | { | 1463 | { |
1322 | logf("pcmrec_stop"); | 1464 | logf("pcmrec_stop"); |
1323 | 1465 | ||
1324 | if (!is_recording) | 1466 | if (is_recording) |
1325 | { | 1467 | { |
1326 | logf("not recording"); | 1468 | dma_lock = true; /* lock dma write position */ |
1327 | goto not_recording; | 1469 | queue_reply(&pcmrec_queue, 0); |
1328 | } | ||
1329 | 1470 | ||
1330 | dma_lock = true; /* lock dma write position */ | 1471 | /* flush all available data first to avoid overflow while waiting |
1331 | queue_reply(&pcmrec_queue, 0); | 1472 | for encoding to finish */ |
1473 | pcmrec_flush(PCMREC_FLUSH_ALL); | ||
1332 | 1474 | ||
1333 | /* flush all available data first to avoid overflow while waiting | 1475 | /* wait for encoder to finish remaining data */ |
1334 | for encoding to finish */ | 1476 | while (errors == 0 && !pcm_buffer_empty) |
1335 | pcmrec_flush(-1); | 1477 | yield(); |
1336 | |||
1337 | /* wait for encoder to finish remaining data */ | ||
1338 | while (errors == 0 && !wav_queue_empty) | ||
1339 | yield(); | ||
1340 | |||
1341 | /* end stream at last data */ | ||
1342 | pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0); | ||
1343 | |||
1344 | /* flush anything else encoder added */ | ||
1345 | pcmrec_flush(-1); | ||
1346 | |||
1347 | /* remove any pending file start not yet processed - should be at | ||
1348 | most one at enc_wr_index */ | ||
1349 | pcmrec_fnq_get_filename(NULL); | ||
1350 | /* encoder should abort any chunk it was in midst of processing */ | ||
1351 | GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT; | ||
1352 | |||
1353 | /* filename queue should be empty */ | ||
1354 | if (!pcmrec_fnq_is_empty()) | ||
1355 | { | ||
1356 | logf("fnq: not empty!"); | ||
1357 | pcmrec_fnq_set_empty(); | ||
1358 | } | ||
1359 | 1478 | ||
1360 | /* be absolutely sure the file is closed */ | 1479 | /* end stream at last data */ |
1361 | if (errors != 0) | 1480 | pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0); |
1362 | pcmrec_close_file(&rec_fdata.rec_file); | 1481 | |
1363 | rec_fdata.rec_file = -1; | 1482 | /* flush anything else encoder added */ |
1483 | pcmrec_flush(PCMREC_FLUSH_ALL); | ||
1364 | 1484 | ||
1365 | is_recording = false; | 1485 | /* remove any pending file start not yet processed - should be at |
1366 | is_paused = false; | 1486 | most one at enc_wr_index */ |
1367 | dma_lock = pre_record_ticks == 0; | 1487 | pcmrec_fnq_get_filename(NULL); |
1488 | /* encoder should abort any chunk it was in midst of processing */ | ||
1489 | GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT; | ||
1490 | |||
1491 | /* filename queue should be empty */ | ||
1492 | if (!pcmrec_fnq_is_empty()) | ||
1493 | { | ||
1494 | logf("fnq: not empty!"); | ||
1495 | pcmrec_fnq_set_empty(); | ||
1496 | } | ||
1497 | |||
1498 | /* be absolutely sure the file is closed */ | ||
1499 | if (errors != 0) | ||
1500 | pcmrec_close_file(&rec_fdata.rec_file); | ||
1501 | rec_fdata.rec_file = -1; | ||
1502 | |||
1503 | is_recording = false; | ||
1504 | is_paused = false; | ||
1505 | dma_lock = pre_record_ticks == 0; | ||
1506 | } | ||
1507 | else | ||
1508 | { | ||
1509 | logf("not recording"); | ||
1510 | } | ||
1368 | 1511 | ||
1369 | not_recording: | ||
1370 | logf("pcmrec_stop done"); | 1512 | logf("pcmrec_stop done"); |
1371 | } /* pcmrec_stop */ | 1513 | } /* pcmrec_stop */ |
1372 | 1514 | ||
@@ -1378,18 +1520,17 @@ static void pcmrec_pause(void) | |||
1378 | if (!is_recording) | 1520 | if (!is_recording) |
1379 | { | 1521 | { |
1380 | logf("not recording"); | 1522 | logf("not recording"); |
1381 | goto not_recording_or_paused; | ||
1382 | } | 1523 | } |
1383 | else if (is_paused) | 1524 | else if (is_paused) |
1384 | { | 1525 | { |
1385 | logf("already paused"); | 1526 | logf("already paused"); |
1386 | goto not_recording_or_paused; | ||
1387 | } | 1527 | } |
1388 | 1528 | else | |
1389 | dma_lock = true; /* fix DMA write pointer at current position */ | 1529 | { |
1390 | is_paused = true; | 1530 | dma_lock = true; |
1391 | 1531 | is_paused = true; | |
1392 | not_recording_or_paused: | 1532 | } |
1533 | |||
1393 | logf("pcmrec_pause done"); | 1534 | logf("pcmrec_pause done"); |
1394 | } /* pcmrec_pause */ | 1535 | } /* pcmrec_pause */ |
1395 | 1536 | ||
@@ -1401,19 +1542,18 @@ static void pcmrec_resume(void) | |||
1401 | if (!is_recording) | 1542 | if (!is_recording) |
1402 | { | 1543 | { |
1403 | logf("not recording"); | 1544 | logf("not recording"); |
1404 | goto not_recording_or_not_paused; | ||
1405 | } | 1545 | } |
1406 | else if (!is_paused) | 1546 | else if (!is_paused) |
1407 | { | 1547 | { |
1408 | logf("not paused"); | 1548 | logf("not paused"); |
1409 | goto not_recording_or_not_paused; | ||
1410 | } | 1549 | } |
1411 | 1550 | else | |
1412 | is_paused = false; | 1551 | { |
1413 | is_recording = true; | 1552 | is_paused = false; |
1414 | dma_lock = false; | 1553 | is_recording = true; |
1415 | 1554 | dma_lock = false; | |
1416 | not_recording_or_not_paused: | 1555 | } |
1556 | |||
1417 | logf("pcmrec_resume done"); | 1557 | logf("pcmrec_resume done"); |
1418 | } /* pcmrec_resume */ | 1558 | } /* pcmrec_resume */ |
1419 | 1559 | ||
@@ -1424,6 +1564,12 @@ static void pcmrec_thread(void) | |||
1424 | 1564 | ||
1425 | logf("thread pcmrec start"); | 1565 | logf("thread pcmrec start"); |
1426 | 1566 | ||
1567 | static void clear_flush_interrupt(void) | ||
1568 | { | ||
1569 | if (--flush_interrupts < 0) | ||
1570 | flush_interrupts = 0; | ||
1571 | } | ||
1572 | |||
1427 | while(1) | 1573 | while(1) |
1428 | { | 1574 | { |
1429 | if (is_recording) | 1575 | if (is_recording) |
@@ -1433,7 +1579,9 @@ static void pcmrec_thread(void) | |||
1433 | 1579 | ||
1434 | if (ev.id == SYS_TIMEOUT) | 1580 | if (ev.id == SYS_TIMEOUT) |
1435 | { | 1581 | { |
1436 | pcmrec_flush(0); /* flush if getting full */ | 1582 | /* Messages that interrupt this will complete it */ |
1583 | pcmrec_flush(PCMREC_FLUSH_IF_HIGH | | ||
1584 | PCMREC_FLUSH_INTERRUPTABLE); | ||
1437 | continue; | 1585 | continue; |
1438 | } | 1586 | } |
1439 | } | 1587 | } |
@@ -1459,14 +1607,17 @@ static void pcmrec_thread(void) | |||
1459 | break; | 1607 | break; |
1460 | 1608 | ||
1461 | case PCMREC_RECORD: | 1609 | case PCMREC_RECORD: |
1610 | clear_flush_interrupt(); | ||
1462 | pcmrec_record((const char *)ev.data); | 1611 | pcmrec_record((const char *)ev.data); |
1463 | break; | 1612 | break; |
1464 | 1613 | ||
1465 | case PCMREC_STOP: | 1614 | case PCMREC_STOP: |
1615 | clear_flush_interrupt(); | ||
1466 | pcmrec_stop(); | 1616 | pcmrec_stop(); |
1467 | break; | 1617 | break; |
1468 | 1618 | ||
1469 | case PCMREC_PAUSE: | 1619 | case PCMREC_PAUSE: |
1620 | clear_flush_interrupt(); | ||
1470 | pcmrec_pause(); | 1621 | pcmrec_pause(); |
1471 | break; | 1622 | break; |
1472 | 1623 | ||
@@ -1483,6 +1634,9 @@ static void pcmrec_thread(void) | |||
1483 | break; | 1634 | break; |
1484 | pcmrec_close(); | 1635 | pcmrec_close(); |
1485 | reset_hardware(); | 1636 | reset_hardware(); |
1637 | /* Be sure other threads are released if waiting */ | ||
1638 | queue_clear(&pcmrec_queue); | ||
1639 | flush_interrupts = 0; | ||
1486 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | 1640 | usb_acknowledge(SYS_USB_CONNECTED_ACK); |
1487 | usb_wait_for_disconnect(&pcmrec_queue); | 1641 | usb_wait_for_disconnect(&pcmrec_queue); |
1488 | break; | 1642 | break; |
@@ -1495,6 +1649,7 @@ static void pcmrec_thread(void) | |||
1495 | /****************************************************************************/ | 1649 | /****************************************************************************/ |
1496 | /* */ | 1650 | /* */ |
1497 | /* following functions will be called by the encoder codec */ | 1651 | /* following functions will be called by the encoder codec */ |
1652 | /* in a free-threaded manner */ | ||
1498 | /* */ | 1653 | /* */ |
1499 | /****************************************************************************/ | 1654 | /****************************************************************************/ |
1500 | 1655 | ||
@@ -1527,6 +1682,7 @@ void enc_set_parameters(struct enc_parameters *params) | |||
1527 | logf("enc sampr:%d", enc_sample_rate); | 1682 | logf("enc sampr:%d", enc_sample_rate); |
1528 | 1683 | ||
1529 | SET_PCM_POS(pcm_rd_pos, dma_wr_pos); | 1684 | SET_PCM_POS(pcm_rd_pos, dma_wr_pos); |
1685 | pcm_enc_pos = pcm_rd_pos; | ||
1530 | 1686 | ||
1531 | enc_config.afmt = params->afmt; | 1687 | enc_config.afmt = params->afmt; |
1532 | /* addition of the header is always implied - chunk size 4-byte aligned */ | 1688 | /* addition of the header is always implied - chunk size 4-byte aligned */ |
@@ -1568,16 +1724,6 @@ void enc_set_parameters(struct enc_parameters *params) | |||
1568 | *wrap_id_p = ENC_CHUNK_MAGIC; | 1724 | *wrap_id_p = ENC_CHUNK_MAGIC; |
1569 | #endif /* PCMREC_PARANOID */ | 1725 | #endif /* PCMREC_PARANOID */ |
1570 | 1726 | ||
1571 | #ifdef HAVE_PRIORITY_SCHEDULING | ||
1572 | /* panic boost thread priority at 1 second remaining */ | ||
1573 | panic_threshold = enc_num_chunks - | ||
1574 | (4*sample_rate + (enc_chunk_size-1)) / enc_chunk_size; | ||
1575 | if (panic_threshold < 0) | ||
1576 | panic_threshold = 0; | ||
1577 | |||
1578 | logf("panic thr:%d", panic_threshold); | ||
1579 | #endif | ||
1580 | |||
1581 | /** set OUT parameters **/ | 1727 | /** set OUT parameters **/ |
1582 | params->enc_buffer = enc_buffer; | 1728 | params->enc_buffer = enc_buffer; |
1583 | params->buf_chunk_size = enc_chunk_size; | 1729 | params->buf_chunk_size = enc_chunk_size; |
@@ -1596,7 +1742,10 @@ void enc_set_parameters(struct enc_parameters *params) | |||
1596 | fnq_wr_pos = 0; /* reset */ | 1742 | fnq_wr_pos = 0; /* reset */ |
1597 | fn_queue = enc_buffer + bufsize; | 1743 | fn_queue = enc_buffer + bufsize; |
1598 | fnq_size = pcm_buffer + rec_buffer_size - fn_queue; | 1744 | fnq_size = pcm_buffer + rec_buffer_size - fn_queue; |
1599 | fnq_size = ALIGN_DOWN(fnq_size, MAX_PATH); | 1745 | fnq_size /= MAX_PATH; |
1746 | if (fnq_size > FNQ_MAX_NUM_PATHS) | ||
1747 | fnq_size = FNQ_MAX_NUM_PATHS; | ||
1748 | fnq_size *= MAX_PATH; | ||
1600 | logf("fnq files: %d", fnq_size / MAX_PATH); | 1749 | logf("fnq files: %d", fnq_size / MAX_PATH); |
1601 | 1750 | ||
1602 | #if 0 | 1751 | #if 0 |
@@ -1626,7 +1775,8 @@ void enc_set_parameters(struct enc_parameters *params) | |||
1626 | logf("enc_set_parameters done"); | 1775 | logf("enc_set_parameters done"); |
1627 | } /* enc_set_parameters */ | 1776 | } /* enc_set_parameters */ |
1628 | 1777 | ||
1629 | /* return encoder chunk at current write position */ | 1778 | /* return encoder chunk at current write position - |
1779 | NOTE: can be called by pcmrec thread when splitting streams */ | ||
1630 | struct enc_chunk_hdr * enc_get_chunk(void) | 1780 | struct enc_chunk_hdr * enc_get_chunk(void) |
1631 | { | 1781 | { |
1632 | struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); | 1782 | struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); |
@@ -1647,7 +1797,8 @@ struct enc_chunk_hdr * enc_get_chunk(void) | |||
1647 | return chunk; | 1797 | return chunk; |
1648 | } /* enc_get_chunk */ | 1798 | } /* enc_get_chunk */ |
1649 | 1799 | ||
1650 | /* releases the current chunk into the available chunks */ | 1800 | /* releases the current chunk into the available chunks - |
1801 | NOTE: can be called by pcmrec thread when splitting streams */ | ||
1651 | void enc_finish_chunk(void) | 1802 | void enc_finish_chunk(void) |
1652 | { | 1803 | { |
1653 | struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); | 1804 | struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); |
@@ -1675,10 +1826,19 @@ void enc_finish_chunk(void) | |||
1675 | } | 1826 | } |
1676 | else if (is_recording) /* buffer full */ | 1827 | else if (is_recording) /* buffer full */ |
1677 | { | 1828 | { |
1678 | /* keep current position - but put up warning flag */ | 1829 | /* keep current position and put up warning flag */ |
1679 | warnings |= PCMREC_W_ENC_BUFFER_OVF; | 1830 | warnings |= PCMREC_W_ENC_BUFFER_OVF; |
1680 | logf("enc_buffer ovf"); | 1831 | logf("enc_buffer ovf"); |
1681 | DEC_ENC_INDEX(enc_wr_index); | 1832 | DEC_ENC_INDEX(enc_wr_index); |
1833 | if (pcmrec_context) | ||
1834 | { | ||
1835 | /* if stream splitting, keep this out of circulation and | ||
1836 | flush a small number, then readd - cannot risk losing | ||
1837 | stream markers */ | ||
1838 | logf("mini flush"); | ||
1839 | pcmrec_flush(PCMREC_FLUSH_MINI); | ||
1840 | INC_ENC_INDEX(enc_wr_index); | ||
1841 | } | ||
1682 | } | 1842 | } |
1683 | else | 1843 | else |
1684 | { | 1844 | { |
@@ -1691,7 +1851,7 @@ void enc_finish_chunk(void) | |||
1691 | int enc_pcm_buf_near_empty(void) | 1851 | int enc_pcm_buf_near_empty(void) |
1692 | { | 1852 | { |
1693 | /* less than 1sec raw data? => unboost encoder */ | 1853 | /* less than 1sec raw data? => unboost encoder */ |
1694 | int wp = dma_wr_pos; | 1854 | int wp = dma_wr_pos; |
1695 | size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK; | 1855 | size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK; |
1696 | return avail < (sample_rate << 2) ? 1 : 0; | 1856 | return avail < (sample_rate << 2) ? 1 : 0; |
1697 | } /* enc_pcm_buf_near_empty */ | 1857 | } /* enc_pcm_buf_near_empty */ |
@@ -1700,7 +1860,7 @@ int enc_pcm_buf_near_empty(void) | |||
1700 | /* TODO: this really should give the actual size returned */ | 1860 | /* TODO: this really should give the actual size returned */ |
1701 | unsigned char * enc_get_pcm_data(size_t size) | 1861 | unsigned char * enc_get_pcm_data(size_t size) |
1702 | { | 1862 | { |
1703 | int wp = dma_wr_pos; | 1863 | int wp = dma_wr_pos; |
1704 | size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK; | 1864 | size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK; |
1705 | 1865 | ||
1706 | /* limit the requested pcm data size */ | 1866 | /* limit the requested pcm data size */ |
@@ -1712,47 +1872,46 @@ unsigned char * enc_get_pcm_data(size_t size) | |||
1712 | unsigned char *ptr = pcm_buffer + pcm_rd_pos; | 1872 | unsigned char *ptr = pcm_buffer + pcm_rd_pos; |
1713 | int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK; | 1873 | int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK; |
1714 | 1874 | ||
1875 | pcm_enc_pos = pcm_rd_pos; | ||
1876 | |||
1715 | SET_PCM_POS(pcm_rd_pos, next_pos); | 1877 | SET_PCM_POS(pcm_rd_pos, next_pos); |
1716 | pcm_rd_pos = next_pos; | ||
1717 | 1878 | ||
1718 | /* ptr must point to continous data at wraparound position */ | 1879 | /* ptr must point to continous data at wraparound position */ |
1719 | if ((size_t)pcm_rd_pos < size) | 1880 | if ((size_t)pcm_rd_pos < size) |
1720 | memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE, | 1881 | memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE, |
1721 | pcm_buffer, pcm_rd_pos); | 1882 | pcm_buffer, pcm_rd_pos); |
1722 | 1883 | ||
1723 | wav_queue_empty = false; | 1884 | pcm_buffer_empty = false; |
1724 | return ptr; | 1885 | return ptr; |
1725 | } | 1886 | } |
1726 | 1887 | ||
1727 | /* not enough data available - encoder should idle */ | 1888 | /* not enough data available - encoder should idle */ |
1728 | wav_queue_empty = true; | 1889 | pcm_buffer_empty = true; |
1729 | return NULL; | 1890 | return NULL; |
1730 | } /* enc_get_pcm_data */ | 1891 | } /* enc_get_pcm_data */ |
1731 | 1892 | ||
1732 | /* puts some pcm data back in the queue */ | 1893 | /* puts some pcm data back in the queue */ |
1733 | size_t enc_unget_pcm_data(size_t size) | 1894 | size_t enc_unget_pcm_data(size_t size) |
1734 | { | 1895 | { |
1735 | /* can't let DMA advance write position when doing this */ | 1896 | int wp = dma_wr_pos; |
1736 | int level = set_irq_level(HIGHEST_IRQ_LEVEL); | 1897 | size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) - |
1898 | 2*PCM_CHUNK_SIZE; | ||
1737 | 1899 | ||
1738 | if (pcm_rd_pos != dma_wr_pos) | 1900 | /* allow one interrupt to occur during this call and not have the |
1901 | new read position inside the DMA destination chunk */ | ||
1902 | if ((ssize_t)old_avail > 0) | ||
1739 | { | 1903 | { |
1740 | /* disallow backing up into current DMA write chunk */ | ||
1741 | size_t old_avail = (pcm_rd_pos - dma_wr_pos - PCM_CHUNK_SIZE) | ||
1742 | & PCM_CHUNK_MASK; | ||
1743 | int next_pos; | ||
1744 | |||
1745 | /* limit size to amount of old data remaining */ | 1904 | /* limit size to amount of old data remaining */ |
1746 | if (size > old_avail) | 1905 | if (size > old_avail) |
1747 | size = old_avail; | 1906 | size = old_avail; |
1748 | 1907 | ||
1749 | next_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK; | 1908 | pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK; |
1750 | SET_PCM_POS(pcm_rd_pos, next_pos); | 1909 | SET_PCM_POS(pcm_rd_pos, pcm_enc_pos); |
1751 | } | ||
1752 | 1910 | ||
1753 | set_irq_level(level); | 1911 | return size; |
1912 | } | ||
1754 | 1913 | ||
1755 | return size; | 1914 | return 0; |
1756 | } /* enc_unget_pcm_data */ | 1915 | } /* enc_unget_pcm_data */ |
1757 | 1916 | ||
1758 | /** Low level pcm recording apis **/ | 1917 | /** Low level pcm recording apis **/ |