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/export/enc_base.h | |
| parent | a9ea1a42695401334717f2e497a7f5576d87691d (diff) | |
| download | rockbox-488813197292bd1db8d533d7b42c38852971c2e8.zip rockbox-488813197292bd1db8d533d7b42c38852971c2e8.tar.gz rockbox-488813197292bd1db8d533d7b42c38852971c2e8.tar.bz2 rockbox-488813197292bd1db8d533d7b42c38852971c2e8.tar.xz | |
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/export/enc_base.h')
| -rw-r--r-- | firmware/export/enc_base.h | 204 |
1 files changed, 72 insertions, 132 deletions
diff --git a/firmware/export/enc_base.h b/firmware/export/enc_base.h index f5dfb65..7228dc4 100644 --- a/firmware/export/enc_base.h +++ b/firmware/export/enc_base.h @@ -9,7 +9,7 @@ * * Base declarations for working with software encoders * - * Copyright (C) 2006 Michael Sevakis + * Copyright (C) 2006-2013 Michael Sevakis * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,7 +24,9 @@ #ifndef ENC_BASE_H #define ENC_BASE_H -/** encoder config structures **/ +#include <sys/types.h> + +/** Encoder config structures **/ /** aiff_enc.codec **/ struct aiff_enc_config @@ -57,18 +59,22 @@ struct aiff_enc_config /* MPEG 1 */ #define MPEG1_SAMPR_CAPS (SAMPR_CAP_32 | SAMPR_CAP_48 | SAMPR_CAP_44) -#define MPEG1_BITR_CAPS (MP3_BITR_CAP_32 | MP3_BITR_CAP_40 | MP3_BITR_CAP_48 | \ - MP3_BITR_CAP_56 | MP3_BITR_CAP_64 | MP3_BITR_CAP_80 | \ - MP3_BITR_CAP_96 | MP3_BITR_CAP_112 | MP3_BITR_CAP_128 | \ - MP3_BITR_CAP_160 | MP3_BITR_CAP_192 | MP3_BITR_CAP_224 | \ +#define MPEG1_BITR_CAPS (MP3_BITR_CAP_32 | MP3_BITR_CAP_40 | \ + MP3_BITR_CAP_48 | MP3_BITR_CAP_56 | \ + MP3_BITR_CAP_64 | MP3_BITR_CAP_80 | \ + MP3_BITR_CAP_96 | MP3_BITR_CAP_112 | \ + MP3_BITR_CAP_128 | MP3_BITR_CAP_160 | \ + MP3_BITR_CAP_192 | MP3_BITR_CAP_224 | \ MP3_BITR_CAP_256 | MP3_BITR_CAP_320) /* MPEG 2 */ #define MPEG2_SAMPR_CAPS (SAMPR_CAP_22 | SAMPR_CAP_24 | SAMPR_CAP_16) -#define MPEG2_BITR_CAPS (MP3_BITR_CAP_8 | MP3_BITR_CAP_16 | MP3_BITR_CAP_24 | \ - MP3_BITR_CAP_32 | MP3_BITR_CAP_40 | MP3_BITR_CAP_48 | \ - MP3_BITR_CAP_56 | MP3_BITR_CAP_64 | MP3_BITR_CAP_80 | \ - MP3_BITR_CAP_96 | MP3_BITR_CAP_112 | MP3_BITR_CAP_128 | \ +#define MPEG2_BITR_CAPS (MP3_BITR_CAP_8 | MP3_BITR_CAP_16 | \ + MP3_BITR_CAP_24 | MP3_BITR_CAP_32 | \ + MP3_BITR_CAP_40 | MP3_BITR_CAP_48 | \ + MP3_BITR_CAP_56 | MP3_BITR_CAP_64 | \ + MP3_BITR_CAP_80 | MP3_BITR_CAP_96 | \ + MP3_BITR_CAP_112 | MP3_BITR_CAP_128 | \ MP3_BITR_CAP_144 | MP3_BITR_CAP_160) #if 0 @@ -131,6 +137,7 @@ struct wavpack_enc_config #endif }; +/* General config information about any encoder */ struct encoder_config { union @@ -149,144 +156,77 @@ struct encoder_config }; /** Encoder chunk macros and definitions **/ -#define CHUNKF_START_FILE 0x0001ul /* This chunk starts a new file */ -#define CHUNKF_END_FILE 0x0002ul /* This chunk ends the current file */ -#define CHUNKF_PRERECORD 0x0010ul /* This chunk is prerecord data, - a new file could start anytime */ -#define CHUNKF_ABORT 0x0020ul /* Encoder should not finish this - chunk */ -#define CHUNKF_ERROR (~0ul ^ (~0ul >> 1)) /* An error has occured - (passed to/from encoder). Use the - sign bit to check (long)flags < 0. */ -#define CHUNKF_ALLFLAGS (0x0033ul | CHUNKF_ERROR) - -/* Header at the beginning of every encoder chunk */ -#ifdef DEBUG -#define H_TO_BE32 htobe32 -#define ENC_CHUNK_MAGIC H_TO_BE32(('P' << 24) | ('T' << 16) | ('Y' << 8) | 'R') -#endif -struct enc_chunk_hdr -{ -#ifdef DEBUG - unsigned long id; /* overflow detection - 'PTYR' - acronym for - "PTYR Tells You Right" ;) */ -#endif - unsigned long flags; /* in/out: flags used by encoder and file - writing */ - size_t enc_size; /* out: amount of encoder data written to - chunk */ - unsigned long num_pcm; /* out: number of PCM samples eaten during - processing - (<= size of allocated buffer) */ - unsigned char *enc_data; /* out: pointer to enc_size_written bytes - of encoded audio data in chunk */ - /* Encoder defined data follows header. Can be audio data + any other - stuff the encoder needs to handle on a per chunk basis */ -}; -/* Paranoia: be sure header size is 4-byte aligned */ -#define ENC_CHUNK_HDR_SIZE \ - ALIGN_UP_P2(sizeof (struct enc_chunk_hdr), 2) -/* Skip the chunk header and return data */ -#define ENC_CHUNK_SKIP_HDR(t, hdr) \ - ((typeof (t))((char *)hdr + ENC_CHUNK_HDR_SIZE)) -/* Cast p to struct enc_chunk_hdr * */ -#define ENC_CHUNK_HDR(p) \ - ((struct enc_chunk_hdr *)(p)) - -enum enc_events +/* What sort of data does the header describe? */ +enum CHUNK_T { - /* File writing events - data points to enc_file_event_data */ - ENC_START_FILE = 0, /* a new file has been opened and no data has yet - been written */ - ENC_WRITE_CHUNK, /* write the current chunk to disk */ - ENC_END_FILE, /* current file about to be closed and all valid - data has been written */ - /* Encoder buffer events - data points to enc_buffer_event_data */ - ENC_REC_NEW_STREAM, /* Take steps to finish current stream and start - new */ + CHUNK_T_DATA = 0x0, /* Encoded audio data */ + CHUNK_T_STREAM_START = 0x1, /* Stream start marker */ + CHUNK_T_STREAM_END = 0x2, /* Stream end marker */ + CHUNK_T_WRAP = 0x3 /* Buffer early wrap marker */ }; -/** - * encoder can write extra data to the file such as headers or more encoded - * samples and must update sizes and samples accordingly. - */ -struct enc_file_event_data +/* Header for every buffer slot and chunk */ +union enc_chunk_hdr { - struct enc_chunk_hdr *chunk; /* Current chunk */ - size_t new_enc_size; /* New size of chunk */ - unsigned long new_num_pcm; /* New number of pcm in chunk */ - const char *filename; /* filename to open if ENC_START_FILE */ - int rec_file; /* Current file or < 0 if none */ - unsigned long num_pcm_samples; /* Current pcm sample count written to - file so far. */ -}; + struct + { + uint32_t type : 2; /* Chunk type (CHUNK_T_*) */ + uint32_t err : 1; /* Encoder error */ + uint32_t pre : 1; /* Chunk is prerecorded data */ + uint32_t aux0 : 1; /* Aux flag 0 - for encoder */ + uint32_t unused : 3; /* */ + uint32_t size : 24; /* size of data */ + }; + uint32_t zero; /* Zero-out struct access */ + intptr_t reserved1; /* Want it at least pointer-sized */ +} __attribute__((__may_alias__)); + +#define ENC_HDR_SIZE (sizeof (union enc_chunk_hdr)) -/** - * encoder may add some data to the end of the last and start of the next - * but must never yield when called so any encoding done should be absolutely - * minimal. - */ -struct enc_buffer_event_data +/* When hdr.type is CHUNK_T_STREAM_START */ +struct enc_chunk_file { - unsigned long flags; /* in: One or more of: - * CHUNKF_PRERECORD - * CHUNKF_END_FILE - * CHUNKF_START_FILE - */ - struct enc_chunk_hdr *pre_chunk; /* in: pointer to first prerecord - * chunk - */ - struct enc_chunk_hdr *chunk; /* in,out: chunk were split occurs - - * first chunk of start - */ -}; + union enc_chunk_hdr hdr; /* This chunk's header */ + /* hdr.size = slot count of chunk */ + char path[]; /* NULL-terminated path of file */ +} __attribute__((__may_alias__)); -/** Callbacks called by encoder codec **/ +/* If flags = CHUNK_T_STREAM_END, just the header exists */ -/* parameters passed to encoder by enc_get_inputs */ -struct enc_inputs +/* When hdr.type is CHUNK_T_DATA */ +struct enc_chunk_data { - unsigned long sample_rate; /* out - pcm frequency */ - int num_channels; /* out - number of audio channels */ - int rec_mono_mode; /* out - how to create mono */ - struct encoder_config *config; /* out - encoder settings */ -}; + union enc_chunk_hdr hdr; /* IN,OUT: This chunk's header */ + /* hdr.size = total size of data[] */ + uint32_t pcm_count; /* OUT: number of PCM samples encoded */ + uint8_t data[]; /* OUT: encoded audio data */ +} __attribute__((__may_alias__)); -void enc_get_inputs(struct enc_inputs *inputs); +/* CHUNK_T_STREAM_END and CHUNK_T_WRAP consist of only the header */ -/* parameters pass from encoder to enc_set_parameters */ -struct enc_parameters +#define ENC_FILE_HDR(hdr) ((struct enc_chunk_file *)(hdr)) +#define ENC_DATA_HDR(hdr) ((struct enc_chunk_data *)(hdr)) + +/* Audio and encoder stream parameters */ +struct enc_inputs { /* IN parameters */ - int afmt; /* AFMT_* id - sanity checker */ - size_t chunk_size; /* max chunk size required */ - unsigned long enc_sample_rate; /* actual sample rate used by encoder - (for recorded time calculation) */ - size_t reserve_bytes; /* number of bytes to reserve immediately - following chunks */ - void (*events_callback)(enum enc_events event, - void *data); /* pointer to events callback */ - /* OUT parameters */ - unsigned char *enc_buffer; /* pointer to enc_buffer */ - size_t buf_chunk_size; /* size of chunks in enc_buffer */ - int num_chunks; /* number of chunks allotted to encoder */ - unsigned char *reserve_buffer; /* pointer to reserve_bytes bytes */ -}; + unsigned long sample_rate; /* PCM samplerate setting */ + int num_channels; /* Number of audio channels */ + struct encoder_config *config; /* Encoder settings */ -/* set the encoder dimensions - called by encoder codec at initialization - and termination */ -void enc_set_parameters(struct enc_parameters *params); -/* returns pointer to next write chunk in circular buffer */ -struct enc_chunk_hdr * enc_get_chunk(void); -/* releases the current chunk into the available chunks */ -void enc_finish_chunk(void); + /* IN,OUT parameters */ + unsigned long enc_sample_rate; /* Actual sample rate accepted by encoder + (for recorded time calculation) */ +}; -#define PCM_MAX_FEED_SIZE 20000 /* max pcm size passed to encoder */ +enum enc_callback_reason +{ + ENC_CB_INPUTS, /* 'params' is struct enc_inputs * */ + ENC_CB_STREAM, /* 'params' is union enc_chunk_hdr * */ +}; -/* passes a pointer to next chunk of unprocessed wav data */ -unsigned char * enc_get_pcm_data(size_t size); -/* puts some pcm data back in the queue */ -size_t enc_unget_pcm_data(size_t size); +typedef int (* enc_callback_t)(enum enc_callback_reason reason, void *params); #endif /* ENC_BASE_H */ |