diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2013-06-22 16:41:16 -0400 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2013-06-30 00:40:27 +0200 |
commit | 488813197292bd1db8d533d7b42c38852971c2e8 (patch) | |
tree | 07ea7247799b1b6b487c5ca73311380fc947700e /firmware | |
parent | a9ea1a42695401334717f2e497a7f5576d87691d (diff) | |
download | rockbox-488813197292bd1db8d533d7b42c38852971c2e8.tar.gz rockbox-488813197292bd1db8d533d7b42c38852971c2e8.zip |
Update software recording engine to latest codec interface.
Basically, just give it a good rewrite.
Software codec recording can be implemented in a more straightforward
and simple manner and made more robust through the better codec
control now available.
Encoded audio buffer uses a packed format instead of fixed-size
chunks and uses smaller data headers leading to more efficient usage.
The greatest benefit is with a VBR format like wavpack which needs
to request a maximum size but only actually ends up committing part
of that request.
No guard buffers are used for either PCM or encoded audio. PCM is
read into the codec's provided buffer and mono conversion done at
that time in the core if required. Any highly-specialized sample
conversion is still done within the codec itself, such as 32-bit
(wavpack) or interleaved mono (mp3).
There is no longer a separate filename array. All metadata goes
onto the main encoded audio buffer, eliminating any predermined
file limit on the buffer as well as not wasting the space for
unused path queue slots.
The core and codec interface is less awkward and a bit more sensible.
Some less useful interface features were removed. Threads are kept
on narrow code paths ie. the audio thread never calls encoding
functions and the codec thread never calls file functions as before.
Codecs no longer call file functions directly. Writes are buffered
in the core and data written to storage in larger chunks to speed up
flushing of data. In fact, codecs are no longer aware of the stream
being a file at all and have no access to the fd.
SPDIF frequency detection no longer requires a restart of recording
or plugging the source before entering the screen. It will poll
for changes and update when stopped or prerecording (which does
discard now-invalid prerecorded data).
I've seen to it that writing a proper header on full disk works
when the format makes it reasonably practical to do so. Other cases
may have incorrect data sizes but sample info will be in tact. File
left that way may play anyway.
mp3_enc.codec acquires the ability to write 'Info' headers with LAME
tags to make it gapless (bonus).
Change-Id: I670685166d5eb32ef58ef317f50b8af766ceb653
Reviewed-on: http://gerrit.rockbox.org/493
Reviewed-by: Michael Sevakis <jethead71@rockbox.org>
Tested-by: Michael Sevakis <jethead71@rockbox.org>
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/export/audio.h | 5 | ||||
-rw-r--r-- | firmware/export/enc_base.h | 204 |
2 files changed, 73 insertions, 136 deletions
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 */ | 215 | unsigned long audio_prerecorded_time(void); |
216 | bool audio_load_encoder(int afmt); | ||
217 | void audio_remove_encoder(void); | ||
218 | unsigned 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 **/ |
30 | struct aiff_enc_config | 32 | struct 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 */ | ||
134 | struct encoder_config | 141 | struct 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 | ||
168 | struct 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 \ | 161 | enum 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 | |||
197 | enum 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 | 170 | union enc_chunk_hdr |
212 | * samples and must update sizes and samples accordingly. | ||
213 | */ | ||
214 | struct 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 | 188 | struct enc_chunk_file |
227 | * but must never yield when called so any encoding done should be absolutely | ||
228 | * minimal. | ||
229 | */ | ||
230 | struct 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 */ |
248 | struct enc_inputs | 198 | struct 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 | ||
256 | void 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)) |
259 | struct enc_parameters | 209 | #define ENC_DATA_HDR(hdr) ((struct enc_chunk_data *)(hdr)) |
210 | |||
211 | /* Audio and encoder stream parameters */ | ||
212 | struct 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 |
279 | void enc_set_parameters(struct enc_parameters *params); | 221 | (for recorded time calculation) */ |
280 | /* returns pointer to next write chunk in circular buffer */ | 222 | }; |
281 | struct enc_chunk_hdr * enc_get_chunk(void); | ||
282 | /* releases the current chunk into the available chunks */ | ||
283 | void enc_finish_chunk(void); | ||
284 | 223 | ||
285 | #define PCM_MAX_FEED_SIZE 20000 /* max pcm size passed to encoder */ | 224 | enum 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 */ | 230 | typedef int (* enc_callback_t)(enum enc_callback_reason reason, void *params); |
288 | unsigned char * enc_get_pcm_data(size_t size); | ||
289 | /* puts some pcm data back in the queue */ | ||
290 | size_t enc_unget_pcm_data(size_t size); | ||
291 | 231 | ||
292 | #endif /* ENC_BASE_H */ | 232 | #endif /* ENC_BASE_H */ |