summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-02-16 08:52:06 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-02-16 08:52:06 +0000
commitec1f4829cd4276137d3877e36a95c7671ea778d8 (patch)
treec8382071c39def196d2d7730304caf40809278c3
parentfce01de0f0eeb72d67eebc66304ec1e7af04a375 (diff)
downloadrockbox-ec1f4829cd4276137d3877e36a95c7671ea778d8.tar.gz
rockbox-ec1f4829cd4276137d3877e36a95c7671ea778d8.zip
SWCODEC Recording: Improve buffer handling by 1) Lowering watermark for flushing 2) Flushing down to a low watermark instead of a fixed number of chunks 3) Allowing in-process automatic flushes to be interrupted so file splits and stops can be marked in a timely manner (especially important when approaching 2GB limit) 4) Flush small amounts if buffer is filled during a split to protect integrity of streams 5) Boost both pcmrec and codec threads then thread priority boost is needed so that encoder doesn't fail to run and keep up with incoming data
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12332 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/pcm_record.c651
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 **/
49extern 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 */
51volatile pcm_more_callback_type2 pcm_callback_more_ready = NULL; 52volatile pcm_more_callback_type2 pcm_callback_more_ready = NULL;
@@ -57,6 +58,14 @@ static bool is_recording; /* We are recording */
57static bool is_paused; /* We have paused */ 58static bool is_paused; /* We have paused */
58static unsigned long errors; /* An error has occured */ 59static unsigned long errors; /* An error has occured */
59static unsigned long warnings; /* Warning */ 60static unsigned long warnings; /* Warning */
61static 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 **/
62static size_t num_rec_bytes; /* Num bytes recorded */ 71static size_t num_rec_bytes; /* Num bytes recorded */
@@ -179,31 +188,62 @@ static unsigned char *pcm_buffer; /* circular recording buffer */
179static unsigned char *enc_buffer; /* circular encoding buffer */ 188static unsigned char *enc_buffer; /* circular encoding buffer */
180static volatile int dma_wr_pos; /* current DMA write pos */ 189static volatile int dma_wr_pos; /* current DMA write pos */
181static int pcm_rd_pos; /* current PCM read pos */ 190static int pcm_rd_pos; /* current PCM read pos */
191static int pcm_enc_pos; /* position encoder is processing */
182static volatile bool dma_lock; /* lock DMA write position */ 192static volatile bool dma_lock; /* lock DMA write position */
183static int enc_wr_index; /* encoder chunk write index */ 193static int enc_wr_index; /* encoder chunk write index */
184static int enc_rd_index; /* encoder chunk read index */ 194static int enc_rd_index; /* encoder chunk read index */
185static int enc_num_chunks; /* number of chunks in ringbuffer */ 195static int enc_num_chunks; /* number of chunks in ringbuffer */
186static size_t enc_chunk_size; /* maximum encoder chunk size */ 196static size_t enc_chunk_size; /* maximum encoder chunk size */
187static unsigned long enc_sample_rate; /* sample rate used by encoder */ 197static unsigned long enc_sample_rate; /* sample rate used by encoder */
188static bool wav_queue_empty; /* all wav chunks processed? */ 198static bool pcmrec_context = false; /* called by pcmrec thread? */
199static bool pcm_buffer_empty; /* all pcm chunks processed? */
189 200
190/** file flushing **/ 201/** file flushing **/
191static int write_threshold; /* max chunk limit for data flush */ 202static int low_watermark; /* Low watermark to stop flush */
192static int spinup_time = -1;/* last ata_spinup_time */ 203static int high_watermark; /* max chunk limit for data flush */
204static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */
205static int last_ata_spinup_time = -1;/* previous spin time used */
193#ifdef HAVE_PRIORITY_SCHEDULING 206#ifdef HAVE_PRIORITY_SCHEDULING
194static int panic_threshold; /* boost thread prio when here */ 207static 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 **/
198static void (*enc_events_callback)(enum enc_events event, void *data); 225static 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 */
202static unsigned char *fn_queue; /* pointer to first filename */ 230static unsigned char *fn_queue; /* pointer to first filename */
203static ssize_t fnq_size; /* capacity of queue in bytes */ 231static ssize_t fnq_size; /* capacity of queue in bytes */
204static int fnq_rd_pos; /* current read position */ 232static int fnq_rd_pos; /* current read position */
205static int fnq_wr_pos; /* current write position */ 233static int fnq_wr_pos; /* current write position */
206 234
235enum
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
209static unsigned long *wrap_id_p; /* magic at end of encoding buffer */ 249static unsigned long *wrap_id_p; /* magic at end of encoding buffer */
@@ -220,6 +260,7 @@ static struct event_queue pcmrec_queue;
220static struct queue_sender_list pcmrec_queue_send; 260static struct queue_sender_list pcmrec_queue_send;
221static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)]; 261static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
222static const char pcmrec_thread_name[] = "pcmrec"; 262static const char pcmrec_thread_name[] = "pcmrec";
263static struct thread_entry *pcmrec_thread_p;
223 264
224static void pcmrec_thread(void); 265static 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)
421void audio_record(const char *filename) 460void 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)
431void audio_stop_recording(void) 472void 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)
441void audio_pause_recording(void) 484void 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 */
842static 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 */
801static void pcmrec_flush(unsigned flush_num) 902static 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 */
1161static void pcmrec_set_recording_options(struct audio_recording_options *options) 1288static 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
1315record_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
1369not_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;
1392not_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;
1416not_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 */
1630struct enc_chunk_hdr * enc_get_chunk(void) 1780struct 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 */
1651void enc_finish_chunk(void) 1802void 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)
1691int enc_pcm_buf_near_empty(void) 1851int 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 */
1701unsigned char * enc_get_pcm_data(size_t size) 1861unsigned 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 */
1733size_t enc_unget_pcm_data(size_t size) 1894size_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 **/