summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/audio_thread.c14
-rw-r--r--apps/audio_thread.h9
-rw-r--r--apps/codec_thread.c59
-rw-r--r--apps/codec_thread.h3
-rw-r--r--apps/codecs.c43
-rw-r--r--apps/playback.c12
-rw-r--r--apps/recorder/pcm_record.c2924
-rw-r--r--apps/recorder/pcm_record.h74
-rw-r--r--apps/recorder/recording.c13
-rw-r--r--firmware/export/audio.h5
-rw-r--r--firmware/export/enc_base.h204
-rw-r--r--lib/rbcodec/codecs/aiff_enc.c399
-rw-r--r--lib/rbcodec/codecs/codecs.h45
-rw-r--r--lib/rbcodec/codecs/mp3_enc.c813
-rw-r--r--lib/rbcodec/codecs/wav_enc.c376
-rw-r--r--lib/rbcodec/codecs/wavpack_enc.c453
16 files changed, 2659 insertions, 2787 deletions
diff --git a/apps/audio_thread.c b/apps/audio_thread.c
index 397d8b0946..bbf73470d2 100644
--- a/apps/audio_thread.c
+++ b/apps/audio_thread.c
@@ -108,6 +108,16 @@ static void NORETURN_ATTR audio_thread(void)
108 } 108 }
109} 109}
110 110
111void audio_queue_post(long id, intptr_t data)
112{
113 queue_post(&audio_queue, id, data);
114}
115
116intptr_t audio_queue_send(long id, intptr_t data)
117{
118 return queue_send(&audio_queue, id, data);
119}
120
111/* Return the playback and recording status */ 121/* Return the playback and recording status */
112int audio_status(void) 122int audio_status(void)
113{ 123{
@@ -156,7 +166,9 @@ void audio_init(void)
156 audio_thread_id); 166 audio_thread_id);
157 167
158 playback_init(); 168 playback_init();
159 /* Recording doesn't need init call */ 169#ifdef AUDIO_HAVE_RECORDING
170 recording_init();
171#endif
160 172
161 /* ...now...audio_reset_buffer must know the size of voicefile buffer so 173 /* ...now...audio_reset_buffer must know the size of voicefile buffer so
162 init talk first which will init the buffers */ 174 init talk first which will init the buffers */
diff --git a/apps/audio_thread.h b/apps/audio_thread.h
index 7e0ad28849..57edf2b5ec 100644
--- a/apps/audio_thread.h
+++ b/apps/audio_thread.h
@@ -74,7 +74,10 @@ enum
74 Q_AUDIO_CLOSE_RECORDING, 74 Q_AUDIO_CLOSE_RECORDING,
75 Q_AUDIO_RECORDING_OPTIONS, 75 Q_AUDIO_RECORDING_OPTIONS,
76 Q_AUDIO_RECORD, 76 Q_AUDIO_RECORD,
77 Q_AUDIO_RESUME, 77 Q_AUDIO_RECORD_STOP,
78 Q_AUDIO_RECORD_PAUSE,
79 Q_AUDIO_RECORD_RESUME,
80 Q_AUDIO_RECORD_FLUSH,
78#endif 81#endif
79 82
80 /*- settings -*/ 83 /*- settings -*/
@@ -99,4 +102,8 @@ void audio_playback_handler(struct queue_event *ev);
99void audio_recording_handler(struct queue_event *ev); 102void audio_recording_handler(struct queue_event *ev);
100#endif 103#endif
101 104
105/** --- audio_queue helpers --- **/
106void audio_queue_post(long id, intptr_t data);
107intptr_t audio_queue_send(long id, intptr_t data);
108
102#endif /* AUDIO_THREAD_H */ 109#endif /* AUDIO_THREAD_H */
diff --git a/apps/codec_thread.c b/apps/codec_thread.c
index 54bc28e19a..1a0b0b255c 100644
--- a/apps/codec_thread.c
+++ b/apps/codec_thread.c
@@ -249,7 +249,7 @@ static void codec_pcmbuf_insert_callback(
249 return; /* No input remains and DSP purged */ 249 return; /* No input remains and DSP purged */
250 } 250 }
251 } 251 }
252 } 252 }
253} 253}
254 254
255/* helper function, not a callback */ 255/* helper function, not a callback */
@@ -365,10 +365,14 @@ static enum codec_command_action
365 365
366 queue_peek(&codec_queue, &ev); /* Find out what it is */ 366 queue_peek(&codec_queue, &ev); /* Find out what it is */
367 367
368 long id = ev.id; 368 intptr_t id = ev.id;
369 369
370 switch (id) 370 switch (id)
371 { 371 {
372 case Q_NULL:
373 LOGFQUEUE("codec < Q_NULL");
374 break;
375
372 case Q_CODEC_RUN: /* Already running */ 376 case Q_CODEC_RUN: /* Already running */
373 LOGFQUEUE("codec < Q_CODEC_RUN"); 377 LOGFQUEUE("codec < Q_CODEC_RUN");
374 break; 378 break;
@@ -388,8 +392,25 @@ static enum codec_command_action
388 break; 392 break;
389 393
390 case Q_CODEC_STOP: /* Must only return 0 in main loop */ 394 case Q_CODEC_STOP: /* Must only return 0 in main loop */
391 LOGFQUEUE("codec < Q_CODEC_STOP"); 395 LOGFQUEUE("codec < Q_CODEC_STOP: %ld", ev.data);
392 dsp_configure(ci.dsp, DSP_FLUSH, 0); /* Discontinuity */ 396#ifdef HAVE_RECORDING
397 if (type_is_encoder(codec_type))
398 {
399 /* Stream finish request (soft stop)? */
400 if (ev.data && param)
401 {
402 /* ev.data is pointer to size */
403 *param = ev.data;
404 action = CODEC_ACTION_STREAM_FINISH;
405 break;
406 }
407 }
408 else
409#endif /* HAVE_RECORDING */
410 {
411 dsp_configure(ci.dsp, DSP_FLUSH, 0); /* Discontinuity */
412 }
413
393 return CODEC_ACTION_HALT; /* Leave in queue */ 414 return CODEC_ACTION_HALT; /* Leave in queue */
394 415
395 default: /* This is in error in this context. */ 416 default: /* This is in error in this context. */
@@ -459,7 +480,8 @@ static void load_codec(const struct codec_load_info *ev_data)
459 } 480 }
460 } 481 }
461 482
462 if (status >= 0) 483 /* Types must agree */
484 if (status >= 0 && encoder == !!codec_get_enc_callback())
463 { 485 {
464 codec_type = data.afmt; 486 codec_type = data.afmt;
465 codec_queue_ack(Q_CODEC_LOAD); 487 codec_queue_ack(Q_CODEC_LOAD);
@@ -558,7 +580,7 @@ static void NORETURN_ATTR codec_thread(void)
558{ 580{
559 struct queue_event ev; 581 struct queue_event ev;
560 582
561 while (1) 583 while (1)
562 { 584 {
563 cancel_cpu_boost(); 585 cancel_cpu_boost();
564 586
@@ -685,10 +707,33 @@ bool codec_pause(void)
685void codec_stop(void) 707void codec_stop(void)
686{ 708{
687 /* Wait until it's in the main loop */ 709 /* Wait until it's in the main loop */
688 LOGFQUEUE("audio >| codec Q_CODEC_STOP"); 710 LOGFQUEUE("audio >| codec Q_CODEC_STOP: 0");
689 while (codec_queue_send(Q_CODEC_STOP, 0) != Q_NULL); 711 while (codec_queue_send(Q_CODEC_STOP, 0) != Q_NULL);
690} 712}
691 713
714#ifdef HAVE_RECORDING
715/* Tells codec to take final encoding step and then exit -
716 Returns minimum buffer size required or 0 if complete */
717size_t codec_finish_stream(void)
718{
719 size_t size = 0;
720
721 LOGFQUEUE("audio >| codec Q_CODEC_STOP: &size");
722 if (codec_queue_send(Q_CODEC_STOP, (intptr_t)&size) != Q_NULL)
723 {
724 /* Sync to keep size in scope and get response */
725 LOGFQUEUE("audio >| codec Q_NULL");
726 codec_queue_send(Q_NULL, 0);
727
728 if (size == 0)
729 codec_stop(); /* Replied with 0 size */
730 }
731 /* else thread running in the main loop */
732
733 return size;
734}
735#endif /* HAVE_RECORDING */
736
692/* Call the codec's exit routine and close all references */ 737/* Call the codec's exit routine and close all references */
693void codec_unload(void) 738void codec_unload(void)
694{ 739{
diff --git a/apps/codec_thread.h b/apps/codec_thread.h
index c33284e141..6aa340bffe 100644
--- a/apps/codec_thread.h
+++ b/apps/codec_thread.h
@@ -46,6 +46,9 @@ void codec_go(void);
46bool codec_pause(void); 46bool codec_pause(void);
47void codec_seek(long time); 47void codec_seek(long time);
48void codec_stop(void); 48void codec_stop(void);
49#ifdef HAVE_RECORDING
50size_t codec_finish_stream(void);
51#endif
49void codec_unload(void); 52void codec_unload(void);
50int codec_loaded(void); 53int codec_loaded(void);
51 54
diff --git a/apps/codecs.c b/apps/codecs.c
index 69204b7c4f..18022d9df3 100644
--- a/apps/codecs.c
+++ b/apps/codecs.c
@@ -73,13 +73,6 @@ static size_t codec_size;
73 73
74extern void* plugin_get_audio_buffer(size_t *buffer_size); 74extern void* plugin_get_audio_buffer(size_t *buffer_size);
75 75
76#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(HAVE_RECORDING)
77#undef open
78static int open(const char* pathname, int flags, ...)
79{
80 return file_open(pathname, flags);
81}
82#endif
83struct codec_api ci = { 76struct codec_api ci = {
84 77
85 0, /* filesize */ 78 0, /* filesize */
@@ -99,7 +92,7 @@ struct codec_api ci = {
99 NULL, /* configure */ 92 NULL, /* configure */
100 NULL, /* get_command */ 93 NULL, /* get_command */
101 NULL, /* loop_track */ 94 NULL, /* loop_track */
102 95
103 /* kernel/ system */ 96 /* kernel/ system */
104#if defined(CPU_ARM) && CONFIG_PLATFORM & PLATFORM_NATIVE 97#if defined(CPU_ARM) && CONFIG_PLATFORM & PLATFORM_NATIVE
105 __div0, 98 __div0,
@@ -147,21 +140,14 @@ struct codec_api ci = {
147#endif 140#endif
148 141
149#ifdef HAVE_RECORDING 142#ifdef HAVE_RECORDING
150 enc_get_inputs, 143 NULL, /* enc_pcmbuf_read */
151 enc_set_parameters, 144 NULL, /* enc_pcmbuf_advance */
152 enc_get_chunk, 145 NULL, /* enc_encbuf_get_buffer */
153 enc_finish_chunk, 146 NULL, /* enc_encbuf_finish_buffer */
154 enc_get_pcm_data, 147 NULL, /* enc_stream_read */
155 enc_unget_pcm_data, 148 NULL, /* enc_stream_lseek */
156 149 NULL, /* enc_stream_write */
157 /* file */
158 (open_func)PREFIX(open),
159 PREFIX(close),
160 (read_func)PREFIX(read),
161 PREFIX(lseek),
162 (write_func)PREFIX(write),
163 round_value_to_list32, 150 round_value_to_list32,
164
165#endif /* HAVE_RECORDING */ 151#endif /* HAVE_RECORDING */
166 152
167 /* new stuff at the end, sort into place next time 153 /* new stuff at the end, sort into place next time
@@ -299,3 +285,16 @@ int codec_close(void)
299 285
300 return status; 286 return status;
301} 287}
288
289#ifdef HAVE_RECORDING
290enc_callback_t codec_get_enc_callback(void)
291{
292 if (curr_handle == NULL ||
293 c_hdr->lc_hdr.magic != CODEC_ENC_MAGIC) {
294 logf("Codec: not an encoder");
295 return NULL;
296 }
297
298 return c_hdr->rec_extension[0];
299}
300#endif /* HAVE_RECORDING */
diff --git a/apps/playback.c b/apps/playback.c
index f676c6d31f..87922bfb82 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -351,18 +351,6 @@ void playback_voice_event(void *data)
351 pcmbuf_soft_mode(*(bool *)data); 351 pcmbuf_soft_mode(*(bool *)data);
352} 352}
353 353
354/** --- audio_queue helpers --- **/
355static void audio_queue_post(long id, intptr_t data)
356{
357 queue_post(&audio_queue, id, data);
358}
359
360static intptr_t audio_queue_send(long id, intptr_t data)
361{
362 return queue_send(&audio_queue, id, data);
363}
364
365
366/** --- MP3Entry --- **/ 354/** --- MP3Entry --- **/
367 355
368/* Does the mp3entry have enough info for us to use it? */ 356/* Does the mp3entry have enough info for us to use it? */
diff --git a/apps/recorder/pcm_record.c b/apps/recorder/pcm_record.c
index 6b9adda8bd..a3fe2097b1 100644
--- a/apps/recorder/pcm_record.c
+++ b/apps/recorder/pcm_record.c
@@ -7,7 +7,10 @@
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing 10 * Copyright (C) 2005 Linus Nielsen Feltzing
11 * Copyright (C) 2006 Antonius Hellmann
12 * Copyright (C) 2006-2013 Michael Sevakis
13 *
11 * 14 *
12 * This program is free software; you can redistribute it and/or 15 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 16 * modify it under the terms of the GNU General Public License
@@ -18,17 +21,14 @@
18 * KIND, either express or implied. 21 * KIND, either express or implied.
19 * 22 *
20 ****************************************************************************/ 23 ****************************************************************************/
21
22#include "config.h" 24#include "config.h"
23#include "gcc_extensions.h"
24#include "pcm_record.h"
25#include "system.h" 25#include "system.h"
26#include "kernel.h" 26#include "kernel.h"
27#include "pcm_record.h"
28#include "codecs.h"
27#include "logf.h" 29#include "logf.h"
28#include "thread.h" 30#include "thread.h"
29#include "string-extra.h"
30#include "storage.h" 31#include "storage.h"
31#include "usb.h"
32#include "general.h" 32#include "general.h"
33#include "codec_thread.h" 33#include "codec_thread.h"
34#include "audio.h" 34#include "audio.h"
@@ -40,1757 +40,1975 @@
40#endif 40#endif
41#include "audio_thread.h" 41#include "audio_thread.h"
42 42
43/***************************************************************************/ 43/* Macros to enable logf for queues
44 logging on SYS_TIMEOUT can be disabled */
45#ifdef SIMULATOR
46/* Define this for logf output of all queuing except SYS_TIMEOUT */
47#define PCMREC_LOGQUEUES
48/* Define this to logf SYS_TIMEOUT messages */
49/*#define PCMREC_LOGQUEUES_SYS_TIMEOUT*/
50#endif /* SIMULATOR */
51
52#ifdef PCMREC_LOGQUEUES
53#define LOGFQUEUE logf
54#else
55#define LOGFQUEUE(...)
56#endif
44 57
45extern struct event_queue audio_queue; 58#ifdef PCMREC_LOGQUEUES_SYS_TIMEOUT
59#define LOGFQUEUE_SYS_TIMEOUT logf
60#else
61#define LOGFQUEUE_SYS_TIMEOUT(...)
62#endif
63
64/** Target-related configuration **/
65
66/**
67 * PCM_NUM_CHUNKS: Number of PCM chunks
68 * PCM_CHUNK_SAMP: Number of samples in a PCM chunk
69 * PCM_BOOST_SECONDS: PCM level at which to boost CPU
70 * PANIC_SECONDS: Flood watermark time until full
71 * FLUSH_SECONDS: Flush watermark time until full
72 * STREAM_BUF_SIZE: Size of stream write buffer
73 * PRIO_SECONDS: Max flush time before prio boost
74 *
75 * Total PCM buffer size should be mem aligned
76 *
77 * Fractions should be left without parentheses so the multiplier is
78 * multiplied by the numerator first.
79 */
80#if MEMORYSIZE <= 2
81#define PCM_NUM_CHUNKS 56
82#define PCM_CHUNK_SAMP 1024
83#define PCM_BOOST_SECONDS 1/2
84#define PANIC_SECONDS 1/2
85#define FLUSH_SECONDS 1
86#define FLUSH_MON_INTERVAL 1/6
87#define STREAM_BUF_SIZE 32768
88#elif MEMORYSIZE <= 16
89#define PANIC_SECONDS 5
90#define FLUSH_SECONDS 7
91#else /* MEMORYSIZE > 16 */
92#define PANIC_SECONDS 8
93#define FLUSH_SECONDS 10
94#endif /* MEMORYSIZE */
95
96/* Default values if not overridden above */
97#ifndef PCM_NUM_CHUNKS
98#define PCM_NUM_CHUNKS 256
99#endif
100#ifndef PCM_CHUNK_SAMP
101#define PCM_CHUNK_SAMP 2048
102#endif
103#ifndef PCM_BOOST_SECONDS
104#define PCM_BOOST_SECONDS 1
105#endif
106#ifndef FLUSH_MON_INTERVAL
107#define FLUSH_MON_INTERVAL 1/4
108#endif
109#ifndef STREAM_BUF_SIZE
110#define STREAM_BUF_SIZE 65536
111#endif
112#ifndef PRIO_SECONDS
113#define PRIO_SECONDS 10
114#endif
115
116/* FAT limit for filesize. Recording will accept no further data from the
117 * codec if this limit is reached in order to preserve its own data
118 * integrity. A split should have made by the higher-ups long before this
119 * point.
120 *
121 * Leave a generous 64k margin for metadata being added to file. */
122#define MAX_NUM_REC_BYTES ((size_t)0x7fff0000u)
123
124/***************************************************************************/
125extern struct codec_api ci; /* in codec_thread.c */
126extern struct event_queue audio_queue; /* in audio_thread.c */
127extern unsigned int audio_thread_id; /* in audio_thread.c */
46 128
47/** General recording state **/ 129/** General recording state **/
48static bool is_recording; /* We are recording */
49static bool is_paused; /* We have paused */
50static unsigned long errors; /* An error has occured */
51static unsigned long warnings; /* Warning */
52static int flush_interrupts = 0; /* Number of messages queued that
53 should interrupt a flush in
54 progress -
55 for a safety net and a prompt
56 response to stop, split and pause
57 requests -
58 only interrupts a flush initiated
59 by pcmrec_flush(0) */
60 130
61/* Utility functions for setting/clearing flushing interrupt flag */ 131/* Recording action being performed */
62static inline void flush_interrupt(void) 132static enum record_status
63{ 133{
64 flush_interrupts++; 134 RECORD_STOPPED = 0,
65 logf("flush int: %d", flush_interrupts); 135 RECORD_PRERECORDING = AUDIO_STATUS_PRERECORD,
66} 136 RECORD_RECORDING = AUDIO_STATUS_RECORD,
67 137 RECORD_PAUSED = (AUDIO_STATUS_RECORD | AUDIO_STATUS_PAUSE),
68static inline void clear_flush_interrupt(void) 138} record_status = RECORD_STOPPED;
139
140/* State of engine operations */
141static enum record_state
69{ 142{
70 if (--flush_interrupts < 0) 143 REC_STATE_IDLE, /* Stopped or prerecording */
71 flush_interrupts = 0; 144 REC_STATE_MONITOR, /* Monitoring buffer status */
72} 145 REC_STATE_FLUSH, /* Flushing buffer */
146} record_state = REC_STATE_IDLE;
73 147
74/** Stats on encoded data for current file **/ 148static uint32_t errors; /* An error has occured (bitmask) */
75static size_t num_rec_bytes; /* Num bytes recorded */ 149static uint32_t warnings; /* Non-fatal warnings (bitmask) */
76static unsigned long num_rec_samples; /* Number of PCM samples recorded */
77 150
78/** Stats on encoded data for all files from start to stop **/ 151static uint32_t rec_errors; /* Mirror of errors but private to
79#if 0 152 * avoid race with controlling
80static unsigned long long accum_rec_bytes; /* total size written to chunks */ 153 * thread. Engine uses this
81static unsigned long long accum_pcm_samples; /* total pcm count processed */ 154 * internally. */
82#endif
83 155
84/* Keeps data about current file and is sent as event data for codec */ 156/** Stats on encoded data for current file **/
85static struct enc_file_event_data rec_fdata IDATA_ATTR = 157static int rec_fd = -1; /* Currently open file descriptor */
86{ 158static size_t num_rec_bytes; /* Number of bytes recorded */
87 .chunk = NULL, 159static uint64_t num_rec_samples; /* Number of PCM samples recorded */
88 .new_enc_size = 0, 160static uint64_t encbuf_rec_count; /* Count of slots written to buffer
89 .new_num_pcm = 0, 161 for current file */
90 .rec_file = -1,
91 .num_pcm_samples = 0
92};
93 162
94/** These apply to current settings **/ 163/** These apply to current settings **/
95static int rec_source; /* current rec_source setting */ 164static int rec_source; /* Current rec_source setting */
96static int rec_frequency; /* current frequency setting */ 165static unsigned long sample_rate; /* Samplerate setting in HZ */
97static unsigned long sample_rate; /* Sample rate in HZ */
98static int num_channels; /* Current number of channels */ 166static int num_channels; /* Current number of channels */
99static int rec_mono_mode; /* how mono is created */
100static struct encoder_config enc_config; /* Current encoder configuration */ 167static struct encoder_config enc_config; /* Current encoder configuration */
101static unsigned long pre_record_ticks; /* pre-record time in ticks */ 168static unsigned int pre_record_seconds; /* Pre-record time in seconds */
102 169
103/**************************************************************************** 170/****************************************************************************
104 use 2 circular buffers: 171 Use 2 circular buffers:
105 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data 172 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
106 enc_buffer=encoded audio buffer: storage for encoder output data 173 enc_buffer=encoded audio buffer: storage for encoder output data
107 174
108 Flow: 175 Flow:
109 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer 176 1. When entering recording_screen DMA feeds the ringbuffer pcm_buffer
110 2. if enough pcm data are available the encoder codec does encoding of pcm 177 2. If enough pcm data are available the encoder codec does encoding of pcm
111 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread 178 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
112 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk 179 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
113 180
114 Functions calls (basic encoder steps): 181 Functions calls (basic encoder steps):
115 1.main: audio_load_encoder(); start the encoder 182 1.audio: codec_load(); load the encoder
116 2.encoder: enc_get_inputs(); get encoder recording settings 183 2.encoder: enc_init_parameters(); set the encoder parameters (at load)
117 3.encoder: enc_set_parameters(); set the encoder parameters 184 3.audio: enc_callback(); configure encoder recording settings
118 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data 185 4.audio: codec_go(); start encoding the new stream
119 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional) 186 5.encoder: enc_encbuf_get_buffer(); obtain an output buffer of size n
120 6.encoder: enc_get_chunk(); get a ptr to next enc chunk 187 6.encoder: enc_pcmbuf_read(); read n bytes of unprocessed pcm data
121 7.encoder: <process enc chunk> compress and store data to enc chunk 188 7.encoder: enc_encbuf_finish_buffer(); add the obtained buffer to output
122 8.encoder: enc_finish_chunk(); inform main about chunk processed and 189 8.encoder: enc_pcmbuf_advance(); advance pcm by n samples
123 is available to be written to a file. 190 9.encoder: while more PCM available, repeat 5. to 9.
124 Encoder can place any number of chunks 191 10.audio: codec_finish_stream(); finish the output for current stream
125 of PCM data in a single output chunk 192
126 but must stay within its output chunk 193 Function calls (basic stream flushing steps through enc_callback()):
127 size 194 1.audio: flush_stream_start(); stream flush destination is opening
128 9.encoder: repeat 4. to 8. 195 2.audio: flush_stream_data(); flush encoded audio to stream
129 A.pcmrec: enc_events_callback(); called for certain events 196 3.audio: while encoded data available, repeat 2.
130 197 4.audio: flush_stream_end(); stream flush destination is closing
131 (*) Optional step 198
132****************************************************************************/ 199****************************************************************************/
133 200
134/** buffer parameters where incoming PCM data is placed **/ 201/** Buffer parameters where incoming PCM data is placed **/
135#if MEMORYSIZE <= 2 202#define PCM_DEPTH_BYTES (sizeof (int16_t))
136#define PCM_NUM_CHUNKS 16 /* Power of 2 */ 203#define PCM_SAMP_SIZE (2*PCM_DEPTH_BYTES)
137#else 204#define PCM_CHUNK_SIZE (PCM_CHUNK_SAMP*PCM_SAMP_SIZE)
138#define PCM_NUM_CHUNKS 256 /* Power of 2 */ 205#define PCM_BUF_SIZE (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE)
139#endif 206
140#define PCM_CHUNK_SIZE 8192 /* Power of 2 */ 207/* Convert byte sizes into buffer slot counts */
141#define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1) 208#define CHUNK_SIZE_COUNT(size) \
142 209 (((size) + ENC_HDR_SIZE - 1) / ENC_HDR_SIZE)
143#define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset))) 210#define CHUNK_FILE_COUNT(size) \
144#define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index)) 211 ({ typeof (size) __size = (size); \
145#define INC_ENC_INDEX(index) \ 212 CHUNK_SIZE_COUNT(MIN(__size, MAX_PATH) + ENC_HDR_SIZE); })
146 { if (++index >= enc_num_chunks) index = 0; } 213#define CHUNK_FILE_COUNT_PATH(path) \
147#define DEC_ENC_INDEX(index) \ 214 CHUNK_FILE_COUNT(strlen(path) + 1)
148 { if (--index < 0) index = enc_num_chunks - 1; } 215#define CHUNK_DATA_COUNT(size) \
149 216 CHUNK_SIZE_COUNT((size) + sizeof (struct enc_chunk_data))
150static size_t rec_buffer_size; /* size of available buffer */ 217
151static unsigned char *pcm_buffer; /* circular recording buffer */ 218/* Min margin to write stream split headers without overwrap risk */
152static unsigned char *enc_buffer; /* circular encoding buffer */ 219#define ENCBUF_MIN_SPLIT_MARGIN \
153#ifdef DEBUG 220 (2*(1 + CHUNK_FILE_COUNT(MAX_PATH)) - 1)
154static unsigned long *wrap_id_p; /* magic at wrap position - a debugging 221
155 aid to check if the encoder data 222static void *rec_buffer; /* Root rec buffer pointer */
156 spilled out of its chunk */ 223static size_t rec_buffer_size; /* Root rec buffer size */
157#endif /* DEBUG */ 224
158static volatile int dma_wr_pos; /* current DMA write pos */ 225static void *pcm_buffer; /* Circular buffer for PCM samples */
159static int pcm_rd_pos; /* current PCM read pos */ 226static volatile bool pcm_pause; /* Freeze DMA write position */
160static int pcm_enc_pos; /* position encoder is processing */ 227static volatile size_t pcm_widx; /* Current DMA write position */
161static volatile bool dma_lock; /* lock DMA write position */ 228static volatile size_t pcm_ridx; /* Current PCM read position */
162static int enc_wr_index; /* encoder chunk write index */ 229
163static int enc_rd_index; /* encoder chunk read index */ 230static union enc_chunk_hdr *enc_buffer; /* Circular encoding buffer */
164static int enc_num_chunks; /* number of chunks in ringbuffer */ 231static size_t enc_widx; /* Encoder chunk write index */
165static size_t enc_chunk_size; /* maximum encoder chunk size */ 232static size_t enc_ridx; /* Encoder chunk read index */
166static unsigned long enc_sample_rate; /* sample rate used by encoder */ 233static size_t enc_buflen; /* Length of buffer in slots */
167static bool pcmrec_context = false; /* called by pcmrec thread? */ 234
168static bool pcm_buffer_empty; /* all pcm chunks processed? */ 235static unsigned char *stream_buffer; /* Stream-to-disk write buffer */
169 236static ssize_t stream_buf_used; /* Stream write buffer occupancy */
170/** file flushing **/ 237
171static int low_watermark; /* Low watermark to stop flush */ 238static struct enc_chunk_file *fname_buf;/* Buffer with next file to create */
172static int high_watermark; /* max chunk limit for data flush */ 239
173static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */ 240static unsigned long enc_sample_rate; /* Samplerate used by encoder */
174static int last_storage_spinup_time = -1;/* previous spin time used */ 241static bool pcm_buffer_empty; /* All PCM chunks processed? */
175#ifdef HAVE_PRIORITY_SCHEDULING 242
176static int flood_watermark; /* boost thread priority when here */ 243static typeof (memcpy) *pcm_copyfn; /* PCM memcpy or copy_buffer_mono */
244static enc_callback_t enc_cb; /* Encoder's recording callback */
245
246/** File flushing **/
247static unsigned long encbuf_datarate; /* Rate of data per second */
248#if (CONFIG_STORAGE & STORAGE_ATA)
249static int spinup_time; /* Last spinup time */
177#endif 250#endif
251static size_t high_watermark; /* Max limit for data flush */
178 252
179/* Constants that control watermarks */
180#define MINI_CHUNKS 10 /* chunk count for mini flush */
181#ifdef HAVE_PRIORITY_SCHEDULING 253#ifdef HAVE_PRIORITY_SCHEDULING
182#define PRIO_SECONDS 10 /* max flush time before priority boost */ 254static size_t flood_watermark; /* Max limit for thread prio boost */
255static bool prio_boosted;
183#endif 256#endif
184#if MEMORYSIZE <= 2
185/* fractions must be integer fractions of 4 because they are evaluated with
186 * X*4*XXX_SECONDS, that way we avoid float calculation */
187#define LOW_SECONDS 1/4 /* low watermark time till empty */
188#define PANIC_SECONDS 1/2 /* flood watermark time until full */
189#define FLUSH_SECONDS 1 /* flush watermark time until full */
190#elif MEMORYSIZE <= 16
191#define LOW_SECONDS 1 /* low watermark time till empty */
192#define PANIC_SECONDS 5 /* flood watermark time until full */
193#define FLUSH_SECONDS 7 /* flush watermark time until full */
194#else
195#define LOW_SECONDS 1 /* low watermark time till empty */
196#define PANIC_SECONDS 8
197#define FLUSH_SECONDS 10
198#endif /* MEMORYSIZE */
199 257
200/** encoder events **/ 258/** Stream marking **/
201static void (*enc_events_callback)(enum enc_events event, void *data); 259enum mark_stream_action
202 260{
203/** Path queue for files to write **/ 261 MARK_STREAM_END = 0x1, /* Mark end current stream */
204#define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */ 262 MARK_STREAM_START = 0x2, /* Mark start of new stream */
205#define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */ 263 MARK_STREAM_SPLIT = 0x3, /* Insert split; orr of above values */
206static unsigned char *fn_queue; /* pointer to first filename */ 264 MARK_STREAM_PRE = 0x4, /* Do prerecord data tally */
207static ssize_t fnq_size; /* capacity of queue in bytes */ 265 MARK_STREAM_START_PRE = MARK_STREAM_PRE | MARK_STREAM_START,
208static int fnq_rd_pos; /* current read position */
209static int fnq_wr_pos; /* current write position */
210#define FNQ_NEXT(pos) \
211 ({ int p = (pos) + MAX_PATH; \
212 if (p >= fnq_size) \
213 p = 0; \
214 p; })
215#define FNQ_PREV(pos) \
216 ({ int p = (pos) - MAX_PATH; \
217 if (p < 0) \
218 p = fnq_size - MAX_PATH; \
219 p; })
220
221enum
222{
223 PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by
224 incoming messages - combine
225 with other constants */
226 PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */
227 PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of
228 chunks */
229 PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark
230 reached */
231}; 266};
232 267
268
233/***************************************************************************/ 269/***************************************************************************/
234 270
235enum 271/* Buffer pointer (p) to PCM sample memory address */
272static inline void * pcmbuf_ptr(size_t p)
236{ 273{
237 PCMREC_NULL = 0, 274 return pcm_buffer + p;
238 PCMREC_INIT, /* enable recording */ 275}
239 PCMREC_CLOSE, /* close recording */
240 PCMREC_OPTIONS, /* set recording options */
241 PCMREC_RECORD, /* record a new file */
242 PCMREC_STOP, /* stop the current recording */
243 PCMREC_PAUSE, /* pause the current recording */
244 PCMREC_RESUME, /* resume the current recording */
245};
246
247/*******************************************************************/
248/* Functions that are not executing in the audio thread first */
249/*******************************************************************/
250 276
251static void pcmrec_raise_error_status(unsigned long e) 277/* Buffer pointer (p) plus value (v), wrapped if necessary */
278static size_t pcmbuf_add(size_t p, size_t v)
252{ 279{
253 pcm_rec_lock(); /* DMA sets this too */ 280 size_t res = p + v;
254 errors |= e; 281
255 pcm_rec_unlock(); 282 if (res >= PCM_BUF_SIZE)
283 res -= PCM_BUF_SIZE;
284
285 return res;
256} 286}
257 287
258static void pcmrec_raise_warning_status(unsigned long w) 288/* Size of data in PCM buffer */
289size_t pcmbuf_used(void)
259{ 290{
260 warnings |= w; 291 size_t p1 = pcm_ridx;
292 size_t p2 = pcm_widx;
293
294 if (p1 > p2)
295 p2 += PCM_BUF_SIZE;
296
297 return p2 - p1;
261} 298}
262 299
263/* Callback for when more data is ready - called in interrupt context */ 300/* Buffer pointer (p) to memory address of header */
264static void pcm_rec_have_more(void **start, size_t *size) 301static inline union enc_chunk_hdr * encbuf_ptr(size_t p)
265{ 302{
266 if (!dma_lock) 303 return enc_buffer + p;
267 { 304}
268 /* advance write position */
269 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
270 305
271 /* set pcm ovf if processing start position is inside current 306/* Buffer pointer (p) plus value (v), wrapped if necessary */
272 write chunk */ 307static size_t encbuf_add(size_t p, size_t v)
273 if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE) 308{
274 pcmrec_raise_warning_status(PCMREC_W_PCM_BUFFER_OVF); 309 size_t res = p + v;
275 310
276 dma_wr_pos = next_pos; 311 if (res >= enc_buflen)
277 } 312 res -= enc_buflen;
278 313
279 *start = GET_PCM_CHUNK(dma_wr_pos); 314 return res;
280 *size = PCM_CHUNK_SIZE; 315}
281} /* pcm_rec_have_more */
282 316
283static enum pcm_dma_status pcm_rec_status_callback(enum pcm_dma_status status) 317/* Number of free buffer slots */
318static size_t encbuf_free(void)
284{ 319{
285 if (status < PCM_DMAST_OK) 320 size_t p1 = enc_ridx;
286 { 321 size_t p2 = enc_widx;
287 /* some error condition */
288 if (status == PCM_DMAST_ERR_DMA)
289 {
290 /* Flush recorded data to disk and stop recording */
291 errors |= PCMREC_E_DMA;
292 return status;
293 }
294 /* else try again next transmission - frame is invalid */
295 }
296 322
297 return PCM_DMAST_OK; 323 if (p2 >= p1)
298} /* pcm_rec_status_callback */ 324 p1 += enc_buflen;
299 325
300static void reset_hardware(void) 326 return p1 - p2;
327}
328
329/* Number of used buffer slots */
330static size_t encbuf_used(void)
301{ 331{
302 /* reset pcm to defaults */ 332 size_t p1 = enc_ridx;
303 pcm_set_frequency(HW_SAMPR_RESET | SAMPR_TYPE_REC); 333 size_t p2 = enc_widx;
304 audio_set_output_source(AUDIO_SRC_PLAYBACK); 334
305 pcm_apply_settings(); 335 if (p1 > p2)
336 p2 += enc_buflen;
337
338 return p2 - p1;
306} 339}
307 340
308/** pcm_rec_* group **/ 341/* Is the encoder buffer empty? */
342static bool encbuf_empty(void)
343{
344 return enc_ridx == enc_widx;
345}
309 346
310/** 347/* Buffer pointer (p) plus size (v), written to enc_widx, new widx
311 * Clear all errors and warnings 348 * zero-initialized */
312 */ 349static void encbuf_widx_advance(size_t widx, size_t v)
313void pcm_rec_error_clear(void)
314{ 350{
315 errors = warnings = 0; 351 widx = encbuf_add(widx, v);
316} /* pcm_rec_error_clear */ 352 encbuf_ptr(widx)->zero = 0;
353 enc_widx = widx;
354}
317 355
318/** 356/* Buffer pointer (p) plus size of chunk at (p), wrapped to (0) if
319 * Check mode, errors and warnings 357 * necessary.
320 */ 358 *
321unsigned int pcm_rec_status(void) 359 * pout points to variable to receive increment result
360 *
361 * Returns NULL if it was a wrap marker */
362static void * encbuf_read_ptr_incr(size_t p, size_t *pout)
322{ 363{
323 unsigned int ret = 0; 364 union enc_chunk_hdr *hdr = encbuf_ptr(p);
365 size_t v;
324 366
325 if (is_recording) 367 switch (hdr->type)
326 ret |= AUDIO_STATUS_RECORD; 368 {
327 else if (pre_record_ticks) 369 case CHUNK_T_DATA:
328 ret |= AUDIO_STATUS_PRERECORD; 370 v = CHUNK_DATA_COUNT(hdr->size);
371 break;
372 case CHUNK_T_STREAM_START:
373 v = hdr->size;
374 break;
375 case CHUNK_T_STREAM_END:
376 default:
377 v = 1;
378 break;
379 case CHUNK_T_WRAP:
380 /* Wrap markers are not returned but caller may have to know that
381 the index was changed since it impacts available space */
382 *pout = 0;
383 return NULL;
384 }
329 385
330 if (is_paused) 386 *pout = encbuf_add(p, v);
331 ret |= AUDIO_STATUS_PAUSE; 387 return hdr;
388}
332 389
333 if (errors) 390/* Buffer pointer (p) of contiguous free space (v), wrapped to (0) if
334 ret |= AUDIO_STATUS_ERROR; 391 * necessary.
392 *
393 * pout points to variable to receive possible-adjusted p
394 *
395 * Returns header at (p) or wrapped header at (0) if wrap was
396 * required in order to provide contiguous space. Header is zero-
397 * initialized.
398 *
399 * Marks the wrap point if a wrap is required to make the allocation. */
400static void * encbuf_get_write_ptr(size_t p, size_t v, size_t *pout)
401{
402 union enc_chunk_hdr *hdr = encbuf_ptr(p);
335 403
336 if (warnings) 404 if (p + v > enc_buflen)
337 ret |= AUDIO_STATUS_WARNING; 405 {
406 hdr->type = CHUNK_T_WRAP; /* All other fields ignored */
407 p = 0;
408 hdr = encbuf_ptr(0);
409 }
338 410
339 return ret; 411 *pout = p;
340} /* pcm_rec_status */ 412 hdr->zero = 0;
413 return hdr;
414}
341 415
342/** 416/* Post a flush request to audio thread, if none is currently queued */
343 * Return warnings that have occured since recording started 417static void encbuf_request_flush(void)
344 */
345unsigned long pcm_rec_get_warnings(void)
346{ 418{
347 return warnings; 419 if (!queue_peek_ex(&audio_queue, NULL, 0,
420 &(const long [2]){ Q_AUDIO_RECORD_FLUSH,
421 Q_AUDIO_RECORD_FLUSH }))
422 queue_post(&audio_queue, Q_AUDIO_RECORD_FLUSH, 0);
348} 423}
349 424
350#if 0 425/* Set the error bits in (e): no lock */
351int pcm_rec_current_bitrate(void) 426static inline void set_error_bits(uint32_t e)
352{ 427{
353 if (accum_pcm_samples == 0) 428 errors |= e;
354 return 0; 429 rec_errors |= e;
355 430}
356 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
357} /* pcm_rec_current_bitrate */
358#endif
359 431
360#if 0 432/* Clear the error bits in (e): no lock */
361int pcm_rec_encoder_afmt(void) 433static inline void clear_error_bits(uint32_t e)
362{ 434{
363 return enc_config.afmt; 435 errors &= ~e;
364} /* pcm_rec_encoder_afmt */ 436}
365#endif
366 437
367#if 0 438/* Set the error bits in (e) */
368int pcm_rec_rec_format(void) 439static void raise_error_status(uint32_t e)
369{ 440{
370 return afmt_rec_format[enc_config.afmt]; 441 pcm_rec_lock();
371} /* pcm_rec_rec_format */ 442 set_error_bits(e);
372#endif 443 pcm_rec_unlock();
444}
373 445
374#ifdef HAVE_SPDIF_IN 446/* Clear the error bits in (e) */
375unsigned long pcm_rec_sample_rate(void) 447static void clear_error_status(uint32_t e)
376{ 448{
377 /* Which is better ?? */ 449 pcm_rec_lock();
378#if 0 450 clear_error_bits(e);
379 return enc_sample_rate; 451 pcm_rec_unlock();
380#endif 452}
381 return sample_rate;
382} /* audio_get_sample_rate */
383#endif
384
385/** audio_* group **/
386 453
387/** 454/* Set the warning bits in (w): no lock */
388 * Initializes recording - call before calling any other recording function 455static inline void set_warning_bits(uint32_t w)
389 */
390void audio_init_recording(void)
391{ 456{
392 logf("audio_init_recording"); 457 warnings |= w;
393 queue_send(&audio_queue, Q_AUDIO_INIT_RECORDING, 1); 458}
394 logf("audio_init_recording done");
395} /* audio_init_recording */
396 459
397/** 460/* Clear the warning bits in (w): no lock */
398 * Closes recording - call audio_stop_recording first 461static inline void clear_warning_bits(uint32_t w)
399 */
400void audio_close_recording(void)
401{ 462{
402 logf("audio_close_recording"); 463 warnings &= ~w;
403 queue_send(&audio_queue, Q_AUDIO_CLOSE_RECORDING, 0); 464}
404 logf("audio_close_recording done");
405} /* audio_close_recording */
406 465
407/** 466/* Set the warning bits in (w) */
408 * Sets recording parameters 467static void raise_warning_status(uint32_t w)
409 */
410void audio_set_recording_options(struct audio_recording_options *options)
411{ 468{
412 logf("audio_set_recording_options"); 469 pcm_rec_lock();
413 queue_send(&audio_queue, Q_AUDIO_RECORDING_OPTIONS, (intptr_t)options); 470 set_warning_bits(w);
414 logf("audio_set_recording_options done"); 471 pcm_rec_unlock();
415} /* audio_set_recording_options */ 472}
416 473
417/** 474/* Clear the warning bits in (w) */
418 * Start recording if not recording or else split 475static void clear_warning_status(uint32_t w)
419 */
420void audio_record(const char *filename)
421{ 476{
422 logf("audio_record: %s", filename); 477 pcm_rec_lock();
423 flush_interrupt(); 478 clear_warning_bits(w);
424 queue_send(&audio_queue, Q_AUDIO_RECORD, (intptr_t)filename); 479 pcm_rec_unlock();
425 logf("audio_record_done"); 480}
426} /* audio_record */
427 481
428/** 482/* Callback for when more data is ready - called by DMA ISR */
429 * audio_record wrapper for API compatibility with HW codec 483static void pcm_rec_have_more(void **start, size_t *size)
430 */
431void audio_new_file(const char *filename)
432{ 484{
433 audio_record(filename); 485 size_t next_idx = pcm_widx;
434} /* audio_new_file */
435 486
436/** 487 if (!pcm_pause)
437 * Stop current recording if recording 488 {
438 */ 489 /* One empty chunk must remain after widx is advanced */
439void audio_stop_recording(void) 490 if (pcmbuf_used() <= PCM_BUF_SIZE - 2*PCM_CHUNK_SIZE)
440{ 491 next_idx = pcmbuf_add(next_idx, PCM_CHUNK_SIZE);
441 logf("audio_stop_recording"); 492 else
442 flush_interrupt(); 493 set_warning_bits(PCMREC_W_PCM_BUFFER_OVF);
443 queue_post(&audio_queue, Q_AUDIO_STOP, 0); 494 }
444 logf("audio_stop_recording done");
445} /* audio_stop_recording */
446 495
447/** 496 *start = pcmbuf_ptr(next_idx);
448 * Pause current recording 497 *size = PCM_CHUNK_SIZE;
449 */
450void audio_pause_recording(void)
451{
452 logf("audio_pause_recording");
453 flush_interrupt();
454 queue_post(&audio_queue, Q_AUDIO_PAUSE, 0);
455 logf("audio_pause_recording done");
456} /* audio_pause_recording */
457 498
458/** 499 pcm_widx = next_idx;
459 * Resume current recording if paused 500}
460 */
461void audio_resume_recording(void)
462{
463 logf("audio_resume_recording");
464 queue_post(&audio_queue, Q_AUDIO_RESUME, 0);
465 logf("audio_resume_recording done");
466} /* audio_resume_recording */
467 501
468/** 502static enum pcm_dma_status pcm_rec_status_callback(enum pcm_dma_status status)
469 * Note that microphone is mono, only left value is used
470 * See audiohw_set_recvol() for exact ranges.
471 *
472 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
473 *
474 */
475void audio_set_recording_gain(int left, int right, int type)
476{ 503{
477 //logf("rcmrec: t=%d l=%d r=%d", type, left, right); 504 if (status < PCM_DMAST_OK)
478 audiohw_set_recvol(left, right, type); 505 {
479} /* audio_set_recording_gain */ 506 /* Some error condition */
507 if (status == PCM_DMAST_ERR_DMA)
508 {
509 set_error_bits(PCMREC_E_DMA);
510 return status;
511 }
512 else
513 {
514 /* Try again next transmission - frame is invalid */
515 set_warning_bits(PCMREC_W_DMA);
516 }
517 }
480 518
481/** Information about current state **/ 519 return PCM_DMAST_OK;
520}
482 521
483/** 522/* Start DMA transfer */
484 * Return current recorded time in ticks (playback eqivalent time) 523static void pcm_start_recording(void)
485 */
486unsigned long audio_recorded_time(void)
487{ 524{
488 if (!is_recording || enc_sample_rate == 0) 525 pcm_record_data(pcm_rec_have_more, pcm_rec_status_callback,
489 return 0; 526 pcmbuf_ptr(pcm_widx), PCM_CHUNK_SIZE);
527}
490 528
491 /* return actual recorded time a la encoded data even if encoder rate 529/* Initialize the various recording buffers */
492 doesn't match the pcm rate */ 530static void init_rec_buffers(void)
493 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate); 531{
494} /* audio_recorded_time */ 532 /* Layout of recording buffer: |PCMBUF|STREAMBUF|FILENAME|ENCBUF| */
533 void *buf = rec_buffer;
534 size_t size = rec_buffer_size;
535
536 /* PCMBUF */
537 pcm_buffer = CACHEALIGN_UP(buf); /* Line align */
538 size -= pcm_buffer + PCM_BUF_SIZE - buf;
539 buf = pcm_buffer + PCM_BUF_SIZE;
540
541 /* STREAMBUF */
542 stream_buffer = buf; /* Also line-aligned */
543 buf += STREAM_BUF_SIZE;
544 size -= STREAM_BUF_SIZE;
545
546 /* FILENAME */
547 fname_buf = buf;
548 buf += CHUNK_FILE_COUNT(MAX_PATH)*ENC_HDR_SIZE;
549 size -= CHUNK_FILE_COUNT(MAX_PATH)*ENC_HDR_SIZE;
550 fname_buf->hdr.zero = 0;
551
552 /* ENCBUF */
553 enc_buffer = buf;
554 enc_buflen = size;
555 ALIGN_BUFFER(enc_buffer, enc_buflen, ENC_HDR_SIZE);
556 enc_buflen = CHUNK_SIZE_COUNT(enc_buflen);
557}
495 558
496/** 559/* Reset the circular buffers */
497 * Return number of bytes encoded to output 560static void reset_fifos(bool hard)
498 */
499unsigned long audio_num_recorded_bytes(void)
500{ 561{
501 if (!is_recording) 562 /* PCM FIFO */
502 return 0; 563 pcm_pause = true;
503 564
504 return num_rec_bytes; 565 if (hard)
505} /* audio_num_recorded_bytes */ 566 pcm_widx = 0; /* Don't just empty but reset it */
506 567
507/***************************************************************************/ 568 pcm_ridx = pcm_widx;
508/* */
509/* Functions that execute in the context of audio thread */
510/* */
511/***************************************************************************/
512 569
513static void pcmrec_init_state(void) 570 /* Encoder FIFO */
514{ 571 encbuf_widx_advance(0, 0);
515 flush_interrupts = 0; 572 enc_ridx = 0;
516 573
517 /* warings and errors */ 574 /* No overflow-related warnings now */
518 warnings = 575 clear_warning_status(PCMREC_W_PCM_BUFFER_OVF | PCMREC_W_ENC_BUFFER_OVF);
519 errors = 0; 576}
520 577
521 /* pcm FIFO */ 578/* Initialize file statistics */
522 dma_lock = true; 579static void reset_rec_stats(void)
523 pcm_rd_pos = 0; 580{
524 dma_wr_pos = 0; 581 num_rec_bytes = 0;
525 pcm_enc_pos = 0; 582 num_rec_samples = 0;
583 encbuf_rec_count = 0;
584 clear_warning_status(PCMREC_W_FILE_SIZE);
585}
526 586
527 /* encoder FIFO */ 587/* Boost or unboost recording threads' priorities */
528 enc_wr_index = 0; 588static void do_prio_boost(bool boost)
529 enc_rd_index = 0; 589{
590#ifdef HAVE_PRIORITY_SCHEDULING
591 prio_boosted = boost;
530 592
531 /* filename queue */ 593 int prio = PRIORITY_RECORDING;
532 fnq_rd_pos = 0;
533 fnq_wr_pos = 0;
534 594
535 /* stats */ 595 if (boost)
536 num_rec_bytes = 0; 596 prio -= 4;
537 num_rec_samples = 0; 597
538#if 0 598 codec_thread_set_priority(prio);
539 accum_rec_bytes = 0; 599 thread_set_priority(audio_thread_id, prio);
540 accum_pcm_samples = 0;
541#endif 600#endif
601 (void)boost;
602}
603
604/* Reset all relevant state */
605static void init_state(void)
606{
607 reset_fifos(true);
608 reset_rec_stats();
609 do_prio_boost(false);
610 cancel_cpu_boost();
611 record_state = REC_STATE_IDLE;
612 record_status = RECORD_STOPPED;
613}
542 614
543 pre_record_ticks = 0; 615/* Set hardware samplerate and save it */
616static void update_samplerate_config(unsigned long sampr)
617{
618 /* PCM samplerate is either the same as the setting or the nearest
619 one hardware supports if using S/PDIF */
620 unsigned long pcm_sampr = sampr;
544 621
545 is_recording = false; 622#ifdef HAVE_SPDIF_IN
546 is_paused = false; 623 if (rec_source == AUDIO_SRC_SPDIF)
547} /* pcmrec_init_state */ 624 {
625 int index = round_value_to_list32(sampr, hw_freq_sampr,
626 HW_NUM_FREQ, false);
627 pcm_sampr = hw_freq_sampr[index];
628 }
629#endif /* HAVE_SPDIF_IN */
548 630
549/** Filename Queue **/ 631 pcm_set_frequency(pcm_sampr | SAMPR_TYPE_REC);
632 sample_rate = sampr;
633}
550 634
551/* returns true if the queue is empty */ 635/* Calculate the average data rate */
552static inline bool pcmrec_fnq_is_empty(void) 636static unsigned long get_encbuf_datarate(void)
553{ 637{
554 return fnq_rd_pos == fnq_wr_pos; 638 /* If not yet calculable, start with uncompressed PCM byterate */
555} /* pcmrec_fnq_is_empty */ 639 if (num_rec_samples && sample_rate && encbuf_rec_count)
640 {
641 return (encbuf_rec_count*sample_rate + num_rec_samples - 1)
642 / num_rec_samples;
643 }
644 else
645 {
646 return CHUNK_SIZE_COUNT(sample_rate*num_channels*PCM_DEPTH_BYTES);
647 }
648}
556 649
557/* empties the filename queue */ 650/* Returns true if the watermarks should be updated due to data rate
558static inline void pcmrec_fnq_set_empty(void) 651 change */
652static bool monitor_encbuf_datarate(void)
559{ 653{
560 fnq_rd_pos = fnq_wr_pos; 654 unsigned long rate = get_encbuf_datarate();
561} /* pcmrec_fnq_set_empty */ 655 long diff = rate - encbuf_datarate;
656 /* Off by more than 1/2 FLUSH_MON_INTERVAL? */
657 return 2*(unsigned long)abs(diff) > encbuf_datarate*FLUSH_MON_INTERVAL;
658}
562 659
563/* returns true if the queue is full */ 660/* Get adjusted spinup time */
564static bool pcmrec_fnq_is_full(void) 661static int get_spinup_time(void)
565{ 662{
566 ssize_t size = fnq_wr_pos - fnq_rd_pos; 663 int spin = storage_spinup_time();
567 if (size < 0)
568 size += fnq_size;
569 664
570 return size >= fnq_size - MAX_PATH; 665#if (CONFIG_STORAGE & STORAGE_ATA)
571} /* pcmrec_fnq_is_full */ 666 /* Write at FLUSH_SECONDS + st remaining in enc_buffer - range fs+2s to
667 fs+10s total - default to 3.5s spinup. */
668 if (spin == 0)
669 spin = 35*HZ/10; /* default - cozy */
670 else if (spin < 2*HZ)
671 spin = 2*HZ; /* ludicrous - ramdisk? */
672 else if (spin > 10*HZ)
673 spin = 10*HZ; /* do you have a functioning HD? */
674#endif /* (CONFIG_STORAGE & STORAGE_ATA) */
675
676 return spin;
677}
572 678
573/* queue another filename - will overwrite oldest one if full */ 679/* Returns true if the watermarks should be updated due to spinup time
574static bool pcmrec_fnq_add_filename(const char *filename) 680 change */
681static inline bool monitor_spinup_time(void)
575{ 682{
576 strlcpy(fn_queue + fnq_wr_pos, filename, MAX_PATH); 683#if (CONFIG_STORAGE & STORAGE_ATA)
577 fnq_wr_pos = FNQ_NEXT(fnq_wr_pos); 684 return get_spinup_time() != spinup_time;
685#else
686 return false;
687#endif
688}
578 689
579 if (fnq_rd_pos != fnq_wr_pos) 690/* Update buffer watermarks with spinup time compensation */
580 return true; 691static void refresh_watermarks(void)
692{
693 int spin = get_spinup_time();
694#if (CONFIG_STORAGE & STORAGE_ATA)
695 logf("ata spinup: %d", spin);
696 spinup_time = spin;
697#endif
581 698
582 /* queue full */ 699 unsigned long rate = get_encbuf_datarate();
583 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos); 700 logf("byterate: %lu", rate * ENC_HDR_SIZE);
584 return true; 701 encbuf_datarate = rate;
585} /* pcmrec_fnq_add_filename */
586 702
587/* replace the last filename added */ 703 /* Try to start writing with FLUSH_SECONDS remaining after disk spinup */
588static bool pcmrec_fnq_replace_tail(const char *filename) 704 high_watermark = (uint64_t)rate*(FLUSH_SECONDS*HZ + spin) / HZ;
589{
590 int pos;
591 705
592 if (pcmrec_fnq_is_empty()) 706 if (high_watermark > enc_buflen)
593 return false; 707 high_watermark = enc_buflen;
594 708
595 pos = FNQ_PREV(fnq_wr_pos); 709 high_watermark = enc_buflen - high_watermark;
596 710
597 strlcpy(fn_queue + pos, filename, MAX_PATH); 711 logf("high wm: %lu", (unsigned long)high_watermark);
598 712
599 return true; 713#ifdef HAVE_PRIORITY_SCHEDULING
600} /* pcmrec_fnq_replace_tail */ 714 /* Boost thread priority if enough ground is lost since flushing started
715 or is taking an unreasonably long time */
716 flood_watermark = rate*PANIC_SECONDS;
601 717
602/* pulls the next filename from the queue */ 718 if (flood_watermark > enc_buflen)
603static bool pcmrec_fnq_get_filename(char *filename) 719 flood_watermark = enc_buflen;
604{
605 if (pcmrec_fnq_is_empty())
606 return false;
607 720
608 if (filename) 721 flood_watermark = enc_buflen - flood_watermark;
609 strlcpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
610 722
611 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos); 723 logf("flood wm: %lu", (unsigned long)flood_watermark);
612 return true; 724#endif /* HAVE_PRIORITY_SCHEDULING */
613} /* pcmrec_fnq_get_filename */ 725}
614 726
615/* close the file number pointed to by fd_p */ 727/* Tell encoder the stream parameters and get information back */
616static void pcmrec_close_file(int *fd_p) 728static bool configure_encoder_stream(void)
617{ 729{
618 if (*fd_p < 0) 730 struct enc_inputs inputs;
619 return; /* preserve error */ 731 inputs.sample_rate = sample_rate;
732 inputs.num_channels = num_channels;
733 inputs.config = &enc_config;
620 734
621 if (close(*fd_p) != 0) 735 /* encoder can change these - init with defaults */
622 pcmrec_raise_error_status(PCMREC_E_IO); 736 inputs.enc_sample_rate = sample_rate;
623 737
624 *fd_p = -1; 738 if (enc_cb(ENC_CB_INPUTS, &inputs) < 0)
625} /* pcmrec_close_file */ 739 {
740 raise_error_status(PCMREC_E_ENC_SETUP);
741 return false;
742 }
626 743
627/** Data Flushing **/ 744 enc_sample_rate = inputs.enc_sample_rate;
628 745
629/** 746 if (enc_sample_rate != sample_rate)
630 * called after callback to update sizes if codec changed the amount of data
631 * a chunk represents
632 */
633static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
634 unsigned long prev_num_pcm)
635{
636 if (rec_fdata.new_enc_size != prev_enc_size)
637 { 747 {
638 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size; 748 /* Codec doesn't want to/can't use the setting and has chosen a
639 num_rec_bytes += size_diff; 749 different sample rate */
640#if 0 750 raise_warning_status(PCMREC_W_SAMPR_MISMATCH);
641 accum_rec_bytes += size_diff; 751 logf("enc sampr:%lu", enc_sample_rate);
642#endif
643 } 752 }
644 753 else
645 if (rec_fdata.new_num_pcm != prev_num_pcm)
646 { 754 {
647 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm; 755 clear_warning_status(PCMREC_W_SAMPR_MISMATCH);
648 num_rec_samples += pcm_diff;
649#if 0
650 accum_pcm_samples += pcm_diff;
651#endif
652 } 756 }
653} /* pcmrec_update_sizes_inl */
654 757
655/* don't need to inline every instance */ 758 refresh_watermarks();
656static void pcmrec_update_sizes(size_t prev_enc_size, 759 return true;
657 unsigned long prev_num_pcm) 760}
761
762#ifdef HAVE_SPDIF_IN
763/* Return the S/PDIF sample rate closest to a value in the master list */
764static unsigned long get_spdif_samplerate(void)
658{ 765{
659 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm); 766 unsigned long sr = spdif_measure_frequency();
660} /* pcmrec_update_sizes */ 767 int index = round_value_to_list32(sr, audio_master_sampr_list,
768 SAMPR_NUM_FREQ, false);
769 return audio_master_sampr_list[index];
770}
661 771
662static void pcmrec_start_file(void) 772/* Check the S/PDIF rate and compare to current setting. Apply the new
773 * rate if it changed. */
774static void check_spdif_samplerate(void)
663{ 775{
664 size_t enc_size = rec_fdata.new_enc_size; 776 unsigned long sampr = get_spdif_samplerate();
665 unsigned long num_pcm = rec_fdata.new_num_pcm;
666 int curr_rec_file = rec_fdata.rec_file;
667 char filename[MAX_PATH];
668 777
669 /* must always pull the filename that matches with this queue */ 778 if (sampr == sample_rate)
670 if (!pcmrec_fnq_get_filename(filename)) 779 return;
671 { 780
672 logf("start file: fnq empty"); 781 codec_stop();
673 *filename = '\0'; 782 pcm_stop_recording();
674 pcmrec_raise_error_status(PCMREC_E_FNQ_DESYNC); 783 reset_fifos(true);
675 } 784 reset_rec_stats();
676 else if (errors != 0) 785 update_samplerate_config(sampr);
677 { 786 pcm_apply_settings();
678 logf("start file: error already");
679 }
680 else if (curr_rec_file >= 0)
681 {
682 /* Any previous file should have been closed */
683 logf("start file: file already open");
684 pcmrec_raise_error_status(PCMREC_E_FNQ_DESYNC);
685 }
686 787
687 if (errors != 0) 788 if (!configure_encoder_stream() || rec_errors)
688 rec_fdata.chunk->flags |= CHUNKF_ERROR; 789 return;
689 790
690 /* encoder can set error flag here and should increase 791 pcm_start_recording();
691 enc_new_size and pcm_new_size to reflect additional
692 data written if any */
693 rec_fdata.filename = filename;
694 enc_events_callback(ENC_START_FILE, &rec_fdata);
695 792
696 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR)) 793 if (record_status == RECORD_PRERECORDING)
697 { 794 {
698 logf("start file: enc error"); 795 codec_go();
699 pcmrec_raise_error_status(PCMREC_E_ENCODER); 796 pcm_pause = false;
700 } 797 }
798}
799#endif /* HAVE_SPDIF_IN */
701 800
702 if (errors != 0) 801/* Discard the stream buffer contents */
802static inline void stream_discard_buf(void)
803{
804 stream_buf_used = 0;
805}
806
807/* Flush stream buffer to disk */
808static bool stream_flush_buf(void)
809{
810 if (stream_buf_used == 0)
811 return true;
812
813 ssize_t rc = write(rec_fd, stream_buffer, stream_buf_used);
814
815 if (LIKELY(rc == stream_buf_used))
703 { 816 {
704 pcmrec_close_file(&curr_rec_file); 817 stream_discard_buf();
705 /* Write no more to this file */ 818 return true;
706 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
707 } 819 }
708 else 820
821 if (rc > 0)
709 { 822 {
710 pcmrec_update_sizes(enc_size, num_pcm); 823 /* Some was written; keep in sync */
824 stream_buf_used -= rc;
825 memmove(stream_buffer, stream_buffer + rc, stream_buf_used);
711 } 826 }
712 827
713 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE; 828 return false;
714} /* pcmrec_start_file */ 829}
715 830
716static inline void pcmrec_write_chunk(void) 831/* Close the output file */
832static void close_rec_file(void)
717{ 833{
718 size_t enc_size = rec_fdata.new_enc_size; 834 if (rec_fd < 0)
719 unsigned long num_pcm = rec_fdata.new_num_pcm; 835 return;
836
837 bool ok = stream_flush_buf();
720 838
721 if (errors != 0) 839 if (close(rec_fd) != 0 || !ok)
722 rec_fdata.chunk->flags |= CHUNKF_ERROR; 840 raise_error_status(PCMREC_E_IO);
723 841
724 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata); 842 rec_fd = -1;
843}
725 844
726 if ((long)rec_fdata.chunk->flags >= 0) 845/* Creates or opens the current path */
846static bool open_rec_file(bool create)
847{
848 if (rec_fd >= 0)
727 { 849 {
728 pcmrec_update_sizes_inl(enc_size, num_pcm); 850 /* Any previous file should have been closed */
851 logf("open file: file already open");
852 close_rec_file();
729 } 853 }
730 else if (errors == 0) 854
855 stream_discard_buf();
856 int oflags = create ? O_CREAT|O_TRUNC : 0;
857 rec_fd = open(fname_buf->path, O_RDWR|oflags, 0666);
858
859 if (rec_fd < 0)
731 { 860 {
732 logf("wr chk enc error %lu %lu", 861 raise_error_status(PCMREC_E_IO);
733 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm); 862 return false;
734 pcmrec_raise_error_status(PCMREC_E_ENCODER);
735 } 863 }
736} /* pcmrec_write_chunk */
737 864
738static void pcmrec_end_file(void) 865 return true;
866}
867
868/* Copy with mono conversion - output 1/2 size of input */
869static void * ICODE_ATTR
870copy_buffer_mono_lr(void *dst, const void *src, size_t src_size)
739{ 871{
740 /* all data in output buffer for current file will have been 872 int16_t *d = dst;
741 written and encoder can now do any nescessary steps to 873 int16_t const *s = src;
742 finalize the written file */
743 size_t enc_size = rec_fdata.new_enc_size;
744 unsigned long num_pcm = rec_fdata.new_num_pcm;
745 874
746 enc_events_callback(ENC_END_FILE, &rec_fdata); 875 /* mono = (L + R) / 2 */
876 do
877 *d++ = ((int32_t){ *s++ } + *s++ + 1) >> 1;
878 while (src_size -= PCM_SAMP_SIZE);
747 879
748 if (errors == 0) 880 return dst;
749 { 881}
750 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
751 {
752 logf("end file: enc error");
753 pcmrec_raise_error_status(PCMREC_E_ENCODER);
754 }
755 else
756 {
757 pcmrec_update_sizes(enc_size, num_pcm);
758 }
759 }
760 882
761 /* Force file close if error */ 883/* Copy with mono conversion - output 1/2 size of input */
762 if (errors != 0) 884static void * ICODE_ATTR
763 pcmrec_close_file(&rec_fdata.rec_file); 885copy_buffer_mono_l(void *dst, const void *src, size_t src_size)
886{
887 int16_t *d = dst;
888 int16_t const *s = (int16_t *)src - 2;
764 889
765 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE; 890 /* mono = L */
766} /* pcmrec_end_file */ 891 do
892 *d++ = *(s += 2);
893 while (src_size -= PCM_SAMP_SIZE);
767 894
768/** 895 return dst;
769 * Update buffer watermarks with spinup time compensation 896}
770 * 897
771 * All this assumes reasonable data rates, chunk sizes and sufficient 898/* Copy with mono conversion - output 1/2 size of input */
772 * memory for the most part. Some dumb checks are included but perhaps 899static void * ICODE_ATTR
773 * are pointless since this all will break down at extreme limits that 900copy_buffer_mono_r(void *dst, const void *src, size_t src_size)
774 * are currently not applicable to any supported device.
775 */
776static void pcmrec_refresh_watermarks(void)
777{ 901{
778 logf("ata spinup: %d", storage_spinup_time()); 902 int16_t *d = dst;
903 int16_t const *s = (int16_t *)src - 1;
779 904
780 /* set the low mark for when flushing stops if automatic */ 905 /* mono = R */
781 /* don't change the order in this expression, LOW_SECONDS can be an 906 do
782 * integer fraction of 4 */ 907 *d++ = *(s += 2);
783 low_watermark = (sample_rate*4*LOW_SECONDS + (enc_chunk_size-1)) 908 while (src_size -= PCM_SAMP_SIZE);
784 / enc_chunk_size;
785 logf("low wmk: %d", low_watermark);
786 909
787#ifdef HAVE_PRIORITY_SCHEDULING 910 return dst;
788 /* panic boost thread priority if 2 seconds of ground is lost - 911}
789 this allows encoder to boost with just under a second of
790 pcm data (if not yet full enough to boost itself)
791 and not falsely trip the alarm. */
792 /* don't change the order in this expression, PANIC_SECONDS can be an
793 * integer fraction of 4 */
794 flood_watermark = enc_num_chunks -
795 (sample_rate*4*PANIC_SECONDS + (enc_chunk_size-1))
796 / enc_chunk_size;
797
798 if (flood_watermark < low_watermark)
799 {
800 logf("warning: panic < low");
801 flood_watermark = low_watermark;
802 }
803 912
804 logf("flood at: %d", flood_watermark); 913
805#endif 914/** pcm_rec_* group **/
806 spinup_time = last_storage_spinup_time = storage_spinup_time(); 915
807#if (CONFIG_STORAGE & STORAGE_ATA) 916/* Clear all errors and warnings */
808 /* write at 8s + st remaining in enc_buffer - range 12s to 917void pcm_rec_error_clear(void)
809 20s total - default to 3.5s spinup. */ 918{
810 if (spinup_time == 0) 919 clear_error_status(PCMREC_E_ALL);
811 spinup_time = 35*HZ/10; /* default - cozy */ 920 clear_warning_status(PCMREC_W_ALL);
812 else if (spinup_time < 2*HZ) 921}
813 spinup_time = 2*HZ; /* ludicrous - ramdisk? */ 922
814 else if (spinup_time > 10*HZ) 923/* Check mode, errors and warnings */
815 spinup_time = 10*HZ; /* do you have a functioning HD? */ 924unsigned int pcm_rec_status(void)
925{
926 unsigned int ret = record_status;
927
928 if (errors)
929 ret |= AUDIO_STATUS_ERROR;
930
931 if (warnings)
932 ret |= AUDIO_STATUS_WARNING;
933
934 return ret;
935}
936
937/* Return warnings that have occured since recording started */
938uint32_t pcm_rec_get_warnings(void)
939{
940 return warnings;
941}
942
943#ifdef HAVE_SPDIF_IN
944/* Return the currently-configured sample rate */
945unsigned long pcm_rec_sample_rate(void)
946{
947 return sample_rate;
948}
816#endif 949#endif
817 950
818 /* try to start writing with 10s remaining after disk spinup */
819 high_watermark = enc_num_chunks -
820 ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate +
821 (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ);
822 951
823 if (high_watermark < low_watermark) 952/** audio_* group **/
824 { 953
825 logf("warning: low 'write at' (%d)", high_watermark); 954/* Initializes recording - call before calling any other recording function */
826 high_watermark = low_watermark; 955void audio_init_recording(void)
827 low_watermark /= 2; 956{
828 } 957 LOGFQUEUE("audio >| pcmrec Q_AUDIO_INIT_RECORDING");
958 audio_queue_send(Q_AUDIO_INIT_RECORDING, 1);
959}
829 960
830 logf("write at: %d", high_watermark); 961/* Closes recording - call audio_stop_recording first or risk data loss */
831} /* pcmrec_refresh_watermarks */ 962void audio_close_recording(void)
963{
964 LOGFQUEUE("audio >| pcmrec Q_AUDIO_CLOSE_RECORDING");
965 audio_queue_send(Q_AUDIO_CLOSE_RECORDING, 0);
966}
832 967
833/** 968/* Sets recording parameters */
834 * Process the chunks 969void audio_set_recording_options(struct audio_recording_options *options)
835 *
836 * This function is called when queue_get_w_tmo times out.
837 *
838 * Set flush_num to the number of files to flush to disk or to
839 * a PCMREC_FLUSH_* constant.
840 */
841static void pcmrec_flush(unsigned flush_num)
842{ 970{
843#ifdef HAVE_PRIORITY_SCHEDULING 971 LOGFQUEUE("audio >| pcmrec Q_AUDIO_RECORDING_OPTIONS");
844 static unsigned long last_flush_tick; /* tick when function returned */ 972 audio_queue_send(Q_AUDIO_RECORDING_OPTIONS, (intptr_t)options);
845 unsigned long start_tick; /* When flush started */ 973}
846 unsigned long prio_tick; /* Timeout for auto boost */ 974
847 int prio_pcmrec; /* Current thread priority for pcmrec */ 975/* Start recording if not recording or else split */
848 int prio_codec; /* Current thread priority for codec */ 976void audio_record(const char *filename)
977{
978 LOGFQUEUE("audio >| pcmrec Q_AUDIO_RECORD: %s", filename);
979 audio_queue_send(Q_AUDIO_RECORD, (intptr_t)filename);
980}
981
982/* audio_record alias for API compatibility with HW codec */
983void audio_new_file(const char *filename)
984 __attribute__((alias("audio_record")));
985
986/* Stop current recording if recording */
987void audio_stop_recording(void)
988{
989 LOGFQUEUE("audio > pcmrec Q_AUDIO_RECORD_STOP");
990 audio_queue_post(Q_AUDIO_RECORD_STOP, 0);
991}
992
993/* Pause current recording */
994void audio_pause_recording(void)
995{
996 LOGFQUEUE("audio > pcmrec Q_AUDIO_RECORD_PAUSE");
997 audio_queue_post(Q_AUDIO_RECORD_PAUSE, 0);
998}
999
1000/* Resume current recording if paused */
1001void audio_resume_recording(void)
1002{
1003 LOGFQUEUE("audio > pcmrec Q_AUDIO_RECORD_RESUME");
1004 audio_queue_post(Q_AUDIO_RECORD_RESUME, 0);
1005}
1006
1007/* Set the input source gain. For mono sources, only left gain is used */
1008void audio_set_recording_gain(int left, int right, int type)
1009{
1010#if 0
1011 logf("pcmrec: t=%d l=%d r=%d", type, left, right);
849#endif 1012#endif
850 int num_ready; /* Number of chunks ready at start */ 1013 audiohw_set_recvol(left, right, type);
851 unsigned remaining; /* Number of file starts remaining */ 1014}
852 unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */
853 bool interruptable; /* Flush can be interupted */
854 1015
855 num_ready = enc_wr_index - enc_rd_index;
856 if (num_ready < 0)
857 num_ready += enc_num_chunks;
858 1016
859 /* save interruptable flag and remove it to get the actual count */ 1017/** Information about current state **/
860 interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0; 1018
861 flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE; 1019/* Return sample clock in HZ */
1020static unsigned long get_samples_time(void)
1021{
1022 if (enc_sample_rate == 0)
1023 return 0;
1024
1025 return (unsigned long)(HZ*num_rec_samples / enc_sample_rate);
1026}
862 1027
863 if (flush_num == 0) 1028/* Return current prerecorded time in ticks (playback equivalent time) */
1029unsigned long audio_prerecorded_time(void)
1030{
1031 if (record_status != RECORD_PRERECORDING)
1032 return 0;
1033
1034 unsigned long t = get_samples_time();
1035 return MIN(t, pre_record_seconds*HZ);
1036}
1037
1038/* Return current recorded time in ticks (playback equivalent time) */
1039unsigned long audio_recorded_time(void)
1040{
1041 if (record_state == REC_STATE_IDLE)
1042 return 0;
1043
1044 return get_samples_time();
1045}
1046
1047/* Return number of bytes encoded to output */
1048unsigned long audio_num_recorded_bytes(void)
1049{
1050 if (record_state == REC_STATE_IDLE)
1051 return 0;
1052
1053 return num_rec_bytes;
1054}
1055
1056
1057/** Data Flushing **/
1058
1059/* Stream start chunk with path was encountered */
1060static void flush_stream_start(struct enc_chunk_file *file)
1061{
1062 /* Save filename; don't open file here which avoids creating files
1063 with no audio content. Splitting while paused can create those
1064 in large numbers. */
1065 fname_buf->hdr = file->hdr;
1066 /* Correct size if this was wrap-padded */
1067 fname_buf->hdr.size = CHUNK_FILE_COUNT(
1068 strlcpy(fname_buf->path, file->path, MAX_PATH) + 1);
1069}
1070
1071/* Data chunk was encountered */
1072static bool flush_stream_data(struct enc_chunk_data *data)
1073{
1074 if (fname_buf->hdr.zero)
864 { 1075 {
865 if (!is_recording) 1076 /* First data chunk; create the file */
866 return; 1077 if (open_rec_file(true))
1078 {
1079 /* Inherit some flags from initial data chunk */
1080 fname_buf->hdr.err = data->hdr.err;
1081 fname_buf->hdr.pre = data->hdr.pre;
1082 fname_buf->hdr.aux0 = data->hdr.aux0;
867 1083
868 if (storage_spinup_time() != last_storage_spinup_time) 1084 if (enc_cb(ENC_CB_STREAM, fname_buf) < 0)
869 pcmrec_refresh_watermarks(); 1085 raise_error_status(PCMREC_E_ENCODER_STREAM);
1086 }
870 1087
871 /* enough available? no? then leave */ 1088 fname_buf->hdr.zero = 0;
872 if (num_ready < high_watermark)
873 return;
874 } /* endif (flush_num == 0) */
875 1089
876#ifdef HAVE_PRIORITY_SCHEDULING 1090 if (rec_errors)
877 start_tick = current_tick; 1091 return false;
878 prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time; 1092 }
1093
1094 if (rec_fd < 0)
1095 return true; /* Just keep discarding */
879 1096
880 if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2)) 1097 if (enc_cb(ENC_CB_STREAM, data) < 0)
881 { 1098 {
882 /* if we're getting called too much and this isn't forced, 1099 raise_error_status(PCMREC_E_ENCODER_STREAM);
883 boost stat by expiring timeout in advance */ 1100 return false;
884 logf("too frequent flush");
885 prio_tick = current_tick - 1;
886 } 1101 }
887 1102
888 prio_pcmrec = -1; 1103 return true;
889 prio_codec = -1; /* GCC is too stoopid to figure out it doesn't 1104}
890 need init */
891#endif
892 1105
893 logf("writing:%d(%d):%s%s", num_ready, flush_num, 1106/* Stream end chunk was encountered */
894 interruptable ? "i" : "", 1107static bool flush_stream_end(union enc_chunk_hdr *hdr)
895 flush_num == PCMREC_FLUSH_MINI ? "m" : ""); 1108{
1109 if (rec_fd < 0)
1110 return true;
896 1111
897 cpu_boost(true); 1112 if (enc_cb(ENC_CB_STREAM, hdr) < 0)
1113 {
1114 raise_error_status(PCMREC_E_ENCODER_STREAM);
1115 return false;
1116 }
898 1117
899 remaining = flush_num; 1118 close_rec_file();
900 chunks_flushed = 0; 1119 return true;
1120}
901 1121
902 while (num_ready > 0) 1122/* Discard remainder of stream in encoder buffer */
1123static void discard_stream(void)
1124{
1125 /* Discard everything up until the next non-data chunk */
1126 while (!encbuf_empty())
903 { 1127 {
904 /* check current number of encoder chunks */ 1128 size_t ridx;
905 int num = enc_wr_index - enc_rd_index; 1129 union enc_chunk_hdr *hdr = encbuf_read_ptr_incr(enc_ridx, &ridx);
906 if (num < 0)
907 num += enc_num_chunks;
908 1130
909 if (num <= low_watermark && 1131 if (hdr && hdr->type != CHUNK_T_DATA)
910 (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0))
911 { 1132 {
912 logf("low data: %d", num); 1133 if (hdr->type != CHUNK_T_STREAM_START)
913 break; /* data remaining is below threshold */ 1134 enc_ridx = ridx;
1135 break;
914 } 1136 }
915 1137
916 if (interruptable && flush_interrupts > 0) 1138 enc_ridx = ridx;
917 { 1139 }
918 logf("int at: %d", num);
919 break; /* interrupted */
920 }
921 1140
1141 /* Try to finish header by closing and reopening the file. A seek or
1142 other operation will likely fail because buffers will need to be
1143 flushed (here and in file code). That will likely fail but a close
1144 will just close the fd and discard everything. We reopen with what
1145 actually made it to disk. Modifying existing file contents will
1146 more than likely succeed even on a full disk. The result might not
1147 be entirely correct as far as the headers' sizes and counts unless
1148 the codec can correct that but the sample format information
1149 should be. */
1150 if (rec_fd >= 0 && open_rec_file(false))
1151 {
1152 /* Synthesize a special end chunk here */
1153 union enc_chunk_hdr end;
1154 end.zero = 0;
1155 end.err = 1; /* Codec should try to correct anything that's off */
1156 end.type = CHUNK_T_STREAM_END;
1157 if (!flush_stream_end(&end))
1158 close_rec_file();
1159 }
1160}
1161
1162/* Flush a chunk to disk
1163 *
1164 * Transitions state from REC_STATE_MONITOR to REC_STATE_FLUSH when buffer
1165 * is filling. 'margin' is fullness threshold that transitions to flush state.
1166 *
1167 * Call with REC_STATE_IDLE to indicate a forced flush which flushes buffer
1168 * to less than 'margin'.
1169 */
1170static enum record_state flush_chunk(enum record_state state, size_t margin)
1171{
922#ifdef HAVE_PRIORITY_SCHEDULING 1172#ifdef HAVE_PRIORITY_SCHEDULING
923 if (prio_pcmrec == -1 && (num >= flood_watermark || 1173 static unsigned long prio_tick; /* Timeout for auto boost */
924 TIME_AFTER(current_tick, prio_tick)))
925 {
926 /* losing ground or holding without progress - boost
927 priority until finished */
928 logf("pcmrec: boost (%s)",
929 num >= flood_watermark ? "num" : "time");
930 prio_pcmrec = thread_set_priority(thread_self(),
931 thread_get_priority(thread_self()) - 4);
932 prio_codec = codec_thread_set_priority(
933 codec_thread_get_priority() - 4);
934 }
935#endif 1174#endif
936 1175
937 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index); 1176 size_t used = encbuf_used();
938 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
939 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
940 1177
941 if (rec_fdata.chunk->flags & CHUNKF_START_FILE) 1178 switch (state)
942 { 1179 {
943 pcmrec_start_file(); 1180 case REC_STATE_MONITOR:
944 if (--remaining == 0) 1181 if (monitor_encbuf_datarate() || monitor_spinup_time())
945 num_ready = 0; /* stop on next loop - must write this 1182 refresh_watermarks();
946 chunk if it has data */
947 }
948 1183
949 pcmrec_write_chunk(); 1184 if (used < margin)
1185 return REC_STATE_MONITOR;
950 1186
951 if (rec_fdata.chunk->flags & CHUNKF_END_FILE) 1187 state = REC_STATE_FLUSH;
952 pcmrec_end_file(); 1188 trigger_cpu_boost();
953 1189
954 INC_ENC_INDEX(enc_rd_index); 1190#ifdef HAVE_PRIORITY_SCHEDULING
1191 prio_tick = current_tick + PRIO_SECONDS*HZ;
1192#if (CONFIG_STORAGE & STORAGE_ATA)
1193 prio_tick += spinup_time;
1194#endif
1195#endif /* HAVE_PRIORITY_SCHEDULING */
955 1196
956 if (errors != 0) 1197 /* Fall-through */
957 { 1198 case REC_STATE_IDLE: /* As a hint for "forced" */
958 pcmrec_end_file(); 1199 if (used < margin)
959 break; 1200 break;
960 }
961 1201
962 if (flush_num == PCMREC_FLUSH_MINI && 1202 /* Fall-through */
963 ++chunks_flushed >= MINI_CHUNKS) 1203 case REC_STATE_FLUSH:
1204#ifdef HAVE_PRIORITY_SCHEDULING
1205 if (!prio_boosted && state != REC_STATE_IDLE &&
1206 (used >= flood_watermark || TIME_AFTER(current_tick, prio_tick)))
1207 do_prio_boost(true);
1208#endif /* HAVE_PRIORITY_SCHEDULING */
1209
1210 while (used)
964 { 1211 {
965 logf("mini flush break"); 1212 union enc_chunk_hdr *hdr = encbuf_ptr(enc_ridx);
966 break; 1213 size_t count = 0;
967 }
968 /* no yielding; the file apis called in the codecs do that
969 sufficiently */
970 } /* end while */
971 1214
972 /* sync file */ 1215 switch (hdr->type)
973 if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0) 1216 {
974 pcmrec_raise_error_status(PCMREC_E_IO); 1217 case CHUNK_T_DATA:
1218 if (flush_stream_data(ENC_DATA_HDR(hdr)))
1219 count = CHUNK_DATA_COUNT(hdr->size);
1220 break;
975 1221
976 cpu_boost(false); 1222 case CHUNK_T_STREAM_START:
1223 /* Doesn't do stream writes */
1224 flush_stream_start(ENC_FILE_HDR(hdr));
1225 count = hdr->size;
1226 break;
977 1227
978#ifdef HAVE_PRIORITY_SCHEDULING 1228 case CHUNK_T_STREAM_END:
979 if (prio_pcmrec != -1) 1229 if (flush_stream_end(hdr))
980 { 1230 count = 1;
981 /* return to original priorities */ 1231 break;
982 logf("pcmrec: unboost priority");
983 thread_set_priority(thread_self(), prio_pcmrec);
984 codec_thread_set_priority(prio_codec);
985 }
986 1232
987 last_flush_tick = current_tick; /* save tick when we left */ 1233 case CHUNK_T_WRAP:
988#endif 1234 enc_ridx = 0;
1235 used = encbuf_used();
1236 continue;
1237 }
989 1238
990 logf("done"); 1239 if (count)
991} /* pcmrec_flush */ 1240 enc_ridx = encbuf_add(enc_ridx, count);
1241 else
1242 discard_stream();
992 1243
993/** 1244 break;
994 * Marks a new stream in the buffer and gives the encoder a chance for special 1245 }
995 * handling of transition from one to the next. The encoder may change the
996 * chunk that ends the old stream by requesting more chunks and similiarly for
997 * the new but must always advance the position though the interface. It can
998 * later reject any data it cares to when writing the file but should mark the
999 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
1000 * a NULL data pointer without error as well.
1001 */
1002static int pcmrec_get_chunk_index(struct enc_chunk_hdr *chunk)
1003{
1004 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
1005} /* pcmrec_get_chunk_index */
1006 1246
1007static struct enc_chunk_hdr * pcmrec_get_prev_chunk(int index) 1247 if (!encbuf_empty())
1008{ 1248 return state;
1009 DEC_ENC_INDEX(index);
1010 return GET_ENC_CHUNK(index);
1011} /* pcmrec_get_prev_chunk */
1012 1249
1013static void pcmrec_new_stream(const char *filename, /* next file name */ 1250 break;
1014 unsigned long flags, /* CHUNKF_* flags */ 1251 }
1015 int pre_index) /* index for prerecorded data */
1016{
1017 logf("pcmrec_new_stream");
1018 char path[MAX_PATH]; /* place to copy filename so sender can be released */
1019 1252
1020 struct enc_buffer_event_data data; 1253 if (encbuf_empty())
1021 bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add 1254 {
1022 new filename */ 1255 do_prio_boost(false);
1023 struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of 1256 cancel_cpu_boost();
1024 stream */ 1257 }
1025 bool did_flush = false; /* did a flush occurr? */
1026 1258
1027 if (filename) 1259 return REC_STATE_MONITOR;
1028 strlcpy(path, filename, MAX_PATH); 1260}
1029 queue_reply(&audio_queue, 0); /* We have all we need */
1030 1261
1031 data.pre_chunk = NULL; 1262/* Monitor buffer and finish stream, freeing-up space at the same time */
1032 data.chunk = GET_ENC_CHUNK(enc_wr_index); 1263static void finish_stream(bool stopping)
1264{
1265 size_t threshold = stopping ? 1 : enc_buflen - ENCBUF_MIN_SPLIT_MARGIN;
1266 enum record_state state = REC_STATE_MONITOR;
1267 size_t need = 1;
1033 1268
1034 /* end chunk */ 1269 while (1)
1035 if (flags & CHUNKF_END_FILE)
1036 { 1270 {
1037 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE; 1271 switch (state)
1038
1039 if (data.chunk->flags & CHUNKF_START_FILE)
1040 {
1041 /* cannot start and end on same unprocessed chunk */
1042 logf("file end on start");
1043 flags &= ~CHUNKF_END_FILE;
1044 }
1045 else if (enc_rd_index == enc_wr_index)
1046 {
1047 /* all data flushed but file not ended - chunk will be left
1048 empty */
1049 logf("end on dead end");
1050 data.chunk->flags = 0;
1051 data.chunk->enc_size = 0;
1052 data.chunk->num_pcm = 0;
1053 data.chunk->enc_data = NULL;
1054 INC_ENC_INDEX(enc_wr_index);
1055 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1056 }
1057 else
1058 { 1272 {
1059 struct enc_chunk_hdr *last = pcmrec_get_prev_chunk(enc_wr_index); 1273 case REC_STATE_IDLE:
1274 state = flush_chunk(state, threshold);
1275 continue;
1276
1277 default:
1278 if (!need)
1279 break;
1060 1280
1061 if (last->flags & CHUNKF_END_FILE) 1281 if (!stopping || pcm_buffer_empty)
1062 { 1282 {
1063 /* end already processed and marked - can't end twice */ 1283 need = codec_finish_stream();
1064 logf("file end again"); 1284
1065 flags &= ~CHUNKF_END_FILE; 1285 if (need)
1286 {
1287 need = 2*CHUNK_DATA_COUNT(need) - 1;
1288
1289 if (need >= enc_buflen)
1290 {
1291 need = 0;
1292 codec_stop();
1293 threshold = 1;
1294 }
1295 else if (threshold > enc_buflen - need)
1296 {
1297 threshold = enc_buflen - need;
1298 }
1299 }
1066 } 1300 }
1301
1302 if (!need || encbuf_used() >= threshold)
1303 state = REC_STATE_IDLE; /* Start flush */
1304 else
1305 sleep(HZ/10); /* Don't flood with pings */
1306
1307 continue;
1067 } 1308 }
1309
1310 break;
1068 } 1311 }
1312}
1069 1313
1070 /* start chunk */ 1314/* Start a new stream, transistion to a new one or end the current one */
1071 if (flags & CHUNKF_START_FILE) 1315static void mark_stream(const char *path, enum mark_stream_action action)
1316{
1317 if (action & MARK_STREAM_END)
1072 { 1318 {
1073 bool pre = flags & CHUNKF_PRERECORD; 1319 size_t widx;
1320 union enc_chunk_hdr *hdr = encbuf_get_write_ptr(enc_widx, 1, &widx);
1321 hdr->type = CHUNK_T_STREAM_END;
1322 encbuf_widx_advance(widx, 1);
1323 }
1074 1324
1075 if (pre) 1325 if (action & MARK_STREAM_START)
1076 { 1326 {
1077 logf("stream prerecord start"); 1327 size_t count = CHUNK_FILE_COUNT_PATH(path);
1078 start = data.pre_chunk = GET_ENC_CHUNK(pre_index); 1328 struct enc_chunk_file *file;
1079 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD; 1329 size_t widx;
1080 }
1081 else
1082 {
1083 logf("stream normal start");
1084 start = data.chunk;
1085 start->flags &= CHUNKF_START_FILE;
1086 }
1087 1330
1088 /* if encoder hasn't yet processed the last start - abort the start 1331 if (action & MARK_STREAM_PRE)
1089 of the previous file queued or else it will be empty and invalid */
1090 if (start->flags & CHUNKF_START_FILE)
1091 { 1332 {
1092 logf("replacing fnq tail: %s", filename); 1333 /* Prerecord: START marker goes first or before existing data */
1093 fnq_add_fn = pcmrec_fnq_replace_tail; 1334 if (enc_ridx < count)
1335 {
1336 /* Adjust to occupy end of buffer and pad accordingly */
1337 count += enc_ridx;
1338 enc_ridx += enc_buflen;
1339 }
1340
1341 enc_ridx -= count;
1342
1343 /* Won't adjust p since enc_ridx is already set as non-wrapping */
1344 file = encbuf_get_write_ptr(enc_ridx, count, &widx);
1094 } 1345 }
1095 else 1346 else
1096 { 1347 {
1097 logf("adding filename: %s", filename); 1348 /* The usual: START marker goes first or after existing data */
1098 fnq_add_fn = pcmrec_fnq_add_filename; 1349 file = encbuf_get_write_ptr(enc_widx, count, &widx);
1350 encbuf_widx_advance(widx, count);
1099 } 1351 }
1100 }
1101 1352
1102 data.flags = flags; 1353 file->hdr.type = CHUNK_T_STREAM_START;
1103 pcmrec_context = true; /* switch encoder context */ 1354 file->hdr.size = count;
1104 enc_events_callback(ENC_REC_NEW_STREAM, &data); 1355 strlcpy(file->path, path, MAX_PATH);
1105 pcmrec_context = false; /* switch back */
1106
1107 if (flags & CHUNKF_END_FILE)
1108 {
1109 int i = pcmrec_get_chunk_index(data.chunk);
1110 pcmrec_get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1111 } 1356 }
1357}
1112 1358
1113 if (start) 1359/* Tally-up and keep the required amount of prerecord data.
1114 { 1360 * Updates record stats accordingly. */
1115 if (!(flags & CHUNKF_PRERECORD)) 1361static void tally_prerecord_data(void)
1116 { 1362{
1117 /* get stats on data added to start - sort of a prerecord 1363 unsigned long count = 0;
1118 operation */ 1364 size_t bytes = 0;
1119 int i = pcmrec_get_chunk_index(data.chunk); 1365 unsigned long samples = 0;
1120 struct enc_chunk_hdr *chunk = data.chunk;
1121 1366
1122 logf("start data: %d %d", i, enc_wr_index); 1367 /* Find out how much is there */
1368 for (size_t idx = enc_ridx; idx != enc_widx;)
1369 {
1370 struct enc_chunk_data *data = encbuf_read_ptr_incr(idx, &idx);
1123 1371
1124 num_rec_bytes = 0; 1372 if (!data)
1125 num_rec_samples = 0; 1373 continue;
1126 1374
1127 while (i != enc_wr_index) 1375 count += CHUNK_DATA_COUNT(data->hdr.size);
1128 { 1376 bytes += data->hdr.size;
1129 num_rec_bytes += chunk->enc_size; 1377 samples += data->pcm_count;
1130 num_rec_samples += chunk->num_pcm; 1378 }
1131 INC_ENC_INDEX(i);
1132 chunk = GET_ENC_CHUNK(i);
1133 }
1134 1379
1135 start->flags &= ~CHUNKF_START_FILE; 1380 /* Have too much? Discard oldest data. */
1136 start = data.chunk; 1381 unsigned long pre_samples = enc_sample_rate*pre_record_seconds;
1137 }
1138 1382
1139 start->flags |= CHUNKF_START_FILE; 1383 while (samples > pre_samples)
1384 {
1385 struct enc_chunk_data *data =
1386 encbuf_read_ptr_incr(enc_ridx, &enc_ridx);
1140 1387
1141 /* flush all pending files out if full and adding */ 1388 if (!data)
1142 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full()) 1389 continue;
1143 {
1144 logf("fnq full");
1145 pcmrec_flush(PCMREC_FLUSH_ALL);
1146 did_flush = true;
1147 }
1148 1390
1149 fnq_add_fn(path); 1391 count -= CHUNK_DATA_COUNT(data->hdr.size);
1392 bytes -= data->hdr.size;
1393 samples -= data->pcm_count;
1150 } 1394 }
1151 1395
1152 /* Make sure to complete any interrupted high watermark */ 1396 encbuf_rec_count = count;
1153 if (!did_flush) 1397 num_rec_bytes = bytes;
1154 pcmrec_flush(PCMREC_FLUSH_IF_HIGH); 1398 num_rec_samples = samples;
1155} /* pcmrec_new_stream */ 1399}
1156 1400
1157 1401
1158/** event handlers for pcmrec thread */ 1402/** Event handlers for recording thread **/
1159 1403
1160/* PCMREC_INIT */ 1404/* Q_AUDIO_INIT_RECORDING */
1161static void pcmrec_init(void) 1405static void on_init_recording(void)
1162{ 1406{
1163 send_event(RECORDING_EVENT_START, NULL); 1407 send_event(RECORDING_EVENT_START, NULL);
1164 pcmrec_close_file(&rec_fdata.rec_file); 1408 rec_buffer = audio_get_buffer(true, &rec_buffer_size);
1165 1409 init_rec_buffers();
1166 pcmrec_init_state(); 1410 init_state();
1167
1168 unsigned char *buffer = audio_get_buffer(true, &rec_buffer_size);
1169
1170 /* Line align pcm_buffer 2^5=32 bytes */
1171 pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 5);
1172 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1173 PCM_MAX_FEED_SIZE, 2);
1174 /* Adjust available buffer for possible align advancement */
1175 rec_buffer_size -= pcm_buffer - buffer;
1176
1177 pcm_init_recording(); 1411 pcm_init_recording();
1178} /* pcmrec_init */ 1412}
1179 1413
1180/* PCMREC_CLOSE */ 1414/* Q_AUDIO_CLOSE_RECORDING */
1181static void pcmrec_close(void) 1415static void on_close_recording(void)
1182{ 1416{
1183 dma_lock = true; 1417 /* Simply shut down the recording system. Whatever wasn't saved is
1184 pre_record_ticks = 0; /* Can't be prerecording any more */ 1418 lost. */
1185 warnings = 0;
1186 codec_unload(); 1419 codec_unload();
1187 pcm_close_recording(); 1420 pcm_close_recording();
1188 reset_hardware(); 1421 close_rec_file();
1422 init_state();
1423
1424 rec_errors = 0;
1425 pcm_rec_error_clear();
1426
1427 /* Reset PCM to defaults */
1428 pcm_set_frequency(HW_SAMPR_RESET | SAMPR_TYPE_REC);
1429 audio_set_output_source(AUDIO_SRC_PLAYBACK);
1430 pcm_apply_settings();
1431
1189 send_event(RECORDING_EVENT_STOP, NULL); 1432 send_event(RECORDING_EVENT_STOP, NULL);
1190} /* pcmrec_close */ 1433}
1191 1434
1192/* PCMREC_OPTIONS */ 1435/* Q_AUDIO_RECORDING_OPTIONS */
1193static void pcmrec_set_recording_options( 1436static void on_recording_options(struct audio_recording_options *options)
1194 struct audio_recording_options *options)
1195{ 1437{
1196 /* stop everything */ 1438 if (!options)
1197 dma_lock = true; 1439 {
1198 codec_unload(); 1440 logf("options: option NULL!");
1441 return;
1442 }
1443
1444 if (record_state != REC_STATE_IDLE)
1445 {
1446 /* This would ruin things */
1447 logf("options: still recording!");
1448 return;
1449 }
1450
1451 /* Stop everything else that might be running */
1199 pcm_stop_recording(); 1452 pcm_stop_recording();
1200 pcmrec_init_state();
1201 1453
1202 rec_frequency = options->rec_frequency; 1454 int afmt = rec_format_afmt[options->enc_config.rec_format];
1455 bool enc_load = true;
1456
1457 if (codec_loaded() != AFMT_UNKNOWN)
1458 {
1459 if (get_audio_base_codec_type(enc_config.afmt) !=
1460 get_audio_base_codec_type(afmt))
1461 {
1462 /* New format, new encoder; unload this one */
1463 codec_unload();
1464 }
1465 else
1466 {
1467 /* Keep current encoder */
1468 codec_stop();
1469 enc_load = false;
1470 }
1471 }
1472
1473 init_state();
1474
1475 /* Read recording options, remember the ones used elsewhere */
1476 unsigned frequency = options->rec_frequency;
1203 rec_source = options->rec_source; 1477 rec_source = options->rec_source;
1204 num_channels = options->rec_channels == 1 ? 1 : 2; 1478 num_channels = options->rec_channels == 1 ? 1 : 2;
1205 rec_mono_mode = options->rec_mono_mode; 1479 unsigned mono_mode = options->rec_mono_mode;
1206 pre_record_ticks = options->rec_prerecord_time * HZ; 1480 pre_record_seconds = options->rec_prerecord_time;
1207 enc_config = options->enc_config; 1481 enc_config = options->enc_config;
1208 enc_config.afmt = rec_format_afmt[enc_config.rec_format]; 1482 enc_config.afmt = afmt;
1209 1483
1210#ifdef HAVE_SPDIF_IN 1484 queue_reply(&audio_queue, 0); /* Let caller go */
1211 if (rec_source == AUDIO_SRC_SPDIF) 1485
1486 /* Pick appropriate PCM copy routine */
1487 pcm_copyfn = memcpy;
1488
1489 if (num_channels == 1)
1212 { 1490 {
1213 /* must measure SPDIF sample rate before configuring codecs */ 1491 static typeof (memcpy) * const copy_buffer_mono[] =
1214 unsigned long sr = spdif_measure_frequency(); 1492 {
1215 /* round to master list for SPDIF rate */ 1493 copy_buffer_mono_lr,
1216 int index = round_value_to_list32(sr, audio_master_sampr_list, 1494 copy_buffer_mono_l,
1217 SAMPR_NUM_FREQ, false); 1495 copy_buffer_mono_r
1218 sample_rate = audio_master_sampr_list[index]; 1496 };
1219 /* round to HW playback rates for monitoring */ 1497
1220 index = round_value_to_list32(sr, hw_freq_sampr, 1498 if (mono_mode >= ARRAYLEN(copy_buffer_mono))
1221 HW_NUM_FREQ, false); 1499 mono_mode = 0;
1222 pcm_set_frequency(hw_freq_sampr[index] | SAMPR_TYPE_REC); 1500
1223 /* encoders with a limited number of rates do their own rounding */ 1501 pcm_copyfn = copy_buffer_mono[mono_mode];
1224 } 1502 }
1503
1504 /* Get the hardware samplerate to be used */
1505 unsigned long sampr;
1506
1507#ifdef HAVE_SPDIF_IN
1508 if (rec_source == AUDIO_SRC_SPDIF)
1509 sampr = get_spdif_samplerate(); /* Determined by source */
1225 else 1510 else
1226#endif 1511#endif /* HAVE_SPDIF_IN */
1227 { 1512 sampr = rec_freq_sampr[frequency];
1228 /* set sample rate from frequency selection */ 1513
1229 sample_rate = rec_freq_sampr[rec_frequency]; 1514 update_samplerate_config(sampr);
1230 pcm_set_frequency(sample_rate | SAMPR_TYPE_REC);
1231 }
1232 1515
1233 /* set monitoring */ 1516 /* Set monitoring */
1234 audio_set_output_source(rec_source); 1517 audio_set_output_source(rec_source);
1235 1518
1236 /* apply hardware setting to start monitoring now */ 1519 /* Apply hardware setting to start monitoring now */
1237 pcm_apply_settings(); 1520 pcm_apply_settings();
1238 1521
1239 queue_reply(&audio_queue, 0); /* Release sender */ 1522 if (!enc_load || codec_load(-1, afmt | CODEC_TYPE_ENCODER))
1240
1241 if (codec_load(-1, enc_config.afmt | CODEC_TYPE_ENCODER))
1242 { 1523 {
1524 enc_cb = codec_get_enc_callback();
1243 1525
1244 /* run immediately */ 1526 if (!enc_cb || !configure_encoder_stream())
1245 codec_go(); 1527 {
1528 codec_unload();
1529 return;
1530 }
1246 1531
1247 /* start DMA transfer */ 1532 if (pre_record_seconds != 0)
1248 dma_lock = pre_record_ticks == 0; 1533 {
1249 pcm_record_data(pcm_rec_have_more, pcm_rec_status_callback, 1534 record_status = RECORD_PRERECORDING;
1250 GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE); 1535 codec_go();
1536 pcm_pause = false;
1537 }
1538
1539 pcm_start_recording();
1251 } 1540 }
1252 else 1541 else
1253 { 1542 {
1254 logf("set rec opt: enc load failed"); 1543 logf("set rec opt: enc load failed");
1255 pcmrec_raise_error_status(PCMREC_E_LOAD_ENCODER); 1544 raise_error_status(PCMREC_E_LOAD_ENCODER);
1256 } 1545 }
1257} /* pcmrec_set_recording_options */ 1546}
1258 1547
1259/* PCMREC_RECORD - start recording (not gapless) 1548/* Q_AUDIO_RECORD - start recording (not gapless)
1260 or split stream (gapless) */ 1549 or split stream (gapless) */
1261static void pcmrec_record(const char *filename) 1550static void on_record(const char *filename)
1262{ 1551{
1263 unsigned long pre_sample_ticks; 1552 if (rec_errors)
1264 int rd_start;
1265 unsigned long flags;
1266 int pre_index;
1267
1268 logf("pcmrec_record: %s", filename);
1269
1270 /* reset stats */
1271 num_rec_bytes = 0;
1272 num_rec_samples = 0;
1273
1274 if (!is_recording)
1275 { 1553 {
1276#if 0 1554 logf("on_record: errors not cleared");
1277 accum_rec_bytes = 0; 1555 return;
1278 accum_pcm_samples = 0; 1556 }
1279#endif
1280 warnings = 0; /* reset warnings */
1281
1282 rd_start = enc_wr_index;
1283 pre_sample_ticks = 0;
1284
1285 pcmrec_refresh_watermarks();
1286
1287 if (pre_record_ticks)
1288 {
1289 int i = rd_start;
1290 /* calculate number of available chunks */
1291 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1292 enc_num_chunks) % enc_num_chunks;
1293 /* overflow at 974 seconds of prerecording at 44.1kHz */
1294 unsigned long pre_record_sample_ticks =
1295 enc_sample_rate*pre_record_ticks;
1296 int pre_chunks = 0; /* Counter to limit prerecorded time to
1297 prevent flood state at outset */
1298
1299 logf("pre-st: %ld", pre_record_sample_ticks);
1300
1301 /* Get exact measure of recorded data as number of samples aren't
1302 nescessarily going to be the max for each chunk */
1303 for (; avail_pre_chunks-- > 0;)
1304 {
1305 struct enc_chunk_hdr *chunk;
1306 unsigned long chunk_sample_ticks;
1307
1308 DEC_ENC_INDEX(i);
1309 1557
1310 chunk = GET_ENC_CHUNK(i); 1558 if (!filename)
1559 {
1560 logf("on_record: No filename");
1561 return;
1562 }
1311 1563
1312 /* must have data to be counted */ 1564 if (codec_loaded() == AFMT_UNKNOWN)
1313 if (chunk->enc_data == NULL) 1565 {
1314 continue; 1566 logf("on_record: Recording options not set");
1567 return;
1568 }
1315 1569
1316 chunk_sample_ticks = chunk->num_pcm*HZ; 1570 logf("on_record: new file '%s'", filename);
1317 1571
1318 rd_start = i; 1572 /* Copy path and let caller go */
1319 pre_sample_ticks += chunk_sample_ticks; 1573 char path[MAX_PATH];
1320 num_rec_bytes += chunk->enc_size; 1574 strlcpy(path, filename, MAX_PATH);
1321 num_rec_samples += chunk->num_pcm;
1322 pre_chunks++;
1323 1575
1324 /* stop here if enough already */ 1576 queue_reply(&audio_queue, 0);
1325 if (pre_chunks >= high_watermark ||
1326 pre_sample_ticks >= pre_record_sample_ticks)
1327 {
1328 logf("pre-chks: %d", pre_chunks);
1329 break;
1330 }
1331 }
1332 1577
1333#if 0 1578 enum mark_stream_action mark_action;
1334 accum_rec_bytes = num_rec_bytes;
1335 accum_pcm_samples = num_rec_samples;
1336#endif
1337 }
1338 1579
1339 enc_rd_index = rd_start; 1580 if (record_state == REC_STATE_IDLE)
1581 {
1582 mark_action = MARK_STREAM_START;
1340 1583
1341 /* filename queue should be empty */ 1584 if (pre_record_seconds)
1342 if (!pcmrec_fnq_is_empty())
1343 { 1585 {
1344 logf("fnq: not empty!"); 1586 codec_pause();
1345 pcmrec_fnq_set_empty(); 1587 tally_prerecord_data();
1588 mark_action = MARK_STREAM_START_PRE;
1346 } 1589 }
1347 1590
1348 flags = CHUNKF_START_FILE; 1591 clear_warning_status(PCMREC_W_ALL &
1349 if (pre_sample_ticks > 0) 1592 ~(PCMREC_W_SAMPR_MISMATCH|PCMREC_W_DMA));
1350 flags |= CHUNKF_PRERECORD; 1593 record_state = REC_STATE_MONITOR;
1351 1594 record_status = RECORD_RECORDING;
1352 pre_index = enc_rd_index;
1353
1354 dma_lock = false;
1355 is_paused = false;
1356 is_recording = true;
1357 } 1595 }
1358 else 1596 else
1359 { 1597 {
1360 /* already recording, just split the stream */ 1598 /* Already recording, just split the stream */
1361 logf("inserting split"); 1599 logf("inserting split");
1362 flags = CHUNKF_START_FILE | CHUNKF_END_FILE; 1600 mark_action = MARK_STREAM_SPLIT;
1363 pre_index = 0; 1601 finish_stream(false);
1602 reset_rec_stats();
1364 } 1603 }
1365 1604
1366 pcmrec_new_stream(filename, flags, pre_index); 1605 if (rec_errors)
1367 logf("pcmrec_record done");
1368} /* pcmrec_record */
1369
1370/* PCMREC_STOP */
1371static void pcmrec_stop(void)
1372{
1373 logf("pcmrec_stop");
1374
1375 if (is_recording)
1376 { 1606 {
1377 dma_lock = true; /* lock dma write position */ 1607 pcm_pause = true;
1608 codec_stop();
1609 reset_fifos(false);
1610 return;
1611 }
1378 1612
1379 /* flush all available data first to avoid overflow while waiting 1613 mark_stream(path, mark_action);
1380 for encoding to finish */
1381 pcmrec_flush(PCMREC_FLUSH_ALL);
1382 1614
1383 /* wait for encoder to finish remaining data */ 1615 codec_go();
1384 while (errors == 0 && !pcm_buffer_empty) 1616 pcm_pause = record_status != RECORD_RECORDING;
1385 yield(); 1617}
1386 1618
1387 /* end stream at last data */ 1619/* Q_AUDIO_RECORD_STOP */
1388 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0); 1620static void on_record_stop(void)
1621{
1622 if (record_state == REC_STATE_IDLE)
1623 return;
1389 1624
1390 /* flush anything else encoder added */ 1625 /* Drain encoder and PCM buffers */
1391 pcmrec_flush(PCMREC_FLUSH_ALL); 1626 pcm_pause = true;
1627 finish_stream(true);
1392 1628
1393 /* remove any pending file start not yet processed - should be at 1629 /* End stream at last data and flush end marker */
1394 most one at enc_wr_index */ 1630 mark_stream(NULL, MARK_STREAM_END);
1395 pcmrec_fnq_get_filename(NULL); 1631 while (flush_chunk(REC_STATE_IDLE, 1) == REC_STATE_IDLE);
1396 /* encoder should abort any chunk it was in midst of processing */
1397 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1398 1632
1399 /* filename queue should be empty */ 1633 reset_fifos(false);
1400 if (!pcmrec_fnq_is_empty())
1401 {
1402 logf("fnq: not empty!");
1403 pcmrec_fnq_set_empty();
1404 }
1405 1634
1406 /* be absolutely sure the file is closed */ 1635 bool prerecord = pre_record_seconds != 0;
1407 if (errors != 0)
1408 pcmrec_close_file(&rec_fdata.rec_file);
1409 rec_fdata.rec_file = -1;
1410 1636
1411 is_recording = false; 1637 if (rec_errors)
1412 is_paused = false;
1413 dma_lock = pre_record_ticks == 0;
1414 }
1415 else
1416 { 1638 {
1417 logf("not recording"); 1639 codec_stop();
1640 prerecord = false;
1418 } 1641 }
1419 1642
1420 logf("pcmrec_stop done"); 1643 close_rec_file();
1421} /* pcmrec_stop */ 1644 rec_errors = 0;
1422 1645
1423/* PCMREC_PAUSE */ 1646 record_state = REC_STATE_IDLE;
1424static void pcmrec_pause(void) 1647 record_status = prerecord ? RECORD_PRERECORDING : RECORD_STOPPED;
1425{ 1648 reset_rec_stats();
1426 logf("pcmrec_pause");
1427 1649
1428 if (!is_recording) 1650 if (prerecord)
1429 {
1430 logf("not recording");
1431 }
1432 else if (is_paused)
1433 {
1434 logf("already paused");
1435 }
1436 else
1437 { 1651 {
1438 dma_lock = true; 1652 codec_go();
1439 is_paused = true; 1653 pcm_pause = false;
1440 } 1654 }
1655}
1441 1656
1442 logf("pcmrec_pause done"); 1657/* Q_AUDIO_RECORD_PAUSE */
1443} /* pcmrec_pause */ 1658static void on_record_pause(void)
1444
1445/* PCMREC_RESUME */
1446static void pcmrec_resume(void)
1447{ 1659{
1448 logf("pcmrec_resume"); 1660 if (record_status != RECORD_RECORDING)
1661 return;
1449 1662
1450 if (!is_recording) 1663 pcm_pause = true;
1451 { 1664 record_status = RECORD_PAUSED;
1452 logf("not recording"); 1665}
1453 } 1666
1454 else if (!is_paused) 1667/* Q_AUDIO_RECORD_RESUME */
1455 { 1668static void on_record_resume(void)
1456 logf("not paused"); 1669{
1457 } 1670 if (record_status != RECORD_PAUSED)
1458 else 1671 return;
1459 {
1460 is_paused = false;
1461 is_recording = true;
1462 dma_lock = false;
1463 }
1464 1672
1465 logf("pcmrec_resume done"); 1673 record_status = RECORD_RECORDING;
1466} /* pcmrec_resume */ 1674 pcm_pause = !!rec_errors;
1675}
1467 1676
1468/* Called by audio thread when recording is initialized */ 1677/* Called by audio thread when recording is initialized */
1469void audio_recording_handler(struct queue_event *ev) 1678void audio_recording_handler(struct queue_event *ev)
1470{ 1679{
1471 logf("audio recording start"); 1680#ifdef HAVE_PRIORITY_SCHEDULING
1681 /* Get current priorities since they get changed */
1682 int old_prio = thread_get_priority(audio_thread_id);
1683 int old_cod_prio = codec_thread_get_priority();
1684#endif
1685
1686 LOGFQUEUE("record < Q_AUDIO_INIT_RECORDING");
1687 on_init_recording();
1472 1688
1473 while (1) 1689 while (1)
1474 { 1690 {
1691 int watermark = high_watermark;
1692
1475 switch (ev->id) 1693 switch (ev->id)
1476 { 1694 {
1477 case Q_AUDIO_INIT_RECORDING:
1478 pcmrec_init();
1479 break;
1480
1481 case SYS_USB_CONNECTED:
1482 if (is_recording)
1483 break;
1484 /* Fall-through */
1485 case Q_AUDIO_CLOSE_RECORDING: 1695 case Q_AUDIO_CLOSE_RECORDING:
1486 pcmrec_close(); 1696 LOGFQUEUE("record < Q_AUDIO_CLOSE_RECORDING");
1487 return; /* no more recording */ 1697 goto recording_done;
1488 1698
1489 case Q_AUDIO_RECORDING_OPTIONS: 1699 case Q_AUDIO_RECORDING_OPTIONS:
1490 pcmrec_set_recording_options( 1700 LOGFQUEUE("record < Q_AUDIO_RECORDING_OPTIONS");
1491 (struct audio_recording_options *)ev->data); 1701 on_recording_options((struct audio_recording_options *)ev->data);
1492 break; 1702 break;
1493 1703
1494 case Q_AUDIO_RECORD: 1704 case Q_AUDIO_RECORD:
1495 clear_flush_interrupt(); 1705 LOGFQUEUE("record < Q_AUDIO_RECORD: %s", (const char *)ev->data);
1496 pcmrec_record((const char *)ev->data); 1706 on_record((const char *)ev->data);
1497 break; 1707 break;
1498 1708
1499 case Q_AUDIO_STOP: 1709 case Q_AUDIO_RECORD_STOP:
1500 clear_flush_interrupt(); 1710 LOGFQUEUE("record < Q_AUDIO_RECORD_STOP");
1501 pcmrec_stop(); 1711 on_record_stop();
1502 break; 1712 break;
1503 1713
1504 case Q_AUDIO_PAUSE: 1714 case Q_AUDIO_RECORD_PAUSE:
1505 clear_flush_interrupt(); 1715 LOGFQUEUE("record < Q_AUDIO_RECORD_PAUSE");
1506 pcmrec_pause(); 1716 on_record_pause();
1507 break; 1717 break;
1508 1718
1509 case Q_AUDIO_RESUME: 1719 case Q_AUDIO_RECORD_RESUME:
1510 pcmrec_resume(); 1720 LOGFQUEUE("record < Q_AUDIO_RECORD_RESUME");
1721 on_record_resume();
1511 break; 1722 break;
1512 1723
1513 case SYS_TIMEOUT: 1724 case Q_AUDIO_RECORD_FLUSH:
1514 /* Messages that interrupt this will complete it */ 1725 watermark = 1;
1515 pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
1516 PCMREC_FLUSH_INTERRUPTABLE);
1517
1518 if (errors & PCMREC_E_DMA)
1519 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
1520 break; 1726 break;
1521 } /* end switch */
1522 1727
1523 queue_wait_w_tmo(&audio_queue, ev, 1728 case SYS_USB_CONNECTED:
1524 is_recording ? HZ/5 : TIMEOUT_BLOCK); 1729 LOGFQUEUE("record < SYS_USB_CONNECTED");
1525 } /* end while */ 1730 if (record_state != REC_STATE_IDLE)
1526} /* audio_recording_handler */ 1731 {
1527 1732 LOGFQUEUE(" still recording");
1528/****************************************************************************/ 1733 break;
1529/* */ 1734 }
1530/* following functions will be called by the encoder codec */
1531/* in a free-threaded manner */
1532/* */
1533/****************************************************************************/
1534 1735
1535/* pass the encoder settings to the encoder */ 1736 goto recording_done;
1536void enc_get_inputs(struct enc_inputs *inputs) 1737 } /* switch */
1537{
1538 inputs->sample_rate = sample_rate;
1539 inputs->num_channels = num_channels;
1540 inputs->rec_mono_mode = rec_mono_mode;
1541 inputs->config = &enc_config;
1542} /* enc_get_inputs */
1543 1738
1544/* set the encoder dimensions (called by encoder codec at initialization and 1739 int timeout;
1545 termination) */
1546void enc_set_parameters(struct enc_parameters *params)
1547{
1548 size_t bufsize, resbytes;
1549 1740
1550 logf("enc_set_parameters"); 1741 switch (record_state)
1742 {
1743 case REC_STATE_FLUSH:
1744 case REC_STATE_MONITOR:
1745 do
1746 record_state = flush_chunk(record_state, watermark);
1747 while (record_state == REC_STATE_FLUSH &&
1748 queue_empty(&audio_queue));
1749
1750 timeout = record_state == REC_STATE_FLUSH ?
1751 HZ*0 : HZ*FLUSH_MON_INTERVAL;
1752 break;
1753 case REC_STATE_IDLE:
1754#ifdef HAVE_SPDIF_IN
1755 if (rec_source == AUDIO_SRC_SPDIF)
1756 {
1757 check_spdif_samplerate();
1758 timeout = HZ/2;
1759 break;
1760 }
1761#endif /* HAVE_SPDIF_IN */
1762 default:
1763 timeout = TIMEOUT_BLOCK;
1764 break;
1765 }
1551 1766
1552 if (!params) 1767 queue_wait_w_tmo(&audio_queue, ev, timeout);
1553 { 1768 } /* while */
1554 logf("reset");
1555 /* Encoder is terminating */
1556 memset(&enc_config, 0, sizeof (enc_config));
1557 enc_sample_rate = 0;
1558 cancel_cpu_boost(); /* Make sure no boost remains */
1559 return;
1560 }
1561 1769
1562 enc_sample_rate = params->enc_sample_rate; 1770recording_done:
1563 logf("enc sampr:%lu", enc_sample_rate); 1771 on_close_recording();
1772#ifdef HAVE_PRIORITY_SCHEDULING
1773 /* Restore normal thread priorities */
1774 thread_set_priority(audio_thread_id, old_prio);
1775 codec_thread_set_priority(old_cod_prio);
1776#endif
1777}
1564 1778
1565 pcm_rd_pos = dma_wr_pos;
1566 pcm_enc_pos = pcm_rd_pos;
1567 1779
1568 enc_config.afmt = params->afmt; 1780/** Encoder callbacks **/
1569 /* addition of the header is always implied - chunk size 4-byte aligned */
1570 enc_chunk_size =
1571 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1572 enc_events_callback = params->events_callback;
1573 1781
1574 logf("chunk size:%lu", enc_chunk_size); 1782/* Read a block of unprocessed PCM data, with mono conversion if
1783 * num_channels == 1
1784 *
1785 * NOTE: Request must be less than the PCM buffer length in samples in order
1786 * to progress.
1787 * (ie. count <= PCM_NUM_CHUNKS*PCM_CHUNK_SAMP)
1788 */
1789static int enc_pcmbuf_read(void *buffer, int count)
1790{
1791 size_t avail = pcmbuf_used();
1792 size_t size = count*PCM_SAMP_SIZE;
1575 1793
1576 /*** Configure the buffers ***/ 1794 if (count > 0 && avail >= size)
1795 {
1796 size_t endidx = pcm_ridx + size;
1577 1797
1578 /* Layout of recording buffer: 1798 if (endidx > PCM_BUF_SIZE)
1579 * [ax] = possible alignment x multiple 1799 {
1580 * [sx] = possible size alignment of x multiple 1800 size_t wrap = endidx - PCM_BUF_SIZE;
1581 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|-> 1801 size_t offset = size -= wrap;
1582 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1583 */
1584 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1585 logf("resbytes:%lu", resbytes);
1586 1802
1587 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) - 1803 if (num_channels == 1)
1588 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH 1804 offset /= 2; /* src offset -> dst offset */
1589#ifdef DEBUG
1590 - sizeof (*wrap_id_p)
1591#endif
1592 ;
1593 1805
1594 enc_num_chunks = bufsize / enc_chunk_size; 1806 pcm_copyfn(buffer + offset, pcmbuf_ptr(0), wrap);
1595 logf("num chunks:%d", enc_num_chunks); 1807 }
1596 1808
1597 /* get real amount used by encoder chunks */ 1809 pcm_copyfn(buffer, pcmbuf_ptr(pcm_ridx), size);
1598 bufsize = enc_num_chunks*enc_chunk_size;
1599 logf("enc size:%lu", bufsize);
1600 1810
1601#ifdef DEBUG 1811 if (avail >= sample_rate*PCM_SAMP_SIZE*PCM_BOOST_SECONDS ||
1602 /* add magic at wraparound for spillover checks */ 1812 avail >= PCM_BUF_SIZE*1/2)
1603 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize); 1813 {
1604 bufsize += sizeof (*wrap_id_p); 1814 /* Filling up - boost threshold data available or more or 1/2 full
1605 *wrap_id_p = ENC_CHUNK_MAGIC; 1815 or more - boost codec */
1606#endif 1816 trigger_cpu_boost();
1817 }
1607 1818
1608 /** set OUT parameters **/ 1819 pcm_buffer_empty = false;
1609 params->enc_buffer = enc_buffer;
1610 params->buf_chunk_size = enc_chunk_size;
1611 params->num_chunks = enc_num_chunks;
1612 1820
1613 /* calculate reserve buffer start and return pointer to encoder */ 1821 return count;
1614 params->reserve_buffer = NULL;
1615 if (resbytes > 0)
1616 {
1617 params->reserve_buffer = enc_buffer + bufsize;
1618 bufsize += resbytes;
1619 } 1822 }
1620 1823
1621 /* place filename queue at end of buffer using up whatever remains */ 1824 /* Not enough data available - encoder should idle */
1622 fnq_rd_pos = 0; /* reset */ 1825 pcm_buffer_empty = true;
1623 fnq_wr_pos = 0; /* reset */
1624 fn_queue = enc_buffer + bufsize;
1625 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1626 fnq_size /= MAX_PATH;
1627 if (fnq_size > FNQ_MAX_NUM_PATHS)
1628 fnq_size = FNQ_MAX_NUM_PATHS;
1629 fnq_size *= MAX_PATH;
1630 logf("fnq files:%ld", fnq_size / MAX_PATH);
1631
1632#if defined(DEBUG)
1633 logf("pcm:%08lX", (uintptr_t)pcm_buffer);
1634 logf("enc:%08lX", (uintptr_t)enc_buffer);
1635 logf("res:%08lX", (uintptr_t)params->reserve_buffer);
1636 logf("wip:%08lX", (uintptr_t)wrap_id_p);
1637 logf("fnq:%08lX", (uintptr_t)fn_queue);
1638 logf("end:%08lX", (uintptr_t)fn_queue + fnq_size);
1639#endif
1640 1826
1641 /* init all chunk headers and reset indexes */ 1827 cancel_cpu_boost();
1642 enc_rd_index = 0; 1828
1643 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; ) 1829 /* Sleep a little bit */
1644 { 1830 sleep(0);
1645 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1646#ifdef DEBUG
1647 chunk->id = ENC_CHUNK_MAGIC;
1648#endif
1649 chunk->flags = 0;
1650 }
1651 1831
1652 logf("enc_set_parameters done"); 1832 return 0;
1653} /* enc_set_parameters */ 1833}
1654 1834
1655/* return encoder chunk at current write position - 1835/* Advance PCM buffer by count samples */
1656 NOTE: can be called by pcmrec thread when splitting streams */ 1836static int enc_pcmbuf_advance(int count)
1657struct enc_chunk_hdr * enc_get_chunk(void)
1658{ 1837{
1659 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); 1838 if (count <= 0)
1839 return 0;
1660 1840
1661#ifdef DEBUG 1841 size_t avail = pcmbuf_used();
1662 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC) 1842 size_t size = count*PCM_SAMP_SIZE;
1843
1844 if (avail < size)
1663 { 1845 {
1664 pcmrec_raise_error_status(PCMREC_E_CHUNK_OVF); 1846 size = avail;
1665 logf("finish chk ovf: %d", enc_wr_index); 1847 count = size / PCM_SAMP_SIZE;
1666 } 1848 }
1667#endif
1668
1669 chunk->flags &= CHUNKF_START_FILE;
1670 1849
1671 if (!is_recording) 1850 pcm_ridx = pcmbuf_add(pcm_ridx, size);
1672 chunk->flags |= CHUNKF_PRERECORD;
1673 1851
1674 return chunk; 1852 return count;
1675} /* enc_get_chunk */ 1853}
1676 1854
1677/* releases the current chunk into the available chunks - 1855/* Return encoder chunk at current write position, wrapping to 0 if
1678 NOTE: can be called by pcmrec thread when splitting streams */ 1856 * requested size demands it.
1679void enc_finish_chunk(void) 1857 *
1858 * NOTE: No request should be more than 1/2 the buffer length, all elements
1859 * included, or progress will not be guaranteed.
1860 * (ie. CHUNK_DATA_COUNT(need) <= enc_buflen / 2)
1861 */
1862static struct enc_chunk_data * enc_encbuf_get_buffer(size_t need)
1680{ 1863{
1681 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); 1864 /* Convert to buffer slot count, including the header */
1865 need = CHUNK_DATA_COUNT(need);
1682 1866
1683 if ((long)chunk->flags < 0) 1867 enum record_state state = record_state;
1868 size_t avail = encbuf_free();
1869
1870 /* Must have the split margin as well but it does not have to be
1871 continuous with the request */
1872 while (avail <= need + ENCBUF_MIN_SPLIT_MARGIN ||
1873 (enc_widx + need > enc_buflen &&
1874 enc_ridx <= need + ENCBUF_MIN_SPLIT_MARGIN))
1684 { 1875 {
1685 /* encoder set error flag */ 1876 if (UNLIKELY(state == REC_STATE_IDLE))
1686 pcmrec_raise_error_status(PCMREC_E_ENCODER); 1877 {
1687 logf("finish chk enc error"); 1878 /* Prerecording - delete some old data */
1688 } 1879 size_t ridx;
1880 struct enc_chunk_data *data =
1881 encbuf_read_ptr_incr(enc_ridx, &ridx);
1689 1882
1690 /* advance enc_wr_index to the next encoder chunk */ 1883 if (data)
1691 INC_ENC_INDEX(enc_wr_index); 1884 {
1885 encbuf_rec_count -= CHUNK_DATA_COUNT(data->hdr.size);
1886 num_rec_bytes -= data->hdr.size;
1887 num_rec_samples -= data->pcm_count;
1888 }
1692 1889
1693 if (enc_rd_index != enc_wr_index) 1890 enc_ridx = ridx;
1694 { 1891 avail = encbuf_free();
1695 num_rec_bytes += chunk->enc_size; 1892 continue;
1696 num_rec_samples += chunk->num_pcm; 1893 }
1697#if 0 1894 else if (avail == enc_buflen)
1698 accum_rec_bytes += chunk->enc_size;
1699 accum_pcm_samples += chunk->num_pcm;
1700#endif
1701 }
1702 else if (is_recording) /* buffer full */
1703 {
1704 /* keep current position and put up warning flag */
1705 pcmrec_raise_warning_status(PCMREC_W_ENC_BUFFER_OVF);
1706 logf("enc_buffer ovf");
1707 DEC_ENC_INDEX(enc_wr_index);
1708 if (pcmrec_context)
1709 { 1895 {
1710 /* if stream splitting, keep this out of circulation and 1896 /* Empty but request larger than any possible space */
1711 flush a small number, then readd - cannot risk losing 1897 raise_warning_status(PCMREC_W_ENC_BUFFER_OVF);
1712 stream markers */
1713 logf("mini flush");
1714 pcmrec_flush(PCMREC_FLUSH_MINI);
1715 INC_ENC_INDEX(enc_wr_index);
1716 } 1898 }
1899 else if (state != REC_STATE_FLUSH && encbuf_used() < high_watermark)
1900 {
1901 /* Not yet even at high watermark but what's needed won't fit */
1902 encbuf_request_flush();
1903 }
1904
1905 sleep(0);
1906 return NULL;
1717 } 1907 }
1718 else
1719 {
1720 /* advance enc_rd_index for prerecording */
1721 INC_ENC_INDEX(enc_rd_index);
1722 }
1723} /* enc_finish_chunk */
1724 1908
1725/* passes a pointer to next chunk of unprocessed wav data */ 1909 struct enc_chunk_data *data =
1726/* TODO: this really should give the actual size returned */ 1910 encbuf_get_write_ptr(enc_widx, need, &enc_widx);
1727unsigned char * enc_get_pcm_data(size_t size)
1728{
1729 int wp = dma_wr_pos;
1730 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1731 1911
1732 /* limit the requested pcm data size */ 1912 if (state == REC_STATE_IDLE)
1733 if (size > PCM_MAX_FEED_SIZE) 1913 data->hdr.pre = 1;
1734 size = PCM_MAX_FEED_SIZE;
1735 1914
1736 if (avail >= size) 1915 return data;
1916}
1917
1918/* Releases the current buffer into the available chunks */
1919static void enc_encbuf_finish_buffer(void)
1920{
1921 struct enc_chunk_data *data = ENC_DATA_HDR(encbuf_ptr(enc_widx));
1922
1923 if (data->hdr.err)
1737 { 1924 {
1738 unsigned char *ptr = pcm_buffer + pcm_rd_pos; 1925 /* Encoder set error flag */
1739 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK; 1926 raise_error_status(PCMREC_E_ENCODER);
1927 return;
1928 }
1740 1929
1741 pcm_enc_pos = pcm_rd_pos; 1930 size_t data_size = data->hdr.size;
1742 pcm_rd_pos = next_pos;
1743 1931
1744 /* ptr must point to continous data at wraparound position */ 1932 if (data_size == 0)
1745 if ((size_t)pcm_rd_pos < size) 1933 return; /* Claims nothing was written */
1746 {
1747 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1748 pcm_buffer, pcm_rd_pos);
1749 }
1750 1934
1751 if (avail >= (sample_rate << 2) || 1935 size_t count = CHUNK_DATA_COUNT(data_size);
1752 avail >= 3*(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE) / 4) 1936 size_t avail = encbuf_free();
1753 {
1754 /* Filling up - 1s data available or more or 3/4 full or more -
1755 boost codec */
1756 trigger_cpu_boost();
1757 }
1758 1937
1759 pcm_buffer_empty = false; 1938 if (avail <= count || enc_widx + count > enc_buflen)
1760 return ptr; 1939 {
1940 /* Claims it wrote too much? */
1941 raise_warning_status(PCMREC_W_ENC_BUFFER_OVF);
1942 return;
1761 } 1943 }
1762 1944
1763 /* not enough data available - encoder should idle */ 1945 if (num_rec_bytes + data_size > MAX_NUM_REC_BYTES)
1764 pcm_buffer_empty = true; 1946 {
1947 /* Would exceed filesize limit; should have split sooner.
1948 This chunk will be dropped. :'( */
1949 raise_warning_status(PCMREC_W_FILE_SIZE);
1950 return;
1951 }
1765 1952
1766 cancel_cpu_boost(); 1953 encbuf_widx_advance(enc_widx, count);
1767 1954
1768 /* Sleep long enough to allow one frame on average */ 1955 encbuf_rec_count += count;
1769 sleep(0); 1956 num_rec_bytes += data_size;
1957 num_rec_samples += data->pcm_count;
1958}
1770 1959
1771 return NULL; 1960/* Read from the output stream */
1772} /* enc_get_pcm_data */ 1961static ssize_t enc_stream_read(void *buf, size_t count)
1962{
1963 if (!stream_flush_buf())
1964 return -1;
1965
1966 return read(rec_fd, buf, count);
1967}
1773 1968
1774/* puts some pcm data back in the queue */ 1969/* Seek the output steam */
1775size_t enc_unget_pcm_data(size_t size) 1970static off_t enc_stream_lseek(off_t offset, int whence)
1776{ 1971{
1777 int wp = dma_wr_pos; 1972 if (!stream_flush_buf())
1778 size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) - 1973 return -1;
1779 2*PCM_CHUNK_SIZE;
1780 1974
1781 /* allow one interrupt to occur during this call and not have the 1975 return lseek(rec_fd, offset, whence);
1782 new read position inside the DMA destination chunk */ 1976}
1783 if ((ssize_t)old_avail > 0) 1977
1978/* Write to the output stream */
1979static ssize_t enc_stream_write(const void *buf, size_t count)
1980{
1981 if (UNLIKELY(count >= STREAM_BUF_SIZE))
1784 { 1982 {
1785 /* limit size to amount of old data remaining */ 1983 /* Too big to buffer */
1786 if (size > old_avail) 1984 if (stream_flush_buf())
1787 size = old_avail; 1985 return write(rec_fd, buf, count);
1986 }
1788 1987
1789 pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK; 1988 if (!count)
1790 pcm_rd_pos = pcm_enc_pos; 1989 return 0;
1791 1990
1792 return size; 1991 if (stream_buf_used + count > STREAM_BUF_SIZE)
1992 {
1993 if (!stream_flush_buf() && stream_buf_used + count > STREAM_BUF_SIZE)
1994 count = STREAM_BUF_SIZE - stream_buf_used;
1793 } 1995 }
1794 1996
1795 return 0; 1997 memcpy(stream_buffer + stream_buf_used, buf, count);
1796} /* enc_unget_pcm_data */ 1998 stream_buf_used += count;
1999
2000 return count;
2001}
2002
2003/* One-time init at startup */
2004void INIT_ATTR recording_init(void)
2005{
2006 /* Init API */
2007 ci.enc_pcmbuf_read = enc_pcmbuf_read;
2008 ci.enc_pcmbuf_advance = enc_pcmbuf_advance;
2009 ci.enc_encbuf_get_buffer = enc_encbuf_get_buffer;
2010 ci.enc_encbuf_finish_buffer = enc_encbuf_finish_buffer;
2011 ci.enc_stream_read = enc_stream_read;
2012 ci.enc_stream_lseek = enc_stream_lseek;
2013 ci.enc_stream_write = enc_stream_write;
2014}
diff --git a/apps/recorder/pcm_record.h b/apps/recorder/pcm_record.h
index bff7881605..cf7197946a 100644
--- a/apps/recorder/pcm_record.h
+++ b/apps/recorder/pcm_record.h
@@ -8,6 +8,7 @@
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing 10 * Copyright (C) 2005 by Linus Nielsen Feltzing
11 * Copyright (C) 2006-2013 by Michael Sevakis
11 * 12 *
12 * This program is free software; you can redistribute it and/or 13 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 14 * modify it under the terms of the GNU General Public License
@@ -18,47 +19,64 @@
18 * KIND, either express or implied. 19 * KIND, either express or implied.
19 * 20 *
20 ****************************************************************************/ 21 ****************************************************************************/
21
22#ifndef PCM_RECORD_H 22#ifndef PCM_RECORD_H
23#define PCM_RECORD_H 23#define PCM_RECORD_H
24 24
25#include "config.h" 25/** Warnings (recording may continue with possible data loss)
26 ** Reset of recording clears, otherwise see notes below
27 */
26 28
27/** Warnings **/ 29/* PCM buffer has overflowed; PCM samples were dropped */
28/* pcm (dma) buffer has overflowed */ 30/* persists until: stop, new file, clear */
29#define PCMREC_W_PCM_BUFFER_OVF 0x00000001 31#define PCMREC_W_PCM_BUFFER_OVF 0x00000001
30/* encoder output buffer has overflowed */ 32/* encoder output buffer has overflowed; encoded data was dropped */
33/* persists until: stop, new file, clear */
31#define PCMREC_W_ENC_BUFFER_OVF 0x00000002 34#define PCMREC_W_ENC_BUFFER_OVF 0x00000002
32/** Errors **/ 35/* encoder and set/detected sample rates do not match */
36/* persists until: rates match, clear */
37#define PCMREC_W_SAMPR_MISMATCH 0x00000004
38/* PCM frame skipped because of recoverable DMA error */
39/* persists until: clear */
40#define PCMREC_W_DMA 0x00000008
41/* internal file size limit was reached; encoded data was dropped */
42/* persists until: stop, new file, clear */
43#define PCMREC_W_FILE_SIZE 0x00000010
44
45/* all warning flags */
46#define PCMREC_W_ALL 0x0000001f
47
48/** Errors (recording should be reset)
49 **
50 ** Stopping recording if recording clears internally and externally visible
51 ** status must be cleared with audio_error_clear()
52 ** reset of recording clears
53 */
54
55/* DMA callback has reported an error */
56#define PCMREC_E_DMA 0x80010000
33/* failed to load encoder */ 57/* failed to load encoder */
34#define PCMREC_E_LOAD_ENCODER 0x80001000 58#define PCMREC_E_LOAD_ENCODER 0x80020000
35/* error originating in encoder */ 59/* error originating in encoder */
36#define PCMREC_E_ENCODER 0x80002000 60#define PCMREC_E_ENCODER 0x80040000
37/* filename queue has desynced from stream markers */ 61/* error from encoder setup of stream parameters */
38#define PCMREC_E_FNQ_DESYNC 0x80004000 62#define PCMREC_E_ENC_SETUP 0x80080000
63/* error writing to output stream */
64#define PCMREC_E_ENCODER_STREAM 0x80100000
39/* I/O error has occurred */ 65/* I/O error has occurred */
40#define PCMREC_E_IO 0x80008000 66#define PCMREC_E_IO 0x80200000
41#ifdef DEBUG
42/* encoder has written past end of allocated space */
43#define PCMREC_E_CHUNK_OVF 0x80010000
44#endif /* DEBUG */
45/* DMA callback has reported an error */
46#define PCMREC_E_DMA 0x80020000
47 67
48/** General functions for high level codec recording **/ 68/* all error flags */
49/* pcm_rec_error_clear is deprecated for general use. audio_error_clear 69#define PCMREC_E_ALL 0x803f0000
50 should be used */ 70
71/* Functions called by audio_thread.c */
51void pcm_rec_error_clear(void); 72void pcm_rec_error_clear(void);
52/* pcm_rec_status is deprecated for general use. audio_status merges the
53 results for consistency with the hardware codec version */
54unsigned int pcm_rec_status(void); 73unsigned int pcm_rec_status(void);
55unsigned long pcm_rec_get_warnings(void); 74uint32_t pcm_rec_get_warnings(void);
56void pcm_rec_init(void) INIT_ATTR; 75#ifdef HAVE_SPDIF_IN
57int pcm_rec_current_bitrate(void);
58int pcm_rec_encoder_afmt(void); /* AFMT_* value, AFMT_UNKNOWN if none */
59int pcm_rec_rec_format(void); /* Format index or -1 otherwise */
60unsigned long pcm_rec_sample_rate(void); 76unsigned long pcm_rec_sample_rate(void);
61int pcm_get_num_unprocessed(void); 77#endif
78
79void recording_init(void);
62 80
63/* audio.h contains audio_* recording functions */ 81/* audio.h contains audio_* recording functions */
64 82
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index 4893f589f1..e246825443 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -1730,7 +1730,7 @@ bool recording_screen(bool no_source)
1730 /* Don't use language string unless agreed upon to make this 1730 /* Don't use language string unless agreed upon to make this
1731 method permanent - could do something in the statusbar */ 1731 method permanent - could do something in the statusbar */
1732 snprintf(buf, sizeof(buf), "Warning: %08lX", 1732 snprintf(buf, sizeof(buf), "Warning: %08lX",
1733 pcm_rec_get_warnings()); 1733 (unsigned long)pcm_rec_get_warnings());
1734 } 1734 }
1735 else 1735 else
1736#endif /* CONFIG_CODEC == SWCODEC */ 1736#endif /* CONFIG_CODEC == SWCODEC */
@@ -1755,8 +1755,16 @@ bool recording_screen(bool no_source)
1755 1755
1756 if(audio_stat & AUDIO_STATUS_PRERECORD) 1756 if(audio_stat & AUDIO_STATUS_PRERECORD)
1757 { 1757 {
1758#if CONFIG_CODEC == SWCODEC
1759 /* Tracks amount of prerecorded data in buffer */
1760 snprintf(buf, sizeof(buf), "%s (%lu/%ds)...",
1761 str(LANG_RECORD_PRERECORD),
1762 audio_prerecorded_time() / HZ,
1763 global_settings.rec_prerecord_time);
1764#else /* !SWCODEC */
1758 snprintf(buf, sizeof(buf), "%s...", 1765 snprintf(buf, sizeof(buf), "%s...",
1759 str(LANG_RECORD_PRERECORD)); 1766 str(LANG_RECORD_PRERECORD));
1767#endif /* CONFIG_CODEC == SWCODEC */
1760 } 1768 }
1761 else 1769 else
1762 { 1770 {
@@ -1915,8 +1923,7 @@ bool recording_screen(bool no_source)
1915 screens[i].update(); 1923 screens[i].update();
1916 1924
1917#if CONFIG_CODEC == SWCODEC 1925#if CONFIG_CODEC == SWCODEC
1918 /* stop recording - some players like H10 freeze otherwise 1926 /* stop recording first and try to finish saving whatever it can */
1919 TO DO: find out why it freezes and fix properly */
1920 rec_command(RECORDING_CMD_STOP); 1927 rec_command(RECORDING_CMD_STOP);
1921 audio_close_recording(); 1928 audio_close_recording();
1922#endif 1929#endif
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index 293956cb37..24e8e9a0e7 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -212,10 +212,7 @@ unsigned long audio_num_recorded_bytes(void);
212 212
213#if CONFIG_CODEC == SWCODEC 213#if CONFIG_CODEC == SWCODEC
214/* SWCODEC recording functions */ 214/* SWCODEC recording functions */
215/* playback.c */ 215unsigned long audio_prerecorded_time(void);
216bool audio_load_encoder(int afmt);
217void audio_remove_encoder(void);
218unsigned char *audio_get_recording_buffer(size_t *buffer_size);
219#endif /* CONFIG_CODEC == SWCODEC */ 216#endif /* CONFIG_CODEC == SWCODEC */
220 217
221#endif /* HAVE_RECORDING */ 218#endif /* HAVE_RECORDING */
diff --git a/firmware/export/enc_base.h b/firmware/export/enc_base.h
index f5dfb65f2a..7228dc4c83 100644
--- a/firmware/export/enc_base.h
+++ b/firmware/export/enc_base.h
@@ -9,7 +9,7 @@
9 * 9 *
10 * Base declarations for working with software encoders 10 * Base declarations for working with software encoders
11 * 11 *
12 * Copyright (C) 2006 Michael Sevakis 12 * Copyright (C) 2006-2013 Michael Sevakis
13 * 13 *
14 * This program is free software; you can redistribute it and/or 14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License 15 * modify it under the terms of the GNU General Public License
@@ -24,7 +24,9 @@
24#ifndef ENC_BASE_H 24#ifndef ENC_BASE_H
25#define ENC_BASE_H 25#define ENC_BASE_H
26 26
27/** encoder config structures **/ 27#include <sys/types.h>
28
29/** Encoder config structures **/
28 30
29/** aiff_enc.codec **/ 31/** aiff_enc.codec **/
30struct aiff_enc_config 32struct aiff_enc_config
@@ -57,18 +59,22 @@ struct aiff_enc_config
57 59
58/* MPEG 1 */ 60/* MPEG 1 */
59#define MPEG1_SAMPR_CAPS (SAMPR_CAP_32 | SAMPR_CAP_48 | SAMPR_CAP_44) 61#define MPEG1_SAMPR_CAPS (SAMPR_CAP_32 | SAMPR_CAP_48 | SAMPR_CAP_44)
60#define MPEG1_BITR_CAPS (MP3_BITR_CAP_32 | MP3_BITR_CAP_40 | MP3_BITR_CAP_48 | \ 62#define MPEG1_BITR_CAPS (MP3_BITR_CAP_32 | MP3_BITR_CAP_40 | \
61 MP3_BITR_CAP_56 | MP3_BITR_CAP_64 | MP3_BITR_CAP_80 | \ 63 MP3_BITR_CAP_48 | MP3_BITR_CAP_56 | \
62 MP3_BITR_CAP_96 | MP3_BITR_CAP_112 | MP3_BITR_CAP_128 | \ 64 MP3_BITR_CAP_64 | MP3_BITR_CAP_80 | \
63 MP3_BITR_CAP_160 | MP3_BITR_CAP_192 | MP3_BITR_CAP_224 | \ 65 MP3_BITR_CAP_96 | MP3_BITR_CAP_112 | \
66 MP3_BITR_CAP_128 | MP3_BITR_CAP_160 | \
67 MP3_BITR_CAP_192 | MP3_BITR_CAP_224 | \
64 MP3_BITR_CAP_256 | MP3_BITR_CAP_320) 68 MP3_BITR_CAP_256 | MP3_BITR_CAP_320)
65 69
66/* MPEG 2 */ 70/* MPEG 2 */
67#define MPEG2_SAMPR_CAPS (SAMPR_CAP_22 | SAMPR_CAP_24 | SAMPR_CAP_16) 71#define MPEG2_SAMPR_CAPS (SAMPR_CAP_22 | SAMPR_CAP_24 | SAMPR_CAP_16)
68#define MPEG2_BITR_CAPS (MP3_BITR_CAP_8 | MP3_BITR_CAP_16 | MP3_BITR_CAP_24 | \ 72#define MPEG2_BITR_CAPS (MP3_BITR_CAP_8 | MP3_BITR_CAP_16 | \
69 MP3_BITR_CAP_32 | MP3_BITR_CAP_40 | MP3_BITR_CAP_48 | \ 73 MP3_BITR_CAP_24 | MP3_BITR_CAP_32 | \
70 MP3_BITR_CAP_56 | MP3_BITR_CAP_64 | MP3_BITR_CAP_80 | \ 74 MP3_BITR_CAP_40 | MP3_BITR_CAP_48 | \
71 MP3_BITR_CAP_96 | MP3_BITR_CAP_112 | MP3_BITR_CAP_128 | \ 75 MP3_BITR_CAP_56 | MP3_BITR_CAP_64 | \
76 MP3_BITR_CAP_80 | MP3_BITR_CAP_96 | \
77 MP3_BITR_CAP_112 | MP3_BITR_CAP_128 | \
72 MP3_BITR_CAP_144 | MP3_BITR_CAP_160) 78 MP3_BITR_CAP_144 | MP3_BITR_CAP_160)
73 79
74#if 0 80#if 0
@@ -131,6 +137,7 @@ struct wavpack_enc_config
131#endif 137#endif
132}; 138};
133 139
140/* General config information about any encoder */
134struct encoder_config 141struct encoder_config
135{ 142{
136 union 143 union
@@ -149,144 +156,77 @@ struct encoder_config
149}; 156};
150 157
151/** Encoder chunk macros and definitions **/ 158/** Encoder chunk macros and definitions **/
152#define CHUNKF_START_FILE 0x0001ul /* This chunk starts a new file */
153#define CHUNKF_END_FILE 0x0002ul /* This chunk ends the current file */
154#define CHUNKF_PRERECORD 0x0010ul /* This chunk is prerecord data,
155 a new file could start anytime */
156#define CHUNKF_ABORT 0x0020ul /* Encoder should not finish this
157 chunk */
158#define CHUNKF_ERROR (~0ul ^ (~0ul >> 1)) /* An error has occured
159 (passed to/from encoder). Use the
160 sign bit to check (long)flags < 0. */
161#define CHUNKF_ALLFLAGS (0x0033ul | CHUNKF_ERROR)
162
163/* Header at the beginning of every encoder chunk */
164#ifdef DEBUG
165#define H_TO_BE32 htobe32
166#define ENC_CHUNK_MAGIC H_TO_BE32(('P' << 24) | ('T' << 16) | ('Y' << 8) | 'R')
167#endif
168struct enc_chunk_hdr
169{
170#ifdef DEBUG
171 unsigned long id; /* overflow detection - 'PTYR' - acronym for
172 "PTYR Tells You Right" ;) */
173#endif
174 unsigned long flags; /* in/out: flags used by encoder and file
175 writing */
176 size_t enc_size; /* out: amount of encoder data written to
177 chunk */
178 unsigned long num_pcm; /* out: number of PCM samples eaten during
179 processing
180 (<= size of allocated buffer) */
181 unsigned char *enc_data; /* out: pointer to enc_size_written bytes
182 of encoded audio data in chunk */
183 /* Encoder defined data follows header. Can be audio data + any other
184 stuff the encoder needs to handle on a per chunk basis */
185};
186 159
187/* Paranoia: be sure header size is 4-byte aligned */ 160/* What sort of data does the header describe? */
188#define ENC_CHUNK_HDR_SIZE \ 161enum CHUNK_T
189 ALIGN_UP_P2(sizeof (struct enc_chunk_hdr), 2)
190/* Skip the chunk header and return data */
191#define ENC_CHUNK_SKIP_HDR(t, hdr) \
192 ((typeof (t))((char *)hdr + ENC_CHUNK_HDR_SIZE))
193/* Cast p to struct enc_chunk_hdr * */
194#define ENC_CHUNK_HDR(p) \
195 ((struct enc_chunk_hdr *)(p))
196
197enum enc_events
198{ 162{
199 /* File writing events - data points to enc_file_event_data */ 163 CHUNK_T_DATA = 0x0, /* Encoded audio data */
200 ENC_START_FILE = 0, /* a new file has been opened and no data has yet 164 CHUNK_T_STREAM_START = 0x1, /* Stream start marker */
201 been written */ 165 CHUNK_T_STREAM_END = 0x2, /* Stream end marker */
202 ENC_WRITE_CHUNK, /* write the current chunk to disk */ 166 CHUNK_T_WRAP = 0x3 /* Buffer early wrap marker */
203 ENC_END_FILE, /* current file about to be closed and all valid
204 data has been written */
205 /* Encoder buffer events - data points to enc_buffer_event_data */
206 ENC_REC_NEW_STREAM, /* Take steps to finish current stream and start
207 new */
208}; 167};
209 168
210/** 169/* Header for every buffer slot and chunk */
211 * encoder can write extra data to the file such as headers or more encoded 170union enc_chunk_hdr
212 * samples and must update sizes and samples accordingly.
213 */
214struct enc_file_event_data
215{ 171{
216 struct enc_chunk_hdr *chunk; /* Current chunk */ 172 struct
217 size_t new_enc_size; /* New size of chunk */ 173 {
218 unsigned long new_num_pcm; /* New number of pcm in chunk */ 174 uint32_t type : 2; /* Chunk type (CHUNK_T_*) */
219 const char *filename; /* filename to open if ENC_START_FILE */ 175 uint32_t err : 1; /* Encoder error */
220 int rec_file; /* Current file or < 0 if none */ 176 uint32_t pre : 1; /* Chunk is prerecorded data */
221 unsigned long num_pcm_samples; /* Current pcm sample count written to 177 uint32_t aux0 : 1; /* Aux flag 0 - for encoder */
222 file so far. */ 178 uint32_t unused : 3; /* */
223}; 179 uint32_t size : 24; /* size of data */
180 };
181 uint32_t zero; /* Zero-out struct access */
182 intptr_t reserved1; /* Want it at least pointer-sized */
183} __attribute__((__may_alias__));
184
185#define ENC_HDR_SIZE (sizeof (union enc_chunk_hdr))
224 186
225/** 187/* When hdr.type is CHUNK_T_STREAM_START */
226 * encoder may add some data to the end of the last and start of the next 188struct enc_chunk_file
227 * but must never yield when called so any encoding done should be absolutely
228 * minimal.
229 */
230struct enc_buffer_event_data
231{ 189{
232 unsigned long flags; /* in: One or more of: 190 union enc_chunk_hdr hdr; /* This chunk's header */
233 * CHUNKF_PRERECORD 191 /* hdr.size = slot count of chunk */
234 * CHUNKF_END_FILE 192 char path[]; /* NULL-terminated path of file */
235 * CHUNKF_START_FILE 193} __attribute__((__may_alias__));
236 */
237 struct enc_chunk_hdr *pre_chunk; /* in: pointer to first prerecord
238 * chunk
239 */
240 struct enc_chunk_hdr *chunk; /* in,out: chunk were split occurs -
241 * first chunk of start
242 */
243};
244 194
245/** Callbacks called by encoder codec **/ 195/* If flags = CHUNK_T_STREAM_END, just the header exists */
246 196
247/* parameters passed to encoder by enc_get_inputs */ 197/* When hdr.type is CHUNK_T_DATA */
248struct enc_inputs 198struct enc_chunk_data
249{ 199{
250 unsigned long sample_rate; /* out - pcm frequency */ 200 union enc_chunk_hdr hdr; /* IN,OUT: This chunk's header */
251 int num_channels; /* out - number of audio channels */ 201 /* hdr.size = total size of data[] */
252 int rec_mono_mode; /* out - how to create mono */ 202 uint32_t pcm_count; /* OUT: number of PCM samples encoded */
253 struct encoder_config *config; /* out - encoder settings */ 203 uint8_t data[]; /* OUT: encoded audio data */
254}; 204} __attribute__((__may_alias__));
255 205
256void enc_get_inputs(struct enc_inputs *inputs); 206/* CHUNK_T_STREAM_END and CHUNK_T_WRAP consist of only the header */
257 207
258/* parameters pass from encoder to enc_set_parameters */ 208#define ENC_FILE_HDR(hdr) ((struct enc_chunk_file *)(hdr))
259struct enc_parameters 209#define ENC_DATA_HDR(hdr) ((struct enc_chunk_data *)(hdr))
210
211/* Audio and encoder stream parameters */
212struct enc_inputs
260{ 213{
261 /* IN parameters */ 214 /* IN parameters */
262 int afmt; /* AFMT_* id - sanity checker */ 215 unsigned long sample_rate; /* PCM samplerate setting */
263 size_t chunk_size; /* max chunk size required */ 216 int num_channels; /* Number of audio channels */
264 unsigned long enc_sample_rate; /* actual sample rate used by encoder 217 struct encoder_config *config; /* Encoder settings */
265 (for recorded time calculation) */
266 size_t reserve_bytes; /* number of bytes to reserve immediately
267 following chunks */
268 void (*events_callback)(enum enc_events event,
269 void *data); /* pointer to events callback */
270 /* OUT parameters */
271 unsigned char *enc_buffer; /* pointer to enc_buffer */
272 size_t buf_chunk_size; /* size of chunks in enc_buffer */
273 int num_chunks; /* number of chunks allotted to encoder */
274 unsigned char *reserve_buffer; /* pointer to reserve_bytes bytes */
275};
276 218
277/* set the encoder dimensions - called by encoder codec at initialization 219 /* IN,OUT parameters */
278 and termination */ 220 unsigned long enc_sample_rate; /* Actual sample rate accepted by encoder
279void enc_set_parameters(struct enc_parameters *params); 221 (for recorded time calculation) */
280/* returns pointer to next write chunk in circular buffer */ 222};
281struct enc_chunk_hdr * enc_get_chunk(void);
282/* releases the current chunk into the available chunks */
283void enc_finish_chunk(void);
284 223
285#define PCM_MAX_FEED_SIZE 20000 /* max pcm size passed to encoder */ 224enum enc_callback_reason
225{
226 ENC_CB_INPUTS, /* 'params' is struct enc_inputs * */
227 ENC_CB_STREAM, /* 'params' is union enc_chunk_hdr * */
228};
286 229
287/* passes a pointer to next chunk of unprocessed wav data */ 230typedef int (* enc_callback_t)(enum enc_callback_reason reason, void *params);
288unsigned char * enc_get_pcm_data(size_t size);
289/* puts some pcm data back in the queue */
290size_t enc_unget_pcm_data(size_t size);
291 231
292#endif /* ENC_BASE_H */ 232#endif /* ENC_BASE_H */
diff --git a/lib/rbcodec/codecs/aiff_enc.c b/lib/rbcodec/codecs/aiff_enc.c
index 8e9246d2bb..fb8db384a3 100644
--- a/lib/rbcodec/codecs/aiff_enc.c
+++ b/lib/rbcodec/codecs/aiff_enc.c
@@ -8,6 +8,7 @@
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2006 Antonius Hellmann 10 * Copyright (C) 2006 Antonius Hellmann
11 * Copyright (C) 2006-2013 Michael Sevakis
11 * 12 *
12 * This program is free software; you can redistribute it and/or 13 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 14 * modify it under the terms of the GNU General Public License
@@ -47,10 +48,15 @@ struct aiff_header
47#define PCM_DEPTH_BYTES 2 48#define PCM_DEPTH_BYTES 2
48#define PCM_DEPTH_BITS 16 49#define PCM_DEPTH_BITS 16
49#define PCM_SAMP_PER_CHUNK 2048 50#define PCM_SAMP_PER_CHUNK 2048
50#define PCM_CHUNK_SIZE (PCM_SAMP_PER_CHUNK*4) 51
52static int num_channels;
53static uint32_t sample_rate;
54static size_t frame_size;
55static size_t pcm_size;
56static uint32_t num_sample_frames;
51 57
52/* Template headers */ 58/* Template headers */
53struct aiff_header aiff_header = 59static const struct aiff_header aiff_template_header =
54{ 60{
55 { 'F', 'O', 'R', 'M' }, /* form_id */ 61 { 'F', 'O', 'R', 'M' }, /* form_id */
56 0, /* form_size (*) */ 62 0, /* form_size (*) */
@@ -65,336 +71,193 @@ struct aiff_header aiff_header =
65 0, /* ssnd_size (*) */ 71 0, /* ssnd_size (*) */
66 htobe32(0), /* offset */ 72 htobe32(0), /* offset */
67 htobe32(0), /* block_size */ 73 htobe32(0), /* block_size */
74 /* (*) updated when finalizing stream */
68}; 75};
69 76
70/* (*) updated when finalizing file */ 77static inline void frame_htobe(uint32_t *p, size_t size)
71 78{
72static int num_channels IBSS_ATTR; 79#ifdef ROCKBOX_LITTLE_ENDIAN
73static int rec_mono_mode IBSS_ATTR; 80 /* Byte-swap samples, stereo or mono */
74static uint32_t sample_rate; 81 do
75static uint32_t enc_size; 82 {
76static int32_t err IBSS_ATTR; 83 uint32_t t;
84 t = swap_odd_even32(*p); *p++ = t;
85 t = swap_odd_even32(*p); *p++ = t;
86 t = swap_odd_even32(*p); *p++ = t;
87 t = swap_odd_even32(*p); *p++ = t;
88 t = swap_odd_even32(*p); *p++ = t;
89 t = swap_odd_even32(*p); *p++ = t;
90 t = swap_odd_even32(*p); *p++ = t;
91 t = swap_odd_even32(*p); *p++ = t;
92 }
93 while (size -= 8 * 2 * PCM_DEPTH_BYTES);
94#endif /* ROCKBOX_LITTLE_ENDIAN */
95 (void)p; (void)size;
96}
77 97
78/* convert unsigned 32 bit value to 80-bit floating point number */ 98/* convert unsigned 32 bit value to 80-bit floating point number */
79static void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l) 99static void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l)
80 ICODE_ATTR;
81static void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l)
82{ 100{
83 int32_t exp;
84
85 ci->memset(f, 0, 10); 101 ci->memset(f, 0, 10);
86 102
87 if (l == 0) 103 if (l == 0)
88 return; 104 return;
89 105
90 for (exp = 30; (l & (1ul << 31)) == 0; exp--) 106 int shift = __builtin_clz(l);
91 l <<= 1;
92 107
93 /* sign always zero - bit 79 */ 108 /* sign always zero - bit 79 */
94 /* exponent is 0-31 (normalized: 31 - shift + 16383) - bits 64-78 */ 109 /* exponent is 0-31 (normalized: 30 - shift + 16383) - bits 64-78 */
95 f[0] = 0x40; 110 f[0] = 0x40;
96 f[1] = (uint8_t)exp; 111 f[1] = (uint8_t)(30 - shift);
97 /* mantissa is value left justified with most significant non-zero 112 /* mantissa is value left justified with most significant non-zero
98 bit stored in bit 63 - bits 0-63 */ 113 bit stored in bit 63 - bits 0-63 */
114 l <<= shift;
99 f[2] = (uint8_t)(l >> 24); 115 f[2] = (uint8_t)(l >> 24);
100 f[3] = (uint8_t)(l >> 16); 116 f[3] = (uint8_t)(l >> 16);
101 f[4] = (uint8_t)(l >> 8); 117 f[4] = (uint8_t)(l >> 8);
102 f[5] = (uint8_t)(l >> 0); 118 f[5] = (uint8_t)(l >> 0);
103} /* uint32_h_to_ieee754_extended_be */ 119}
104
105/* called version often - inline */
106static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR;
107static inline bool is_file_data_ok(struct enc_file_event_data *data)
108{
109 return data->rec_file >= 0 && (long)data->chunk->flags >= 0;
110} /* is_file_data_ok */
111 120
112/* called version often - inline */ 121static int on_stream_data(struct enc_chunk_data *data)
113static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR;
114static inline bool on_write_chunk(struct enc_file_event_data *data)
115{ 122{
116 if (!is_file_data_ok(data)) 123 size_t size = data->hdr.size;
117 return false;
118 124
119 if (data->chunk->enc_data == NULL) 125 if (ci->enc_stream_write(data->data, size) != (ssize_t)size)
120 { 126 return -1;
121#ifdef ROCKBOX_HAS_LOGF
122 ci->logf("aiff enc: NULL data");
123#endif
124 return true;
125 }
126 127
127 if (ci->write(data->rec_file, data->chunk->enc_data, 128 pcm_size += size;
128 data->chunk->enc_size) != (ssize_t)data->chunk->enc_size) 129 num_sample_frames += data->pcm_count;
129 return false;
130 130
131 data->num_pcm_samples += data->chunk->num_pcm; 131 return 0;
132 return true; 132}
133} /* on_write_chunk */
134 133
135static bool on_start_file(struct enc_file_event_data *data) 134static int on_stream_start(void)
136{ 135{
137 if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0')
138 return false;
139
140 data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
141
142 if (data->rec_file < 0)
143 return false;
144
145 /* reset sample count */ 136 /* reset sample count */
146 data->num_pcm_samples = 0; 137 pcm_size = 0;
138 num_sample_frames = 0;
147 139
148 /* write template headers */ 140 /* write template header */
149 if (ci->write(data->rec_file, &aiff_header, sizeof (aiff_header)) 141 if (ci->enc_stream_write(&aiff_template_header,
150 != sizeof (aiff_header)) 142 sizeof (struct aiff_header))
151 { 143 != sizeof (struct aiff_header))
152 return false; 144 return -1;
153 }
154 145
155 data->new_enc_size += sizeof(aiff_header); 146 return 0;
156 return true; 147}
157} /* on_start_file */
158 148
159static bool on_end_file(struct enc_file_event_data *data) 149static int on_stream_end(union enc_chunk_hdr *hdr)
160{ 150{
161 /* update template headers */ 151 /* update template header */
162 struct aiff_header hdr; 152 struct aiff_header aiff;
163 uint32_t data_size;
164 153
165 if (!is_file_data_ok(data)) 154 if (hdr->err)
166 return false;
167
168 if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 ||
169 ci->read(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr))
170 { 155 {
171 return false; 156 /* Called for stream error; get correct data size */
157 ssize_t size = ci->enc_stream_lseek(0, SEEK_END);
158
159 if (size > (ssize_t)sizeof (aiff))
160 {
161 pcm_size = size - sizeof (aiff);
162 num_sample_frames = pcm_size / (PCM_DEPTH_BYTES*num_channels);
163 }
172 } 164 }
173 165
174 data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES; 166 if (ci->enc_stream_lseek(0, SEEK_SET) != 0)
167 return -1;
168
169 if (ci->enc_stream_read(&aiff, sizeof (aiff)) != sizeof (aiff))
170 return -2;
175 171
176 /* 'FORM' chunk */ 172 /* 'FORM' chunk */
177 hdr.form_size = htobe32(data_size + sizeof (hdr) - 8); 173 aiff.form_size = htobe32(pcm_size + sizeof (aiff) - 8);
178 174
179 /* 'COMM' chunk */ 175 /* 'COMM' chunk */
180 hdr.num_channels = htobe16(num_channels); 176 aiff.num_channels = htobe16(num_channels);
181 hdr.num_sample_frames = htobe32(data->num_pcm_samples); 177 aiff.num_sample_frames = htobe32(num_sample_frames);
182 uint32_h_to_ieee754_extended_be(hdr.sample_rate, sample_rate); 178 uint32_h_to_ieee754_extended_be(aiff.sample_rate, sample_rate);
183 179
184 /* 'SSND' chunk */ 180 /* 'SSND' chunk */
185 hdr.ssnd_size = htobe32(data_size + 8); 181 aiff.ssnd_size = htobe32(pcm_size + 8);
186 182
187 if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || 183 if (ci->enc_stream_lseek(0, SEEK_SET) != 0)
188 ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr) || 184 return -3;
189 ci->close(data->rec_file) != 0)
190 {
191 return false;
192 }
193 185
194 data->rec_file = -1; 186 if (ci->enc_stream_write(&aiff, sizeof (aiff)) != sizeof (aiff))
187 return -4;
195 188
196 return true; 189 return 0;
197} /* on_end_file */ 190}
198 191
199static void enc_events_callback(enum enc_events event, void *data) 192/* this is the codec entry point */
200 ICODE_ATTR; 193enum codec_status codec_main(enum codec_entry_call_reason reason)
201static void enc_events_callback(enum enc_events event, void *data)
202{ 194{
203 switch (event) 195 return CODEC_OK;
204 { 196 (void)reason;
205 case ENC_WRITE_CHUNK: 197}
206 if (on_write_chunk((struct enc_file_event_data *)data))
207 return;
208
209 break;
210
211 case ENC_START_FILE:
212 if (on_start_file((struct enc_file_event_data *)data))
213 return;
214
215 break;
216
217 case ENC_END_FILE:
218 if (on_end_file((struct enc_file_event_data *)data))
219 return;
220
221 break;
222
223 default:
224 return;
225 }
226
227 /* Something failed above. Signal error back to core. */
228 ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR;
229} /* enc_events_callback */
230 198
231/* convert native pcm samples to aiff format samples */ 199/* this is called for each file to process */
232static inline void sample_to_mono(uint32_t **src, uint32_t **dst) 200enum codec_status ICODE_ATTR codec_run(void)
233{ 201{
234 int32_t lr1, lr2; 202 enum { GETBUF_ENC, GETBUF_PCM } getbuf = GETBUF_ENC;
203 struct enc_chunk_data *data = NULL;
235 204
236 switch(rec_mono_mode) 205 /* main encoding loop */
206 while (1)
237 { 207 {
238 case 1: 208 enum codec_command_action action = ci->get_command(NULL);
239 /* mono = L */ 209
240 lr1 = *(*src)++; 210 if (action != CODEC_ACTION_NULL)
241 lr1 = lr1 >> 16;
242 lr2 = *(*src)++;
243 lr2 = lr2 >> 16;
244 break;
245 case 2:
246 /* mono = R */
247 lr1 = *(*src)++;
248 lr1 = (int16_t)lr1;
249 lr2 = *(*src)++;
250 lr2 = (int16_t)lr2;
251 break;
252 case 0:
253 default:
254 /* mono = (L+R)/2 */
255 lr1 = *(*src)++;
256 lr1 = (int16_t)lr1 + (lr1 >> 16) + err;
257 err = lr1 & 1;
258 lr1 >>= 1;
259
260 lr2 = *(*src)++;
261 lr2 = (int16_t)lr2 + (lr2 >> 16) + err;
262 err = lr2 & 1;
263 lr2 >>= 1;
264 break; 211 break;
265 }
266 *(*dst)++ = htobe32((lr1 << 16) | (uint16_t)lr2);
267} /* sample_to_mono */
268 212
269static void chunk_to_aiff_format(uint32_t *src, uint32_t *dst) ICODE_ATTR; 213 /* First obtain output buffer; when available, get PCM data */
270static void chunk_to_aiff_format(uint32_t *src, uint32_t *dst) 214 switch (getbuf)
271{
272 if (num_channels == 1)
273 {
274 /* On big endian:
275 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr|
276 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
277 * |MMMMMMMMmmmmmmmm|MMMMMMMMmmmmmmmm|
278 *
279 * On little endian:
280 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
281 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
282 * |MMMMMMMMmmmmmmmm|MMMMMMMMmmmmmmmm|
283 */
284 uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
285
286 do
287 {
288 sample_to_mono(&src, &dst);
289 sample_to_mono(&src, &dst);
290 sample_to_mono(&src, &dst);
291 sample_to_mono(&src, &dst);
292 sample_to_mono(&src, &dst);
293 sample_to_mono(&src, &dst);
294 sample_to_mono(&src, &dst);
295 sample_to_mono(&src, &dst);
296 }
297 while (src < src_end);
298 }
299 else
300 {
301#ifdef ROCKBOX_BIG_ENDIAN
302 /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
303 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr|
304 */
305 ci->memcpy(dst, src, PCM_CHUNK_SIZE);
306#else
307 /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
308 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr|
309 */
310 uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
311
312 do
313 { 215 {
314 *dst++ = swap_odd_even32(*src++); 216 case GETBUF_ENC:
315 *dst++ = swap_odd_even32(*src++); 217 if (!(data = ci->enc_encbuf_get_buffer(frame_size)))
316 *dst++ = swap_odd_even32(*src++); 218 continue;
317 *dst++ = swap_odd_even32(*src++); 219 getbuf = GETBUF_PCM;
318 *dst++ = swap_odd_even32(*src++); 220 case GETBUF_PCM:
319 *dst++ = swap_odd_even32(*src++); 221 if (!ci->enc_pcmbuf_read(data->data, PCM_SAMP_PER_CHUNK))
320 *dst++ = swap_odd_even32(*src++); 222 continue;
321 *dst++ = swap_odd_even32(*src++); 223 getbuf = GETBUF_ENC;
322 } 224 }
323 while (src < src_end);
324#endif
325 }
326} /* chunk_to_aiff_format */
327 225
328static bool init_encoder(void) 226 data->hdr.size = frame_size;
329{ 227 data->pcm_count = PCM_SAMP_PER_CHUNK;
330 struct enc_inputs inputs;
331 struct enc_parameters params;
332
333 if (ci->enc_get_inputs == NULL ||
334 ci->enc_set_parameters == NULL ||
335 ci->enc_get_chunk == NULL ||
336 ci->enc_finish_chunk == NULL ||
337 ci->enc_get_pcm_data == NULL )
338 return false;
339
340 ci->enc_get_inputs(&inputs);
341
342 if (inputs.config->afmt != AFMT_AIFF)
343 return false;
344
345 sample_rate = inputs.sample_rate;
346 num_channels = inputs.num_channels;
347 rec_mono_mode = inputs.rec_mono_mode;
348 err = 0;
349
350 /* configure the buffer system */
351 params.afmt = AFMT_AIFF;
352 enc_size = PCM_CHUNK_SIZE*inputs.num_channels / 2;
353 params.chunk_size = enc_size;
354 params.enc_sample_rate = sample_rate;
355 params.reserve_bytes = 0;
356 params.events_callback = enc_events_callback;
357 ci->enc_set_parameters(&params);
358
359 return true;
360} /* init_encoder */
361 228
362/* this is the codec entry point */ 229 frame_htobe((uint32_t *)data->data, frame_size);
363enum codec_status codec_main(enum codec_entry_call_reason reason) 230
364{ 231 ci->enc_pcmbuf_advance(PCM_SAMP_PER_CHUNK);
365 if (reason == CODEC_LOAD) { 232 ci->enc_encbuf_finish_buffer();
366 if (!init_encoder())
367 return CODEC_ERROR;
368 }
369 else if (reason == CODEC_UNLOAD) {
370 /* reset parameters to initial state */
371 ci->enc_set_parameters(NULL);
372 } 233 }
373 234
374 return CODEC_OK; 235 return CODEC_OK;
375} 236}
376 237
377/* this is called for each file to process */ 238/* this is called by recording system */
378enum codec_status codec_run(void) 239int ICODE_ATTR enc_callback(enum enc_callback_reason reason,
240 void *params)
379{ 241{
380 /* main encoding loop */ 242 if (LIKELY(reason == ENC_CB_STREAM))
381 while (ci->get_command(NULL) != CODEC_ACTION_HALT)
382 { 243 {
383 uint32_t *src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE); 244 switch (((union enc_chunk_hdr *)params)->type)
384 struct enc_chunk_hdr *chunk; 245 {
385 246 case CHUNK_T_DATA:
386 if (src == NULL) 247 return on_stream_data(params);
387 continue; 248 case CHUNK_T_STREAM_START:
388 249 return on_stream_start();
389 chunk = ci->enc_get_chunk(); 250 case CHUNK_T_STREAM_END:
390 chunk->enc_size = enc_size; 251 return on_stream_end(params);
391 chunk->num_pcm = PCM_SAMP_PER_CHUNK; 252 }
392 chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); 253 }
393 254 else if (reason == ENC_CB_INPUTS)
394 chunk_to_aiff_format(src, (uint32_t *)chunk->enc_data); 255 {
395 256 struct enc_inputs *inputs = params;
396 ci->enc_finish_chunk(); 257 sample_rate = inputs->sample_rate;
258 num_channels = inputs->num_channels;
259 frame_size = PCM_SAMP_PER_CHUNK*PCM_DEPTH_BYTES*num_channels;
397 } 260 }
398 261
399 return CODEC_OK; 262 return 0;
400} 263}
diff --git a/lib/rbcodec/codecs/codecs.h b/lib/rbcodec/codecs/codecs.h
index ae4233b7a6..672b1ded53 100644
--- a/lib/rbcodec/codecs/codecs.h
+++ b/lib/rbcodec/codecs/codecs.h
@@ -36,7 +36,7 @@
36#endif 36#endif
37#if (CONFIG_CODEC == SWCODEC) 37#if (CONFIG_CODEC == SWCODEC)
38#ifdef HAVE_RECORDING 38#ifdef HAVE_RECORDING
39#include "pcm_record.h" 39#include "enc_base.h"
40#endif 40#endif
41#include "dsp_core.h" 41#include "dsp_core.h"
42#include "dsp_misc.h" 42#include "dsp_misc.h"
@@ -72,12 +72,12 @@
72#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */ 72#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */
73 73
74/* increase this every time the api struct changes */ 74/* increase this every time the api struct changes */
75#define CODEC_API_VERSION 45 75#define CODEC_API_VERSION 46
76 76
77/* update this to latest version if a change to the api struct breaks 77/* update this to latest version if a change to the api struct breaks
78 backwards compatibility (and please take the opportunity to sort in any 78 backwards compatibility (and please take the opportunity to sort in any
79 new function which are "waiting" at the end of the function table) */ 79 new function which are "waiting" at the end of the function table) */
80#define CODEC_MIN_API_VERSION 45 80#define CODEC_MIN_API_VERSION 46
81 81
82/* reasons for calling codec main entrypoint */ 82/* reasons for calling codec main entrypoint */
83enum codec_entry_call_reason { 83enum codec_entry_call_reason {
@@ -96,6 +96,9 @@ enum codec_command_action {
96 CODEC_ACTION_HALT = -1, 96 CODEC_ACTION_HALT = -1,
97 CODEC_ACTION_NULL = 0, 97 CODEC_ACTION_NULL = 0,
98 CODEC_ACTION_SEEK_TIME = 1, 98 CODEC_ACTION_SEEK_TIME = 1,
99#ifdef HAVE_RECORDING
100 CODEC_ACTION_STREAM_FINISH = 2,
101#endif
99}; 102};
100 103
101/* NOTE: To support backwards compatibility, only add new functions at 104/* NOTE: To support backwards compatibility, only add new functions at
@@ -200,24 +203,18 @@ struct codec_api {
200#endif 203#endif
201 204
202#ifdef HAVE_RECORDING 205#ifdef HAVE_RECORDING
203 void (*enc_get_inputs)(struct enc_inputs *inputs); 206 int (*enc_pcmbuf_read)(void *buf, int count);
204 void (*enc_set_parameters)(struct enc_parameters *params); 207 int (*enc_pcmbuf_advance)(int count);
205 struct enc_chunk_hdr * (*enc_get_chunk)(void); 208 struct enc_chunk_data * (*enc_encbuf_get_buffer)(size_t need);
206 void (*enc_finish_chunk)(void); 209 void (*enc_encbuf_finish_buffer)(void);
207 unsigned char * (*enc_get_pcm_data)(size_t size); 210 ssize_t (*enc_stream_read)(void *buf, size_t count);
208 size_t (*enc_unget_pcm_data)(size_t size); 211 off_t (*enc_stream_lseek)(off_t offset, int whence);
209 212 ssize_t (*enc_stream_write)(const void *buf, size_t count);
210 /* file */
211 int (*open)(const char* pathname, int flags, ...);
212 int (*close)(int fd);
213 ssize_t (*read)(int fd, void* buf, size_t count);
214 off_t (*lseek)(int fd, off_t offset, int whence);
215 ssize_t (*write)(int fd, const void* buf, size_t count);
216 int (*round_value_to_list32)(unsigned long value, 213 int (*round_value_to_list32)(unsigned long value,
217 const unsigned long list[], 214 const unsigned long list[],
218 int count, 215 int count,
219 bool signd); 216 bool signd);
220#endif 217#endif /* HAVE_RECORDING */
221 218
222 /* new stuff at the end, sort into place next time 219 /* new stuff at the end, sort into place next time
223 the API gets incompatible */ 220 the API gets incompatible */
@@ -229,6 +226,7 @@ struct codec_header {
229 enum codec_status(*entry_point)(enum codec_entry_call_reason reason); 226 enum codec_status(*entry_point)(enum codec_entry_call_reason reason);
230 enum codec_status(*run_proc)(void); 227 enum codec_status(*run_proc)(void);
231 struct codec_api **api; 228 struct codec_api **api;
229 void * rec_extension[]; /* extension for encoders */
232}; 230};
233 231
234#ifdef CODEC 232#ifdef CODEC
@@ -249,7 +247,7 @@ extern unsigned char plugin_end_addr[];
249 __attribute__ ((section (".header")))= { \ 247 __attribute__ ((section (".header")))= { \
250 { CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, \ 248 { CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, \
251 plugin_start_addr, plugin_end_addr }, codec_start, \ 249 plugin_start_addr, plugin_end_addr }, codec_start, \
252 codec_run, &ci }; 250 codec_run, &ci, { enc_callback } };
253 251
254#else /* def SIMULATOR */ 252#else /* def SIMULATOR */
255/* decoders */ 253/* decoders */
@@ -262,7 +260,7 @@ extern unsigned char plugin_end_addr[];
262#define CODEC_ENC_HEADER \ 260#define CODEC_ENC_HEADER \
263 const struct codec_header __header = { \ 261 const struct codec_header __header = { \
264 { CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \ 262 { CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \
265 codec_start, codec_run, &ci }; 263 codec_start, codec_run, &ci, { enc_callback } };
266#endif /* SIMULATOR */ 264#endif /* SIMULATOR */
267#endif /* CODEC */ 265#endif /* CODEC */
268 266
@@ -277,12 +275,19 @@ void *codec_get_buffer_callback(size_t *size);
277int codec_load_buf(int hid, struct codec_api *api); 275int codec_load_buf(int hid, struct codec_api *api);
278int codec_load_file(const char* codec, struct codec_api *api); 276int codec_load_file(const char* codec, struct codec_api *api);
279int codec_run_proc(void); 277int codec_run_proc(void);
280int codec_halt(void);
281int codec_close(void); 278int codec_close(void);
279#if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING)
280enc_callback_t codec_get_enc_callback(void);
281#else
282#define codec_get_enc_callback() NULL
283#endif
282 284
283/* defined by the codec */ 285/* defined by the codec */
284enum codec_status codec_start(enum codec_entry_call_reason reason); 286enum codec_status codec_start(enum codec_entry_call_reason reason);
285enum codec_status codec_main(enum codec_entry_call_reason reason); 287enum codec_status codec_main(enum codec_entry_call_reason reason);
286enum codec_status codec_run(void); 288enum codec_status codec_run(void);
289#if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING)
290int enc_callback(enum enc_callback_reason reason, void *params);
291#endif
287 292
288#endif /* _CODECS_H_ */ 293#endif /* _CODECS_H_ */
diff --git a/lib/rbcodec/codecs/mp3_enc.c b/lib/rbcodec/codecs/mp3_enc.c
index 000eedd849..a349f99f25 100644
--- a/lib/rbcodec/codecs/mp3_enc.c
+++ b/lib/rbcodec/codecs/mp3_enc.c
@@ -8,6 +8,7 @@
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2006 Antonius Hellmann 10 * Copyright (C) 2006 Antonius Hellmann
11 * Copyright (C) 2006-2013 Michael Sevakis
11 * 12 *
12 * This program is free software; you can redistribute it and/or 13 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 14 * modify it under the terms of the GNU General Public License
@@ -37,17 +38,9 @@
37 38
38CODEC_ENC_HEADER 39CODEC_ENC_HEADER
39 40
40#define ENC_PADDING_FRAMES1 2 41#define SAMPL2 576
41#define ENC_PADDING_FRAMES2 4 42#define SBLIMIT 32
42#define ENC_DELAY_SAMP 576 43#define HTN 16
43#define ENC_DELAY_SIZE (ENC_DELAY_SAMP*4)
44#define SAMP_PER_FRAME1 1152
45#define SAMP_PER_FRAME2 576
46#define PCM_CHUNK_SIZE1 (SAMP_PER_FRAME1*4)
47#define PCM_CHUNK_SIZE2 (SAMP_PER_FRAME2*4)
48#define SAMPL2 576
49#define SBLIMIT 32
50#define HTN 16
51#define memcpy ci->memcpy 44#define memcpy ci->memcpy
52#define memset ci->memset 45#define memset ci->memset
53#define putlong(c, s) if(s+sz <= 32) { cc = (cc << s) | c; sz+= s; } \ 46#define putlong(c, s) if(s+sz <= 32) { cc = (cc << s) | c; sz+= s; } \
@@ -79,18 +72,24 @@ typedef struct {
79} side_info_t; 72} side_info_t;
80 73
81typedef struct { 74typedef struct {
82 side_info_t cod_info[2][2]; 75 side_info_t cod_info[2][2];
83 mpeg_t mpg; 76 mpeg_t mpg;
84 long frac_per_frame; 77 long frac_per_frame;
85 long byte_per_frame; 78 long byte_per_frame;
86 long slot_lag; 79 long req_byte_per_frame;
87 int sideinfo_len; 80 long slot_lag;
88 int mean_bits; 81 int sideinfo_len;
89 int ResvSize; 82 int mean_bits;
90 int channels; 83 int ResvSize;
91 int rec_mono_mode; 84 int channels;
92 int granules; 85 int granules;
93 long samplerate; 86 long src_samplerate;
87 long samplerate;
88 short *samp_buffer;
89 unsigned samp_per_frame;
90 int flush_frames;
91 int delay;
92 int padding;
94} config_t; 93} config_t;
95 94
96typedef struct { 95typedef struct {
@@ -118,7 +117,8 @@ struct huffcodebig {
118#define shft_n(x,n) ((x) >> n) 117#define shft_n(x,n) ((x) >> n)
119#define SQRT 724 /* sqrt(2) * 512 */ 118#define SQRT 724 /* sqrt(2) * 512 */
120 119
121static short mfbuf [2*(1152+512)] IBSS_ATTR; /* 3328 Bytes */ 120static short mfbuf [2*(1152+512)] IBSS_ATTR
121 /* for memcpy and 32-bit access */ MEM_ALIGN_ATTR; /* 3328 Bytes */
122static int sb_data [2][2][18][SBLIMIT] IBSS_ATTR; /* 13824 Bytes */ 122static int sb_data [2][2][18][SBLIMIT] IBSS_ATTR; /* 13824 Bytes */
123static int mdct_freq [SAMPL2] IBSS_ATTR; /* 2304 Bytes */ 123static int mdct_freq [SAMPL2] IBSS_ATTR; /* 2304 Bytes */
124static char mdct_sign [SAMPL2] IBSS_ATTR; /* 576 Bytes */ 124static char mdct_sign [SAMPL2] IBSS_ATTR; /* 576 Bytes */
@@ -171,12 +171,7 @@ static uint8_t t16l [256] IBSS_ATTR;
171static uint8_t t24l [256] IBSS_ATTR; 171static uint8_t t24l [256] IBSS_ATTR;
172static struct huffcodetab ht [HTN] IBSS_ATTR; 172static struct huffcodetab ht [HTN] IBSS_ATTR;
173 173
174static unsigned pcm_chunk_size IBSS_ATTR;
175static unsigned samp_per_frame IBSS_ATTR;
176
177static config_t cfg IBSS_ATTR; 174static config_t cfg IBSS_ATTR;
178static char *res_buffer;
179static int32_t err IBSS_ATTR;
180static uint8_t band_scale_f[22]; 175static uint8_t band_scale_f[22];
181 176
182static const uint8_t ht_count_const[2][2][16] = 177static const uint8_t ht_count_const[2][2][16] =
@@ -848,42 +843,56 @@ static int count_bit1 ( short *ix, uint32_t start, uint32_t end, int *bits );
848static int count_bigv ( short *ix, uint32_t start, uint32_t end, int table0, int table1, 843static int count_bigv ( short *ix, uint32_t start, uint32_t end, int table0, int table1,
849 int *bits); 844 int *bits);
850 845
846static inline uint32_t encodeHeader( int padding, long bitr_id )
847{
848 /*
849 * MPEG header layout:
850 * AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM
851 * A (31-21) = frame sync
852 * B (20-19) = MPEG type
853 * C (18-17) = MPEG layer
854 * D (16) = protection bit
855 * E (15-12) = bitrate index
856 * F (11-10) = samplerate index
857 * G (9) = padding bit
858 * H (8) = private bit
859 * I (7-6) = channel mode
860 * J (5-4) = mode extension (jstereo only)
861 * K (3) = copyright bit
862 * L (2) = original
863 * M (1-0) = emphasis
864 */
865 return (0xffe00000 ) /* frame sync (AAAAAAAAA AAA) */
866 | (0x2 << 19) /* mp3 type (upper): 1 (BB) */
867 | (cfg.mpg.type << 19)
868 | (0x1 << 17) /* mp3 layer: 01 (CC) */
869 | (0x1 << 16) /* mp3 crc: 1 (D) */
870 | (bitr_id << 12)
871 | (cfg.mpg.smpl_id << 10)
872 | (padding << 9)
873 | (cfg.mpg.mode << 6)
874 | (0x1 << 2); /* mp3 org: 1 (L) */
875 /* no emphasis (bits 0-1) */
876}
877
878static long calcFrameSize(int bitr_id, long *frac)
879{
880 unsigned long v = bitr_index[cfg.mpg.type][bitr_id];
881 v = SAMPL2 * 16000 * v / (2 - cfg.mpg.type);
882 v /= cfg.samplerate;
883
884 if (frac)
885 *frac = v % 64;
886
887 return v / 64;
851 888
889}
852static void encodeSideInfo( side_info_t si[2][2] ) 890static void encodeSideInfo( side_info_t si[2][2] )
853{ 891{
854 int gr, ch, header; 892 int gr, ch;
855 uint32_t cc=0, sz=0; 893 uint32_t cc=0, sz=0;
856 894
857 /* 895 putbits( encodeHeader( cfg.mpg.padding, cfg.mpg.bitr_id ), 32 );
858 * MPEG header layout:
859 * AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM
860 * A (31-21) = frame sync
861 * B (20-19) = MPEG type
862 * C (18-17) = MPEG layer
863 * D (16) = protection bit
864 * E (15-12) = bitrate index
865 * F (11-10) = samplerate index
866 * G (9) = padding bit
867 * H (8) = private bit
868 * I (7-6) = channel mode
869 * J (5-4) = mode extension (jstereo only)
870 * K (3) = copyright bit
871 * L (2) = original
872 * M (1-0) = emphasis
873 */
874
875 header = (0xfff00000) | /* frame sync (AAAAAAAAA AAA)
876 mp3 type (upper): 1 (B) */
877 (0x01 << 17) | /* mp3 layer: 01 (CC) */
878 ( 0x1 << 16) | /* mp3 crc: 1 (D) */
879 ( 0x1 << 2); /* mp3 org: 1 (L) */
880 header |= cfg.mpg.type << 19;
881 header |= cfg.mpg.bitr_id << 12;
882 header |= cfg.mpg.smpl_id << 10;
883 header |= cfg.mpg.padding << 9;
884 header |= cfg.mpg.mode << 6;
885 /* no emphasis (bits 0-1) */
886 putbits( header, 32 );
887 896
888 if(cfg.mpg.type == 1) 897 if(cfg.mpg.type == 1)
889 { /* MPEG1 */ 898 { /* MPEG1 */
@@ -1501,8 +1510,8 @@ static void iteration_loop(int *xr, side_info_t *si, int gr_cnt)
1501 1510
1502 1511
1503/* returns sum_j=0^31 a[j]*cos(PI*j*(k+1/2)/32), 0<=k<32 */ 1512/* returns sum_j=0^31 a[j]*cos(PI*j*(k+1/2)/32), 0<=k<32 */
1504void window_subband1(short *wk, int sb0[SBLIMIT], int sb1[SBLIMIT]) ICODE_ATTR; 1513static void ICODE_ATTR window_subband1(short *wk, int sb0[SBLIMIT],
1505void window_subband1(short *wk, int sb0[SBLIMIT], int sb1[SBLIMIT]) 1514 int sb1[SBLIMIT])
1506{ 1515{
1507 int k, i, u, v; 1516 int k, i, u, v;
1508 short *wp, *x1, *x2; 1517 short *wp, *x1, *x2;
@@ -1761,8 +1770,7 @@ void window_subband1(short *wk, int sb0[SBLIMIT], int sb1[SBLIMIT])
1761#endif 1770#endif
1762} 1771}
1763 1772
1764void window_subband2(short *x1, int a[SBLIMIT]) ICODE_ATTR; 1773static void ICODE_ATTR window_subband2(short *x1, int a[SBLIMIT])
1765void window_subband2(short *x1, int a[SBLIMIT])
1766{ 1774{
1767 int xr; 1775 int xr;
1768 short *wp = enwindow; 1776 short *wp = enwindow;
@@ -1879,8 +1887,7 @@ void window_subband2(short *x1, int a[SBLIMIT])
1879 xr = a[29]; a[29] += a[ 2]; a[ 2] -= xr; 1887 xr = a[29]; a[29] += a[ 2]; a[ 2] -= xr;
1880} 1888}
1881 1889
1882void mdct_long(int *out, int *in) ICODE_ATTR; 1890static void ICODE_ATTR mdct_long(int *out, int *in)
1883void mdct_long(int *out, int *in)
1884{ 1891{
1885 int ct,st; 1892 int ct,st;
1886 int tc1, tc2, tc3, tc4, ts5, ts6, ts7, ts8; 1893 int tc1, tc2, tc3, tc4, ts5, ts6, ts7, ts8;
@@ -1969,44 +1976,51 @@ static int find_samplerate_index(long freq, int *mp3_type)
1969 return i; 1976 return i;
1970} 1977}
1971 1978
1972static bool init_mp3_encoder_engine(int sample_rate, 1979static void mp3_encoder_reset(void)
1973 int num_channels, 1980{
1974 int rec_mono_mode, 1981 memset(&cfg.cod_info, 0, sizeof(cfg.cod_info));
1975 struct encoder_config *enc_cfg) 1982 memset(mfbuf , 0, sizeof(mfbuf ));
1983 memset(mdct_freq , 0, sizeof(mdct_freq ));
1984 memset(enc_data , 0, sizeof(enc_data ));
1985 memset(sb_data , 0, sizeof(sb_data ));
1986 memset(&CodedData , 0, sizeof(CodedData ));
1987 cfg.slot_lag = 0;
1988}
1989
1990static void mp3_encoder_init(unsigned long sample_rate, int num_channels,
1991 unsigned long bitrate)
1976{ 1992{
1977 const bool stereo = num_channels > 1; 1993 mp3_encoder_reset();
1978 uint32_t avg_byte_per_frame; 1994
1979 1995 const bool stereo = num_channels > 1;
1980 cfg.channels = stereo ? 2 : 1; 1996 cfg.channels = stereo ? 2 : 1;
1981 cfg.rec_mono_mode = rec_mono_mode; 1997 cfg.mpg.mode = stereo ? 0 : 3; /* 0=stereo, 3=mono */
1982 cfg.mpg.mode = stereo ? 0 : 3; /* 0=stereo, 3=mono */ 1998 cfg.mpg.smpl_id = find_samplerate_index(sample_rate, &cfg.mpg.type);
1983 cfg.mpg.smpl_id = find_samplerate_index(sample_rate, &cfg.mpg.type); 1999 cfg.samplerate = sampr_index[cfg.mpg.type][cfg.mpg.smpl_id];
1984 cfg.samplerate = sampr_index[cfg.mpg.type][cfg.mpg.smpl_id]; 2000 cfg.src_samplerate = sample_rate;
1985 cfg.mpg.bitr_id = find_bitrate_index(cfg.mpg.type, 2001 cfg.mpg.bitr_id = find_bitrate_index(cfg.mpg.type, bitrate, stereo);
1986 enc_cfg->mp3_enc.bitrate, 2002 cfg.mpg.bitrate = bitr_index[cfg.mpg.type][cfg.mpg.bitr_id];
1987 stereo); 2003 cfg.mpg.num_bands = num_bands[stereo ? cfg.mpg.type : 2][cfg.mpg.bitr_id];
1988 cfg.mpg.bitrate = bitr_index[cfg.mpg.type][cfg.mpg.bitr_id];
1989 cfg.mpg.num_bands = num_bands[stereo ? cfg.mpg.type : 2][cfg.mpg.bitr_id];
1990 2004
1991 if (cfg.mpg.type == 1) 2005 if (cfg.mpg.type == 1)
1992 { 2006 {
1993 cfg.granules = 2; 2007 cfg.granules = 2;
1994 pcm_chunk_size = PCM_CHUNK_SIZE1; 2008 cfg.samp_per_frame = 1152;
1995 samp_per_frame = SAMP_PER_FRAME1; 2009 cfg.flush_frames = 2;
1996 } 2010 }
1997 else 2011 else
1998 { 2012 {
1999 cfg.granules = 1; 2013 cfg.granules = 1;
2000 pcm_chunk_size = PCM_CHUNK_SIZE2; 2014 cfg.samp_per_frame = 576;
2001 samp_per_frame = SAMP_PER_FRAME2; 2015 cfg.flush_frames = 3;
2002 } 2016 }
2003 2017
2018 cfg.delay = 576-16;
2019 cfg.padding = 3*576+16;
2020
2021 cfg.samp_buffer = mfbuf + 2*512;
2022
2004 memcpy(scalefac, sfBand[cfg.mpg.smpl_id + 3*cfg.mpg.type], sizeof(scalefac)); 2023 memcpy(scalefac, sfBand[cfg.mpg.smpl_id + 3*cfg.mpg.type], sizeof(scalefac));
2005 memset(mfbuf , 0 , sizeof(mfbuf ));
2006 memset(mdct_freq , 0 , sizeof(mdct_freq ));
2007 memset(enc_data , 0 , sizeof(enc_data ));
2008 memset(sb_data , 0 , sizeof(sb_data ));
2009 memset(&CodedData, 0 , sizeof(CodedData ));
2010 memcpy(ca , ca_const , sizeof(ca )); 2024 memcpy(ca , ca_const , sizeof(ca ));
2011 memcpy(cs , cs_const , sizeof(cs )); 2025 memcpy(cs , cs_const , sizeof(cs ));
2012 memcpy(cx , cx_const , sizeof(cx )); 2026 memcpy(cx , cx_const , sizeof(cx ));
@@ -2052,6 +2066,7 @@ static bool init_mp3_encoder_engine(int sample_rate,
2052 memcpy(t16l , t16l_const , sizeof(t16l )); 2066 memcpy(t16l , t16l_const , sizeof(t16l ));
2053 memcpy(t24l , t24l_const , sizeof(t24l )); 2067 memcpy(t24l , t24l_const , sizeof(t24l ));
2054 memcpy(ht , ht_const , sizeof(ht )); 2068 memcpy(ht , ht_const , sizeof(ht ));
2069 memset(band_scale_f, 0 , sizeof(band_scale_f));
2055 2070
2056 ht[ 0].table = NULL; ht[ 0].hlen = NULL; /* Apparently not used */ 2071 ht[ 0].table = NULL; ht[ 0].hlen = NULL; /* Apparently not used */
2057 ht[ 1].table = t1HB; ht[ 1].hlen = t1l; 2072 ht[ 1].table = t1HB; ht[ 1].hlen = t1l;
@@ -2071,90 +2086,14 @@ static bool init_mp3_encoder_engine(int sample_rate,
2071 ht[15].table = t15HB; ht[15].hlen = t15l; 2086 ht[15].table = t15HB; ht[15].hlen = t15l;
2072 2087
2073 /* Figure average number of 'bytes' per frame */ 2088 /* Figure average number of 'bytes' per frame */
2074 avg_byte_per_frame = SAMPL2 * 16000 * cfg.mpg.bitrate / (2 - cfg.mpg.type); 2089 cfg.byte_per_frame = calcFrameSize(cfg.mpg.bitr_id, &cfg.frac_per_frame);
2075 avg_byte_per_frame = avg_byte_per_frame / cfg.samplerate;
2076 cfg.byte_per_frame = avg_byte_per_frame / 64;
2077 cfg.frac_per_frame = avg_byte_per_frame & 63;
2078 cfg.slot_lag = 0;
2079 cfg.sideinfo_len = 32 + (cfg.mpg.type ? (cfg.channels == 1 ? 136 : 256) 2090 cfg.sideinfo_len = 32 + (cfg.mpg.type ? (cfg.channels == 1 ? 136 : 256)
2080 : (cfg.channels == 1 ? 72 : 136)); 2091 : (cfg.channels == 1 ? 72 : 136));
2081 2092
2082 return true; 2093 cfg.req_byte_per_frame = ALIGN_UP(cfg.byte_per_frame + 1,
2094 sizeof (uint32_t));
2083} 2095}
2084 2096
2085static inline void to_mono(uint16_t **samp)
2086{
2087 int16_t l = **samp;
2088 int16_t r = *(*samp+1);
2089 int32_t m;
2090
2091 switch(cfg.rec_mono_mode)
2092 {
2093 case 1:
2094 /* mono = L */
2095 m = l;
2096 break;
2097 case 2:
2098 /* mono = R */
2099 m = r;
2100 break;
2101 case 0:
2102 default:
2103 /* mono = (L+R)/2 */
2104 m = l + r + err;
2105 err = m & 1;
2106 m >>= 1;
2107 break;
2108 }
2109 *(*samp)++ = (uint16_t)m;
2110 *(*samp)++ = (uint16_t)m;
2111} /* to_mono */
2112
2113static void to_mono_mm(void) ICODE_ATTR;
2114static void to_mono_mm(void)
2115{
2116 /* |llllllllllllllll|rrrrrrrrrrrrrrrr| =>
2117 * |mmmmmmmmmmmmmmmm|mmmmmmmmmmmmmmmm|
2118 */
2119 uint16_t *samp = &mfbuf[2*512];
2120 uint16_t *samp_end = samp + 2*samp_per_frame;
2121
2122 do
2123 {
2124 to_mono(&samp);
2125 to_mono(&samp);
2126 to_mono(&samp);
2127 to_mono(&samp);
2128 to_mono(&samp);
2129 to_mono(&samp);
2130 to_mono(&samp);
2131 to_mono(&samp);
2132 }
2133 while (samp < samp_end);
2134} /* to_mono_mm */
2135
2136#ifdef ROCKBOX_LITTLE_ENDIAN
2137/* Swaps a frame to big endian */
2138static inline void byte_swap_frame32(uint32_t *dst, uint32_t *src,
2139 size_t size)
2140{
2141 uint32_t *src_end = SKIPBYTES(src, size);
2142
2143 do
2144 {
2145 *dst++ = swap32(*src++);
2146 *dst++ = swap32(*src++);
2147 *dst++ = swap32(*src++);
2148 *dst++ = swap32(*src++);
2149 *dst++ = swap32(*src++);
2150 *dst++ = swap32(*src++);
2151 *dst++ = swap32(*src++);
2152 *dst++ = swap32(*src++);
2153 }
2154 while(src < src_end);
2155} /* byte_swap_frame32 */
2156#endif /* ROCKBOX_LITTLE_ENDIAN */
2157
2158static void set_scale_facs(int *mdct_freq) 2097static void set_scale_facs(int *mdct_freq)
2159{ 2098{
2160 unsigned int i, is, ie, k, s; 2099 unsigned int i, is, ie, k, s;
@@ -2188,12 +2127,10 @@ static void set_scale_facs(int *mdct_freq)
2188 } 2127 }
2189} 2128}
2190 2129
2191static void encode_frame(char *buffer, struct enc_chunk_hdr *chunk) 2130static size_t ICODE_ATTR mp3_encoder_encode_frame(uint8_t *outbuf)
2192 ICODE_ATTR;
2193static void encode_frame(char *buffer, struct enc_chunk_hdr *chunk)
2194{ 2131{
2195 int gr, gr_cnt; 2132 int gr, gr_cnt;
2196 uint32_t max; 2133 uint32_t max;
2197 2134
2198 /* encode one mp3 frame in this loop */ 2135 /* encode one mp3 frame in this loop */
2199 CodedData.bitpos = 0; 2136 CodedData.bitpos = 0;
@@ -2211,28 +2148,6 @@ static void encode_frame(char *buffer, struct enc_chunk_hdr *chunk)
2211 - cfg.sideinfo_len) / cfg.granules / cfg.channels 2148 - cfg.sideinfo_len) / cfg.granules / cfg.channels
2212 - 42; // reserved for scale_facs 2149 - 42; // reserved for scale_facs
2213 2150
2214 /* shift out old samples */
2215 memcpy(mfbuf, mfbuf + 2*cfg.granules*576, 4*512);
2216
2217 if (chunk->flags & CHUNKF_START_FILE)
2218 {
2219 /* prefix silent samples for encoder delay */
2220 memset(mfbuf + 2*512, 0, ENC_DELAY_SIZE);
2221 /* read new samples to iram for further processing */
2222 memcpy(mfbuf + 2*512 + ENC_DELAY_SIZE/2,
2223 buffer, pcm_chunk_size - ENC_DELAY_SIZE);
2224 chunk->num_pcm = samp_per_frame - ENC_DELAY_SAMP;
2225 }
2226 else
2227 {
2228 /* read new samples to iram for further processing */
2229 memcpy(mfbuf + 2*512, buffer, pcm_chunk_size);
2230 chunk->num_pcm = samp_per_frame;
2231 }
2232
2233 if (cfg.channels == 1)
2234 to_mono_mm();
2235
2236 cfg.ResvSize = 0; 2151 cfg.ResvSize = 0;
2237 gr_cnt = cfg.granules * cfg.channels; 2152 gr_cnt = cfg.granules * cfg.channels;
2238 CodedData.bitpos = cfg.sideinfo_len; /* leave space for mp3 header */ 2153 CodedData.bitpos = cfg.sideinfo_len; /* leave space for mp3 header */
@@ -2366,264 +2281,398 @@ static void encode_frame(char *buffer, struct enc_chunk_hdr *chunk)
2366 } 2281 }
2367 } 2282 }
2368 2283
2369 chunk->enc_size = cfg.byte_per_frame + cfg.mpg.padding; 2284 /* shift out old samples */
2285 memmove(mfbuf, mfbuf + 2*cfg.granules*576, 4*512);
2370 2286
2371 /* finish this chunk by adding sideinfo header data */ 2287 /* finish this chunk by adding sideinfo header data */
2372 CodedData.bitpos = 0; 2288 CodedData.bitpos = 0;
2373 encodeSideInfo( cfg.cod_info ); 2289 encodeSideInfo( cfg.cod_info );
2374 2290
2375#ifdef ROCKBOX_BIG_ENDIAN 2291 long size = cfg.byte_per_frame + cfg.mpg.padding;
2376 /* copy chunk to enc_buffer */ 2292
2377 memcpy(chunk->enc_data, CodedData.bbuf, chunk->enc_size); 2293#ifdef ROCKBOX_LITTLE_ENDIAN
2294 /* convert frame to big endian */
2295 const uint32_t *src = CodedData.bbuf;
2296 uint32_t *dst = (uint32_t *)outbuf;
2297
2298 for(long i = 0; i < size; i += sizeof(uint32_t))
2299 *dst++ = swap32(*src++);
2378#else 2300#else
2379 /* swap frame to big endian */ 2301 memcpy(outbuf, CodedData.bbuf, size);
2380 byte_swap_frame32((uint32_t *)chunk->enc_data, CodedData.bbuf, chunk->enc_size); 2302#endif /* ROCKBOX_LITTLE_ENDIAN */
2381#endif
2382} /* encode_frame */
2383 2303
2384/* called very often - inline */ 2304 return size;
2385static inline bool is_file_data_ok(struct enc_file_event_data *filed) 2305}
2386{
2387 return filed->rec_file >= 0 && (long)filed->chunk->flags >= 0;
2388} /* is_event_ok */
2389 2306
2390static unsigned char mp3_data[16384] __attribute__((aligned(4)));
2391static unsigned int mp3_data_len; /* current data size in buffer */
2392 2307
2393/* called very often - inline */ 2308/*======== Codec section ========*/
2394static inline bool on_write_chunk(struct enc_file_event_data *data)
2395{
2396 if (!is_file_data_ok(data))
2397 return false;
2398 2309
2399 if (data->chunk->enc_data == NULL) 2310/* CRC code lovingly ripped from:
2400 { 2311 * github.com/CFR-maniac/lame/blob/master/libmp3lame/VbrTag.c */
2401#ifdef ROCKBOX_HAS_LOGF
2402 ci->logf("mp3 enc: NULL data");
2403#endif
2404 return true;
2405 }
2406 2312
2407 /* if current chunk doesn't fit => write collected data */ 2313/* Lookup table for fast CRC computation
2408 if (mp3_data_len + data->chunk->enc_size > sizeof(mp3_data)) 2314 * See 'crc_update_lookup'
2409 { 2315 * Uses the polynomial x^16+x^15+x^2+1 */
2410 if (ci->write(data->rec_file, mp3_data, 2316static const uint16_t crc16_lookup[256] ICONST_ATTR =
2411 mp3_data_len) != (ssize_t)mp3_data_len) 2317{
2412 return false; 2318 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
2319 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
2320 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
2321 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
2322 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
2323 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
2324 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
2325 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
2326 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
2327 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
2328 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
2329 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
2330 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
2331 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
2332 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
2333 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
2334 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
2335 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
2336 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
2337 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
2338 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
2339 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
2340 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
2341 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
2342 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
2343 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
2344 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
2345 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
2346 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
2347 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
2348 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
2349 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
2350};
2413 2351
2414 mp3_data_len = 0; 2352static ssize_t header_size;
2415 } 2353static unsigned int mp3_crc16;
2416 2354
2417 memcpy(mp3_data+mp3_data_len, data->chunk->enc_data, 2355/* fast CRC-16 computation - uses table crc16_lookup 8*/
2418 data->chunk->enc_size); 2356static inline unsigned int crc_update_lookup(unsigned int value,
2357 unsigned int crc)
2358{
2359 unsigned int tmp = crc ^ value;
2360 crc = (crc >> 8) ^ crc16_lookup[tmp & 0xff];
2361 return crc & 0xffff;
2362}
2419 2363
2420 mp3_data_len += data->chunk->enc_size; 2364/* Calculate position of 'Info' header */
2365static int get_info_offset(uint32_t header)
2366{
2367 uint32_t type = (header & (0x3 << 19)) >> 19;
2368 uint32_t mode = (header & (0x3 << 6)) >> 6;
2421 2369
2422 data->num_pcm_samples += data->chunk->num_pcm; 2370 return type == 3 ? (mode == 3 ? 21 : 36) : (mode == 3 ? 13 : 21);
2423 return true; 2371}
2424} /* on_write_chunk */
2425 2372
2426static bool on_start_file(struct enc_file_event_data *data) 2373/* Write very basic 'Info' header with delay, padding and a bit of
2374 * miscellaneous info. */
2375static bool write_info_header(bool first_encode)
2427{ 2376{
2428 if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0') 2377 ssize_t size = cfg.byte_per_frame;
2429 return false;
2430 2378
2431 data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666); 2379 /* By default the MP3 frame header for the info frame is the same as
2380 unpadded audio frames */
2381 uint32_t header = encodeHeader(0, cfg.mpg.bitr_id);
2432 2382
2433 if (data->rec_file < 0) 2383 int i = get_info_offset(header);
2434 return false;
2435 2384
2436 /* reset sample count */ 2385 if (i + 8 + 36 > size)
2437 data->num_pcm_samples = 0; 2386 {
2387 /* The default frame size too small so find the smallest one that
2388 may accomodate it by increasing the bit rate for this empty
2389 MP3 frame */
2390 int j;
2391 for (j = cfg.mpg.bitr_id + 1; j < 15; j++)
2392 {
2393 size = calcFrameSize(j, NULL);
2438 2394
2439 /* reset buffer write position */ 2395 if (size >= i + 8 + 36)
2440 mp3_data_len = 0; 2396 break;
2397 }
2441 2398
2442 return true; 2399 if (j >= 15)
2443} /* on_start_file */ 2400 {
2401 /* Shouldn't really happen but... */
2402 header_size = -1;
2403 return true;
2404 }
2444 2405
2445static bool on_end_file(struct enc_file_event_data *data) 2406 header = encodeHeader(0, j);
2446{ 2407 /* Info offset won't change */
2447 if (data->rec_file < 0) 2408 }
2448 return false; /* file already closed, nothing more we can do */
2449 2409
2450 /* write the remaining mp3_data */ 2410 uint8_t frame[size];
2451 if (ci->write(data->rec_file, mp3_data, mp3_data_len) 2411 memset(frame, 0, size);
2452 != (ssize_t)mp3_data_len)
2453 return false;
2454 2412
2455 /* reset buffer write position */ 2413 frame[0] = header >> 24;
2456 mp3_data_len = 0; 2414 frame[1] = header >> 16;
2415 frame[2] = header >> 8;
2416 frame[3] = header >> 0;
2457 2417
2458 /* always _try_ to write the file header, even on error */ 2418 /* 'Info' header (CBR 'Xing') */
2459 if (ci->close(data->rec_file) != 0) 2419 memcpy(&frame[i], "Info", 4);
2460 return false;
2461 2420
2462 data->rec_file = -1; 2421 /* flags = 0; Info contains no other sections and is 8 bytes */
2463 2422
2464 return true; 2423 /* Just mark the LAMEness to indicate header presence; we're not
2465} /* on_end_file */ 2424 actually _the_ LAME so 'rbshn' is the version we give */
2425 memcpy(&frame[i + 8], "LAMErbshn", 9);
2466 2426
2467static void on_rec_new_stream(struct enc_buffer_event_data *data) 2427 /* Fill-in some info about us
2468{ 2428 * reference: http://gabriel.mp3-tech.org/mp3infotag.html
2469 int num_frames = cfg.mpg.type == 1 ? 2429 */
2470 ENC_PADDING_FRAMES1 : ENC_PADDING_FRAMES2;
2471 2430
2472 if (data->flags & CHUNKF_END_FILE) 2431 /* Revision + VBR method:
2473 { 2432 * [7:4] = Revision (0 ??)
2474 /* add silent frames to end - encoder will also be flushed for start 2433 * [3:0] = VBR method (CBR)
2475 of next file if any */ 2434 */
2476 memset(res_buffer, 0, pcm_chunk_size); 2435 frame[i + 17] = (0 << 4) | (1 << 0);
2477 2436
2478 /* the initial chunk given for the end is at enc_wr_index */ 2437 /* If first frame since encoder reset is long gone (not unlikely in
2479 while (num_frames-- > 0) 2438 prerecording), then the delay is long passed and no trimming done
2480 { 2439 at the start */
2481 data->chunk->enc_data = ENC_CHUNK_SKIP_HDR(data->chunk->enc_data, 2440 unsigned int delay = first_encode ? cfg.delay : 0;
2482 data->chunk); 2441 unsigned int padding = cfg.padding;
2483 2442
2484 encode_frame(res_buffer, data->chunk); 2443 /* Delay and padding:
2485 data->chunk->num_pcm = samp_per_frame; 2444 * [23:12] = delay
2445 * [11: 0] = padding
2446 */
2447 frame[i + 29] = delay >> 4;
2448 frame[i + 30] = (delay << 4) | (padding >> 8);
2449 frame[i + 31] = padding;
2450
2451 /* Misc:
2452 * [7:6] = source frequency
2453 * [ 5] = unwise settings (of course not :)
2454 * [4:2] = stereo mode (mono or stereo)
2455 * [1:0] = noise shaping (who knows, 0)
2456 */
2457 uint8_t misc;
2486 2458
2487 ci->enc_finish_chunk(); 2459 if (cfg.src_samplerate <= 32000)
2488 data->chunk = ci->enc_get_chunk(); 2460 misc = (0 << 6);
2489 } 2461 else if (cfg.src_samplerate <= 44100)
2490 } 2462 misc = (1 << 6);
2491 else if (data->flags & CHUNKF_PRERECORD) 2463 else if (cfg.src_samplerate <= 48000)
2492 { 2464 misc = (2 << 6);
2493 /* nothing to add and we cannot change prerecorded data */ 2465 else /* > 48000 */
2494 } 2466 misc = (3 << 6);
2495 else if (data->flags & CHUNKF_START_FILE)
2496 {
2497 /* starting fresh ... be sure to flush encoder first */
2498 struct enc_chunk_hdr *chunk = ENC_CHUNK_HDR(res_buffer);
2499 2467
2500 chunk->flags = 0; 2468 if (cfg.channels > 1)
2501 chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); 2469 misc |= (1 << 2); /* Stereo */
2502 2470
2503 while (num_frames-- > 0) 2471 frame[i + 32] = misc;
2504 { 2472
2505 memset(chunk->enc_data, 0, pcm_chunk_size); 2473 if (ci->enc_stream_write(frame, size) != size)
2506 encode_frame(chunk->enc_data, chunk); 2474 {
2507 } 2475 ci->enc_stream_lseek(0, SEEK_SET);
2476 header_size = -1;
2477 return false;
2508 } 2478 }
2509} /* on_rec_new_stream */
2510 2479
2511static void enc_events_callback(enum enc_events event, void *data) 2480 header_size = size;
2481 return true;
2482}
2483
2484static inline int on_stream_data(struct enc_chunk_data *data)
2512{ 2485{
2513 switch (event) 2486 ssize_t size = data->hdr.size;
2487
2488 if (header_size > 0)
2514 { 2489 {
2515 case ENC_WRITE_CHUNK: 2490 /* Header is layed-down; keep running CRC of audio data */
2516 if (on_write_chunk((struct enc_file_event_data *)data)) 2491 uint8_t *p = data->data;
2517 return; 2492 uint8_t *p_end = p + size;
2518 2493
2519 break; 2494 while (p < p_end)
2495 mp3_crc16 = crc_update_lookup(*p++, mp3_crc16);
2496 }
2520 2497
2521 case ENC_START_FILE: 2498 if (ci->enc_stream_write(data->data, size) != size)
2522 if (on_start_file((struct enc_file_event_data *)data)) 2499 return -1;
2523 return;
2524 2500
2525 break; 2501 return 0;
2502}
2526 2503
2527 case ENC_END_FILE: 2504static int on_stream_start(struct enc_chunk_file *file)
2528 if (on_end_file((struct enc_file_event_data *)data)) 2505{
2529 return; 2506 mp3_crc16 = 0x0000;
2530 2507
2531 break; 2508 if (!write_info_header(file->hdr.aux0))
2509 return -1;
2532 2510
2533 case ENC_REC_NEW_STREAM: 2511 return 0;
2534 on_rec_new_stream((struct enc_buffer_event_data *)data); 2512}
2535 return;
2536 2513
2537 default: 2514static int on_stream_end(union enc_chunk_hdr *hdr)
2538 return; 2515{
2539 } 2516 ssize_t size = header_size;
2540 2517
2541 /* Something failed above. Signal error back to core. */ 2518 if (size <= 0)
2542 ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR; 2519 return 0; /* No header possible/none yet written */
2543} /* enc_events_callback */
2544 2520
2545static bool enc_init(void) 2521 /* Update audio CRC and header CRC */
2546{ 2522 uint8_t frame[size];
2547 struct enc_inputs inputs;
2548 struct enc_parameters params;
2549
2550 if (ci->enc_get_inputs == NULL ||
2551 ci->enc_set_parameters == NULL ||
2552 ci->enc_get_chunk == NULL ||
2553 ci->enc_finish_chunk == NULL ||
2554 ci->enc_get_pcm_data == NULL ||
2555 ci->enc_unget_pcm_data == NULL )
2556 return false;
2557 2523
2558 ci->enc_get_inputs(&inputs); 2524 /* Won't fail this since it could still be useable if some decoder
2525 plays loose with the CRC info (like Rockbox :) */
2526 if (ci->enc_stream_lseek(0, SEEK_SET) != 0 ||
2527 ci->enc_stream_read(frame, size) != size)
2528 return 0;
2559 2529
2560 if (inputs.config->afmt != AFMT_MPA_L3) 2530 uint32_t header = (frame[0] << 24) | (frame[1] << 16) |
2561 return false; 2531 (frame[2] << 8) | (frame[3] << 0);
2532 int i = get_info_offset(header); /* Get 'Info' header */
2562 2533
2563 init_mp3_encoder_engine(inputs.sample_rate, inputs.num_channels, 2534 /* 'Info' header = 8 bytes */
2564 inputs.rec_mono_mode, inputs.config);
2565 2535
2566 err = 0; 2536 /* Fill-in audio data CRC16 */
2567 2537
2568 /* configure the buffer system */ 2538 /* On error, fixing data CRC would require scanning file since it
2569 params.afmt = AFMT_MPA_L3; 2539 has probably dropped something we tried to write and the likely
2570 params.chunk_size = cfg.byte_per_frame + 1; 2540 reason is that the disk filled; just leave it 0 in that case. */
2571 params.enc_sample_rate = cfg.samplerate; 2541 if (!hdr->err)
2572 /* need enough reserved bytes to hold one frame of pcm samples + hdr 2542 {
2573 for padding and flushing */ 2543 frame[i + 40] = mp3_crc16 >> 8;
2574 params.reserve_bytes = ENC_CHUNK_HDR_SIZE + pcm_chunk_size; 2544 frame[i + 41] = mp3_crc16;
2575 params.events_callback = enc_events_callback; 2545 }
2576 ci->enc_set_parameters(&params);
2577 2546
2578 res_buffer = params.reserve_buffer; 2547 /* Fill-in header CRC16 */
2548 unsigned int hdr_crc16 = 0x0000;
2549 for (int j = 0; j < i + 42; j++)
2550 hdr_crc16 = crc_update_lookup(frame[j], hdr_crc16);
2579 2551
2580#ifdef CPU_COLDFIRE 2552 frame[i + 42] = hdr_crc16 >> 8;
2581 asm volatile ("move.l #0, %macsr"); /* integer mode */ 2553 frame[i + 43] = hdr_crc16;
2582#endif
2583 2554
2584 return true; 2555 /* Update file */
2585} /* enc_init */ 2556 if (ci->enc_stream_lseek(0, SEEK_SET) == 0)
2557 ci->enc_stream_write(frame, size);
2558
2559 return 0;
2560}
2586 2561
2587/* this is the codec entry point */ 2562/* this is the codec entry point */
2588enum codec_status codec_main(enum codec_entry_call_reason reason) 2563enum codec_status codec_main(enum codec_entry_call_reason reason)
2589{ 2564{
2590 if (reason == CODEC_LOAD) { 2565#ifdef CPU_COLDFIRE
2591 if (!enc_init()) 2566 if (reason == CODEC_LOAD)
2592 return CODEC_ERROR; 2567 asm volatile ("move.l #0, %macsr"); /* integer mode */
2593 } 2568#endif
2594 else if (reason == CODEC_UNLOAD) {
2595 /* reset parameters to initial state */
2596 ci->enc_set_parameters(NULL);
2597 }
2598
2599 return CODEC_OK; 2569 return CODEC_OK;
2570 (void)reason;
2600} 2571}
2601 2572
2602/* this is called for each file to process */ 2573/* this is called for each file to process */
2603enum codec_status codec_run(void) 2574enum codec_status codec_run(void)
2604{ 2575{
2576 mp3_encoder_reset();
2577 uint32_t first = 1;
2578
2579 /* Needs to do stream finishing steps to flush-out all samples */
2580 int frames_rem = -1; /* -1 = indeterminate */
2581
2582 enum { GETBUF_ENC, GETBUF_PCM } getbuf = GETBUF_ENC;
2583 struct enc_chunk_data *data = NULL;
2584
2605 /* main encoding loop */ 2585 /* main encoding loop */
2606 while(ci->get_command(NULL) != CODEC_ACTION_HALT) 2586 while (frames_rem)
2607 { 2587 {
2608 char *buffer = buffer = ci->enc_get_pcm_data(pcm_chunk_size); 2588 intptr_t param;
2609 struct enc_chunk_hdr *chunk; 2589 enum codec_command_action action = ci->get_command(&param);
2610 2590
2611 if(buffer == NULL) 2591 if (action != CODEC_ACTION_NULL)
2612 continue; 2592 {
2593 if (action != CODEC_ACTION_STREAM_FINISH)
2594 break;
2613 2595
2614 chunk = ci->enc_get_chunk(); 2596 if (frames_rem < 0)
2615 chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); 2597 frames_rem = cfg.flush_frames;
2616 2598
2617 encode_frame(buffer, chunk); 2599 /* Reply with required space */
2600 *(size_t *)param = cfg.req_byte_per_frame*frames_rem;
2601 }
2618 2602
2619 if (chunk->num_pcm < samp_per_frame) 2603 /* First obtain output buffer; when available, get PCM data */
2604 switch (getbuf)
2620 { 2605 {
2621 ci->enc_unget_pcm_data(pcm_chunk_size - chunk->num_pcm*4); 2606 case GETBUF_ENC:
2622 chunk->num_pcm = samp_per_frame; 2607 if (!(data = ci->enc_encbuf_get_buffer(cfg.req_byte_per_frame)))
2608 continue;
2609 getbuf = GETBUF_PCM;
2610 case GETBUF_PCM:
2611 if (LIKELY(frames_rem < 0))
2612 {
2613 /* Encoding audio */
2614 int count = cfg.samp_per_frame;
2615 if (!ci->enc_pcmbuf_read(cfg.samp_buffer, count))
2616 continue;
2617
2618 ci->enc_pcmbuf_advance(cfg.samp_per_frame);
2619
2620 if (cfg.channels == 1)
2621 {
2622 /* Interleave the mono samples to stereo as required by
2623 encoder */
2624 uint16_t *src = cfg.samp_buffer + count;
2625 uint32_t *dst = (uint32_t *)(src + count);
2626
2627 for (int i = count; i > 0; i--)
2628 { uint32_t s = *--src; *--dst = s | (s << 16); }
2629 }
2630 }
2631 else
2632 {
2633 /* Flushing encoder */
2634 memset(cfg.samp_buffer, 0, cfg.samp_per_frame*4);
2635 frames_rem--;
2636 }
2637 getbuf = GETBUF_ENC;
2623 } 2638 }
2624 2639
2625 ci->enc_finish_chunk(); 2640 data->hdr.aux0 = first;
2641 first = 0;
2642 data->hdr.size = mp3_encoder_encode_frame(data->data);
2643 data->pcm_count = cfg.samp_per_frame;
2644 ci->enc_encbuf_finish_buffer();
2626 } 2645 }
2627 2646
2628 return CODEC_OK; 2647 return CODEC_OK;
2629} 2648}
2649
2650/* this is called by recording system */
2651int ICODE_ATTR enc_callback(enum enc_callback_reason reason,
2652 void *params)
2653{
2654 if (LIKELY(reason == ENC_CB_STREAM))
2655 {
2656 switch (((union enc_chunk_hdr *)params)->type)
2657 {
2658 case CHUNK_T_DATA:
2659 return on_stream_data(params);
2660 case CHUNK_T_STREAM_START:
2661 return on_stream_start(params);
2662 case CHUNK_T_STREAM_END:
2663 return on_stream_end(params);
2664 }
2665 }
2666 else if (reason == ENC_CB_INPUTS)
2667 {
2668 struct enc_inputs *inputs = params;
2669
2670 mp3_encoder_init(inputs->sample_rate, inputs->num_channels,
2671 inputs->config->mp3_enc.bitrate);
2672
2673 /* Return the actual configuration */
2674 inputs->enc_sample_rate = cfg.samplerate;
2675 }
2676
2677 return 0;
2678}
diff --git a/lib/rbcodec/codecs/wav_enc.c b/lib/rbcodec/codecs/wav_enc.c
index 01d0f79bcf..71bb652374 100644
--- a/lib/rbcodec/codecs/wav_enc.c
+++ b/lib/rbcodec/codecs/wav_enc.c
@@ -8,6 +8,7 @@
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2006 Antonius Hellmann 10 * Copyright (C) 2006 Antonius Hellmann
11 * Copyright (C) 2006-2013 Michael Sevakis
11 * 12 *
12 * This program is free software; you can redistribute it and/or 13 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 14 * modify it under the terms of the GNU General Public License
@@ -40,12 +41,12 @@ struct riff_header
40 uint16_t block_align; /* 20h - num_channels*bits_per_samples/8 */ 41 uint16_t block_align; /* 20h - num_channels*bits_per_samples/8 */
41 uint16_t bits_per_sample; /* 22h - 8=8 bits, 16=16 bits, etc. */ 42 uint16_t bits_per_sample; /* 22h - 8=8 bits, 16=16 bits, etc. */
42 /* Not for audio_format=1 (PCM) */ 43 /* Not for audio_format=1 (PCM) */
43/* unsigned short extra_param_size; 24h - size of extra data */ 44/* uint16_t extra_param_size; 24h - size of extra data */
44/* unsigned char *extra_params; */ 45/* uint8_t extra_params[extra_param_size]; */
45 /* data header */ 46 /* data header */
46 uint8_t data_id[4]; /* 24h - "data" */ 47 uint8_t data_id[4]; /* 24h - "data" */
47 uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */ 48 uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */
48/* unsigned char *data; 2ch - actual sound data */ 49/* uint8_t data[data_size]; 2Ch - actual sound data */
49} __attribute__((packed)); 50} __attribute__((packed));
50 51
51#define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */ 52#define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */
@@ -55,19 +56,17 @@ struct riff_header
55#define PCM_DEPTH_BYTES 2 56#define PCM_DEPTH_BYTES 2
56#define PCM_DEPTH_BITS 16 57#define PCM_DEPTH_BITS 16
57#define PCM_SAMP_PER_CHUNK 2048 58#define PCM_SAMP_PER_CHUNK 2048
58#define PCM_CHUNK_SIZE (PCM_SAMP_PER_CHUNK*4)
59 59
60static int num_channels IBSS_ATTR; 60static int num_channels;
61static int rec_mono_mode IBSS_ATTR;
62static uint32_t sample_rate; 61static uint32_t sample_rate;
63static uint32_t enc_size; 62static size_t frame_size;
64static int32_t err IBSS_ATTR; 63static size_t data_size;
65 64
66static const struct riff_header riff_header = 65static const struct riff_header riff_template_header =
67{ 66{
68 /* "RIFF" header */ 67 /* "RIFF" header */
69 { 'R', 'I', 'F', 'F' }, /* riff_id */ 68 { 'R', 'I', 'F', 'F' }, /* riff_id */
70 0, /* riff_size (*) */ 69 0, /* riff_size (*) */
71 /* format header */ 70 /* format header */
72 { 'W', 'A', 'V', 'E' }, /* format */ 71 { 'W', 'A', 'V', 'E' }, /* format */
73 { 'f', 'm', 't', ' ' }, /* format_id */ 72 { 'f', 'm', 't', ' ' }, /* format_id */
@@ -82,305 +81,164 @@ static const struct riff_header riff_header =
82 /* data header */ 81 /* data header */
83 { 'd', 'a', 't', 'a' }, /* data_id */ 82 { 'd', 'a', 't', 'a' }, /* data_id */
84 0 /* data_size (*) */ 83 0 /* data_size (*) */
85 /* (*) updated during ENC_END_FILE event */ 84 /* (*) updated when finalizing stream */
86}; 85};
87 86
88/* called version often - inline */ 87static inline void frame_htole(uint32_t *p, size_t size)
89static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR;
90static inline bool is_file_data_ok(struct enc_file_event_data *data)
91{ 88{
92 return data->rec_file >= 0 && (long)data->chunk->flags >= 0; 89#ifdef ROCKBOX_BIG_ENDIAN
93} /* is_file_data_ok */ 90 /* Byte-swap samples, stereo or mono */
94 91 do
95/* called version often - inline */
96static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR;
97static inline bool on_write_chunk(struct enc_file_event_data *data)
98{
99 if (!is_file_data_ok(data))
100 return false;
101
102 if (data->chunk->enc_data == NULL)
103 { 92 {
104#ifdef ROCKBOX_HAS_LOGF 93 uint32_t t;
105 ci->logf("wav enc: NULL data"); 94 t = swap_odd_even32(*p); *p++ = t;
106#endif 95 t = swap_odd_even32(*p); *p++ = t;
107 return true; 96 t = swap_odd_even32(*p); *p++ = t;
97 t = swap_odd_even32(*p); *p++ = t;
98 t = swap_odd_even32(*p); *p++ = t;
99 t = swap_odd_even32(*p); *p++ = t;
100 t = swap_odd_even32(*p); *p++ = t;
101 t = swap_odd_even32(*p); *p++ = t;
108 } 102 }
103 while (size -= 8 * 2 * PCM_DEPTH_BYTES);
104#endif /* ROCKBOX_BIG_ENDIAN */
105 (void)p; (void)size;
106}
109 107
110 if (ci->write(data->rec_file, data->chunk->enc_data, 108static int on_stream_data(struct enc_chunk_data *data)
111 data->chunk->enc_size) != (ssize_t)data->chunk->enc_size)
112 return false;
113
114 data->num_pcm_samples += data->chunk->num_pcm;
115 return true;
116} /* on_write_chunk */
117
118static bool on_start_file(struct enc_file_event_data *data)
119{ 109{
120 if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0') 110 size_t size = data->hdr.size;
121 return false; 111
112 if (ci->enc_stream_write(data->data, size) != (ssize_t)size)
113 return -1;
122 114
123 data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666); 115 data_size += size;
124 116
125 if (data->rec_file < 0) 117 return 0;
126 return false; 118}
127 119
120static int on_stream_start(void)
121{
128 /* reset sample count */ 122 /* reset sample count */
129 data->num_pcm_samples = 0; 123 data_size = 0;
130 124
131 /* write template header */ 125 /* write template header */
132 if (ci->write(data->rec_file, &riff_header, sizeof (riff_header)) 126 if (ci->enc_stream_write(&riff_template_header, sizeof (struct riff_header))
133 != sizeof (riff_header)) 127 != sizeof (struct riff_header))
134 { 128 return -1;
135 return false;
136 }
137 129
138 data->new_enc_size += sizeof (riff_header); 130 return 0;
139 return true; 131}
140} /* on_start_file */
141 132
142static bool on_end_file(struct enc_file_event_data *data) 133static int on_stream_end(union enc_chunk_hdr *hdr)
143{ 134{
144 /* update template header */ 135 /* update template header */
145 struct riff_header hdr; 136 struct riff_header riff;
146 uint32_t data_size;
147
148 if (data->rec_file < 0)
149 return false; /* file already closed, nothing more we can do */
150 137
151 /* always _try_ to write the file header, even on error */ 138 if (hdr->err)
152 if ((ci->lseek(data->rec_file, 0, SEEK_SET)) ||
153 (ci->read(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr)))
154 { 139 {
155 return false; 140 /* Called for stream error; get correct data size */
141 ssize_t size = ci->enc_stream_lseek(0, SEEK_END);
142
143 if (size > (ssize_t)sizeof (riff))
144 data_size = size - sizeof (riff);
156 } 145 }
157 146
158 data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES; 147 if (ci->enc_stream_lseek(0, SEEK_SET) ||
148 ci->enc_stream_read(&riff, sizeof (riff)) != sizeof (riff))
149 return -1;
159 150
160 /* "RIFF" header */ 151 /* "RIFF" header */
161 hdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE + RIFF_FMT_DATA_SIZE 152 riff.riff_size = htole32(RIFF_FMT_HEADER_SIZE + RIFF_FMT_DATA_SIZE
162 + RIFF_DATA_HEADER_SIZE + data_size); 153 + RIFF_DATA_HEADER_SIZE + data_size);
163 154
164 /* format data */ 155 /* format data */
165 hdr.num_channels = htole16(num_channels); 156 riff.num_channels = htole16(num_channels);
166 hdr.sample_rate = htole32(sample_rate); 157 riff.sample_rate = htole32(sample_rate);
167 hdr.byte_rate = htole32(sample_rate*num_channels* PCM_DEPTH_BYTES); 158 riff.byte_rate = htole32(sample_rate*num_channels*PCM_DEPTH_BYTES);
168 hdr.block_align = htole16(num_channels*PCM_DEPTH_BYTES); 159 riff.block_align = htole16(num_channels*PCM_DEPTH_BYTES);
169 160
170 /* data header */ 161 /* data header */
171 hdr.data_size = htole32(data_size); 162 riff.data_size = htole32(data_size);
172 163
173 if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || 164 if (ci->enc_stream_lseek(0, SEEK_SET) != 0)
174 ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr) || 165 return -2;
175 ci->close(data->rec_file) != 0)
176 {
177 return false;
178 }
179 166
180 data->rec_file = -1; 167 if (ci->enc_stream_write(&riff, sizeof (riff)) != sizeof (riff))
168 return -3;
181 169
182 return true; 170 return 0;
183} /* on_end_file */ 171}
184 172
185static void enc_events_callback(enum enc_events event, void *data) 173/* this is the codec entry point */
186 ICODE_ATTR; 174enum codec_status codec_main(enum codec_entry_call_reason reason)
187static void enc_events_callback(enum enc_events event, void *data)
188{ 175{
189 switch (event) 176 return CODEC_OK;
190 { 177 (void)reason;
191 case ENC_WRITE_CHUNK: 178}
192 if (on_write_chunk((struct enc_file_event_data *)data))
193 return;
194
195 break;
196
197 case ENC_START_FILE:
198 if (on_start_file((struct enc_file_event_data *)data))
199 return;
200
201 break;
202
203 case ENC_END_FILE:
204 if (on_end_file((struct enc_file_event_data *)data))
205 return;
206
207 break;
208
209 default:
210 return;
211 }
212
213 /* Something failed above. Signal error back to core. */
214 ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR;
215} /* enc_events_callback */
216 179
217/* convert native pcm samples to wav format samples */ 180/* this is called for each file to process */
218static inline void sample_to_mono(uint32_t **src, uint32_t **dst) 181enum codec_status ICODE_ATTR codec_run(void)
219{ 182{
220 int32_t lr1, lr2; 183 enum { GETBUF_ENC, GETBUF_PCM } getbuf = GETBUF_ENC;
184 struct enc_chunk_data *data = NULL;
221 185
222 switch(rec_mono_mode) 186 /* main encoding loop */
187 while (1)
223 { 188 {
224 case 1: 189 enum codec_command_action action = ci->get_command(NULL);
225 /* mono = L */ 190
226 lr1 = *(*src)++; 191 if (action != CODEC_ACTION_NULL)
227 lr1 = lr1 >> 16;
228 lr2 = *(*src)++;
229 lr2 = lr2 >> 16;
230 break;
231 case 2:
232 /* mono = R */
233 lr1 = *(*src)++;
234 lr1 = (uint16_t)lr1;
235 lr2 = *(*src)++;
236 lr2 = (uint16_t)lr2;
237 break;
238 case 0:
239 default:
240 /* mono = (L+R)/2 */
241 lr1 = *(*src)++;
242 lr1 = (int16_t)lr1 + (lr1 >> 16) + err;
243 err = lr1 & 1;
244 lr1 >>= 1;
245
246 lr2 = *(*src)++;
247 lr2 = (int16_t)lr2 + (lr2 >> 16) + err;
248 err = lr2 & 1;
249 lr2 >>= 1;
250 break; 192 break;
251 }
252 *(*dst)++ = htole32((lr2 << 16) | (uint16_t)lr1);
253} /* sample_to_mono */
254 193
255static void chunk_to_wav_format(uint32_t *src, uint32_t *dst) ICODE_ATTR; 194 /* First obtain output buffer; when available, get PCM data */
256static void chunk_to_wav_format(uint32_t *src, uint32_t *dst) 195 switch (getbuf)
257{
258 if (num_channels == 1)
259 {
260 /* On big endian:
261 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr|
262 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
263 * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM|
264 *
265 * On little endian:
266 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
267 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
268 * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM|
269 */
270 uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
271
272 do
273 { 196 {
274 sample_to_mono(&src, &dst); 197 case GETBUF_ENC:
275 sample_to_mono(&src, &dst); 198 if (!(data = ci->enc_encbuf_get_buffer(frame_size)))
276 sample_to_mono(&src, &dst); 199 continue;
277 sample_to_mono(&src, &dst); 200 getbuf = GETBUF_PCM;
278 sample_to_mono(&src, &dst); 201 case GETBUF_PCM:
279 sample_to_mono(&src, &dst); 202 if (!ci->enc_pcmbuf_read(data->data, PCM_SAMP_PER_CHUNK))
280 sample_to_mono(&src, &dst); 203 continue;
281 sample_to_mono(&src, &dst); 204 getbuf = GETBUF_ENC;
282 } 205 }
283 while (src < src_end);
284 }
285 else
286 {
287#ifdef ROCKBOX_BIG_ENDIAN
288 /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
289 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
290 */
291 uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
292 206
293 do 207 data->hdr.size = frame_size;
294 { 208 data->pcm_count = PCM_SAMP_PER_CHUNK;
295 *dst++ = swap_odd_even32(*src++);
296 *dst++ = swap_odd_even32(*src++);
297 *dst++ = swap_odd_even32(*src++);
298 *dst++ = swap_odd_even32(*src++);
299 *dst++ = swap_odd_even32(*src++);
300 *dst++ = swap_odd_even32(*src++);
301 *dst++ = swap_odd_even32(*src++);
302 *dst++ = swap_odd_even32(*src++);
303 }
304 while (src < src_end);
305#else
306 /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
307 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
308 */
309 ci->memcpy(dst, src, PCM_CHUNK_SIZE);
310#endif
311 }
312} /* chunk_to_wav_format */
313 209
314static bool init_encoder(void) 210 frame_htole((uint32_t *)data->data, frame_size);
315{
316 struct enc_inputs inputs;
317 struct enc_parameters params;
318
319 if (ci->enc_get_inputs == NULL ||
320 ci->enc_set_parameters == NULL ||
321 ci->enc_get_chunk == NULL ||
322 ci->enc_finish_chunk == NULL ||
323 ci->enc_get_pcm_data == NULL )
324 return false;
325
326 ci->enc_get_inputs(&inputs);
327
328 if (inputs.config->afmt != AFMT_PCM_WAV)
329 return false;
330
331 sample_rate = inputs.sample_rate;
332 num_channels = inputs.num_channels;
333 rec_mono_mode = inputs.rec_mono_mode;
334 err = 0;
335
336 /* configure the buffer system */
337 params.afmt = AFMT_PCM_WAV;
338 enc_size = PCM_CHUNK_SIZE*inputs.num_channels / 2;
339 params.chunk_size = enc_size;
340 params.enc_sample_rate = sample_rate;
341 params.reserve_bytes = 0;
342 params.events_callback = enc_events_callback;
343 ci->enc_set_parameters(&params);
344
345 return true;
346} /* init_encoder */
347 211
348/* this is the codec entry point */ 212 ci->enc_pcmbuf_advance(PCM_SAMP_PER_CHUNK);
349enum codec_status codec_main(enum codec_entry_call_reason reason) 213 ci->enc_encbuf_finish_buffer();
350{
351 if (reason == CODEC_LOAD) {
352 if (!init_encoder())
353 return CODEC_ERROR;
354 }
355 else if (reason == CODEC_UNLOAD) {
356 /* reset parameters to initial state */
357 ci->enc_set_parameters(NULL);
358 } 214 }
359 215
360 return CODEC_OK; 216 return CODEC_OK;
361} 217}
362 218
363/* this is called for each file to process */ 219/* this is called by recording system */
364enum codec_status codec_run(void) 220int ICODE_ATTR enc_callback(enum enc_callback_reason reason,
221 void *params)
365{ 222{
366 /* main encoding loop */ 223 if (LIKELY(reason == ENC_CB_STREAM))
367 while(ci->get_command(NULL) != CODEC_ACTION_HALT)
368 { 224 {
369 uint32_t *src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE); 225 switch (((union enc_chunk_hdr *)params)->type)
370 struct enc_chunk_hdr *chunk; 226 {
371 227 case CHUNK_T_DATA:
372 if(src == NULL) 228 return on_stream_data(params);
373 continue; 229 case CHUNK_T_STREAM_START:
374 230 return on_stream_start();
375 chunk = ci->enc_get_chunk(); 231 case CHUNK_T_STREAM_END:
376 chunk->enc_size = enc_size; 232 return on_stream_end(params);
377 chunk->num_pcm = PCM_SAMP_PER_CHUNK; 233 }
378 chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); 234 }
379 235 else if (reason == ENC_CB_INPUTS)
380 chunk_to_wav_format(src, (uint32_t *)chunk->enc_data); 236 {
381 237 struct enc_inputs *inputs = params;
382 ci->enc_finish_chunk(); 238 sample_rate = inputs->sample_rate;
239 num_channels = inputs->num_channels;
240 frame_size = PCM_SAMP_PER_CHUNK*PCM_DEPTH_BYTES*num_channels;
383 } 241 }
384 242
385 return CODEC_OK; 243 return 0;
386} 244}
diff --git a/lib/rbcodec/codecs/wavpack_enc.c b/lib/rbcodec/codecs/wavpack_enc.c
index 1fae2d46a7..864012b4cd 100644
--- a/lib/rbcodec/codecs/wavpack_enc.c
+++ b/lib/rbcodec/codecs/wavpack_enc.c
@@ -8,6 +8,7 @@
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2006 Antonius Hellmann 10 * Copyright (C) 2006 Antonius Hellmann
11 * Copyright (C) 2006-2013 Michael Sevakis
11 * 12 *
12 * This program is free software; you can redistribute it and/or 13 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 14 * modify it under the terms of the GNU General Public License
@@ -47,29 +48,39 @@ struct riff_header
47 uint16_t block_align; /* 20h - num_channels*bits_per_samples/8 */ 48 uint16_t block_align; /* 20h - num_channels*bits_per_samples/8 */
48 uint16_t bits_per_sample; /* 22h - 8=8 bits, 16=16 bits, etc. */ 49 uint16_t bits_per_sample; /* 22h - 8=8 bits, 16=16 bits, etc. */
49 /* Not for audio_format=1 (PCM) */ 50 /* Not for audio_format=1 (PCM) */
50/* unsigned short extra_param_size; 24h - size of extra data */ 51/* uint16_t extra_param_size; 24h - size of extra data */
51/* unsigned char *extra_params; */ 52/* uint8_t extra_params[extra_param_size]; */
52 /* data header */ 53 /* data header */
53 uint8_t data_id[4]; /* 24h - "data" */ 54 uint8_t data_id[4]; /* 24h - "data" */
54 uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */ 55 uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */
55/* unsigned char *data; 2ch - actual sound data */ 56/* uint8_t data[data_size]; 2Ch - actual sound data */
56} __attribute__((packed)); 57} __attribute__((packed));
57 58
58#define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */ 59#define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */
59#define RIFF_FMT_DATA_SIZE 16 /* audio_format -> bits_per_sample */ 60#define RIFF_FMT_DATA_SIZE 16 /* audio_format -> bits_per_sample */
60#define RIFF_DATA_HEADER_SIZE 8 /* data_id -> data_size */ 61#define RIFF_DATA_HEADER_SIZE 8 /* data_id -> data_size */
61 62
63struct wvpk_chunk_data
64{
65 struct enc_chunk_data ckhdr; /* The base data chunk header */
66 WavpackHeader wphdr; /* The block wavpack info */
67 uint8_t data[]; /* Encoded audio data */
68};
69
62#define PCM_DEPTH_BITS 16 70#define PCM_DEPTH_BITS 16
63#define PCM_DEPTH_BYTES 2 71#define PCM_DEPTH_BYTES 2
64#define PCM_SAMP_PER_CHUNK 5000 72#define PCM_SAMP_PER_CHUNK 5000
65#define PCM_CHUNK_SIZE (4*PCM_SAMP_PER_CHUNK)
66 73
67/** Data **/ 74/** Data **/
68static int8_t input_buffer[PCM_CHUNK_SIZE*2] IBSS_ATTR; 75static int32_t input_buffer[PCM_SAMP_PER_CHUNK*2] IBSS_ATTR;
69static WavpackConfig config IBSS_ATTR; 76
77static WavpackConfig config IBSS_ATTR;
70static WavpackContext *wpc; 78static WavpackContext *wpc;
71static int32_t data_size, input_size, input_step IBSS_ATTR; 79static uint32_t sample_rate;
72static int32_t err IBSS_ATTR; 80static int num_channels;
81static uint32_t total_samples;
82static size_t out_reqsize;
83static size_t frame_size;
73 84
74static const WavpackMetadataHeader wvpk_mdh = 85static const WavpackMetadataHeader wvpk_mdh =
75{ 86{
@@ -77,7 +88,7 @@ static const WavpackMetadataHeader wvpk_mdh =
77 sizeof (struct riff_header) / sizeof (uint16_t), 88 sizeof (struct riff_header) / sizeof (uint16_t),
78}; 89};
79 90
80static const struct riff_header riff_header = 91static const struct riff_header riff_template_header =
81{ 92{
82 /* "RIFF" header */ 93 /* "RIFF" header */
83 { 'R', 'I', 'F', 'F' }, /* riff_id */ 94 { 'R', 'I', 'F', 'F' }, /* riff_id */
@@ -96,157 +107,75 @@ static const struct riff_header riff_header =
96 /* data header */ 107 /* data header */
97 { 'd', 'a', 't', 'a' }, /* data_id */ 108 { 'd', 'a', 't', 'a' }, /* data_id */
98 0 /* data_size (*) */ 109 0 /* data_size (*) */
99 /* (*) updated during ENC_END_FILE event */ 110 /* (*) updated when finalizing stream */
100}; 111};
101 112
102static inline void sample_to_int32_mono(int32_t **src, int32_t **dst) 113static inline void sample_to_int32(int32_t **dst, int32_t **src)
103{
104 int32_t t = *(*src)++;
105 /* endianness irrelevant */
106 t = (int16_t)t + (t >> 16) + err;
107 err = t & 1;
108 *(*dst)++ = t >> 1;
109} /* sample_to_int32_mono */
110
111static inline void sample_to_int32_stereo(int32_t **src, int32_t **dst)
112{ 114{
113 int32_t t = *(*src)++; 115 uint32_t t = *(*src)++;
114#ifdef ROCKBOX_BIG_ENDIAN 116#ifdef ROCKBOX_BIG_ENDIAN
115 *(*dst)++ = t >> 16, *(*dst)++ = (int16_t)t; 117 *(*dst)++ = (int32_t)t >> 16;
118 *(*dst)++ = (int16_t)t;
116#else 119#else
117 *(*dst)++ = (int16_t)t, *(*dst)++ = t >> 16; 120 *(*dst)++ = (int16_t)t;
121 *(*dst)++ = (int32_t)t >> 16;
118#endif 122#endif
119} /* sample_to_int32_stereo */ 123}
120 124
121static void chunk_to_int32(int32_t *src) ICODE_ATTR; 125static void ICODE_ATTR input_buffer_to_int32(size_t size)
122static void chunk_to_int32(int32_t *src)
123{ 126{
124 int32_t *src_end, *dst; 127 int32_t *dst = input_buffer;
125#ifdef USE_IRAM 128 int32_t *src = input_buffer + PCM_SAMP_PER_CHUNK;
126 /* copy to IRAM before converting data */
127 dst = (int32_t *)input_buffer + PCM_SAMP_PER_CHUNK;
128 src_end = dst + PCM_SAMP_PER_CHUNK;
129
130 memcpy(dst, src, PCM_CHUNK_SIZE);
131
132 src = dst;
133#else
134 src_end = src + PCM_SAMP_PER_CHUNK;
135#endif
136
137 dst = (int32_t *)input_buffer;
138 129
139 if (config.num_channels == 1) 130 do
140 { 131 {
141 /* 132 sample_to_int32(&dst, &src);
142 * |llllllllllllllll|rrrrrrrrrrrrrrrr| => 133 sample_to_int32(&dst, &src);
143 * |mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm| 134 sample_to_int32(&dst, &src);
144 */ 135 sample_to_int32(&dst, &src);
145 do 136 sample_to_int32(&dst, &src);
146 { 137 sample_to_int32(&dst, &src);
147 /* read 10 longs and write 10 longs */ 138 sample_to_int32(&dst, &src);
148 sample_to_int32_mono(&src, &dst); 139 sample_to_int32(&dst, &src);
149 sample_to_int32_mono(&src, &dst); 140 sample_to_int32(&dst, &src);
150 sample_to_int32_mono(&src, &dst); 141 sample_to_int32(&dst, &src);
151 sample_to_int32_mono(&src, &dst);
152 sample_to_int32_mono(&src, &dst);
153 sample_to_int32_mono(&src, &dst);
154 sample_to_int32_mono(&src, &dst);
155 sample_to_int32_mono(&src, &dst);
156 sample_to_int32_mono(&src, &dst);
157 sample_to_int32_mono(&src, &dst);
158 }
159 while(src < src_end);
160
161 return;
162 } 142 }
163 else 143 while (size -= 10 * 2 * PCM_DEPTH_BYTES);
164 { 144}
165 /*
166 * |llllllllllllllll|rrrrrrrrrrrrrrrr| =>
167 * |llllllllllllllllllllllllllllllll|rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr|
168 */
169 do
170 {
171 /* read 10 longs and write 20 longs */
172 sample_to_int32_stereo(&src, &dst);
173 sample_to_int32_stereo(&src, &dst);
174 sample_to_int32_stereo(&src, &dst);
175 sample_to_int32_stereo(&src, &dst);
176 sample_to_int32_stereo(&src, &dst);
177 sample_to_int32_stereo(&src, &dst);
178 sample_to_int32_stereo(&src, &dst);
179 sample_to_int32_stereo(&src, &dst);
180 sample_to_int32_stereo(&src, &dst);
181 sample_to_int32_stereo(&src, &dst);
182 }
183 while (src < src_end);
184
185 return;
186 }
187} /* chunk_to_int32 */
188
189/* called very often - inline */
190static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR;
191static inline bool is_file_data_ok(struct enc_file_event_data *data)
192{
193 return data->rec_file >= 0 && (long)data->chunk->flags >= 0;
194} /* is_file_data_ok */
195 145
196/* called very often - inline */ 146static int on_stream_data(struct wvpk_chunk_data *wpdata)
197static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR;
198static inline bool on_write_chunk(struct enc_file_event_data *data)
199{ 147{
200 if (!is_file_data_ok(data))
201 return false;
202
203 if (data->chunk->enc_data == NULL)
204 {
205#ifdef ROCKBOX_HAS_LOGF
206 ci->logf("wvpk enc: NULL data");
207#endif
208 return true;
209 }
210
211 /* update timestamp (block_index) */ 148 /* update timestamp (block_index) */
212 ((WavpackHeader *)data->chunk->enc_data)->block_index = 149 wpdata->wphdr.block_index = htole32(total_samples);
213 htole32(data->num_pcm_samples);
214 150
215 if (ci->write(data->rec_file, data->chunk->enc_data, 151 size_t size = wpdata->ckhdr.hdr.size;
216 data->chunk->enc_size) != (ssize_t)data->chunk->enc_size) 152 if (ci->enc_stream_write(wpdata->ckhdr.data, size) != (ssize_t)size)
217 return false; 153 return -1;
218 154
219 data->num_pcm_samples += data->chunk->num_pcm; 155 total_samples += wpdata->ckhdr.pcm_count;
220 return true;
221} /* on_write_chunk */
222 156
223static bool on_start_file(struct enc_file_event_data *data) 157 return 0;
224{ 158}
225 if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0')
226 return false;
227
228 data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
229
230 if (data->rec_file < 0)
231 return false;
232 159
160static int on_stream_start(void)
161{
233 /* reset sample count */ 162 /* reset sample count */
234 data->num_pcm_samples = 0; 163 total_samples = 0;
235 164
236 /* write template headers */ 165 /* write template headers */
237 if (ci->write(data->rec_file, &wvpk_mdh, sizeof (wvpk_mdh)) 166 if (ci->enc_stream_write(&wvpk_mdh, sizeof (wvpk_mdh))
238 != sizeof (wvpk_mdh) || 167 != sizeof (wvpk_mdh))
239 ci->write(data->rec_file, &riff_header, sizeof (riff_header)) 168 return -1;
240 != sizeof (riff_header)) 169
241 { 170 if (ci->enc_stream_write(&riff_template_header,
242 return false; 171 sizeof (riff_template_header))
243 } 172 != sizeof (riff_template_header))
173 return -2;
244 174
245 data->new_enc_size += sizeof(wvpk_mdh) + sizeof(riff_header); 175 return 0;
246 return true; 176}
247} /* on_start_file */
248 177
249static bool on_end_file(struct enc_file_event_data *data) 178static int on_stream_end(void)
250{ 179{
251 struct 180 struct
252 { 181 {
@@ -255,19 +184,16 @@ static bool on_end_file(struct enc_file_event_data *data)
255 WavpackHeader wph; 184 WavpackHeader wph;
256 } __attribute__ ((packed)) h; 185 } __attribute__ ((packed)) h;
257 186
258 uint32_t data_size; 187 /* Correcting sizes on error is a bit of a pain */
259
260 if (data->rec_file < 0)
261 return false; /* file already closed, nothing more we can do */
262
263 /* always _try_ to write the file header, even on error */
264 188
265 /* read template headers at start */ 189 /* read template headers at start */
266 if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || 190 if (ci->enc_stream_lseek(0, SEEK_SET) != 0)
267 ci->read(data->rec_file, &h, sizeof (h)) != sizeof (h)) 191 return -1;
268 return false;
269 192
270 data_size = data->num_pcm_samples*config.num_channels*PCM_DEPTH_BYTES; 193 if (ci->enc_stream_read(&h, sizeof (h)) != sizeof (h))
194 return -2;
195
196 size_t data_size = total_samples*config.num_channels*PCM_DEPTH_BYTES;
271 197
272 /** "RIFF" header **/ 198 /** "RIFF" header **/
273 h.rhdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE + 199 h.rhdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE +
@@ -286,121 +212,29 @@ static bool on_end_file(struct enc_file_event_data *data)
286 /** Wavpack header **/ 212 /** Wavpack header **/
287 h.wph.ckSize = htole32(letoh32(h.wph.ckSize) + sizeof (h.wpmdh) 213 h.wph.ckSize = htole32(letoh32(h.wph.ckSize) + sizeof (h.wpmdh)
288 + sizeof (h.rhdr)); 214 + sizeof (h.rhdr));
289 h.wph.total_samples = htole32(data->num_pcm_samples); 215 h.wph.total_samples = htole32(total_samples);
290 216
291 /* MDH|RIFF|WVPK => WVPK|MDH|RIFF */ 217 /* MDH|RIFF|WVPK => WVPK|MDH|RIFF */
292 if (ci->lseek(data->rec_file, 0, SEEK_SET) 218 if (ci->enc_stream_lseek(0, SEEK_SET) != 0)
293 != 0 || 219 return -3;
294 ci->write(data->rec_file, &h.wph, sizeof (h.wph))
295 != sizeof (h.wph) ||
296 ci->write(data->rec_file, &h.wpmdh, sizeof (h.wpmdh))
297 != sizeof (h.wpmdh) ||
298 ci->write(data->rec_file, &h.rhdr, sizeof (h.rhdr))
299 != sizeof (h.rhdr) ||
300 ci->close(data->rec_file) != 0 )
301 {
302 return false;
303 }
304
305 data->rec_file = -1;
306
307 return true;
308} /* on_end_file */
309 220
310static void enc_events_callback(enum enc_events event, void *data) 221 if (ci->enc_stream_write(&h.wph, sizeof (h.wph)) != sizeof (h.wph))
311 ICODE_ATTR; 222 return -4;
312static void enc_events_callback(enum enc_events event, void *data)
313{
314 switch (event)
315 {
316 case ENC_WRITE_CHUNK:
317 if (on_write_chunk((struct enc_file_event_data *)data))
318 return;
319
320 break;
321
322 case ENC_START_FILE:
323 /* write metadata header and RIFF header */
324 if (on_start_file((struct enc_file_event_data *)data))
325 return;
326
327 break;
328
329 case ENC_END_FILE:
330 if (on_end_file((struct enc_file_event_data *)data))
331 return;
332
333 break;
334
335 default:
336 return;
337 }
338
339 /* Something failed above. Signal error back to core. */
340 ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR;
341} /* enc_events_callback */
342
343static bool init_encoder(void)
344{
345 struct enc_inputs inputs;
346 struct enc_parameters params;
347
348 codec_init();
349
350 if (ci->enc_get_inputs == NULL ||
351 ci->enc_set_parameters == NULL ||
352 ci->enc_get_chunk == NULL ||
353 ci->enc_finish_chunk == NULL ||
354 ci->enc_get_pcm_data == NULL ||
355 ci->enc_unget_pcm_data == NULL )
356 return false;
357
358 ci->enc_get_inputs(&inputs);
359
360 if (inputs.config->afmt != AFMT_WAVPACK)
361 return false;
362
363 memset(&config, 0, sizeof (config));
364 config.bits_per_sample = PCM_DEPTH_BITS;
365 config.bytes_per_sample = PCM_DEPTH_BYTES;
366 config.sample_rate = inputs.sample_rate;
367 config.num_channels = inputs.num_channels;
368 223
369 wpc = WavpackOpenFileOutput (); 224 if (ci->enc_stream_write(&h.wpmdh, sizeof (h.wpmdh)) != sizeof (h.wpmdh))
225 return -5;
370 226
371 if (!WavpackSetConfiguration(wpc, &config, -1)) 227 if (ci->enc_stream_write(&h.rhdr, sizeof (h.rhdr)) != sizeof (h.rhdr))
372 return false; 228 return -6;
373 229
374 err = 0; 230 return 0;
375 231}
376 /* configure the buffer system */
377 params.afmt = AFMT_WAVPACK;
378 input_size = PCM_CHUNK_SIZE*inputs.num_channels / 2;
379 data_size = 105*input_size / 100;
380 input_size *= 2;
381 input_step = input_size / 4;
382 params.chunk_size = data_size;
383 params.enc_sample_rate = inputs.sample_rate;
384 params.reserve_bytes = 0;
385 params.events_callback = enc_events_callback;
386
387 ci->enc_set_parameters(&params);
388
389 return true;
390} /* init_encoder */
391 232
392/* this is the codec entry point */ 233/* this is the codec entry point */
393enum codec_status codec_main(enum codec_entry_call_reason reason) 234enum codec_status codec_main(enum codec_entry_call_reason reason)
394{ 235{
395 if (reason == CODEC_LOAD) { 236 if (reason == CODEC_LOAD)
396 /* initialize params and config */ 237 codec_init();
397 if (!init_encoder())
398 return CODEC_ERROR;
399 }
400 else if (reason == CODEC_UNLOAD) {
401 /* reset parameters to initial state */
402 ci->enc_set_parameters(NULL);
403 }
404 238
405 return CODEC_OK; 239 return CODEC_OK;
406} 240}
@@ -408,60 +242,89 @@ enum codec_status codec_main(enum codec_entry_call_reason reason)
408/* this is called for each file to process */ 242/* this is called for each file to process */
409enum codec_status codec_run(void) 243enum codec_status codec_run(void)
410{ 244{
245 enum { GETBUF_ENC, GETBUF_PCM } getbuf = GETBUF_ENC;
246 struct enc_chunk_data *data = NULL;
247
411 /* main encoding loop */ 248 /* main encoding loop */
412 while(ci->get_command(NULL) != CODEC_ACTION_HALT) 249 while (1)
413 { 250 {
414 uint8_t *src = (uint8_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE); 251 enum codec_command_action action = ci->get_command(NULL);
415 struct enc_chunk_hdr *chunk;
416 bool abort_chunk;
417 uint8_t *dst;
418 uint8_t *src_end;
419
420 if(src == NULL)
421 continue;
422
423 chunk = ci->enc_get_chunk();
424
425 /* reset counts and pointer */
426 chunk->enc_size = 0;
427 chunk->num_pcm = 0;
428 chunk->enc_data = NULL;
429
430 dst = ENC_CHUNK_SKIP_HDR(dst, chunk);
431 252
432 WavpackStartBlock(wpc, dst, dst + data_size); 253 if (action != CODEC_ACTION_NULL)
254 break;
433 255
434 chunk_to_int32((uint32_t*)src); 256 /* First obtain output buffer; when available, get PCM data */
435 src = input_buffer; 257 switch (getbuf)
436 src_end = src + input_size;
437
438 /* encode chunk in four steps yielding between each */
439 do
440 { 258 {
441 abort_chunk = true; 259 case GETBUF_ENC:
442 if (WavpackPackSamples(wpc, (int32_t *)src, 260 if (!(data = ci->enc_encbuf_get_buffer(out_reqsize)))
443 PCM_SAMP_PER_CHUNK/4)) 261 continue;
444 { 262 getbuf = GETBUF_PCM;
445 chunk->num_pcm += PCM_SAMP_PER_CHUNK/4; 263 case GETBUF_PCM:
446 ci->yield(); 264 if (!ci->enc_pcmbuf_read(input_buffer + PCM_SAMP_PER_CHUNK,
447 /* could've been stopped in some way */ 265 PCM_SAMP_PER_CHUNK))
448 abort_chunk = chunk->flags & CHUNKF_ABORT; 266 continue;
449 } 267 getbuf = GETBUF_ENC;
450
451 src += input_step;
452 } 268 }
453 while (!abort_chunk && src < src_end);
454 269
455 if (!abort_chunk) 270 input_buffer_to_int32(frame_size);
271
272 if (WavpackStartBlock(wpc, data->data, data->data + out_reqsize) &&
273 WavpackPackSamples(wpc, input_buffer, PCM_SAMP_PER_CHUNK))
456 { 274 {
457 chunk->enc_data = dst;
458 if (chunk->num_pcm < PCM_SAMP_PER_CHUNK)
459 ci->enc_unget_pcm_data(PCM_CHUNK_SIZE - chunk->num_pcm*4);
460 /* finish the chunk and store chunk size info */ 275 /* finish the chunk and store chunk size info */
461 chunk->enc_size = WavpackFinishBlock(wpc); 276 data->hdr.size = WavpackFinishBlock(wpc);
462 ci->enc_finish_chunk(); 277 data->pcm_count = PCM_SAMP_PER_CHUNK;
463 } 278 }
279 else
280 {
281 data->hdr.err = 1;
282 }
283
284 ci->enc_pcmbuf_advance(PCM_SAMP_PER_CHUNK);
285 ci->enc_encbuf_finish_buffer();
464 } 286 }
465 287
466 return CODEC_OK; 288 return CODEC_OK;
467} 289}
290
291/* this is called by recording system */
292int ICODE_ATTR enc_callback(enum enc_callback_reason reason,
293 void *params)
294{
295 if (LIKELY(reason == ENC_CB_STREAM))
296 {
297 switch (((union enc_chunk_hdr *)params)->type)
298 {
299 case CHUNK_T_DATA:
300 return on_stream_data(params);
301 case CHUNK_T_STREAM_START:
302 return on_stream_start();
303 case CHUNK_T_STREAM_END:
304 return on_stream_end();
305 }
306 }
307 else if (reason == ENC_CB_INPUTS)
308 {
309 /* Save parameters */
310 struct enc_inputs *inputs = params;
311 sample_rate = inputs->sample_rate;
312 num_channels = inputs->num_channels;
313 frame_size = PCM_SAMP_PER_CHUNK*PCM_DEPTH_BYTES*num_channels;
314 out_reqsize = frame_size*110 / 100; /* Add 10% */
315
316 /* Setup Wavpack encoder */
317 memset(&config, 0, sizeof (config));
318 config.bits_per_sample = PCM_DEPTH_BITS;
319 config.bytes_per_sample = PCM_DEPTH_BYTES;
320 config.sample_rate = sample_rate;
321 config.num_channels = num_channels;
322
323 wpc = WavpackOpenFileOutput();
324
325 if (!WavpackSetConfiguration(wpc, &config, -1))
326 return -1;
327 }
328
329 return 0;
330}