diff --git a/apps/SOURCES b/apps/SOURCES index e1d8e7bbdd..ccfc7fa280 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -73,6 +73,9 @@ pcmbuf.c playback.c codecs.c dsp.c +#ifdef HAVE_RECORDING +enc_config.c +#endif eq.c #if defined(CPU_COLDFIRE) && !defined(SIMULATOR) dsp_cf.S diff --git a/apps/codecs.c b/apps/codecs.c index f33957eba2..4491dadf49 100644 --- a/apps/codecs.c +++ b/apps/codecs.c @@ -50,6 +50,7 @@ #include "sound.h" #include "database.h" #include "splash.h" +#include "general.h" #ifdef SIMULATOR #if CONFIG_CODEC == SWCODEC @@ -104,6 +105,7 @@ struct codec_api ci = { PREFIX(remove), PREFIX(rename), PREFIX(ftruncate), + PREFIX(fsync), fdprintf, read_line, settings_parseline, @@ -187,6 +189,7 @@ struct codec_api ci = { get_time, set_time, plugin_get_audio_buffer, + round_value_to_list32, #if defined(DEBUG) || defined(SIMULATOR) debugf, @@ -213,11 +216,11 @@ struct codec_api ci = { false, enc_get_inputs, enc_set_parameters, - enc_alloc_chunk, - enc_free_chunk, - enc_wavbuf_near_empty, - enc_get_wav_data, - &enc_set_header_callback, + enc_get_chunk, + enc_finish_chunk, + enc_pcm_buf_near_empty, + enc_get_pcm_data, + enc_unget_pcm_data #endif /* new stuff at the end, sort into place next time @@ -225,10 +228,10 @@ struct codec_api ci = { }; -void codec_get_full_path(char *path, const char *codec_fn) +void codec_get_full_path(char *path, const char *codec_root_fn) { - /* Create full codec path */ - snprintf(path, MAX_PATH-1, CODECS_DIR "/%s", codec_fn); + snprintf(path, MAX_PATH-1, CODECS_DIR "/%s." CODEC_EXTENSION, + codec_root_fn); } int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap, @@ -254,7 +257,11 @@ int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap, hdr = (struct codec_header *)codecbuf; if (size <= (signed)sizeof(struct codec_header) - || hdr->magic != CODEC_MAGIC + || (hdr->magic != CODEC_MAGIC +#ifdef HAVE_RECORDING + && hdr->magic != CODEC_ENC_MAGIC +#endif + ) || hdr->target_id != TARGET_ID || hdr->load_addr != codecbuf || hdr->end_addr > codecbuf + CODEC_SIZE) diff --git a/apps/codecs.h b/apps/codecs.h index 96804a889b..0b90ef9c19 100644 --- a/apps/codecs.h +++ b/apps/codecs.h @@ -46,7 +46,7 @@ #include "profile.h" #endif #if (CONFIG_CODEC == SWCODEC) -#if !defined(SIMULATOR) +#if !defined(SIMULATOR) && defined(HAVE_RECORDING) #include "pcm_record.h" #endif #include "dsp.h" @@ -84,15 +84,18 @@ #define PREFIX(_x_) _x_ #endif +/* magic for normal codecs */ #define CODEC_MAGIC 0x52434F44 /* RCOD */ +/* magic for encoder codecs */ +#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */ /* increase this every time the api struct changes */ -#define CODEC_API_VERSION 9 +#define CODEC_API_VERSION 10 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any new function which are "waiting" at the end of the function table) */ -#define CODEC_MIN_API_VERSION 8 +#define CODEC_MIN_API_VERSION 10 /* codec return codes */ enum codec_status { @@ -176,6 +179,7 @@ struct codec_api { int (*PREFIX(remove))(const char* pathname); int (*PREFIX(rename))(const char* path, const char* newname); int (*PREFIX(ftruncate))(int fd, off_t length); + int (*PREFIX(fsync))(int fd); int (*fdprintf)(int fd, const char *fmt, ...); int (*read_line)(int fd, char* buffer, int buffer_size); @@ -232,7 +236,8 @@ struct codec_api { /* sound */ void (*sound_set)(int setting, int value); #ifndef SIMULATOR - void (*mp3_play_data)(const unsigned char* start, int size, void (*get_more)(unsigned char** start, int* size)); + void (*mp3_play_data)(const unsigned char* start, + int size, void (*get_more)(unsigned char** start, int* size)); void (*mp3_play_pause)(bool play); void (*mp3_play_stop)(void); bool (*mp3_is_playing)(void); @@ -263,6 +268,10 @@ struct codec_api { struct tm* (*get_time)(void); int (*set_time)(const struct tm *tm); void* (*plugin_get_audio_buffer)(int* buffer_size); + int (*round_value_to_list32)(unsigned long value, + const unsigned long list[], + int count, + bool signd); #if defined(DEBUG) || defined(SIMULATOR) void (*debugf)(const char *fmt, ...); @@ -291,18 +300,14 @@ struct codec_api { #endif #if defined(HAVE_RECORDING) && !defined(SIMULATOR) - bool enc_codec_loaded; - void (*enc_get_inputs)(int *buffer_size, - int *channels, int *quality); - void (*enc_set_parameters)(int chunk_size, int num_chunks, - int samp_per_chunk, char *head_ptr, int head_size, - int enc_id); - unsigned int* (*enc_alloc_chunk)(void); - void (*enc_free_chunk)(void); - int (*enc_wavbuf_near_empty)(void); - char* (*enc_get_wav_data)(int size); - void (**enc_set_header_callback)(void *head_buffer, - int head_size, int num_samples, bool is_file_header); + volatile int enc_codec_loaded; /* <0=error, 0=pending, >0=ok */ + void (*enc_get_inputs)(struct enc_inputs *inputs); + void (*enc_set_parameters)(struct enc_parameters *params); + struct enc_chunk_hdr * (*enc_get_chunk)(void); + void (*enc_finish_chunk)(void); + int (*enc_pcm_buf_near_empty)(void); + unsigned char * (*enc_get_pcm_data)(size_t size); + size_t (*enc_unget_pcm_data)(size_t size); #endif /* new stuff at the end, sort into place next time @@ -312,34 +317,49 @@ struct codec_api { /* codec header */ struct codec_header { - unsigned long magic; + unsigned long magic; /* RCOD or RENC */ unsigned short target_id; unsigned short api_version; unsigned char *load_addr; unsigned char *end_addr; enum codec_status(*entry_point)(struct codec_api*); }; + #ifdef CODEC #ifndef SIMULATOR /* plugin_* is correct, codecs use the plugin linker script */ extern unsigned char plugin_start_addr[]; extern unsigned char plugin_end_addr[]; +/* decoders */ #define CODEC_HEADER \ const struct codec_header __header \ __attribute__ ((section (".header")))= { \ CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, \ plugin_start_addr, plugin_end_addr, codec_start }; -#else /* SIMULATOR */ +/* encoders */ +#define CODEC_ENC_HEADER \ + const struct codec_header __header \ + __attribute__ ((section (".header")))= { \ + CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, \ + plugin_start_addr, plugin_end_addr, codec_start }; + +#else /* def SIMULATOR */ +/* decoders */ #define CODEC_HEADER \ const struct codec_header __header = { \ CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, \ NULL, NULL, codec_start }; -#endif -#endif +/* encoders */ +#define CODEC_ENC_HEADER \ + const struct codec_header __header = { \ + CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, \ + NULL, NULL, codec_start }; +#endif /* SIMULATOR */ +#endif /* CODEC */ -/* create full codec path from filenames in audio_formats[] +/* create full codec path from root filenames in audio_formats[] assumes buffer size is MAX_PATH */ -void codec_get_full_path(char *path, const char *codec_fn); +void codec_get_full_path(char *path, const char *codec_root_fn); /* defined by the codec loader (codec.c) */ int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap, diff --git a/apps/codecs/libwavpack/bits.c b/apps/codecs/libwavpack/bits.c index 0a148e123f..0f0e79c292 100644 --- a/apps/codecs/libwavpack/bits.c +++ b/apps/codecs/libwavpack/bits.c @@ -15,6 +15,7 @@ // the malloc() system is provided. #include "wavpack.h" +#include "system.h" #include @@ -118,19 +119,16 @@ uint32_t bs_close_write (Bitstream *bs) void little_endian_to_native (void *data, char *format) { uchar *cp = (uchar *) data; - int32_t temp; while (*format) { switch (*format) { case 'L': - temp = cp [0] + ((int32_t) cp [1] << 8) + ((int32_t) cp [2] << 16) + ((int32_t) cp [3] << 24); - * (int32_t *) cp = temp; + *(long *)cp = letoh32(*(long *)cp); cp += 4; break; case 'S': - temp = cp [0] + (cp [1] << 8); - * (short *) cp = (short) temp; + *(short *)cp = letoh16(*(short *)cp); cp += 2; break; @@ -148,28 +146,22 @@ void little_endian_to_native (void *data, char *format) void native_to_little_endian (void *data, char *format) { uchar *cp = (uchar *) data; - int32_t temp; while (*format) { switch (*format) { case 'L': - temp = * (int32_t *) cp; - *cp++ = (uchar) temp; - *cp++ = (uchar) (temp >> 8); - *cp++ = (uchar) (temp >> 16); - *cp++ = (uchar) (temp >> 24); + *(long *)cp = htole32(*(long *)cp); + cp += 4; break; case 'S': - temp = * (short *) cp; - *cp++ = (uchar) temp; - *cp++ = (uchar) (temp >> 8); + *(short *)cp = htole16(*(short *)cp); + cp += 2; break; default: if (*format >= '0' && *format <= '9') cp += *format - '0'; - break; } diff --git a/apps/codecs/mp3_enc.c b/apps/codecs/mp3_enc.c index 3caca94f35..cb727ce01e 100644 --- a/apps/codecs/mp3_enc.c +++ b/apps/codecs/mp3_enc.c @@ -32,9 +32,26 @@ #ifndef SIMULATOR +#include #include "codeclib.h" -CODEC_HEADER +CODEC_ENC_HEADER + +#define ENC_PADDING_FRAMES1 2 +#define ENC_PADDING_FRAMES2 4 +#define ENC_DELAY_SAMP 576 +#define ENC_DELAY_SIZE (ENC_DELAY_SAMP*4) +#define SAMP_PER_FRAME1 1152 +#define SAMP_PER_FRAME2 576 +#define PCM_CHUNK_SIZE1 (SAMP_PER_FRAME1*4) +#define PCM_CHUNK_SIZE2 (SAMP_PER_FRAME2*4) +#define SAMPL2 576 +#define SBLIMIT 32 +#define HTN 16 +#define memcpy ci->memcpy +#define memset ci->memset +#define putlong(c, s) if(s+sz <= 32) { cc = (cc << s) | c; sz+= s; } \ + else { putbits(cc, sz); cc = c; sz = s; } #ifdef USE_IRAM extern char iramcopy[]; @@ -44,25 +61,8 @@ extern char iedata[]; extern char iend[]; #endif - -#define SAMP_PER_FRAME 1152 -#define SAMPL2 576 -#define SBLIMIT 32 -#define HTN 16 -#define memcpy ci->memcpy -#define memset ci->memset -#define putlong(c, s) if(s+sz <= 32) { cc = (cc << s) | c; sz+= s; } \ - else { putbits(cc, sz); cc = c; sz = s; } - -enum e_byte_order { order_unknown, order_bigEndian, order_littleEndian }; - -typedef unsigned long uint32; -typedef unsigned short uint16; -typedef unsigned char uint8; - - typedef struct { - int type; /* 0=(22.05,24,16kHz) 1=(44.1,48,32kHz) */ + int type; /* 0=(MPEG2 - 22.05,24,16kHz) 1=(MPEG1 - 44.1,48,32kHz) */ int mode; /* 0=stereo, 1=jstereo, 2=dual, 3=mono */ int bitrate; int padding; @@ -73,21 +73,20 @@ typedef struct { /* Side information */ typedef struct { - uint32 part2_3_length; + uint32_t part2_3_length; int count1; /* number of 0-1-quadruples */ - uint32 global_gain; - uint32 table_select[4]; - uint32 region_0_1; - uint32 address1; - uint32 address2; - uint32 address3; + uint32_t global_gain; + uint32_t table_select[4]; + uint32_t region_0_1; + uint32_t address1; + uint32_t address2; + uint32_t address3; long quantStep; long additStep; long max_val; } side_info_t; typedef struct { - enum e_byte_order byte_order; side_info_t cod_info[2][2]; mpeg_t mpg; long frac_per_frame; @@ -98,19 +97,18 @@ typedef struct { int ResvSize; int channels; int granules; - int resample; long samplerate; } config_t; typedef struct { int bitpos; /* current bitpos for writing */ - uint32 bbuf[263]; + uint32_t bbuf[263]; } BF_Data; struct huffcodetab { int len; /* max. index */ - const uint8 *table; /* pointer to array[len][len] */ - const uint8 *hlen; /* pointer to array[len][len] */ + const uint8_t *table; /* pointer to array[len][len] */ + const uint8_t *hlen; /* pointer to array[len][len] */ }; struct huffcodebig { @@ -127,102 +125,105 @@ struct huffcodebig { #define shft_n(x,n) ((x) >> n) #define SQRT 724 /* sqrt(2) * 512 */ -short mfbuf [2*(1152+512)] IBSS_ATTR; /* 3328 Bytes */ -int sb_data [2][2][18][SBLIMIT] IBSS_ATTR; /* 13824 Bytes */ -int mdct_freq [SAMPL2] IBSS_ATTR; /* 9216 Bytes */ -short enc_data [SAMPL2] IBSS_ATTR; /* 4608 Bytes */ -uint32 scalefac [23] IBSS_ATTR; /* 92 Bytes */ -BF_Data CodedData IBSS_ATTR; /* 1056 Bytes */ -int ca [8] IBSS_ATTR; /* 32 Bytes */ -int cs [8] IBSS_ATTR; /* 32 Bytes */ -int cx [9] IBSS_ATTR; /* 36 Bytes */ -int win [18][4] IBSS_ATTR; /* 288 Bytes */ -short enwindow [15*27+24] IBSS_ATTR; /* 862 Bytes */ -short int2idx [4096] IBSS_ATTR; /* 8192 Bytes */ -uint8 ht_count [2][2][16] IBSS_ATTR; /* 64 Bytes */ -uint32 tab01 [ 16] IBSS_ATTR; /* 64 Bytes */ -uint32 tab23 [ 9] IBSS_ATTR; /* 36 Bytes */ -uint32 tab56 [ 16] IBSS_ATTR; /* 64 Bytes */ -uint32 tab1315 [256] IBSS_ATTR; /* 1024 Bytes */ -uint32 tab1624 [256] IBSS_ATTR; /* 1024 Bytes */ -uint32 tab789 [ 36] IBSS_ATTR; /* 144 Bytes */ -uint32 tabABC [ 64] IBSS_ATTR; /* 256 Bytes */ -uint8 t1HB [ 4] IBSS_ATTR; -uint8 t2HB [ 9] IBSS_ATTR; -uint8 t3HB [ 9] IBSS_ATTR; -uint8 t5HB [ 16] IBSS_ATTR; -uint8 t6HB [ 16] IBSS_ATTR; -uint8 t7HB [ 36] IBSS_ATTR; -uint8 t8HB [ 36] IBSS_ATTR; -uint8 t9HB [ 36] IBSS_ATTR; -uint8 t10HB [ 64] IBSS_ATTR; -uint8 t11HB [ 64] IBSS_ATTR; -uint8 t12HB [ 64] IBSS_ATTR; -uint8 t13HB [256] IBSS_ATTR; -uint8 t15HB [256] IBSS_ATTR; -uint16 t16HB [256] IBSS_ATTR; -uint16 t24HB [256] IBSS_ATTR; -uint8 t1l [ 8] IBSS_ATTR; -uint8 t2l [ 9] IBSS_ATTR; -uint8 t3l [ 9] IBSS_ATTR; -uint8 t5l [ 16] IBSS_ATTR; -uint8 t6l [ 16] IBSS_ATTR; -uint8 t7l [ 36] IBSS_ATTR; -uint8 t8l [ 36] IBSS_ATTR; -uint8 t9l [ 36] IBSS_ATTR; -uint8 t10l [ 64] IBSS_ATTR; -uint8 t11l [ 64] IBSS_ATTR; -uint8 t12l [ 64] IBSS_ATTR; -uint8 t13l [256] IBSS_ATTR; -uint8 t15l [256] IBSS_ATTR; -uint8 t16l [256] IBSS_ATTR; -uint8 t24l [256] IBSS_ATTR; -struct huffcodetab ht [HTN] IBSS_ATTR; +static short mfbuf [2*(1152+512)] IBSS_ATTR; /* 3328 Bytes */ +static int sb_data [2][2][18][SBLIMIT] IBSS_ATTR; /* 13824 Bytes */ +static int mdct_freq [SAMPL2] IBSS_ATTR; /* 9216 Bytes */ +static short enc_data [SAMPL2] IBSS_ATTR; /* 4608 Bytes */ +static uint32_t scalefac [23] IBSS_ATTR; /* 92 Bytes */ +static BF_Data CodedData IBSS_ATTR; /* 1056 Bytes */ +static int ca [8] IBSS_ATTR; /* 32 Bytes */ +static int cs [8] IBSS_ATTR; /* 32 Bytes */ +static int cx [9] IBSS_ATTR; /* 36 Bytes */ +static int win [18][4] IBSS_ATTR; /* 288 Bytes */ +static short enwindow [15*27+24] IBSS_ATTR; /* 862 Bytes */ +static short int2idx [4096] IBSS_ATTR; /* 8192 Bytes */ +static uint8_t ht_count [2][2][16] IBSS_ATTR; /* 64 Bytes */ +static uint32_t tab01 [ 16] IBSS_ATTR; /* 64 Bytes */ +static uint32_t tab23 [ 9] IBSS_ATTR; /* 36 Bytes */ +static uint32_t tab56 [ 16] IBSS_ATTR; /* 64 Bytes */ +static uint32_t tab1315 [256] IBSS_ATTR; /* 1024 Bytes */ +static uint32_t tab1624 [256] IBSS_ATTR; /* 1024 Bytes */ +static uint32_t tab789 [ 36] IBSS_ATTR; /* 144 Bytes */ +static uint32_t tabABC [ 64] IBSS_ATTR; /* 256 Bytes */ +static uint8_t t1HB [ 4] IBSS_ATTR; +static uint8_t t2HB [ 9] IBSS_ATTR; +static uint8_t t3HB [ 9] IBSS_ATTR; +static uint8_t t5HB [ 16] IBSS_ATTR; +static uint8_t t6HB [ 16] IBSS_ATTR; +static uint8_t t7HB [ 36] IBSS_ATTR; +static uint8_t t8HB [ 36] IBSS_ATTR; +static uint8_t t9HB [ 36] IBSS_ATTR; +static uint8_t t10HB [ 64] IBSS_ATTR; +static uint8_t t11HB [ 64] IBSS_ATTR; +static uint8_t t12HB [ 64] IBSS_ATTR; +static uint8_t t13HB [256] IBSS_ATTR; +static uint8_t t15HB [256] IBSS_ATTR; +static uint16_t t16HB [256] IBSS_ATTR; +static uint16_t t24HB [256] IBSS_ATTR; +static uint8_t t1l [ 8] IBSS_ATTR; +static uint8_t t2l [ 9] IBSS_ATTR; +static uint8_t t3l [ 9] IBSS_ATTR; +static uint8_t t5l [ 16] IBSS_ATTR; +static uint8_t t6l [ 16] IBSS_ATTR; +static uint8_t t7l [ 36] IBSS_ATTR; +static uint8_t t8l [ 36] IBSS_ATTR; +static uint8_t t9l [ 36] IBSS_ATTR; +static uint8_t t10l [ 64] IBSS_ATTR; +static uint8_t t11l [ 64] IBSS_ATTR; +static uint8_t t12l [ 64] IBSS_ATTR; +static uint8_t t13l [256] IBSS_ATTR; +static uint8_t t15l [256] IBSS_ATTR; +static uint8_t t16l [256] IBSS_ATTR; +static uint8_t t24l [256] IBSS_ATTR; +static struct huffcodetab ht [HTN] IBSS_ATTR; -static config_t cfg; +static unsigned pcm_chunk_size IBSS_ATTR; +static unsigned samp_per_frame IBSS_ATTR; + +static config_t cfg IBSS_ATTR; static struct codec_api *ci; -static int enc_channels; +static char *res_buffer; -static const uint8 ht_count_const[2][2][16] = +static const uint8_t ht_count_const[2][2][16] = { { { 1, 5, 4, 5, 6, 5, 4, 4, 7, 3, 6, 0, 7, 2, 3, 1 }, /* table0 */ { 1, 5, 5, 7, 5, 8, 7, 9, 5, 7, 7, 9, 7, 9, 9,10 } }, /* hleng0 */ { {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, /* table1 */ { 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 } } }; /* hleng1 */ -static const uint8 t1HB_const[4] = {1,1,1,0}; -static const uint8 t2HB_const[9] = {1,2,1,3,1,1,3,2,0}; -static const uint8 t3HB_const[9] = {3,2,1,1,1,1,3,2,0}; -static const uint8 t5HB_const[16] = {1,2,6,5,3,1,4,4,7,5,7,1,6,1,1,0}; -static const uint8 t6HB_const[16] = {7,3,5,1,6,2,3,2,5,4,4,1,3,3,2,0}; +static const uint8_t t1HB_const[4] = {1,1,1,0}; +static const uint8_t t2HB_const[9] = {1,2,1,3,1,1,3,2,0}; +static const uint8_t t3HB_const[9] = {3,2,1,1,1,1,3,2,0}; +static const uint8_t t5HB_const[16] = {1,2,6,5,3,1,4,4,7,5,7,1,6,1,1,0}; +static const uint8_t t6HB_const[16] = {7,3,5,1,6,2,3,2,5,4,4,1,3,3,2,0}; -static const uint8 t7HB_const[36] = +static const uint8_t t7HB_const[36] = { 1, 2,10,19,16,10, 3, 3, 7,10, 5, 3,11, 4,13,17, 8, 4, 12,11,18,15,11, 2, 7, 6, 9,14, 3, 1, 6, 4, 5, 3, 2, 0 }; -static const uint8 t8HB_const[36] = +static const uint8_t t8HB_const[36] = { 3, 4, 6,18,12, 5, 5, 1, 2,16, 9, 3, 7, 3, 5,14, 7, 3, 19,17,15,13,10, 4,13, 5, 8,11, 5, 1,12, 4, 4, 1, 1, 0 }; -static const uint8 t9HB_const[36] = +static const uint8_t t9HB_const[36] = { 7, 5, 9,14,15, 7, 6, 4, 5, 5, 6, 7, 7, 6, 8, 8, 8, 5, 15, 6, 9,10, 5, 1,11, 7, 9, 6, 4, 1,14, 4, 6, 2, 6, 0 }; -static const uint8 t10HB_const[64] = +static const uint8_t t10HB_const[64] = {1,2,10,23,35,30,12,17,3,3,8,12,18,21,12,7,11,9,15,21,32, 40,19,6,14,13,22,34,46,23,18,7,20,19,33,47,27,22,9,3,31,22, 41,26,21,20,5,3,14,13,10,11,16,6,5,1,9,8,7,8,4,4,2,0 }; -static const uint8 t11HB_const[64] = +static const uint8_t t11HB_const[64] = {3,4,10,24,34,33,21,15,5,3,4,10,32,17,11,10,11,7,13,18,30, 31,20,5,25,11,19,59,27,18,12,5,35,33,31,58,30,16,7,5,28,26, 32,19,17,15,8,14,14,12,9,13,14,9,4,1,11,4,6,6,6,3,2,0 }; -static const uint8 t12HB_const[64] = +static const uint8_t t12HB_const[64] = {9,6,16,33,41,39,38,26,7,5,6,9,23,16,26,11,17,7,11,14,21, 30,10,7,17,10,15,12,18,28,14,5,32,13,22,19,18,16,9,5,40,17, 31,29,17,13,4,2,27,12,11,15,10,7,4,1,27,12,8,12,6,3,1,0 }; -static const uint8 t13HB_const[256] = +static const uint8_t t13HB_const[256] = {1,5,14,21,34,51,46,71,42,52,68,52,67,44,43,19,3,4,12,19,31,26,44,33,31,24,32, 24,31,35,22,14,15,13,23,36,59,49,77,65,29,40,30,40,27,33,42,16,22,20,37,61,56, 79,73,64,43,76,56,37,26,31,25,14,35,16,60,57,97,75,114,91,54,73,55,41,48,53, @@ -234,7 +235,7 @@ static const uint8 t13HB_const[256] = 45,21,34,64,56,50,49,45,31,19,12,15,10,7,6,3,48,23,20,39,36,35,53,21,16,23,13, 10,6,1,4,2,16,15,17,27,25,20,29,11,17,12,16,8,1,1,0,1 }; -static const uint8 t15HB_const[256] = +static const uint8_t t15HB_const[256] = {7,12,18,53,47,76,124,108,89,123,108,119,107,81,122,63,13,5,16,27,46,36,61,51, 42,70,52,83,65,41,59,36,19,17,15,24,41,34,59,48,40,64,50,78,62,80,56,33,29,28, 25,43,39,63,55,93,76,59,93,72,54,75,50,29,52,22,42,40,67,57,95,79,72,57,89,69, @@ -246,7 +247,7 @@ static const uint8 t15HB_const[256] = 24,16,22,13,14,7,91,44,39,38,34,63,52,45,31,52,28,19,14,8,9,3,123,60,58,53,47, 43,32,22,37,24,17,12,15,10,2,1,71,37,34,30,28,20,17,26,21,16,10,6,8,6,2,0}; -static const uint16 t16HB_const[256] = +static const uint16_t t16HB_const[256] = {1,5,14,44,74,63,110,93,172,149,138,242,225,195,376,17,3,4,12,20,35,62,53,47, 83,75,68,119,201,107,207,9,15,13,23,38,67,58,103,90,161,72,127,117,110,209, 206,16,45,21,39,69,64,114,99,87,158,140,252,212,199,387,365,26,75,36,68,65, @@ -261,7 +262,7 @@ static const uint16 t16HB_const[256] = 358,711,709,866,1734,871,3458,870,434,0,12,10,7,11,10,17,11,9,13,12,10,7,5,3, 1,3}; -static const uint16 t24HB_const[256] = +static const uint16_t t24HB_const[256] = {15,13,46,80,146,262,248,434,426,669,653,649,621,517,1032,88,14,12,21,38,71, 130,122,216,209,198,327,345,319,297,279,42,47,22,41,74,68,128,120,221,207,194, 182,340,315,295,541,18,81,39,75,70,134,125,116,220,204,190,178,325,311,293, @@ -276,7 +277,7 @@ static const uint16 t24HB_const[256] = 374,369,365,361,357,2,1033,280,278,274,267,264,259,382,378,372,367,363,360, 358,356,0,43,20,19,17,15,13,11,9,7,6,4,7,5,3,1,3}; -static const uint32 tab1315_const[256] = +static const uint32_t tab1315_const[256] = { 0x010003,0x050005,0x070006,0x080008,0x090008,0x0a0009,0x0a000a,0x0b000a, 0x0a000a,0x0b000b,0x0c000b,0x0c000c,0x0d000c,0x0d000c,0x0e000d,0x0e000e, 0x040005,0x060005,0x080007,0x090008,0x0a0009,0x0a0009,0x0b000a,0x0b000a, @@ -310,18 +311,18 @@ static const uint32 tab1315_const[256] = 0x0d000d,0x0e000d,0x0f000d,0x10000d,0x10000d,0x10000d,0x11000d,0x10000e, 0x11000e,0x11000e,0x12000e,0x12000e,0x15000f,0x14000f,0x15000f,0x12000f }; -static const uint32 tab01_const[16] = +static const uint32_t tab01_const[16] = { 0x10004,0x50005,0x50005,0x70006,0x50005,0x80006,0x70006,0x90007, 0x50005,0x70006,0x70006,0x90007,0x70006,0x90007,0x90007,0xa0008 }; -static const uint32 tab23_const[ 9] = +static const uint32_t tab23_const[ 9] = { 0x10002,0x40003,0x70007,0x40004,0x50004,0x70007,0x60006,0x70007,0x80008 }; -static const uint32 tab56_const[16] = +static const uint32_t tab56_const[16] = { 0x10003,0x40004,0x70006,0x80008,0x40004,0x50004,0x80006,0x90007, 0x70005,0x80006,0x90007,0xa0008,0x80007,0x80007,0x90008,0xa0009 }; -static const uint32 tab789_const[36] = +static const uint32_t tab789_const[36] = {0x00100803,0x00401004,0x00701c06,0x00902407,0x00902409,0x00a0280a,0x00401004, 0x00601005,0x00801806,0x00902807,0x00902808,0x00a0280a,0x00701c05,0x00701806, 0x00902007,0x00a02808,0x00a02809,0x00b02c0a,0x00802407,0x00902807,0x00a02808, @@ -329,7 +330,7 @@ static const uint32 tab789_const[36] = 0x00b0300a,0x00c0300b,0x00902809,0x00a02809,0x00b02c0a,0x00c02c0a,0x00c0340b, 0x00c0340b}; -static const uint32 tabABC_const[64] = +static const uint32_t tabABC_const[64] = {0x00100804,0x00401004,0x00701806,0x00902008,0x00a02409,0x00a0280a,0x00a0240a, 0x00b0280a,0x00401004,0x00601405,0x00801806,0x00902007,0x00a02809,0x00b02809, 0x00a0240a,0x00a0280a,0x00701806,0x00801c06,0x00902007,0x00a02408,0x00b02809, @@ -341,7 +342,7 @@ static const uint32 tabABC_const[64] = 0x00a0240a,0x00a0240a,0x00b0280a,0x00c02c0b,0x00c0300b,0x00d0300b,0x00d0300b, 0x00d0300c}; -static const uint32 tab1624_const[256] = +static const uint32_t tab1624_const[256] = {0x00010004,0x00050005,0x00070007,0x00090008,0x000a0009,0x000a000a,0x000b000a, 0x000b000b,0x000c000b,0x000c000c,0x000c000c,0x000d000c,0x000d000c,0x000d000c, 0x000e000d,0x000a000a,0x00040005,0x00060006,0x00080007,0x00090008,0x000a0009, @@ -380,34 +381,34 @@ static const uint32 tab1624_const[256] = 0x000c0009,0x000c0009,0x000c0009,0x000d0009,0x000d0009,0x000d0009,0x000d000a, 0x000d000a,0x000d000a,0x000d000a,0x000a0006}; -static const uint8 t1l_const[8] = {1,3,2,3,1,4,3,5}; -static const uint8 t2l_const[9] = {1,3,6,3,3,5,5,5,6}; -static const uint8 t3l_const[9] = {2,2,6,3,2,5,5,5,6}; -static const uint8 t5l_const[16] = {1,3,6,7,3,3,6,7,6,6,7,8,7,6,7,8}; -static const uint8 t6l_const[16] = {3,3,5,7,3,2,4,5,4,4,5,6,6,5,6,7}; +static const uint8_t t1l_const[8] = {1,3,2,3,1,4,3,5}; +static const uint8_t t2l_const[9] = {1,3,6,3,3,5,5,5,6}; +static const uint8_t t3l_const[9] = {2,2,6,3,2,5,5,5,6}; +static const uint8_t t5l_const[16] = {1,3,6,7,3,3,6,7,6,6,7,8,7,6,7,8}; +static const uint8_t t6l_const[16] = {3,3,5,7,3,2,4,5,4,4,5,6,6,5,6,7}; -static const uint8 t7l_const[36] = +static const uint8_t t7l_const[36] = {1,3,6,8,8,9,3,4,6,7,7,8,6,5,7,8,8,9,7,7,8,9,9,9,7,7,8,9,9,10,8,8,9,10,10,10}; -static const uint8 t8l_const[36] = +static const uint8_t t8l_const[36] = {2,3,6,8,8,9,3,2,4,8,8,8,6,4,6,8,8,9,8,8,8,9,9,10,8,7,8,9,10,10,9,8,9,9,11,11}; -static const uint8 t9l_const[36] = +static const uint8_t t9l_const[36] = {3,3,5,6,8,9,3,3,4,5,6,8,4,4,5,6,7,8,6,5,6,7,7,8,7,6,7,7,8,9,8,7,8,8,9,9}; -static const uint8 t10l_const[64] = +static const uint8_t t10l_const[64] = {1,3,6,8,9,9,9,10,3,4,6,7,8,9,8,8,6,6,7,8,9,10,9,9,7,7,8,9,10,10,9,10,8,8,9,10, 10,10,10,10,9,9,10,10,11,11,10,11,8,8,9,10,10,10,11,11,9,8,9,10,10,11,11,11}; -static const uint8 t11l_const[64] = +static const uint8_t t11l_const[64] = {2,3,5,7,8,9,8,9,3,3,4,6,8,8,7,8,5,5,6,7,8,9,8,8,7,6,7,9,8,10,8,9,8,8,8,9,9,10, 9,10,8,8,9,10,10,11,10,11,8,7,7,8,9,10,10,10,8,7,8,9,10,10,10,10}; -static const uint8 t12l_const[64] = +static const uint8_t t12l_const[64] = {4,3,5,7,8,9,9,9,3,3,4,5,7,7,8,8,5,4,5,6,7,8,7,8,6,5,6,6,7,8,8,8,7,6,7,7,8, 8,8,9,8,7,8,8,8,9,8,9,8,7,7,8,8,9,9,10,9,8,8,9,9,9,9,10}; -static const uint8 t13l_const[256] = +static const uint8_t t13l_const[256] = {1,4,6,7,8,9,9,10,9,10,11,11,12,12,13,13,3,4,6,7,8,8,9,9,9,9,10,10,11,12,12,12, 6,6,7,8,9,9,10,10,9,10,10,11,11,12,13,13,7,7,8,9,9,10,10,10,10,11,11,11,11,12, 13,13,8,7,9,9,10,10,11,11,10,11,11,12,12,13,13,14,9,8,9,10,10,10,11,11,11,11, @@ -419,7 +420,7 @@ static const uint8 t13l_const[256] = 16,16,13,12,12,13,13,13,15,14,14,17,15,15,15,17,16,16,12,12,13,14,14,14,15,14, 15,15,16,16,19,18,19,16}; -static const uint8 t15l_const[256] = +static const uint8_t t15l_const[256] = {3,4,5,7,7,8,9,9,9,10,10,11,11,11,12,13,4,3,5,6,7,7,8,8,8,9,9,10,10,10,11,11,5, 5,5,6,7,7,8,8,8,9,9,10,10,11,11,11,6,6,6,7,7,8,8,9,9,9,10,10,10,11,11,11,7,6, 7,7,8,8,9,9,9,9,10,10,10,11,11,11,8,7,7,8,8,8,9,9,9,9,10,10,11,11,11,12,9,7,8, @@ -430,7 +431,7 @@ static const uint8 t15l_const[256] = 11,11,11,11,12,12,12,12,12,13,13,12,11,11,11,11,11,11,11,12,12,12,12,13,13,12, 13,12,11,11,11,11,11,11,12,12,12,12,12,13,13,13,13}; -static const uint8 t16l_const[256] = +static const uint8_t t16l_const[256] = {1,4,6,8,9,9,10,10,11,11,11,12,12,12,13,9,3,4,6,7,8,9,9,9,10,10,10,11,12,11,12, 8,6,6,7,8,9,9,10,10,11,10,11,11,11,12,12,9,8,7,8,9,9,10,10,10,11,11,12,12,12, 13,13,10,9,8,9,9,10,10,11,11,11,12,12,12,13,13,13,9,9,8,9,9,10,11,11,12,11,12, @@ -442,7 +443,7 @@ static const uint8 t16l_const[256] = 17,15,11,13,13,11,12,14,14,13,14,14,15,16,15,17,15,14,11,9,8,8,9,9,10,10,10, 11,11,11,11,11,11,11,8}; -static const uint8 t24l_const[256] = +static const uint8_t t24l_const[256] = {4,4,6,7,8,9,9,10,10,11,11,11,11,11,12,9,4,4,5,6,7,8,8,9,9,9,10,10,10,10,10,8, 6,5,6,7,7,8,8,9,9,9,9,10,10,10,11,7,7,6,7,7,8,8,8,9,9,9,9,10,10,10,10,7,8,7,7, 8,8,8,8,9,9,9,10,10,10,10,11,7,9,7,8,8,8,8,9,9,9,9,10,10,10,10,10,7,9,8,8,8,8, @@ -491,8 +492,8 @@ static const struct huffcodebig ht_big[HTN] = static const struct { - uint32 region0_cnt; - uint32 region1_cnt; + uint32_t region0_cnt; + uint32_t region1_cnt; } subdv_table[23] = { {0, 0}, /* 0 bands */ {0, 0}, /* 1 bands */ @@ -519,7 +520,7 @@ static const struct {6, 7}, /* 22 bands */ }; -static const uint32 sfBand[6][23] = +static const uint32_t sfBand[6][23] = { /* Table B.2.b: 22.05 kHz */ {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576}, @@ -747,9 +748,13 @@ static const int order[32] = { 0, 1, 16, 17, 8, 9, 24, 25, 4, 5, 20, 21, 12, 13, 28, 29, 2, 3, 18, 19,10,11, 26, 27, 6, 7, 22, 23, 14, 15, 30, 31 }; -static const int bitr_index[2][15] = -{ {0, 8,16,24,32,40,48,56, 64, 80, 96,112,128,144,160}, - {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320} }; +static const long sampr_index[2][3] = +{ { 22050, 24000, 16000 }, /* MPEG 2 */ + { 44100, 48000, 32000 } }; /* MPEG 1 */ + +static const long bitr_index[2][15] = +{ {0, 8,16,24,32,40,48,56, 64, 80, 96,112,128,144,160}, /* MPEG 2 */ + {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320} }; /* MPEG 1 */ static const int num_bands[3][15] = { {0,10,10,10,10,12,14,16, 20, 22, 24, 26, 28, 30, 32}, @@ -837,35 +842,55 @@ static const int win_const[18][4] = { { 134, -146,-3352,-3072 } }; /* forward declarations */ -int HuffmanCode( short *ix, int *xr, uint32 begin, uint32 end, int table); -int HuffmanCod1( short *ix, int *xr, uint32 begin, uint32 end, int table); -void putbits(uint32 val, uint32 nbit); -int find_best_2( short *ix, uint32 start, uint32 end, const uint32 *table, - uint32 len, int *bits); -int find_best_3( short *ix, uint32 start, uint32 end, const uint32 *table, - uint32 len, int *bits); -int count_bit1 ( short *ix, uint32 start, uint32 end, int *bits ); -int count_bigv ( short *ix, uint32 start, uint32 end, int table0, int table1, +static int HuffmanCode( short *ix, int *xr, uint32_t begin, uint32_t end, int table); +static int HuffmanCod1( short *ix, int *xr, uint32_t begin, uint32_t end, int table); +static void putbits(uint32_t val, uint32_t nbit); +static int find_best_2( short *ix, uint32_t start, uint32_t end, const uint32_t *table, + uint32_t len, int *bits); +static int find_best_3( short *ix, uint32_t start, uint32_t end, const uint32_t *table, + uint32_t len, int *bits); +static int count_bit1 ( short *ix, uint32_t start, uint32_t end, int *bits ); +static int count_bigv ( short *ix, uint32_t start, uint32_t end, int table0, int table1, int *bits); -void encodeSideInfo( side_info_t si[2][2] ) +static void encodeSideInfo( side_info_t si[2][2] ) { int gr, ch, header; - uint32 cc=0, sz=0; + uint32_t cc=0, sz=0; - header = 0xfff00000; - header |= cfg.mpg.type << 19; /* mp3 type: 1 */ - header |= 1 << 17; /* mp3 layer: 1 */ - header |= 1 << 16; /* mp3 crc: 0 */ + /* + * MPEG header layout: + * AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM + * A (31-21) = frame sync + * B (20-19) = MPEG type + * C (18-17) = MPEG layer + * D (16) = protection bit + * E (15-12) = bitrate index + * F (11-10) = samplerate index + * G (9) = padding bit + * H (8) = private bit + * I (7-6) = channel mode + * J (5-4) = mode extension (jstereo only) + * K (3) = copyright bit + * L (2) = original + * M (1-0) = emphasis + */ + + header = (0xfff00000) | /* frame sync (AAAAAAAAA AAA) + mp3 type (upper): 1 (B) */ + (0x01 << 17) | /* mp3 layer: 01 (CC) */ + ( 0x1 << 16) | /* mp3 crc: 1 (D) */ + ( 0x1 << 2); /* mp3 org: 1 (L) */ + header |= cfg.mpg.type << 19; header |= cfg.mpg.bitr_id << 12; header |= cfg.mpg.smpl_id << 10; header |= cfg.mpg.padding << 9; header |= cfg.mpg.mode << 6; - header |= 1 << 2; /* mp3 original: 1 */ + /* no emphasis (bits 0-1) */ putbits( header, 32 ); - if(cfg.mpg.type) + if(cfg.mpg.type == 1) { /* MPEG1 */ if(cfg.channels == 2) { putlong( 0, 20); } else { putlong( 0, 18); } @@ -910,7 +935,7 @@ void encodeSideInfo( side_info_t si[2][2] ) /* Note the discussion of huffmancodebits() on pages 28 and 29 of the IS, as well as the definitions of the side information on pages 26 and 27. */ -void Huffmancodebits( short *ix, int *xr, side_info_t *gi ) +static void Huffmancodebits( short *ix, int *xr, side_info_t *gi ) { int region1 = gi->address1; int region2 = gi->address2; @@ -944,10 +969,10 @@ void Huffmancodebits( short *ix, int *xr, side_info_t *gi ) } } -int HuffmanCod1( short *ix, int *xr, uint32 begin, uint32 end, int tbl) +int HuffmanCod1( short *ix, int *xr, uint32_t begin, uint32_t end, int tbl) { - uint32 cc=0, sz=0; - uint32 i, d, p; + uint32_t cc=0, sz=0; + uint32_t i, d, p; int sumbit=0, s=0, l=0, v, w, x, y; #define sgnv (xr[i+0] < 0 ? 1 : 0) #define sgnw (xr[i+1] < 0 ? 1 : 0) @@ -995,10 +1020,10 @@ int HuffmanCod1( short *ix, int *xr, uint32 begin, uint32 end, int tbl) } /* Implements the pseudocode of page 98 of the IS */ -int HuffmanCode( short *ix, int *xr, uint32 begin, uint32 end, int table) +int HuffmanCode( short *ix, int *xr, uint32_t begin, uint32_t end, int table) { - uint32 cc=0, sz=0, code; - uint32 i, xl=0, yl=0, idx; + uint32_t cc=0, sz=0, code; + uint32_t i, xl=0, yl=0, idx; int x, y, bit, sumbit=0; #define sign_x (xr[i+0] < 0 ? 1 : 0) #define sign_y (xr[i+1] < 0 ? 1 : 0) @@ -1008,9 +1033,9 @@ int HuffmanCode( short *ix, int *xr, uint32 begin, uint32 end, int table) if( table > 15 ) { /* ESC-table is used */ - uint32 linbits = ht_big[table-16].linbits; - uint16 *hffcode = table < 24 ? t16HB : t24HB; - uint8 *hlen = table < 24 ? t16l : t24l; + uint32_t linbits = ht_big[table-16].linbits; + uint16_t *hffcode = table < 24 ? t16HB : t24HB; + uint8_t *hlen = table < 24 ? t16l : t24l; for(i=begin; i> 5; val = val & (0xffffffff >> (32 - nbit)); - /* data fit in one uint32 */ + /* data fit in one uint32_t */ if(((new_bitpos - 1) >> 5) == ptrpos) CodedData.bbuf[ptrpos] |= val << ((32 - new_bitpos) & 31); else @@ -1114,9 +1139,9 @@ void putbits(uint32 val, uint32 nbit) /* of the Huffman tables as defined in the IS (Table B.7), and will not */ /* work with any arbitrary tables. */ /***************************************************************************/ -int choose_table( short *ix, uint32 begin, uint32 end, int *bits ) +int choose_table( short *ix, uint32_t begin, uint32_t end, int *bits ) { - uint32 i; + uint32_t i; int max, table0, table1; for(i=begin,max=0; icount1 = 0; @@ -1899,151 +1924,545 @@ void mdct_long(int *out, int *in) out[16] = ct - st; } -static int find_bitrate_index(int type, int bitrate) +static int find_bitrate_index(int type, int bitrate, bool stereo) { - int i; + if (type == 1 && !stereo && bitrate > 160) + bitrate = 160; - for(i=0;i<14;i++) - if(bitrate == bitr_index[type][i]) - break; - - return i; + return ci->round_value_to_list32(bitrate, + &bitr_index[type][1], 14, true) + 1; } static int find_samplerate_index(long freq, int *mp3_type) -{ /* MPEG 2 */ /* MPEG 1 */ - static long mpeg[2][3] = { {22050, 24000, 16000}, {44100, 48000, 32000} }; - int mpg, rate; - - /* set default values: MPEG1 at 44100/s */ - *mp3_type = 1; - - for(mpg=0; mpg<2; mpg++) - for(rate=0; rate<3; rate++) - if(freq == mpeg[mpg][rate]) - { *mp3_type = mpg; return rate; } - - return 0; -} - -void init_mp3_encoder_engine(bool stereo, int quality, int sample_rate) { - /* keep in sync with rec_quality_info_afmt in id3.h/.c */ - static int bitr_s[9] = { 64, 96, 128, 160, 192, 224, 320, 64, 64 }; - static int bitr_m[9] = { 64, 96, 128, 160, 160, 160, 160, 64, 64 }; - uint32 avg_byte_per_frame; - - if(quality == 0 && stereo && sample_rate >= 32000) - { /* use MPEG2 format */ - sample_rate >>= 1; - cfg.resample = 1; - cfg.granules = 1; - } - else - { /* use MPEG1 format */ - cfg.resample = 0; - cfg.granules = 2; - } - - cfg.byte_order = order_bigEndian; - cfg.samplerate = sample_rate; - cfg.channels = stereo ? 2 : 1; - cfg.mpg.mode = stereo ? 0 : 3; /* 0=stereo, 3=mono */ - cfg.mpg.bitrate = stereo ? bitr_s[quality] : bitr_m[quality]; - cfg.mpg.smpl_id = find_samplerate_index(cfg.samplerate, &cfg.mpg.type); - cfg.mpg.bitr_id = find_bitrate_index(cfg.mpg.type, cfg.mpg.bitrate); - cfg.mpg.num_bands = num_bands[stereo ? cfg.mpg.type : 2][cfg.mpg.bitr_id]; - - memcpy(scalefac, sfBand[cfg.mpg.smpl_id + 3*cfg.mpg.type], sizeof(scalefac)); - memset(mfbuf , 0 , sizeof(mfbuf )); - memset(mdct_freq , 0 , sizeof(mdct_freq )); - memset(enc_data , 0 , sizeof(enc_data )); - memset(sb_data , 0 , sizeof(sb_data )); - memset(&CodedData, 0 , sizeof(CodedData )); - memcpy(ca , ca_const , sizeof(ca )); - memcpy(cs , cs_const , sizeof(cs )); - memcpy(cx , cx_const , sizeof(cx )); - memcpy(win , win_const , sizeof(win )); - memcpy(enwindow , enwindow_const , sizeof(enwindow )); - memcpy(int2idx , int2idx_const , sizeof(int2idx )); - memcpy(ht_count , ht_count_const , sizeof(ht_count )); - memcpy( tab01 , tab01_const , sizeof(tab01 )); - memcpy( tab23 , tab23_const , sizeof(tab23 )); - memcpy( tab56 , tab56_const , sizeof(tab56 )); - memcpy( tab1315 , tab1315_const , sizeof(tab1315 )); - memcpy( tab1624 , tab1624_const , sizeof(tab1624 )); - memcpy( tab789 , tab789_const , sizeof(tab789 )); - memcpy( tabABC , tabABC_const , sizeof(tabABC )); - memcpy( t1HB , t1HB_const , sizeof(t1HB )); - memcpy( t2HB , t2HB_const , sizeof(t2HB )); - memcpy( t3HB , t3HB_const , sizeof(t3HB )); - memcpy( t5HB , t5HB_const , sizeof(t5HB )); - memcpy( t6HB , t6HB_const , sizeof(t6HB )); - memcpy( t7HB , t7HB_const , sizeof(t7HB )); - memcpy( t8HB , t8HB_const , sizeof(t8HB )); - memcpy( t9HB , t9HB_const , sizeof(t9HB )); - memcpy(t10HB , t10HB_const , sizeof(t10HB )); - memcpy(t11HB , t11HB_const , sizeof(t11HB )); - memcpy(t12HB , t12HB_const , sizeof(t12HB )); - memcpy(t13HB , t13HB_const , sizeof(t13HB )); - memcpy(t15HB , t15HB_const , sizeof(t15HB )); - memcpy(t16HB , t16HB_const , sizeof(t16HB )); - memcpy(t24HB , t24HB_const , sizeof(t24HB )); - memcpy( t1l , t1l_const , sizeof(t1l )); - memcpy( t2l , t2l_const , sizeof(t2l )); - memcpy( t3l , t3l_const , sizeof(t3l )); - memcpy( t5l , t5l_const , sizeof(t5l )); - memcpy( t6l , t6l_const , sizeof(t6l )); - memcpy( t7l , t7l_const , sizeof(t7l )); - memcpy( t8l , t8l_const , sizeof(t8l )); - memcpy( t9l , t9l_const , sizeof(t9l )); - memcpy(t10l , t10l_const , sizeof(t10l )); - memcpy(t11l , t11l_const , sizeof(t11l )); - memcpy(t12l , t12l_const , sizeof(t12l )); - memcpy(t13l , t13l_const , sizeof(t13l )); - memcpy(t15l , t15l_const , sizeof(t15l )); - memcpy(t16l , t16l_const , sizeof(t16l )); - memcpy(t24l , t24l_const , sizeof(t24l )); - memcpy(ht , ht_const , sizeof(ht )); - - ht[ 0].table = NULL; ht[ 0].hlen = NULL; /* Apparently not used */ - ht[ 1].table = t1HB; ht[ 1].hlen = t1l; - ht[ 2].table = t2HB; ht[ 2].hlen = t2l; - ht[ 3].table = t3HB; ht[ 3].hlen = t3l; - ht[ 4].table = NULL; ht[ 4].hlen = NULL; /* Apparently not used */ - ht[ 5].table = t5HB; ht[ 5].hlen = t5l; - ht[ 6].table = t6HB; ht[ 6].hlen = t6l; - ht[ 7].table = t7HB; ht[ 7].hlen = t7l; - ht[ 8].table = t8HB; ht[ 8].hlen = t8l; - ht[ 9].table = t9HB; ht[ 9].hlen = t9l; - ht[10].table = t10HB; ht[10].hlen = t10l; - ht[11].table = t11HB; ht[11].hlen = t11l; - ht[12].table = t12HB; ht[12].hlen = t12l; - ht[13].table = t13HB; ht[13].hlen = t13l; - ht[14].table = NULL; ht[14].hlen = NULL; /* Apparently not used */ - ht[15].table = t15HB; ht[15].hlen = t15l; - - /* Figure average number of 'bytes' per frame */ - avg_byte_per_frame = SAMPL2 * 16000 * cfg.mpg.bitrate / (2 - cfg.mpg.type); - avg_byte_per_frame = avg_byte_per_frame / cfg.samplerate; - cfg.byte_per_frame = avg_byte_per_frame / 64; - cfg.frac_per_frame = avg_byte_per_frame & 63; - cfg.slot_lag = 0; - cfg.sideinfo_len = 32 + (cfg.mpg.type ? (cfg.channels == 1 ? 136 : 256) - : (cfg.channels == 1 ? 72 : 136)); + int mpeg = freq >= (32000+24000)/2 ? 1 : 0; + int i = ci->round_value_to_list32(freq, sampr_index[mpeg], 3, true); + *mp3_type = mpeg; + return i; } +bool init_mp3_encoder_engine(int sample_rate, + int num_channels, + struct encoder_config *enc_cfg) +{ + const bool stereo = num_channels > 1; + uint32_t avg_byte_per_frame; + + cfg.channels = stereo ? 2 : 1; + cfg.mpg.mode = stereo ? 0 : 3; /* 0=stereo, 3=mono */ + cfg.mpg.smpl_id = find_samplerate_index(sample_rate, &cfg.mpg.type); + cfg.samplerate = sampr_index[cfg.mpg.type][cfg.mpg.smpl_id]; + cfg.mpg.bitr_id = find_bitrate_index(cfg.mpg.type, + enc_cfg->mp3_enc.bitrate, + stereo); + cfg.mpg.bitrate = bitr_index[cfg.mpg.type][cfg.mpg.bitr_id]; + cfg.mpg.num_bands = num_bands[stereo ? cfg.mpg.type : 2][cfg.mpg.bitr_id]; + + if (cfg.mpg.type == 1) + { + cfg.granules = 2; + pcm_chunk_size = PCM_CHUNK_SIZE1; + samp_per_frame = SAMP_PER_FRAME1; + } + else + { + cfg.granules = 1; + pcm_chunk_size = PCM_CHUNK_SIZE2; + samp_per_frame = SAMP_PER_FRAME2; + } + + memcpy(scalefac, sfBand[cfg.mpg.smpl_id + 3*cfg.mpg.type], sizeof(scalefac)); + memset(mfbuf , 0 , sizeof(mfbuf )); + memset(mdct_freq , 0 , sizeof(mdct_freq )); + memset(enc_data , 0 , sizeof(enc_data )); + memset(sb_data , 0 , sizeof(sb_data )); + memset(&CodedData, 0 , sizeof(CodedData )); + memcpy(ca , ca_const , sizeof(ca )); + memcpy(cs , cs_const , sizeof(cs )); + memcpy(cx , cx_const , sizeof(cx )); + memcpy(win , win_const , sizeof(win )); + memcpy(enwindow , enwindow_const , sizeof(enwindow )); + memcpy(int2idx , int2idx_const , sizeof(int2idx )); + memcpy(ht_count , ht_count_const , sizeof(ht_count )); + memcpy( tab01 , tab01_const , sizeof(tab01 )); + memcpy( tab23 , tab23_const , sizeof(tab23 )); + memcpy( tab56 , tab56_const , sizeof(tab56 )); + memcpy( tab1315 , tab1315_const , sizeof(tab1315 )); + memcpy( tab1624 , tab1624_const , sizeof(tab1624 )); + memcpy( tab789 , tab789_const , sizeof(tab789 )); + memcpy( tabABC , tabABC_const , sizeof(tabABC )); + memcpy( t1HB , t1HB_const , sizeof(t1HB )); + memcpy( t2HB , t2HB_const , sizeof(t2HB )); + memcpy( t3HB , t3HB_const , sizeof(t3HB )); + memcpy( t5HB , t5HB_const , sizeof(t5HB )); + memcpy( t6HB , t6HB_const , sizeof(t6HB )); + memcpy( t7HB , t7HB_const , sizeof(t7HB )); + memcpy( t8HB , t8HB_const , sizeof(t8HB )); + memcpy( t9HB , t9HB_const , sizeof(t9HB )); + memcpy(t10HB , t10HB_const , sizeof(t10HB )); + memcpy(t11HB , t11HB_const , sizeof(t11HB )); + memcpy(t12HB , t12HB_const , sizeof(t12HB )); + memcpy(t13HB , t13HB_const , sizeof(t13HB )); + memcpy(t15HB , t15HB_const , sizeof(t15HB )); + memcpy(t16HB , t16HB_const , sizeof(t16HB )); + memcpy(t24HB , t24HB_const , sizeof(t24HB )); + memcpy( t1l , t1l_const , sizeof(t1l )); + memcpy( t2l , t2l_const , sizeof(t2l )); + memcpy( t3l , t3l_const , sizeof(t3l )); + memcpy( t5l , t5l_const , sizeof(t5l )); + memcpy( t6l , t6l_const , sizeof(t6l )); + memcpy( t7l , t7l_const , sizeof(t7l )); + memcpy( t8l , t8l_const , sizeof(t8l )); + memcpy( t9l , t9l_const , sizeof(t9l )); + memcpy(t10l , t10l_const , sizeof(t10l )); + memcpy(t11l , t11l_const , sizeof(t11l )); + memcpy(t12l , t12l_const , sizeof(t12l )); + memcpy(t13l , t13l_const , sizeof(t13l )); + memcpy(t15l , t15l_const , sizeof(t15l )); + memcpy(t16l , t16l_const , sizeof(t16l )); + memcpy(t24l , t24l_const , sizeof(t24l )); + memcpy(ht , ht_const , sizeof(ht )); + + ht[ 0].table = NULL; ht[ 0].hlen = NULL; /* Apparently not used */ + ht[ 1].table = t1HB; ht[ 1].hlen = t1l; + ht[ 2].table = t2HB; ht[ 2].hlen = t2l; + ht[ 3].table = t3HB; ht[ 3].hlen = t3l; + ht[ 4].table = NULL; ht[ 4].hlen = NULL; /* Apparently not used */ + ht[ 5].table = t5HB; ht[ 5].hlen = t5l; + ht[ 6].table = t6HB; ht[ 6].hlen = t6l; + ht[ 7].table = t7HB; ht[ 7].hlen = t7l; + ht[ 8].table = t8HB; ht[ 8].hlen = t8l; + ht[ 9].table = t9HB; ht[ 9].hlen = t9l; + ht[10].table = t10HB; ht[10].hlen = t10l; + ht[11].table = t11HB; ht[11].hlen = t11l; + ht[12].table = t12HB; ht[12].hlen = t12l; + ht[13].table = t13HB; ht[13].hlen = t13l; + ht[14].table = NULL; ht[14].hlen = NULL; /* Apparently not used */ + ht[15].table = t15HB; ht[15].hlen = t15l; + + /* Figure average number of 'bytes' per frame */ + avg_byte_per_frame = SAMPL2 * 16000 * cfg.mpg.bitrate / (2 - cfg.mpg.type); + avg_byte_per_frame = avg_byte_per_frame / cfg.samplerate; + cfg.byte_per_frame = avg_byte_per_frame / 64; + cfg.frac_per_frame = avg_byte_per_frame & 63; + cfg.slot_lag = 0; + cfg.sideinfo_len = 32 + (cfg.mpg.type ? (cfg.channels == 1 ? 136 : 256) + : (cfg.channels == 1 ? 72 : 136)); + + return true; +} + +static void to_mono_mm(void) ICODE_ATTR; +static void to_mono_mm(void) +{ + /* |llllllllllllllll|rrrrrrrrrrrrrrrr| => + * |mmmmmmmmmmmmmmmm|mmmmmmmmmmmmmmmm| + */ + uint32_t *samp = (uint32_t *)&mfbuf[2*512]; + uint32_t *samp_end = samp + samp_per_frame; + + inline void to_mono(uint32_t **samp) + { + int32_t lr = **samp; + int32_t m = ((int16_t)lr + (lr >> 16)) >> 1; + *(*samp)++ = (m << 16) | (uint16_t)m; + } /* to_mono */ + + do + { + to_mono(&samp); + to_mono(&samp); + to_mono(&samp); + to_mono(&samp); + to_mono(&samp); + to_mono(&samp); + to_mono(&samp); + to_mono(&samp); + } + while (samp < samp_end); +} /* to_mono_mm */ + +#ifdef ROCKBOX_LITTLE_ENDIAN +/* Swaps a frame to big endian */ +static inline void byte_swap_frame32(uint32_t *dst, uint32_t *src, + size_t size) ICODE_ATTR; +static inline void byte_swap_frame32(uint32_t *dst, uint32_t *src, + size_t size) +{ + uint32_t *src_end = SKIPBYTES(src, size); + + do + { + *dst++ = swap32(*src++); + *dst++ = swap32(*src++); + *dst++ = swap32(*src++); + *dst++ = swap32(*src++); + *dst++ = swap32(*src++); + *dst++ = swap32(*src++); + *dst++ = swap32(*src++); + *dst++ = swap32(*src++); + } + while(src < src_end); +} /* byte_swap_frame32 */ +#endif /* ROCKBOX_LITTLE_ENDIAN */ + +static void encode_frame(char *buffer, struct enc_chunk_hdr *chunk) ICODE_ATTR; +static void encode_frame(char *buffer, struct enc_chunk_hdr *chunk) +{ + int gr, gr_cnt; + int max, min; + + /* encode one mp3 frame in this loop */ + CodedData.bitpos = 0; + memset(CodedData.bbuf, 0, sizeof(CodedData.bbuf)); + + if((cfg.slot_lag += cfg.frac_per_frame) >= 64) + { /* Padding for this frame */ + cfg.slot_lag -= 64; + cfg.mpg.padding = 1; + } + else + cfg.mpg.padding = 0; + + cfg.mean_bits = (8 * cfg.byte_per_frame + 8 * cfg.mpg.padding + - cfg.sideinfo_len) / cfg.granules / cfg.channels; + + /* shift out old samples */ + memcpy(mfbuf, mfbuf + 2*cfg.granules*576, 4*512); + + if (chunk->flags & CHUNKF_START_FILE) + { + /* prefix silent samples for encoder delay */ + memset(mfbuf + 2*512, 0, ENC_DELAY_SIZE); + /* read new samples to iram for further processing */ + memcpy(mfbuf + 2*512 + ENC_DELAY_SIZE/2, + buffer, pcm_chunk_size - ENC_DELAY_SIZE); + chunk->num_pcm = samp_per_frame - ENC_DELAY_SAMP; + } + else + { + /* read new samples to iram for further processing */ + memcpy(mfbuf + 2*512, buffer, pcm_chunk_size); + chunk->num_pcm = samp_per_frame; + } + + if (cfg.channels == 1) + to_mono_mm(); + + cfg.ResvSize = 0; + gr_cnt = cfg.granules * cfg.channels; + CodedData.bitpos = cfg.sideinfo_len; /* leave space for mp3 header */ + + for(gr=0; gr=0; --k) + { + int bu, bd; + bu = shft15(mdct[k]) * ca[k] + + shft15(mdct[-1-k]) * cs[k]; + bd = shft15(mdct[k]) * cs[k] - + shft15(mdct[-1-k]) * ca[k]; + mdct[-1-k] = bu; + mdct[ k ] = bd; + } + } + } + + max = min = 0; + for(k=0; k<576; k++) + { + mdct_freq[k] = shft13(mdct_freq[k]); + if(max < mdct_freq[k]) max = mdct_freq[k]; + if(min > mdct_freq[k]) min = mdct_freq[k]; + } + + max = (max > -min) ? max : -min; + cfg.cod_info[gr][ch].max_val = (long)max; + + /* calc new shift for higher integer precision */ + for(k=0; max<(0x3c00>>k); k++); + shift = 12 - k; + } + + cfg.cod_info[gr][ch].quantStep += + cfg.cod_info[gr][ch].additStep; + + /* bit and noise allocation */ + iteration_loop(mdct_freq, &cfg.cod_info[gr][ch], + gr_cnt--); + /* write the frame to the bitstream */ + Huffmancodebits(enc_data, mdct_freq, + &cfg.cod_info[gr][ch]); + + cfg.cod_info[gr][ch].quantStep -= + cfg.cod_info[gr][ch].additStep; + + if(cfg.granules == 1) + { + memcpy(sb_data[ch][0], sb_data[ch][1], + sizeof(sb_data[ch][0])); + } + } + } + + chunk->enc_size = cfg.byte_per_frame + cfg.mpg.padding; + + /* finish this chunk by adding sideinfo header data */ + CodedData.bitpos = 0; + encodeSideInfo( cfg.cod_info ); + +#ifdef ROCKBOX_BIG_ENDIAN + /* copy chunk to enc_buffer */ + memcpy(chunk->enc_data, CodedData.bbuf, chunk->enc_size); +#else + /* swap frame to big endian */ + byte_swap_frame32(chunk->enc_data, CodedData.bbuf, chunk->enc_size); +#endif +} /* encode_frame */ + +/* called very often - inline */ +static inline bool is_file_data_ok(struct enc_file_event_data *filed) ICODE_ATTR; +static inline bool is_file_data_ok(struct enc_file_event_data *filed) +{ + return filed->rec_file >= 0 && (long)filed->chunk->flags >= 0; +} /* is_event_ok */ + +/* called very often - inline */ +static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR; +static inline bool on_write_chunk(struct enc_file_event_data *data) +{ + if (!is_file_data_ok(data)) + return false; + + if (data->chunk->enc_data == NULL) + { +#ifdef ROCKBOX_HAS_LOGF + ci->logf("mp3 enc: NULL data"); +#endif + return true; + } + + if (ci->write(data->rec_file, data->chunk->enc_data, + data->chunk->enc_size) != (ssize_t)data->chunk->enc_size) + return false; + + data->num_pcm_samples += data->chunk->num_pcm; + return true; +} /* on_write_chunk */ + +static bool on_start_file(struct enc_file_event_data *data) +{ + if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0') + return false; + + data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC); + + if (data->rec_file < 0) + return false; + + /* reset sample count */ + data->num_pcm_samples = 0; + return true; +} /* on_start_file */ + +static bool on_end_file(struct enc_file_event_data *data) +{ + if (!is_file_data_ok(data)) + return false; + + ci->fsync(data->rec_file); + ci->close(data->rec_file); + data->rec_file = -1; + + return true; +} /* on_end_file */ + +static void on_rec_new_stream(struct enc_buffer_event_data *data) +{ + int num_frames = cfg.mpg.type == 1 ? + ENC_PADDING_FRAMES1 : ENC_PADDING_FRAMES2; + + if (data->flags & CHUNKF_END_FILE) + { + /* add silent frames to end - encoder will also be flushed for start + of next file if any */ + memset(res_buffer, 0, pcm_chunk_size); + + /* the initial chunk given for the end is at enc_wr_index */ + while (num_frames-- > 0) + { + data->chunk->enc_data = ENC_CHUNK_SKIP_HDR(data->chunk->enc_data, + data->chunk); + + encode_frame(res_buffer, data->chunk); + data->chunk->num_pcm = samp_per_frame; + + ci->enc_finish_chunk(); + data->chunk = ci->enc_get_chunk(); + } + } + else if (data->flags & CHUNKF_PRERECORD) + { + /* nothing to add and we cannot change prerecorded data */ + } + else if (data->flags & CHUNKF_START_FILE) + { + /* starting fresh ... be sure to flush encoder first */ + struct enc_chunk_hdr *chunk = ENC_CHUNK_HDR(res_buffer); + + chunk->flags = 0; + chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); + + while (num_frames-- > 0) + { + memset(chunk->enc_data, 0, pcm_chunk_size); + encode_frame(chunk->enc_data, chunk); + } + } +} /* on_rec_new_stream */ + +static void enc_events_callback(enum enc_events event, void *data) ICODE_ATTR; +static void enc_events_callback(enum enc_events event, void *data) +{ + if (event == ENC_WRITE_CHUNK) + { + if (on_write_chunk((struct enc_file_event_data *)data)) + return; + } + else if (event == ENC_START_FILE) + { + if (on_start_file((struct enc_file_event_data *)data)) + return; + } + else if (event == ENC_END_FILE) + { + if (on_end_file((struct enc_file_event_data *)data)) + return; + } + else if (event == ENC_REC_NEW_STREAM) + { + on_rec_new_stream((struct enc_buffer_event_data *)data); + return; + } + else + { + return; + } + + ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR; +} /* enc_events_callback */ + +static bool enc_init(void) +{ + struct enc_inputs inputs; + struct enc_parameters params; + + if (ci->enc_get_inputs == NULL || + ci->enc_set_parameters == NULL || + ci->enc_get_chunk == NULL || + ci->enc_finish_chunk == NULL || + ci->enc_pcm_buf_near_empty == NULL || + ci->enc_get_pcm_data == NULL || + ci->enc_unget_pcm_data == NULL ) + return false; + + ci->enc_get_inputs(&inputs); + + if (inputs.config->afmt != AFMT_MPA_L3) + return false; + + init_mp3_encoder_engine(inputs.sample_rate, inputs.num_channels, + inputs.config); + + /* configure the buffer system */ + params.afmt = AFMT_MPA_L3; + params.chunk_size = cfg.byte_per_frame + 1; + params.enc_sample_rate = cfg.samplerate; + /* need enough reserved bytes to hold one frame of pcm samples + hdr + for padding and flushing */ + params.reserve_bytes = ENC_CHUNK_HDR_SIZE + pcm_chunk_size; + params.events_callback = enc_events_callback; + ci->enc_set_parameters(¶ms); + + res_buffer = params.reserve_buffer; + +#ifdef CPU_COLDFIRE + asm volatile ("move.l #0, %macsr"); /* integer mode */ +#endif + + return true; +} /* enc_init */ enum codec_status codec_start(struct codec_api* api) { - int i, ii, gr, k, ch, shift, gr_cnt; - int max, min; - long *buffer; - int chunk_size, num_chunks; - int enc_buffer_size; - int enc_quality; - uint32 *mp3_chunk_ptr; - bool cpu_boosted = true; /* start boosted */ + bool cpu_boosted; /* Generic codec initialisation */ ci = api; @@ -2053,207 +2472,58 @@ enum codec_status codec_start(struct codec_api* api) memset(iedata, 0, iend - iedata); #endif - if(ci->enc_get_inputs == NULL || - ci->enc_set_parameters == NULL || - ci->enc_alloc_chunk == NULL || - ci->enc_free_chunk == NULL || - ci->enc_wavbuf_near_empty == NULL || - ci->enc_get_wav_data == NULL || - ci->enc_set_header_callback == NULL ) + if (!enc_init()) + { + ci->enc_codec_loaded = -1; return CODEC_ERROR; - - ci->cpu_boost(true); - - *ci->enc_set_header_callback = NULL; - ci->enc_get_inputs(&enc_buffer_size, &enc_channels, &enc_quality); - - init_mp3_encoder_engine(enc_channels == 2, enc_quality, 44100); - - /* must be 4byte aligned */ - chunk_size = (sizeof(long) + cfg.byte_per_frame + 1 + 3) & ~3; - num_chunks = enc_buffer_size / chunk_size; - - /* inform the main program about the buffer dimensions */ - ci->enc_set_parameters(chunk_size, num_chunks, SAMP_PER_FRAME, - NULL, 0, AFMT_MPA_L3); - -#ifdef CPU_COLDFIRE - asm volatile ("move.l #0, %macsr"); /* integer mode */ -#endif + } /* main application waits for this flag during encoder loading */ - ci->enc_codec_loaded = true; + ci->enc_codec_loaded = 1; + + ci->cpu_boost(true); + cpu_boosted = true; /* main encoding loop */ - while(!ci->stop_codec) + while (!ci->stop_codec) { - while((buffer = (long*)ci->enc_get_wav_data(SAMP_PER_FRAME*4)) != NULL) + char *buffer; + + while ((buffer = ci->enc_get_pcm_data(pcm_chunk_size)) != NULL) { - if(ci->stop_codec) + struct enc_chunk_hdr *chunk; + + if (ci->stop_codec) break; - if(ci->enc_wavbuf_near_empty() == 0) + if (!cpu_boosted && ci->enc_pcm_buf_near_empty() == 0) { - if(!cpu_boosted) - { - ci->cpu_boost(true); - cpu_boosted = true; - } + ci->cpu_boost(true); + cpu_boosted = true; } - /* encode one mp3 frame in this loop */ - CodedData.bitpos = 0; - memset(CodedData.bbuf, 0, sizeof(CodedData.bbuf)); - - if((cfg.slot_lag += cfg.frac_per_frame) >= 64) - { /* Padding for this frame */ - cfg.slot_lag -= 64; - cfg.mpg.padding = 1; - } - else - cfg.mpg.padding = 0; + chunk = ci->enc_get_chunk(); + chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); - cfg.mean_bits = (8 * cfg.byte_per_frame + 8 * cfg.mpg.padding - - cfg.sideinfo_len) / cfg.granules / cfg.channels; + encode_frame(buffer, chunk); - /* shift out old samples */ - memcpy(mfbuf, mfbuf + 2*cfg.granules*576, 4*512); - /* read new samples to iram for further processing */ - memcpy((uint32*)(mfbuf + 2*512), buffer, 4*SAMP_PER_FRAME); - - if(cfg.resample) /* downsample to half of original */ - for(i=2*512; i<2*512+2*SAMP_PER_FRAME; i+=4) - { - mfbuf[i/2+512] = (short)(((int)mfbuf[i+0] + mfbuf[i+2]) >> 1); - mfbuf[i/2+513] = (short)(((int)mfbuf[i+1] + mfbuf[i+3]) >> 1); - } - - if(cfg.channels == 1) /* mix left and right channels to mono */ - for(i=2*512; i<2*512+2*SAMP_PER_FRAME; i+=2) - mfbuf[i] = mfbuf[i+1] = (short)(((int)mfbuf[i] + mfbuf[i+1]) >> 1); - - cfg.ResvSize = 0; - gr_cnt = cfg.granules * cfg.channels; - CodedData.bitpos = cfg.sideinfo_len; /* leave space for mp3 header */ - - for(gr=0; grnum_pcm < samp_per_frame) { - short *wk = mfbuf + 2*286 + gr*1152; - - /* 16bit packed wav data can be windowed efficiently on coldfire */ - window_subband1(wk, sb_data[0][1-gr][0], sb_data[1][1-gr][0]); - - for(ch=0; ch=0; --k) - { - int bu, bd; - bu = shft15(mdct[k]) * ca[k] + shft15(mdct[-1-k]) * cs[k]; - bd = shft15(mdct[k]) * cs[k] - shft15(mdct[-1-k]) * ca[k]; - mdct[-1-k] = bu; - mdct[ k ] = bd; - } - } - - max = min = 0; - for(k=0; k<576; k++) - { - mdct_freq[k] = shft13(mdct_freq[k]); - if(max < mdct_freq[k]) max = mdct_freq[k]; - if(min > mdct_freq[k]) min = mdct_freq[k]; - } - - max = (max > -min) ? max : -min; - cfg.cod_info[gr][ch].max_val = (long)max; - - /* calc new shift for higher integer precision */ - for(k=0; max<(0x3c00>>k); k++); - shift = 12 - k; - } - - cfg.cod_info[gr][ch].quantStep += cfg.cod_info[gr][ch].additStep; - - /* bit and noise allocation */ - iteration_loop(mdct_freq, &cfg.cod_info[gr][ch], gr_cnt--); - /* write the frame to the bitstream */ - Huffmancodebits(enc_data, mdct_freq, &cfg.cod_info[gr][ch]); - - cfg.cod_info[gr][ch].quantStep -= cfg.cod_info[gr][ch].additStep; - - if(cfg.granules == 1) - memcpy(sb_data[ch][0], sb_data[ch][1], sizeof(sb_data[ch][0])); - } + ci->enc_unget_pcm_data(pcm_chunk_size - chunk->num_pcm*4); + chunk->num_pcm = samp_per_frame; } - mp3_chunk_ptr = (uint32*)ci->enc_alloc_chunk(); - mp3_chunk_ptr[0] = cfg.byte_per_frame + cfg.mpg.padding; //(CodedData.bitpos + 7) >> 3; - /* finish this chunk by adding sideinfo header data */ - CodedData.bitpos = 0; - encodeSideInfo( cfg.cod_info ); - - /* allocate mp3 chunk, set chunk size, copy chunk to enc_buffer */ - memcpy(&mp3_chunk_ptr[1], CodedData.bbuf, mp3_chunk_ptr[0]); - ci->enc_free_chunk(); + ci->enc_finish_chunk(); ci->yield(); } - if(ci->enc_wavbuf_near_empty()) - { - if(cpu_boosted) + if (cpu_boosted && ci->enc_pcm_buf_near_empty()) { ci->cpu_boost(false); cpu_boosted = false; } - } + ci->yield(); } @@ -2261,12 +2531,12 @@ enum codec_status codec_start(struct codec_api* api) ci->cpu_boost(false); /* reset parameters to initial state */ - ci->enc_set_parameters(0, 0, 0, 0, 0, 0); + ci->enc_set_parameters(NULL); /* main application waits for this flag during encoder removing */ - ci->enc_codec_loaded = false; + ci->enc_codec_loaded = 0; return CODEC_OK; -} +} /* codec_start */ -#endif +#endif /* ndef SIMULATOR */ diff --git a/apps/codecs/wav_enc.c b/apps/codecs/wav_enc.c index 5aabb5d8e8..974a903310 100644 --- a/apps/codecs/wav_enc.c +++ b/apps/codecs/wav_enc.c @@ -19,140 +19,364 @@ #ifndef SIMULATOR +#include #include "codeclib.h" -CODEC_HEADER +CODEC_ENC_HEADER + +#ifdef USE_IRAM +extern char iramcopy[]; +extern char iramstart[]; +extern char iramend[]; +extern char iedata[]; +extern char iend[]; +#endif + +struct riff_header +{ + uint8_t riff_id[4]; /* 00h - "RIFF" */ + uint32_t riff_size; /* 04h - sz following headers + data_size */ + /* format header */ + uint8_t format[4]; /* 08h - "WAVE" */ + uint8_t format_id[4]; /* 0Ch - "fmt " */ + uint32_t format_size; /* 10h - 16 for PCM (sz format data) */ + /* format data */ + uint16_t audio_format; /* 14h - 1=PCM */ + uint16_t num_channels; /* 16h - 1=M, 2=S, etc. */ + uint32_t sample_rate; /* 18h - HZ */ + uint32_t byte_rate; /* 1Ch - num_channels*sample_rate*bits_per_sample/8 */ + uint16_t block_align; /* 20h - num_channels*bits_per_samples/8 */ + uint16_t bits_per_sample; /* 22h - 8=8 bits, 16=16 bits, etc. */ + /* Not for audio_format=1 (PCM) */ +/* unsigned short extra_param_size; 24h - size of extra data */ +/* unsigned char *extra_params; */ + /* data header */ + uint8_t data_id[4]; /* 24h - "data" */ + uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */ +/* unsigned char *data; 2ch - actual sound data */ +}; + +#define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */ +#define RIFF_FMT_DATA_SIZE 16 /* audio_format -> bits_per_sample */ +#define RIFF_DATA_HEADER_SIZE 8 /* data_id -> data_size */ + +#define PCM_DEPTH_BYTES 2 +#define PCM_DEPTH_BITS 16 +#define PCM_SAMP_PER_CHUNK 2048 +#define PCM_CHUNK_SIZE (PCM_SAMP_PER_CHUNK*4) static struct codec_api *ci; -static int enc_channels; +static int num_channels; +uint32_t sample_rate; +uint32_t enc_size; -#define CHUNK_SIZE 8192 - -static unsigned char wav_header[44] = -{'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',16, - 0,0,0,1,0,2,0,0x44,0xac,0,0,0x10,0xb1,2,0,4,0,16,0,'d','a','t','a',0,0,0,0}; - -static unsigned char wav_header_mono[44] = -{'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',16, - 0,0,0,1,0,1,0,0x44,0xac,0,0,0x88,0x58,1,0,2,0,16,0,'d','a','t','a',0,0,0,0}; - -/* update file header info callback function (called by main application) */ -void enc_set_header(void *head_buffer, /* ptr to the file header data */ - int head_size, /* size of this header data */ - int num_pcm_samples, /* amount of processed pcm samples */ - bool is_file_header) +static const struct riff_header riff_header = { - int num_file_bytes = num_pcm_samples * 2 * enc_channels; + /* "RIFF" header */ + { 'R', 'I', 'F', 'F' }, /* riff_id */ + 0, /* riff_size (*) */ + /* format header */ + { 'W', 'A', 'V', 'E' }, /* format */ + { 'f', 'm', 't', ' ' }, /* format_id */ + H_TO_LE32(16), /* format_size */ + /* format data */ + H_TO_LE16(1), /* audio_format */ + 0, /* num_channels (*) */ + 0, /* sample_rate (*) */ + 0, /* byte_rate (*) */ + 0, /* block_align (*) */ + H_TO_LE16(PCM_DEPTH_BITS), /* bits_per_sample */ + /* data header */ + { 'd', 'a', 't', 'a' }, /* data_id */ + 0 /* data_size (*) */ + /* (*) updated during ENC_END_FILE event */ +}; - if(is_file_header) +/* called version often - inline */ +static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR; +static inline bool is_file_data_ok(struct enc_file_event_data *data) +{ + return data->rec_file >= 0 && (long)data->chunk->flags >= 0; +} /* is_file_data_ok */ + +/* called version often - inline */ +static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR; +static inline bool on_write_chunk(struct enc_file_event_data *data) +{ + if (!is_file_data_ok(data)) + return false; + + if (data->chunk->enc_data == NULL) { - /* update file header before file closing */ - if((int)sizeof(wav_header) < head_size) - { - /* update wave header size entries: special to WAV format */ - *(long*)(head_buffer+ 4) = htole32(num_file_bytes + 36); - *(long*)(head_buffer+40) = htole32(num_file_bytes); - } +#ifdef ROCKBOX_HAS_LOGF + ci->logf("wav enc: NULL data"); +#endif + return true; } -} + + if (ci->write(data->rec_file, data->chunk->enc_data, + data->chunk->enc_size) != (ssize_t)data->chunk->enc_size) + return false; + + data->num_pcm_samples += data->chunk->num_pcm; + return true; +} /* on_write_chunk */ + +static bool on_start_file(struct enc_file_event_data *data) +{ + if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0') + return false; + + data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC); + + if (data->rec_file < 0) + return false; + + /* reset sample count */ + data->num_pcm_samples = 0; + + /* write template header */ + if (ci->write(data->rec_file, &riff_header, sizeof (riff_header)) + != sizeof (riff_header)) + { + return false; + } + + data->new_enc_size += sizeof (riff_header); + return true; +} /* on_start_file */ + +static bool on_end_file(struct enc_file_event_data *data) +{ + /* update template header */ + struct riff_header hdr; + uint32_t data_size; + + if (!is_file_data_ok(data)) + return false; + + if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || + ci->read(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr)) + { + return false; + } + + data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES; + + /* "RIFF" header */ + hdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE + RIFF_FMT_DATA_SIZE + + RIFF_DATA_HEADER_SIZE + data_size); + + /* format data */ + hdr.num_channels = htole16(num_channels); + hdr.sample_rate = htole32(sample_rate); + hdr.byte_rate = htole32(sample_rate*num_channels* PCM_DEPTH_BYTES); + hdr.block_align = htole16(num_channels*PCM_DEPTH_BYTES); + + /* data header */ + hdr.data_size = htole32(data_size); + + if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || + ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr)) + { + return false; + } + + ci->fsync(data->rec_file); + ci->close(data->rec_file); + data->rec_file = -1; + + return true; +} /* on_end_file */ + +static void enc_events_callback(enum enc_events event, void *data) ICODE_ATTR; +static void enc_events_callback(enum enc_events event, void *data) +{ + if (event == ENC_WRITE_CHUNK) + { + if (on_write_chunk((struct enc_file_event_data *)data)) + return; + } + else if (event == ENC_START_FILE) + { + if (on_start_file((struct enc_file_event_data *)data)) + return; + } + else if (event == ENC_END_FILE) + { + if (on_end_file((struct enc_file_event_data *)data)) + return; + } + else + { + return; + } + + ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR; +} /* enc_events_callback */ + +/* convert native pcm samples to wav format samples */ +static void chunk_to_wav_format(uint32_t *src, uint32_t *dst) ICODE_ATTR; +static void chunk_to_wav_format(uint32_t *src, uint32_t *dst) +{ + if (num_channels == 1) + { + /* On big endian: + * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| + * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| => + * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM| + * + * On little endian: + * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| + * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| => + * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM| + */ + uint32_t *src_end = src + PCM_SAMP_PER_CHUNK; + + inline void to_mono(uint32_t **src, uint32_t **dst) + { + int32_t lr1, lr2; + + lr1 = *(*src)++; + lr1 = ((int16_t)lr1 + (lr1 >> 16)) >> 1; + + lr2 = *(*src)++; + lr2 = ((int16_t)lr2 + (lr2 >> 16)) >> 1; + *(*dst)++ = swap_odd_even_be32((lr1 << 16) | (uint16_t)lr2); + } /* to_mono */ + + do + { + to_mono(&src, &dst); + to_mono(&src, &dst); + to_mono(&src, &dst); + to_mono(&src, &dst); + to_mono(&src, &dst); + to_mono(&src, &dst); + to_mono(&src, &dst); + to_mono(&src, &dst); + } + while (src < src_end); + } + else + { +#ifdef ROCKBOX_BIG_ENDIAN + /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| => + * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| + */ + uint32_t *src_end = src + PCM_SAMP_PER_CHUNK; + + do + { + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + } + while (src < src_end); +#else + /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| => + * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| + */ + ci->memcpy(dst, src, PCM_CHUNK_SIZE); +#endif + } +} /* chunk_to_wav_format */ + +static bool init_encoder(void) +{ + struct enc_inputs inputs; + struct enc_parameters params; + + if (ci->enc_get_inputs == NULL || + ci->enc_set_parameters == NULL || + ci->enc_get_chunk == NULL || + ci->enc_finish_chunk == NULL || + ci->enc_pcm_buf_near_empty == NULL || + ci->enc_get_pcm_data == NULL ) + return false; + + ci->enc_get_inputs(&inputs); + + if (inputs.config->afmt != AFMT_PCM_WAV) + return false; + + sample_rate = inputs.sample_rate; + num_channels = inputs.num_channels; + + /* configure the buffer system */ + params.afmt = AFMT_PCM_WAV; + enc_size = PCM_CHUNK_SIZE*inputs.num_channels / 2; + params.chunk_size = enc_size; + params.enc_sample_rate = sample_rate; + params.reserve_bytes = 0; + params.events_callback = enc_events_callback; + ci->enc_set_parameters(¶ms); + + return true; +} /* init_encoder */ /* main codec entry point */ enum codec_status codec_start(struct codec_api* api) { - int i; - long lr; - unsigned long t; - unsigned long *src; - unsigned long *dst; - int chunk_size, num_chunks, samp_per_chunk; - int enc_buffer_size; - int enc_quality; - bool cpu_boosted = true; /* start boosted */ + bool cpu_boosted; ci = api; // copy to global api pointer - if(ci->enc_get_inputs == NULL || - ci->enc_set_parameters == NULL || - ci->enc_alloc_chunk == NULL || - ci->enc_free_chunk == NULL || - ci->enc_wavbuf_near_empty == NULL || - ci->enc_get_wav_data == NULL || - ci->enc_set_header_callback == NULL ) +#ifdef USE_IRAM + ci->memcpy(iramstart, iramcopy, iramend - iramstart); + ci->memset(iedata, 0, iend - iedata); +#endif + + if (!init_encoder()) + { + ci->enc_codec_loaded = -1; return CODEC_ERROR; - - ci->cpu_boost(true); - - *ci->enc_set_header_callback = enc_set_header; - ci->enc_get_inputs(&enc_buffer_size, &enc_channels, &enc_quality); - - /* configure the buffer system */ - chunk_size = sizeof(long) + CHUNK_SIZE * enc_channels / 2; - num_chunks = enc_buffer_size / chunk_size; - samp_per_chunk = CHUNK_SIZE / 4; - - /* inform the main program about buffer dimensions and other params */ - ci->enc_set_parameters(chunk_size, num_chunks, samp_per_chunk, - (enc_channels == 2) ? wav_header : wav_header_mono, - sizeof(wav_header), AFMT_PCM_WAV); + } /* main application waits for this flag during encoder loading */ - ci->enc_codec_loaded = true; + ci->enc_codec_loaded = 1; + + ci->cpu_boost(true); + cpu_boosted = true; /* main encoding loop */ while(!ci->stop_codec) { - while((src = (unsigned long*)ci->enc_get_wav_data(CHUNK_SIZE)) != NULL) + uint32_t *src; + + while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL) { - if(ci->stop_codec) + struct enc_chunk_hdr *chunk; + + if (ci->stop_codec) break; - if(ci->enc_wavbuf_near_empty() == 0) + if (!cpu_boosted && ci->enc_pcm_buf_near_empty() == 0) { - if(!cpu_boosted) - { - ci->cpu_boost(true); - cpu_boosted = true; - } + ci->cpu_boost(true); + cpu_boosted = true; } - dst = (unsigned long*)ci->enc_alloc_chunk(); - *dst++ = CHUNK_SIZE * enc_channels / 2; /* set size info */ + chunk = ci->enc_get_chunk(); + chunk->enc_size = enc_size; + chunk->num_pcm = PCM_SAMP_PER_CHUNK; + chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); - if(enc_channels == 2) - { - /* swap byte order & copy to destination */ - for (i=0; i> 8) & 0xff00ff) | ((t << 8) & 0xff00ff00); - } - } - else - { - /* mix left/right, swap byte order & copy to destination */ - for (i=0; i>16) + (lr>>16)) >> 1; /* left+right */ - t = (lr << 16); - lr = (long)*src++; - lr = (((lr<<16)>>16) + (lr>>16)) >> 1; /* left+right */ - t |= lr & 0xffff; - *dst++ = ((t >> 8) & 0xff00ff) | ((t << 8) & 0xff00ff00); - } - } + chunk_to_wav_format(src, (uint32_t *)chunk->enc_data); - ci->enc_free_chunk(); + ci->enc_finish_chunk(); ci->yield(); } - if(ci->enc_wavbuf_near_empty()) + if (cpu_boosted && ci->enc_pcm_buf_near_empty() != 0) { - if(cpu_boosted) - { - ci->cpu_boost(false); - cpu_boosted = false; - } + ci->cpu_boost(false); + cpu_boosted = false; } ci->yield(); @@ -162,11 +386,12 @@ enum codec_status codec_start(struct codec_api* api) ci->cpu_boost(false); /* reset parameters to initial state */ - ci->enc_set_parameters(0, 0, 0, 0, 0, 0); + ci->enc_set_parameters(NULL); /* main application waits for this flag during encoder removing */ - ci->enc_codec_loaded = false; + ci->enc_codec_loaded = 0; return CODEC_OK; -} -#endif +} /* codec_start */ + +#endif /* ndef SIMULATOR */ diff --git a/apps/codecs/wavpack_enc.c b/apps/codecs/wavpack_enc.c index eced7f1f4e..5318abc8fb 100644 --- a/apps/codecs/wavpack_enc.c +++ b/apps/codecs/wavpack_enc.c @@ -22,201 +22,474 @@ #include "codeclib.h" #include "libwavpack/wavpack.h" -CODEC_HEADER +CODEC_ENC_HEADER -typedef unsigned long uint32; -typedef unsigned short uint16; -typedef unsigned char uint8; +#ifdef USE_IRAM +extern char iramcopy[]; +extern char iramstart[]; +extern char iramend[]; +extern char iedata[]; +extern char iend[]; +#endif -static unsigned char wav_header_ster [46] = -{33,22,'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',16, - 0,0,0,1,0,2,0,0x44,0xac,0,0,0x10,0xb1,2,0,4,0,16,0,'d','a','t','a',0,0,0,0}; - -static unsigned char wav_header_mono [46] = -{33,22,'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',16, - 0,0,0,1,0,1,0,0x44,0xac,0,0,0x88,0x58,1,0,2,0,16,0,'d','a','t','a',0,0,0,0}; - -static struct codec_api *ci; -static int enc_channels; - -#define CHUNK_SIZE 20000 - -static long input_buffer[CHUNK_SIZE/2] IBSS_ATTR; - -/* update file header info callback function */ -void enc_set_header(void *head_buffer, /* ptr to the file header data */ - int head_size, /* size of this header data */ - int num_pcm_sampl, /* amount of processed pcm samples */ - bool is_file_header) /* update file/chunk header */ +/** Types **/ +typedef struct { - if(is_file_header) + uint8_t type; /* Type of metadata */ + uint8_t word_size; /* Size of metadata in words */ +} WavpackMetadataHeader; + +struct riff_header +{ + uint8_t riff_id[4]; /* 00h - "RIFF" */ + uint32_t riff_size; /* 04h - sz following headers + data_size */ + /* format header */ + uint8_t format[4]; /* 08h - "WAVE" */ + uint8_t format_id[4]; /* 0Ch - "fmt " */ + uint32_t format_size; /* 10h - 16 for PCM (sz format data) */ + /* format data */ + uint16_t audio_format; /* 14h - 1=PCM */ + uint16_t num_channels; /* 16h - 1=M, 2=S, etc. */ + uint32_t sample_rate; /* 18h - HZ */ + uint32_t byte_rate; /* 1Ch - num_channels*sample_rate*bits_per_sample/8 */ + uint16_t block_align; /* 20h - num_channels*bits_per_samples/8 */ + uint16_t bits_per_sample; /* 22h - 8=8 bits, 16=16 bits, etc. */ + /* Not for audio_format=1 (PCM) */ +/* unsigned short extra_param_size; 24h - size of extra data */ +/* unsigned char *extra_params; */ + /* data header */ + uint8_t data_id[4]; /* 24h - "data" */ + uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */ +/* unsigned char *data; 2ch - actual sound data */ +}; + +#define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */ +#define RIFF_FMT_DATA_SIZE 16 /* audio_format -> bits_per_sample */ +#define RIFF_DATA_HEADER_SIZE 8 /* data_id -> data_size */ + +#define PCM_DEPTH_BITS 16 +#define PCM_DEPTH_BYTES 2 +#define PCM_SAMP_PER_CHUNK 5000 +#define PCM_CHUNK_SIZE (4*PCM_SAMP_PER_CHUNK) + +/** Data **/ +static struct codec_api *ci; +static int8_t input_buffer[PCM_CHUNK_SIZE*2] IBSS_ATTR; +static WavpackConfig config IBSS_ATTR; +static WavpackContext *wpc; +static int32_t data_size, input_size, input_step IBSS_ATTR; + +static const WavpackMetadataHeader wvpk_mdh = +{ + ID_RIFF_HEADER, + sizeof (struct riff_header) / sizeof (uint16_t), +}; + +static const struct riff_header riff_header = +{ + /* "RIFF" header */ + { 'R', 'I', 'F', 'F' }, /* riff_id */ + 0, /* riff_size (*) */ + /* format header */ + { 'W', 'A', 'V', 'E' }, /* format */ + { 'f', 'm', 't', ' ' }, /* format_id */ + H_TO_LE32(16), /* format_size */ + /* format data */ + H_TO_LE16(1), /* audio_format */ + 0, /* num_channels (*) */ + 0, /* sample_rate (*) */ + 0, /* byte_rate (*) */ + 0, /* block_align (*) */ + H_TO_LE16(PCM_DEPTH_BITS), /* bits_per_sample */ + /* data header */ + { 'd', 'a', 't', 'a' }, /* data_id */ + 0 /* data_size (*) */ + /* (*) updated during ENC_END_FILE event */ +}; + +static void chunk_to_int32(int32_t *src) ICODE_ATTR; +static void chunk_to_int32(int32_t *src) +{ + int32_t *dst = (int32_t *)input_buffer + PCM_SAMP_PER_CHUNK; + int32_t *src_end = dst + PCM_SAMP_PER_CHUNK; + + /* copy to IRAM before converting data */ + memcpy(dst, src, PCM_CHUNK_SIZE); + + src = dst; + dst = (int32_t *)input_buffer; + + if (config.num_channels == 1) { - /* update file header before file closing */ - if(sizeof(WavpackHeader) + sizeof(wav_header_mono) < (unsigned)head_size) + /* + * |llllllllllllllll|rrrrrrrrrrrrrrrr| => + * |mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm| + */ + inline void to_int32(int32_t **src, int32_t **dst) { - char* riff_header = (char*)head_buffer + sizeof(WavpackHeader); - char* wv_header = (char*)head_buffer + sizeof(wav_header_mono); - int num_file_bytes = num_pcm_sampl * 2 * enc_channels; - unsigned long ckSize; + int32_t t = *(*src)++; + /* endianness irrelevant */ + *(*dst)++ = ((int16_t)t + (t >> 16)) >> 1; + } /* to_int32 */ - /* RIFF header and WVPK header have to be swapped */ - /* copy wavpack header to file start position */ - ci->memcpy(head_buffer, wv_header, sizeof(WavpackHeader)); - wv_header = head_buffer; /* recalc wavpack header position */ - - if(enc_channels == 2) - ci->memcpy(riff_header, wav_header_ster, sizeof(wav_header_ster)); - else - ci->memcpy(riff_header, wav_header_mono, sizeof(wav_header_mono)); - - /* update the Wavpack header first chunk size & total frame count */ - ckSize = htole32(((WavpackHeader*)wv_header)->ckSize) - + sizeof(wav_header_mono); - ((WavpackHeader*)wv_header)->total_samples = htole32(num_pcm_sampl); - ((WavpackHeader*)wv_header)->ckSize = htole32(ckSize); - - /* update the RIFF WAV header size entries */ - *(long*)(riff_header+ 6) = htole32(num_file_bytes + 36); - *(long*)(riff_header+42) = htole32(num_file_bytes); + do + { + /* read 10 longs and write 10 longs */ + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); } + while(src < src_end); + + return; } else { - /* update timestamp (block_index) */ - ((WavpackHeader*)head_buffer)->block_index = htole32(num_pcm_sampl); + /* + * |llllllllllllllll|rrrrrrrrrrrrrrrr| => + * |llllllllllllllllllllllllllllllll|rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr| + */ + inline void to_int32(int32_t **src, int32_t **dst) + { + int32_t t = *(*src)++; +#ifdef ROCKBOX_BIG_ENDIAN + *(*dst)++ = t >> 16, *(*dst)++ = (int16_t)t; +#else + *(*dst)++ = (int16_t)t, *(*dst)++ = t >> 16; +#endif + } /* to_int32 */ + + do + { + /* read 10 longs and write 20 longs */ + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + to_int32(&src, &dst); + } + while (src < src_end); + + return; } -} +} /* chunk_to_int32 */ - -enum codec_status codec_start(struct codec_api* api) +/* called very often - inline */ +static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR; +static inline bool is_file_data_ok(struct enc_file_event_data *data) { - int i; - long t; - uint32 *src; - uint32 *dst; - int chunk_size, num_chunks, samp_per_chunk; - int enc_buffer_size; - int enc_quality; - WavpackConfig config; - WavpackContext *wpc; - bool cpu_boosted = true; /* start boosted */ + return data->rec_file >= 0 && (long)data->chunk->flags >= 0; +} /* is_file_data_ok */ - ci = api; // copy to global api pointer +/* called very often - inline */ +static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR; +static inline bool on_write_chunk(struct enc_file_event_data *data) +{ + if (!is_file_data_ok(data)) + return false; + + if (data->chunk->enc_data == NULL) + { +#ifdef ROCKBOX_HAS_LOGF + ci->logf("wvpk enc: NULL data"); +#endif + return true; + } + + /* update timestamp (block_index) */ + ((WavpackHeader *)data->chunk->enc_data)->block_index = + htole32(data->num_pcm_samples); + + if (ci->write(data->rec_file, data->chunk->enc_data, + data->chunk->enc_size) != (ssize_t)data->chunk->enc_size) + return false; + + data->num_pcm_samples += data->chunk->num_pcm; + return true; +} /* on_write_chunk */ + +static bool on_start_file(struct enc_file_event_data *data) +{ + if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0') + return false; + + data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC); + + if (data->rec_file < 0) + return false; + + /* reset sample count */ + data->num_pcm_samples = 0; + + /* write template headers */ + if (ci->write(data->rec_file, &wvpk_mdh, sizeof (wvpk_mdh)) + != sizeof (wvpk_mdh) || + ci->write(data->rec_file, &riff_header, sizeof (riff_header)) + != sizeof (riff_header)) + { + return false; + } + + data->new_enc_size += sizeof(wvpk_mdh) + sizeof(riff_header); + return true; +} /* on_start_file */ + +static bool on_end_file(struct enc_file_event_data *data) +{ + struct + { + WavpackMetadataHeader wpmdh; + struct riff_header rhdr; + WavpackHeader wph; + } __attribute__ ((packed)) h; + + uint32_t data_size; + + if (!is_file_data_ok(data)) + return false; + + /* read template headers at start */ + if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || + ci->read(data->rec_file, &h, sizeof (h)) != sizeof (h)) + return false; + + data_size = data->num_pcm_samples*config.num_channels*PCM_DEPTH_BYTES; + + /** "RIFF" header **/ + h.rhdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE + + RIFF_FMT_DATA_SIZE + RIFF_DATA_HEADER_SIZE + data_size); + + /* format data */ + h.rhdr.num_channels = htole16(config.num_channels); + h.rhdr.sample_rate = htole32(config.sample_rate); + h.rhdr.byte_rate = htole32(config.sample_rate*config.num_channels* + PCM_DEPTH_BYTES); + h.rhdr.block_align = htole16(config.num_channels*PCM_DEPTH_BYTES); + + /* data header */ + h.rhdr.data_size = htole32(data_size); + + /** Wavpack header **/ + h.wph.ckSize = htole32(letoh32(h.wph.ckSize) + sizeof (h.wpmdh) + + sizeof (h.rhdr)); + h.wph.total_samples = htole32(data->num_pcm_samples); + + /* MDH|RIFF|WVPK => WVPK|MDH|RIFF */ + if (ci->lseek(data->rec_file, 0, SEEK_SET) + != 0 || + ci->write(data->rec_file, &h.wph, sizeof (h.wph)) + != sizeof (h.wph) || + ci->write(data->rec_file, &h.wpmdh, sizeof (h.wpmdh)) + != sizeof (h.wpmdh) || + ci->write(data->rec_file, &h.rhdr, sizeof (h.rhdr)) + != sizeof (h.rhdr)) + { + return false; + } + + ci->fsync(data->rec_file); + ci->close(data->rec_file); + data->rec_file = -1; + + return true; +} /* on_end_file */ + +static void enc_events_callback(enum enc_events event, void *data) ICODE_ATTR; +static void enc_events_callback(enum enc_events event, void *data) +{ + if (event == ENC_WRITE_CHUNK) + { + if (on_write_chunk((struct enc_file_event_data *)data)) + return; + } + else if (event == ENC_START_FILE) + { + /* write metadata header and RIFF header */ + if (on_start_file((struct enc_file_event_data *)data)) + return; + } + else if (event == ENC_END_FILE) + { + if (on_end_file((struct enc_file_event_data *)data)) + return; + } + else + { + return; + } + + ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR; +} /* enc_events_callback */ + +static bool init_encoder(void) +{ + struct enc_inputs inputs; + struct enc_parameters params; codec_init(ci); - if(ci->enc_get_inputs == NULL || - ci->enc_set_parameters == NULL || - ci->enc_alloc_chunk == NULL || - ci->enc_free_chunk == NULL || - ci->enc_wavbuf_near_empty == NULL || - ci->enc_get_wav_data == NULL || - ci->enc_set_header_callback == NULL ) - return CODEC_ERROR; + if (ci->enc_get_inputs == NULL || + ci->enc_set_parameters == NULL || + ci->enc_get_chunk == NULL || + ci->enc_finish_chunk == NULL || + ci->enc_pcm_buf_near_empty == NULL || + ci->enc_get_pcm_data == NULL || + ci->enc_unget_pcm_data == NULL ) + return false; - ci->cpu_boost(true); + ci->enc_get_inputs(&inputs); - *ci->enc_set_header_callback = enc_set_header; - ci->enc_get_inputs(&enc_buffer_size, &enc_channels, &enc_quality); + if (inputs.config->afmt != AFMT_WAVPACK) + return false; - /* configure the buffer system */ - chunk_size = sizeof(long) + CHUNK_SIZE * enc_channels / 2; - num_chunks = enc_buffer_size / chunk_size; - samp_per_chunk = CHUNK_SIZE / 4; - - /* inform the main program about buffer dimensions and other params */ - /* add wav_header_mono as place holder to file start position */ - /* wav header and wvpk header have to be reordered later */ - ci->enc_set_parameters(chunk_size, num_chunks, samp_per_chunk, - wav_header_mono, sizeof(wav_header_mono), - AFMT_WAVPACK); + memset(&config, 0, sizeof (config)); + config.bits_per_sample = PCM_DEPTH_BITS; + config.bytes_per_sample = PCM_DEPTH_BYTES; + config.sample_rate = inputs.sample_rate; + config.num_channels = inputs.num_channels; wpc = WavpackOpenFileOutput (); - memset (&config, 0, sizeof (config)); - config.bits_per_sample = 16; - config.bytes_per_sample = 2; - config.sample_rate = 44100; - config.num_channels = enc_channels; + if (!WavpackSetConfiguration(wpc, &config, -1)) + return false; - if (!WavpackSetConfiguration (wpc, &config, 1)) + /* configure the buffer system */ + params.afmt = AFMT_WAVPACK; + input_size = PCM_CHUNK_SIZE*inputs.num_channels / 2; + data_size = 105*input_size / 100; + input_size *= 2; + input_step = input_size / 4; + params.chunk_size = data_size; + params.enc_sample_rate = inputs.sample_rate; + params.reserve_bytes = 0; + params.events_callback = enc_events_callback; + + ci->enc_set_parameters(¶ms); + + return true; +} /* init_encoder */ + +enum codec_status codec_start(struct codec_api* api) +{ + bool cpu_boosted; + + ci = api; /* copy to global api pointer */ + +#ifdef USE_IRAM + ci->memcpy(iramstart, iramcopy, iramend - iramstart); + ci->memset(iedata, 0, iend - iedata); +#endif + + /* initialize params and config */ + if (!init_encoder()) + { + ci->enc_codec_loaded = -1; return CODEC_ERROR; + } /* main application waits for this flag during encoder loading */ - ci->enc_codec_loaded = true; + ci->enc_codec_loaded = 1; + + ci->cpu_boost(true); + cpu_boosted = true; /* main encoding loop */ while(!ci->stop_codec) { - while((src = (uint32*)ci->enc_get_wav_data(CHUNK_SIZE)) != NULL) + uint8_t *src; + + while ((src = ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL) { + struct enc_chunk_hdr *chunk; + bool abort_chunk; + uint8_t *dst; + uint8_t *src_end; + if(ci->stop_codec) break; - if(ci->enc_wavbuf_near_empty() == 0) + abort_chunk = true; + + if (!cpu_boosted && ci->enc_pcm_buf_near_empty() == 0) { - if(!cpu_boosted) - { - ci->cpu_boost(true); - cpu_boosted = true; - } + ci->cpu_boost(true); + cpu_boosted = true; } - dst = (uint32*)ci->enc_alloc_chunk() + 1; + chunk = ci->enc_get_chunk(); - WavpackStartBlock (wpc, (uint8*)dst, (uint8*)dst + CHUNK_SIZE); + /* reset counts and pointer */ + chunk->enc_size = 0; + chunk->num_pcm = 0; + chunk->enc_data = NULL; - if(enc_channels == 2) + dst = ENC_CHUNK_SKIP_HDR(dst, chunk); + + WavpackStartBlock(wpc, dst, dst + data_size); + + chunk_to_int32((uint32_t*)src); + src = input_buffer; + src_end = src + input_size; + + /* encode chunk in four steps yielding between each */ + do { - for (i=0; i> 16; - input_buffer[2*i + 1] = (short)t; + chunk->num_pcm += PCM_SAMP_PER_CHUNK/4; + ci->yield(); + /* could've been stopped in some way */ + abort_chunk = ci->stop_codec || + (chunk->flags & CHUNKF_ABORT); } + + src += input_step; } - else + while (!abort_chunk && src < src_end); + + if (!abort_chunk) { - for (i=0; i>16) + (t>>16)) >> 1; /* left+right */ - - input_buffer[i] = t; - } - } - - if (!WavpackPackSamples (wpc, input_buffer, CHUNK_SIZE/4)) - return CODEC_ERROR; - + chunk->enc_data = dst; + if (chunk->num_pcm < PCM_SAMP_PER_CHUNK) + ci->enc_unget_pcm_data(PCM_CHUNK_SIZE - chunk->num_pcm*4); /* finish the chunk and store chunk size info */ - dst[-1] = WavpackFinishBlock (wpc); - - ci->enc_free_chunk(); - ci->yield(); - } - - if(ci->enc_wavbuf_near_empty()) - { - if(cpu_boosted) - { - ci->cpu_boost(false); - cpu_boosted = false; + chunk->enc_size = WavpackFinishBlock(wpc); + ci->enc_finish_chunk(); } } + + if (cpu_boosted && ci->enc_pcm_buf_near_empty() != 0) + { + ci->cpu_boost(false); + cpu_boosted = false; + } + ci->yield(); } - if(cpu_boosted) /* set initial boost state */ + if (cpu_boosted) /* set initial boost state */ ci->cpu_boost(false); /* reset parameters to initial state */ - ci->enc_set_parameters(0, 0, 0, 0, 0, 0); + ci->enc_set_parameters(NULL); /* main application waits for this flag during encoder removing */ - ci->enc_codec_loaded = false; + ci->enc_codec_loaded = 0; return CODEC_OK; -} -#endif +} /* codec_start */ + +#endif /* ndef SIMULATOR */ diff --git a/apps/eq_menu.c b/apps/eq_menu.c index 6c4dde4a78..9939ee77fe 100644 --- a/apps/eq_menu.c +++ b/apps/eq_menu.c @@ -710,7 +710,8 @@ static bool eq_save_preset(void) char filename[MAX_PATH]; int *setting; - create_numbered_filename(filename, EQS_DIR, "eq", ".cfg", 2); + create_numbered_filename(filename, EQS_DIR, "eq", ".cfg", 2 + IF_CNFN_NUM_(, NULL)); /* allow user to modify filename */ while (true) { diff --git a/apps/gui/statusbar.c b/apps/gui/statusbar.c index a8b8c5061b..ae8bba0538 100644 --- a/apps/gui/statusbar.c +++ b/apps/gui/statusbar.c @@ -124,19 +124,6 @@ #endif #define STATUSBAR_TIME_X_END(statusbar_width) statusbar_width - 1 - \ STATUSBAR_DISK_WIDTH -#if defined(HAVE_RECORDING) -/* analogue frequency numbers taken from the order of frequencies in sample_rate */ -#define FREQ_44 7 -#define FREQ_48 8 -#define FREQ_32 6 -#define FREQ_22 4 -#define FREQ_24 5 -#define FREQ_16 3 -#ifdef HAVE_SPDIF_IN -#define SOURCE_SPDIF 2 -#endif -#endif - struct gui_syncstatusbar statusbars; void gui_statusbar_init(struct gui_statusbar * bar) @@ -600,41 +587,113 @@ void gui_statusbar_time(struct screen * display, int hour, int minute) #endif #ifdef HAVE_RECORDING +#if CONFIG_CODEC == SWCODEC +/** + * Write a number to the display using bitmaps and return new position + */ +static int write_bitmap_number(struct screen * display, int value, + int x, int y) +{ + char buf[12], *ptr; + snprintf(buf, sizeof(buf), "%d", value); + + for (ptr = buf; *ptr != '\0'; ptr++, x += BM_GLYPH_WIDTH) + display->mono_bitmap(bitmap_glyphs_4x8[*ptr - '0'], x, y, + BM_GLYPH_WIDTH, STATUSBAR_HEIGHT); + return x; +} + +/** + * Write format info bitmaps - right justified + */ +static void gui_statusbar_write_format_info(struct screen * display) +{ + /* Can't fit info for sw codec targets in statusbar using FONT_SYSFIXED + so must use icons */ + int rec_format = global_settings.rec_format; + unsigned bitrk = 0; /* compiler warns about unitialized use !! */ + int xpos = STATUSBAR_ENCODER_X_POS; + int width = STATUSBAR_ENCODER_WIDTH; + const unsigned char *bm = bitmap_formats_18x8[rec_format]; + + if (rec_format == REC_FORMAT_MPA_L3) + { + /* Special handling for mp3 */ + bitrk = global_settings.mp3_enc_config.bitrate; + bitrk = mp3_enc_bitr[bitrk]; + + width = BM_MPA_L3_M_WIDTH; + + /* Slide 'M' to right if fewer than three digits used */ + if (bitrk > 999) + bitrk = 999; /* neurotic safety check if corrupted */ + else + { + if (bitrk < 100) + xpos += BM_GLYPH_WIDTH; + if (bitrk < 10) + xpos += BM_GLYPH_WIDTH; + } + } + + + /* Show bitmap - clipping right edge if needed */ + display->mono_bitmap_part(bm, 0, 0, STATUSBAR_ENCODER_WIDTH, + xpos, STATUSBAR_Y_POS, width, STATUSBAR_HEIGHT); + + if (rec_format == REC_FORMAT_MPA_L3) + { + xpos += BM_MPA_L3_M_WIDTH; /* to right of 'M' */ + write_bitmap_number(display, bitrk, xpos, STATUSBAR_Y_POS); + } +} + +/** + * Write sample rate using bitmaps - left justified + */ +static void gui_statusbar_write_samplerate_info(struct screen * display) +{ + unsigned long samprk; + int xpos; + +#ifdef SIMULATOR + samprk = 44100; +#else +#ifdef HAVE_SPDIF_IN + if (global_settings.rec_source == AUDIO_SRC_SPDIF) + /* Use rate in use, not current measured rate if it changed */ + samprk = pcm_rec_sample_rate(); + else +#endif + samprk = rec_freq_sampr[global_settings.rec_frequency]; +#endif /* SIMULATOR */ + + samprk /= 1000; + if (samprk > 99) + samprk = 99; /* Limit to 3 glyphs */ + + xpos = write_bitmap_number(display, (unsigned)samprk, + STATUSBAR_RECFREQ_X_POS, STATUSBAR_Y_POS); + + /* write the 'k' */ + display->mono_bitmap(bitmap_glyphs_4x8[Glyph_4x8_k], xpos, + STATUSBAR_Y_POS, BM_GLYPH_WIDTH, + STATUSBAR_HEIGHT); +} +#endif /* CONFIG_CODEC == SWCODEC */ + void gui_statusbar_icon_recording_info(struct screen * display) { -#if (CONFIG_CODEC != SWCODEC) || (defined(SIMULATOR) && defined(HAVE_SPDIF_IN)) - char buffer[3]; -#endif #if CONFIG_CODEC != SWCODEC + char buffer[3]; int width, height; - static char* const sample_rate[12] = - { - "8", - "11", - "12", - "16", - "22", - "24", - "32", - "44", - "48", - "64", - "88", - "96" - }; - display->setfont(FONT_SYSFIXED); -#endif +#endif /* CONFIG_CODEC != SWCODEC */ /* Display Codec info in statusbar */ #if CONFIG_CODEC == SWCODEC - /* Can't fit info for sw codec targets in statusbar using FONT_SYSFIXED - so must use icons */ - display->mono_bitmap(bitmap_icons_18x8[global_settings.rec_quality], - STATUSBAR_ENCODER_X_POS, STATUSBAR_Y_POS, - STATUSBAR_ENCODER_WIDTH, STATUSBAR_HEIGHT); -#else - + gui_statusbar_write_format_info(display); +#else /* !SWCODEC */ display->mono_bitmap(bitmap_icons_5x8[Icon_q], STATUSBAR_ENCODER_X_POS + 8, STATUSBAR_Y_POS, 5, STATUSBAR_HEIGHT); @@ -643,56 +702,37 @@ void gui_statusbar_icon_recording_info(struct screen * display) display->getstringsize(buffer, &width, &height); if (height <= STATUSBAR_HEIGHT) display->putsxy(STATUSBAR_ENCODER_X_POS + 13, STATUSBAR_Y_POS, buffer); -#endif +#endif /* CONFIG_CODEC == SWCODEC */ /* Display Samplerate info in statusbar */ -#if defined(HAVE_SPDIF_IN) - if (global_settings.rec_source == SOURCE_SPDIF) +#if CONFIG_CODEC == SWCODEC + /* SWCODEC targets use bitmaps for glyphs */ + gui_statusbar_write_samplerate_info(display); +#else /* !SWCODEC */ + /* hwcodec targets have sysfont characters */ +#ifdef HAVE_SPDIF_IN + if (global_settings.rec_source == AUDIO_SRC_SPDIF) { -#if (CONFIG_CODEC != MAS3587F) && !defined(SIMULATOR) - display->mono_bitmap(bitmap_icons_12x8[audio_get_spdif_sample_rate()], - STATUSBAR_RECFREQ_X_POS, STATUSBAR_Y_POS, - STATUSBAR_RECFREQ_WIDTH, STATUSBAR_HEIGHT); -#else /* Can't measure S/PDIF sample rate on Archos/Sim yet */ snprintf(buffer, sizeof(buffer), "--"); -#endif } else #endif /* HAVE_SPDIF_IN */ { - /* Analogue frequency in wrong order so remap settings numbers */ - int freq = global_settings.rec_frequency; - if (freq == 0) - freq = FREQ_44; - else if (freq == 1) - freq = FREQ_48; - else if (freq == 2) - freq = FREQ_32; - else if (freq == 3) - freq = FREQ_22; - else if (freq == 4) - freq = FREQ_24; - else if (freq == 5) - freq = FREQ_16; - -#if CONFIG_CODEC == SWCODEC - /* samplerate icons for swcodec targets*/ - display->mono_bitmap(bitmap_icons_12x8[freq], - STATUSBAR_RECFREQ_X_POS, STATUSBAR_Y_POS, - STATUSBAR_RECFREQ_WIDTH, STATUSBAR_HEIGHT); - } -#else - /* hwcodec targets have sysfont characters */ - snprintf(buffer, sizeof(buffer), "%s", sample_rate[freq]); - display->getstringsize(buffer, &width, &height); + static char const * const freq_strings[12] = + { "44", "48", "32", "22", "24", "16" }; + snprintf(buffer, sizeof(buffer), "%s", + freq_strings[global_settings.rec_frequency]); } - if (height <= STATUSBAR_HEIGHT) - display->putsxy(STATUSBAR_RECFREQ_X_POS, STATUSBAR_Y_POS, buffer); + display->getstringsize(buffer, &width, &height); + + if (height <= STATUSBAR_HEIGHT) + display->putsxy(STATUSBAR_RECFREQ_X_POS, STATUSBAR_Y_POS, buffer); + + display->setfont(FONT_UI); +#endif /* CONFIG_CODEC == SWCODEC */ - display->setfont(FONT_UI); -#endif /* Display Channel status in status bar */ if(global_settings.rec_channels) { diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 40d7bb7b19..8f7deb78a9 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -9802,7 +9802,7 @@ id: VOICE_KBIT_PER_SEC - desc: spoken only, for file extension + desc: spoken only, a unit postfix user: *: "" @@ -10032,3 +10032,115 @@ *: "" + + id: LANG_RECORDING_FORMAT + desc: audio format item in recording menu + user: + + *: "Format" + + + *: "Format" + + + *: "Format" + + + + id: LANG_AFMT_MPA_L3 + desc: audio format description + user: + + *: "MPEG Layer 3" + + + *: "MPEG Layer 3" + + + *: "MPEG Layer 3" + + + + id: LANG_AFMT_PCM_WAV + desc: audio format description + user: + + *: "PCM Wave" + + + *: "PCM Wave" + + + *: "PCM Wave" + + + + id: LANG_AFMT_WAVPACK + desc: audio format description + user: + + *: "WavPack" + + + *: "WavPack" + + + *: "WavPack" + + + + id: LANG_ENCODER_SETTINGS + desc: encoder settings + user: + + *: "Encoder Settings" + + + *: "Encoder Settings" + + + *: "Encoder Settings" + + + + id: LANG_NO_SETTINGS + desc: when something has settings in a certain context + user: + + *: "(No Settings)" + + + *: "(No Settings)" + + + *: "No settings available" + + + + id: LANG_SOURCE_FREQUENCY + desc: when recording source frequency setting must follow source + user: + + *: "(Same As Source)" + + + *: "(Same As Source)" + + + *: "Same As Source" + + + + id: LANG_BITRATE + desc: bits-kilobits per unit time + user: + + *: "Bitrate" + + + *: "Bitrate" + + + *: "Bitrate" + + diff --git a/apps/main.c b/apps/main.c index c4ee45cb89..05b4ab54a3 100644 --- a/apps/main.c +++ b/apps/main.c @@ -286,6 +286,9 @@ void init(void) #ifdef HAVE_ADJUSTABLE_CPU_FREQ set_cpu_frequency(CPUFREQ_NORMAL); +#ifdef CPU_COLDFIRE + coldfire_set_pllcr_audio_bits(DEFAULT_PLLCR_AUDIO_BITS); +#endif cpu_boost_id(true, CPUBOOSTID_MAININIT); #endif diff --git a/apps/metadata.c b/apps/metadata.c index ee0100ecf7..845536877c 100644 --- a/apps/metadata.c +++ b/apps/metadata.c @@ -88,36 +88,6 @@ struct apetag_item_header long flags; }; -struct format_list -{ - char format; - char extension[5]; -}; - -static const struct format_list formats[] = -{ - { AFMT_MPA_L1, "mp1" }, - { AFMT_MPA_L2, "mp2" }, - { AFMT_MPA_L2, "mpa" }, - { AFMT_MPA_L3, "mp3" }, -#if CONFIG_CODEC == SWCODEC - { AFMT_OGG_VORBIS, "ogg" }, - { AFMT_PCM_WAV, "wav" }, - { AFMT_FLAC, "flac" }, - { AFMT_MPC, "mpc" }, - { AFMT_A52, "a52" }, - { AFMT_A52, "ac3" }, - { AFMT_WAVPACK, "wv" }, - { AFMT_ALAC, "m4a" }, - { AFMT_AAC, "mp4" }, - { AFMT_SHN, "shn" }, - { AFMT_AIFF, "aif" }, - { AFMT_AIFF, "aiff" }, - { AFMT_SID, "sid" }, - { AFMT_ADX, "adx" }, -#endif -}; - #if CONFIG_CODEC == SWCODEC static const unsigned short a52_bitrates[] = { @@ -1691,14 +1661,24 @@ unsigned int probe_file_format(const char *filename) return AFMT_UNKNOWN; } - suffix += 1; - - for (i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) + /* skip '.' */ + suffix++; + + for (i = 1; i < AFMT_NUM_CODECS; i++) { - if (strcasecmp(suffix, formats[i].extension) == 0) + /* search extension list for type */ + const char *ext = audio_formats[i].ext_list; + + do + { + if (strcasecmp(suffix, ext) == 0) { - return formats[i].format; + return i; + } + + ext += strlen(ext) + 1; } + while (*ext != '\0'); } return AFMT_UNKNOWN; diff --git a/apps/misc.c b/apps/misc.c index c36d61914b..01463851be 100644 --- a/apps/misc.c +++ b/apps/misc.c @@ -58,6 +58,8 @@ #endif /* End HAVE_LCD_BITMAP */ #include "gui/gwps-common.h" +#include "misc.h" + /* Format a large-range value for output, using the appropriate unit so that * the displayed value is in the range 1 <= display < 1000 (1024 for "binary" * units) if possible, and 3 significant digits are shown. If a buffer is @@ -114,16 +116,20 @@ char *output_dyn_value(char *buf, int buf_size, int value, } /* Create a filename with a number part in a way that the number is 1 - higher than the highest numbered file matching the same pattern. - It is allowed that buffer and path point to the same memory location, - saving a strcpy(). Path must always be given without trailing slash,. */ + * higher than the highest numbered file matching the same pattern. + * It is allowed that buffer and path point to the same memory location, + * saving a strcpy(). Path must always be given without trailing slash. + * "num" can point to an int specifying the number to use or NULL or a value + * less than zero to number automatically. The final number used will also + * be returned in *num. If *num is >= 0 then *num will be incremented by + * one. */ char *create_numbered_filename(char *buffer, const char *path, const char *prefix, const char *suffix, - int numberlen) + int numberlen IF_CNFN_NUM_(, int *num)) { DIR *dir; struct dirent *entry; - int max_num = 0; + int max_num; int pathlen; int prefixlen = strlen(prefix); char fmtstring[12]; @@ -133,6 +139,18 @@ char *create_numbered_filename(char *buffer, const char *path, pathlen = strlen(buffer); +#ifdef IF_CNFN_NUM + if (num && *num >= 0) + { + /* number specified */ + max_num = *num; + } + else +#endif + { + /* automatic numbering */ + max_num = 0; + dir = opendir(pathlen ? buffer : "/"); if (!dir) return NULL; @@ -149,11 +167,20 @@ char *create_numbered_filename(char *buffer, const char *path, if (curr_num > max_num) max_num = curr_num; } + closedir(dir); + } + + max_num++; snprintf(fmtstring, sizeof(fmtstring), "/%%s%%0%dd%%s", numberlen); snprintf(buffer + pathlen, MAX_PATH - pathlen, fmtstring, prefix, - max_num + 1, suffix); + max_num, suffix); + +#ifdef IF_CNFN_NUM + if (num) + *num = max_num; +#endif return buffer; } @@ -161,13 +188,22 @@ char *create_numbered_filename(char *buffer, const char *path, #ifdef CONFIG_RTC /* Create a filename with a date+time part. It is allowed that buffer and path point to the same memory location, - saving a strcpy(). Path must always be given without trailing slash. */ + saving a strcpy(). Path must always be given without trailing slash. + unique_time as true makes the function wait until the current time has + changed. */ char *create_datetime_filename(char *buffer, const char *path, - const char *prefix, const char *suffix) + const char *prefix, const char *suffix, + bool unique_time) { struct tm *tm = get_time(); + static struct tm last_tm; int pathlen; + while (unique_time && !memcmp(get_time(), &last_tm, sizeof (struct tm))) + sleep(HZ/10); + + last_tm = *tm; + if (buffer != path) strncpy(buffer, path, MAX_PATH); @@ -356,9 +392,10 @@ void screen_dump(void) #endif #ifdef CONFIG_RTC - create_datetime_filename(filename, "", "dump ", ".bmp"); + create_datetime_filename(filename, "", "dump ", ".bmp", false); #else - create_numbered_filename(filename, "", "dump_", ".bmp", 4); + create_numbered_filename(filename, "", "dump_", ".bmp", 4 + IF_CNFN_NUM_(, NULL)); #endif fh = creat(filename, O_WRONLY); diff --git a/apps/misc.h b/apps/misc.h index 1bc9a23447..6c660e0a5e 100644 --- a/apps/misc.h +++ b/apps/misc.h @@ -19,21 +19,46 @@ #ifndef MISC_H #define MISC_H +#include + /* Format a large-range value for output, using the appropriate unit so that * the displayed value is in the range 1 <= display < 1000 (1024 for "binary" * units) if possible, and 3 significant digits are shown. If a buffer is * given, the result is snprintf()'d into that buffer, otherwise the result is * voiced.*/ -void output_dyn_value(char *buf, int buf_size, int value, +char *output_dyn_value(char *buf, int buf_size, int value, const unsigned char **units, bool bin_scale); +/* Create a filename with a number part in a way that the number is 1 + * higher than the highest numbered file matching the same pattern. + * It is allowed that buffer and path point to the same memory location, + * saving a strcpy(). Path must always be given without trailing slash. + * + * "num" can point to an int specifying the number to use or NULL or a value + * less than zero to number automatically. The final number used will also + * be returned in *num. If *num is >= 0 then *num will be incremented by + * one. */ +#if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) && !defined(CONFIG_RTC) +/* this feature is needed by SWCODEC recording without a RTC to prevent + disk access when changing files */ +#define IF_CNFN_NUM_(...) __VA_ARGS__ +#define IF_CNFN_NUM +#else +#define IF_CNFN_NUM_(...) +#endif char *create_numbered_filename(char *buffer, const char *path, const char *prefix, const char *suffix, - int numberlen); + int numberlen IF_CNFN_NUM_(, int *num)); #ifdef CONFIG_RTC +/* Create a filename with a date+time part. + It is allowed that buffer and path point to the same memory location, + saving a strcpy(). Path must always be given without trailing slash. + unique_time as true makes the function wait until the current time has + changed. */ char *create_datetime_filename(char *buffer, const char *path, - const char *prefix, const char *suffix); -#endif + const char *prefix, const char *suffix, + bool unique_time); +#endif /* CONFIG_RTC */ /* Read (up to) a line of text from fd into buffer and return number of bytes * read (which may be larger than the number of bytes stored in buffer). If @@ -57,4 +82,4 @@ long default_event_handler(long event); void car_adapter_mode_init(void); extern int show_logo(void); -#endif +#endif /* MISC_H */ diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c index 44f175c67d..5119d20ebd 100644 --- a/apps/pcmbuf.c +++ b/apps/pcmbuf.c @@ -51,9 +51,11 @@ struct pcmbufdesc void (*callback)(void); }; +#define PCMBUF_DESCS(bufsize) ((bufsize) / PCMBUF_MINAVG_CHUNK) + /* Size of the PCM buffer. */ static size_t pcmbuf_size IDATA_ATTR = 0; - +static char *pcmbuf_bufend IDATA_ATTR; static char *audiobuffer IDATA_ATTR; /* Current audio buffer write index. */ static size_t audiobuffer_pos IDATA_ATTR; @@ -360,7 +362,7 @@ int pcmbuf_used_descs(void) { } int pcmbuf_descs(void) { - return pcmbuf_size / PCMBUF_MINAVG_CHUNK; + return PCMBUF_DESCS(pcmbuf_size); } size_t get_pcmbuf_descsize(void) { @@ -371,28 +373,37 @@ static void pcmbuf_init_pcmbuffers(void) { struct pcmbufdesc *next = pcmbuf_write; next++; pcmbuf_write_end = pcmbuf_write; - while ((void *)next < (void *)audiobufend) { + while ((void *)next < (void *)pcmbuf_bufend) { pcmbuf_write_end->link=next; pcmbuf_write_end=next; next++; } } +bool pcmbuf_is_same_size(size_t bufsize) +{ + /* keep calculations synced with pcmbuf_init */ + bufsize += PCMBUF_MIX_CHUNK * 2 + PCMBUF_DESCS(bufsize); + return bufsize == (size_t)(pcmbuf_bufend - audiobuffer); +} + /* Initialize the pcmbuffer the structure looks like this: - * ...CODECBUFFER|---------PCMBUF---------|GUARDBUF|DESCS| */ -void pcmbuf_init(size_t bufsize) + * ...|---------PCMBUF---------|FADEBUF|VOICEBUF|DESCS|... */ +size_t pcmbuf_init(size_t bufsize, char *bufend) { pcmbuf_size = bufsize; + pcmbuf_bufend = bufend; pcmbuf_descsize = pcmbuf_descs()*sizeof(struct pcmbufdesc); - audiobuffer = (char *)&audiobuf[(audiobufend - audiobuf) - - (pcmbuf_size + PCMBUF_MIX_CHUNK * 2 + pcmbuf_descsize)]; + audiobuffer = pcmbuf_bufend - (pcmbuf_size + PCMBUF_MIX_CHUNK * 2 + + pcmbuf_descsize); fadebuf = &audiobuffer[pcmbuf_size]; voicebuf = &fadebuf[PCMBUF_MIX_CHUNK]; - pcmbuf_write = (struct pcmbufdesc *)(&voicebuf[PCMBUF_MIX_CHUNK]); + pcmbuf_write = (struct pcmbufdesc *)&voicebuf[PCMBUF_MIX_CHUNK]; pcmbuf_init_pcmbuffers(); position_callback = NULL; pcmbuf_event_handler = NULL; pcmbuf_play_stop(); + return pcmbuf_bufend - audiobuffer; } size_t pcmbuf_get_bufsize(void) diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h index b5035f4405..a408cdae42 100644 --- a/apps/pcmbuf.h +++ b/apps/pcmbuf.h @@ -38,7 +38,7 @@ /* Returns true if the buffer needs to change size */ bool pcmbuf_is_same_size(size_t bufsize); -void pcmbuf_init(size_t bufsize); +size_t pcmbuf_init(size_t bufsize, char *bufend); /* Size in bytes used by the pcmbuffer */ size_t pcmbuf_get_bufsize(void); size_t get_pcmbuf_descsize(void); diff --git a/apps/playback.c b/apps/playback.c index f8372665a4..af6b573f1d 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -54,8 +54,6 @@ #include "playlist.h" #include "playback.h" #include "pcmbuf.h" -#include "pcm_playback.h" -#include "pcm_record.h" #include "buffer.h" #include "dsp.h" #include "abrepeat.h" @@ -78,6 +76,7 @@ #ifdef HAVE_RECORDING #include "recording.h" +#include "talk.h" #endif #define PLAYBACK_VOICE @@ -93,9 +92,13 @@ * for their correct seeek target, 32k seems a good size */ #define AUDIO_REBUFFER_GUESS_SIZE (1024*32) -/* macros to enable logf for queues */ +/* macros to enable logf for queues + logging on SYS_TIMEOUT can be disabled */ #ifdef SIMULATOR -#define PLAYBACK_LOGQUEUES /* Define this for logf output of all queuing */ +/* Define this for logf output of all queuing except SYS_TIMEOUT */ +#define PLAYBACK_LOGQUEUES +/* Define this to logf SYS_TIMEOUT messages */ +#define PLAYBACK_LOGQUEUES_SYS_TIMEOUT #endif #ifdef PLAYBACK_LOGQUEUES @@ -104,6 +107,18 @@ #define LOGFQUEUE(s) #endif +#ifdef PLAYBACK_LOGQUEUES_SYS_TIMEOUT +#define LOGFQUEUE_SYS_TIMEOUT(s) logf("%s", s) +#else +#define LOGFQUEUE_SYS_TIMEOUT(s) +#endif + + +/* Define one constant that includes recording related functionality */ +#if defined(HAVE_RECORDING) && !defined(SIMULATOR) +#define AUDIO_HAVE_RECORDING +#endif + enum { Q_AUDIO_PLAY = 1, Q_AUDIO_STOP, @@ -122,6 +137,9 @@ enum { #if MEM > 8 Q_AUDIO_FILL_BUFFER_IF_ACTIVE_ATA, #endif +#ifdef AUDIO_HAVE_RECORDING + Q_AUDIO_LOAD_ENCODER, +#endif Q_CODEC_REQUEST_PENDING, Q_CODEC_REQUEST_COMPLETE, @@ -133,7 +151,7 @@ enum { Q_CODEC_LOAD, Q_CODEC_LOAD_DISK, -#if defined(HAVE_RECORDING) && !defined(SIMULATOR) +#ifdef AUDIO_HAVE_RECORDING Q_ENCODER_LOAD_DISK, Q_ENCODER_RECORD, #endif @@ -178,11 +196,16 @@ static volatile bool paused; /* Is audio paused? (A/C-) */ static volatile bool filling IDATA_ATTR; /* Is file buffer currently being refilled? (A/C-) */ /* Ring buffer where tracks and codecs are loaded */ -static char *filebuf; /* Pointer to start of ring buffer (A/C-) */ +static unsigned char *filebuf; /* Pointer to start of ring buffer (A/C-) */ size_t filebuflen; /* Total size of the ring buffer FIXME: make static (A/C-)*/ static volatile size_t buf_ridx IDATA_ATTR; /* Ring buffer read position (A/C) FIXME? should be (C/A-) */ static volatile size_t buf_widx IDATA_ATTR; /* Ring buffer read position (A/C-) */ +#define BUFFER_STATE_TRASHED -1 /* Buffer is in a trashed state and must be reset */ +#define BUFFER_STATE_NORMAL 0 /* Buffer is arranged for voice and audio */ +#define BUFFER_STATE_VOICED_ONLY 1 /* Buffer is arranged for voice-only use */ +static int buffer_state = BUFFER_STATE_TRASHED; /* Buffer state */ + #define RINGBUF_ADD(p,v) ((p+v)=v) ? p-v : p+filebuflen-v) #define RINGBUF_ADD_CROSS(p1,v,p2) ((p1 codec Q_ENCODER_LOAD_DISK"); - queue_post(&codec_queue, Q_ENCODER_LOAD_DISK, (void *)enc_fn); + LOGFQUEUE("audio > Q_AUDIO_LOAD_ENCODER"); + queue_post(&audio_queue, Q_AUDIO_LOAD_ENCODER, (void *)enc_fn); - while (!ci.enc_codec_loaded) + while (ci.enc_codec_loaded == 0) yield(); + + logf("codec loaded: %d", ci.enc_codec_loaded); + + return ci.enc_codec_loaded > 0; +#else + (void)afmt; + return true; #endif - return; - (void)enc_id; } /* audio_load_encoder */ void audio_remove_encoder(void) { -#if defined(HAVE_RECORDING) && !defined(SIMULATOR) - /* force encoder codec unload (if previously loaded) */ - if (!ci.enc_codec_loaded) +#ifndef SIMULATOR + /* force encoder codec unload (if currently loaded) */ + if (ci.enc_codec_loaded <= 0) return; ci.stop_codec = true; - while (ci.enc_codec_loaded) + while (ci.enc_codec_loaded > 0) yield(); #endif } /* audio_remove_encoder */ +#endif /* HAVE_RECORDING */ + struct mp3entry* audio_current_track(void) { const char *filename; @@ -553,6 +637,9 @@ void audio_flush_and_reload_tracks(void) void audio_error_clear(void) { +#ifdef AUDIO_HAVE_RECORDING + pcm_rec_error_clear(); +#endif } int audio_status(void) @@ -573,11 +660,6 @@ int audio_status(void) return ret; } -bool audio_query_poweroff(void) -{ - return !(playing && paused); -} - int audio_get_file_pos(void) { return 0; @@ -617,7 +699,7 @@ void audio_set_crossfade(int enable) enable = 0; size = NATIVE_FREQUENCY*2; #endif - if (pcmbuf_get_bufsize() == size) + if (buffer_state == BUFFER_STATE_NORMAL && pcmbuf_is_same_size(size)) return ; if (was_playing) @@ -633,9 +715,8 @@ void audio_set_crossfade(int enable) voice_stop(); /* Re-initialize audio system. */ - pcmbuf_init(size); + audio_reset_buffer(size); pcmbuf_crossfade_enable(enable); - audio_reset_buffer(); logf("abuf:%dB", pcmbuf_get_bufsize()); logf("fbuf:%dB", filebuflen); @@ -714,8 +795,7 @@ void voice_stop(void) { #ifdef PLAYBACK_VOICE /* Messages should not be posted to voice codec queue unless it is the - current codec or deadlocks happen. - -- jhMikeS */ + current codec or deadlocks happen. */ if (current_codec != CODEC_IDX_VOICE) return; @@ -784,21 +864,32 @@ static void set_filebuf_watermark(int seconds) conf_watermark = bytes; } -static const char * get_codec_filename(int enc_spec) +static const char * get_codec_filename(int cod_spec) { const char *fname; - int type = enc_spec & CODEC_TYPE_MASK; - int afmt = enc_spec & CODEC_AFMT_MASK; + +#ifdef HAVE_RECORDING + /* Can choose decoder or encoder if one available */ + int type = cod_spec & CODEC_TYPE_MASK; + int afmt = cod_spec & CODEC_AFMT_MASK; if ((unsigned)afmt >= AFMT_NUM_CODECS) type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK); - fname = (type == CODEC_TYPE_DECODER) ? - audio_formats[afmt].codec_fn : audio_formats[afmt].codec_enc_fn; + fname = (type == CODEC_TYPE_ENCODER) ? + audio_formats[afmt].codec_enc_root_fn : + audio_formats[afmt].codec_root_fn; logf("%s: %d - %s", (type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder", afmt, fname ? fname : ""); +#else /* !HAVE_RECORDING */ + /* Always decoder */ + if ((unsigned)cod_spec >= AFMT_NUM_CODECS) + cod_spec = AFMT_UNKNOWN; + fname = audio_formats[cod_spec].codec_root_fn; + logf("Codec: %d - %s", cod_spec, fname ? fname : ""); +#endif /* HAVE_RECORDING */ return fname; } /* get_codec_filename */ @@ -940,7 +1031,7 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize) } break; -#if defined(HAVE_RECORDING) && !defined(SIMULATOR) +#ifdef AUDIO_HAVE_RECORDING case Q_ENCODER_RECORD: LOGFQUEUE("voice < Q_ENCODER_RECORD"); swap_codec(); @@ -995,7 +1086,7 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize) goto voice_play_clip; case SYS_TIMEOUT: - LOGFQUEUE("voice < SYS_TIMEOUT"); + LOGFQUEUE_SYS_TIMEOUT("voice < SYS_TIMEOUT"); goto voice_play_clip; default: @@ -1773,7 +1864,7 @@ static void codec_thread(void) #endif break ; -#if defined(HAVE_RECORDING) && !defined(SIMULATOR) +#ifdef AUDIO_HAVE_RECORDING case Q_ENCODER_LOAD_DISK: LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK"); audio_codec_loaded = false; /* Not audio codec! */ @@ -1785,12 +1876,14 @@ static void codec_thread(void) } #endif mutex_lock(&mutex_codecthread); + logf("loading encoder"); current_codec = CODEC_IDX_AUDIO; ci.stop_codec = false; status = codec_load_file((const char *)ev.data, &ci); mutex_unlock(&mutex_codecthread); + logf("encoder stopped"); break; -#endif +#endif /* AUDIO_HAVE_RECORDING */ #ifndef SIMULATOR case SYS_USB_CONNECTED: @@ -1872,6 +1965,24 @@ static void codec_thread(void) } break; +#ifdef AUDIO_HAVE_RECORDING + case Q_ENCODER_LOAD_DISK: + LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK"); + + if (status == CODEC_OK) + break; + + logf("Encoder failure"); + gui_syncsplash(HZ*2, true, "Encoder failure"); + + if (ci.enc_codec_loaded < 0) + break; + + logf("Encoder failed to load"); + ci.enc_codec_loaded = -1; + break; +#endif /* AUDIO_HAVE_RECORDING */ + default: LOGFQUEUE("codec < default"); @@ -2992,6 +3103,10 @@ static void audio_play_start(size_t offset) /* Wait for any previously playing audio to flush - TODO: Not necessary? */ audio_stop_codec_flush(); + /* must reset the buffer before any playback begins if needed */ + if (buffer_state != BUFFER_STATE_NORMAL) + audio_reset_buffer(pcmbuf_get_bufsize()); + track_changed = true; playlist_end = false; @@ -3084,51 +3199,60 @@ static void audio_initiate_dir_change(long direction) ci.new_track = direction; } -static void audio_reset_buffer(void) +/* + * Layout audio buffer as follows: + * [|TALK]|MALLOC|FILE|GUARD|PCM|AUDIOCODEC|[VOICECODEC|] + */ +static void audio_reset_buffer(size_t pcmbufsize) { + /* see audio_get_recording_buffer if this is modified */ size_t offset; - /* Set up file buffer as all space available */ - filebuf = (char *)&audiobuf[talk_get_bufsize()+MALLOC_BUFSIZE]; - filebuflen = audiobufend - (unsigned char *) filebuf - GUARD_BUFSIZE - - (pcmbuf_get_bufsize() + get_pcmbuf_descsize() + PCMBUF_MIX_CHUNK * 2); + logf("audio_reset_buffer"); + logf(" size:%08X", pcmbufsize); - /* Allow for codec(s) at end of file buffer */ + /* Initially set up file buffer as all space available */ + filebuf = audiobuf + MALLOC_BUFSIZE + talk_get_bufsize(); + filebuflen = audiobufend - filebuf; + + /* Allow for codec(s) at end of audio buffer */ if (talk_voice_required()) { - /* Allow 2 codecs at end of file buffer */ +#ifdef PLAYBACK_VOICE + /* Allow 2 codecs at end of audio buffer */ filebuflen -= 2 * (CODEC_IRAM_SIZE + CODEC_SIZE); -#ifdef PLAYBACK_VOICE - iram_buf[0] = &filebuf[filebuflen]; - iram_buf[1] = &filebuf[filebuflen+CODEC_IRAM_SIZE]; - dram_buf[0] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE*2]; - dram_buf[1] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE*2+CODEC_SIZE]; + iram_buf[CODEC_IDX_AUDIO] = filebuf + filebuflen; + dram_buf[CODEC_IDX_AUDIO] = iram_buf[CODEC_IDX_AUDIO] + CODEC_IRAM_SIZE; + iram_buf[CODEC_IDX_VOICE] = dram_buf[CODEC_IDX_AUDIO] + CODEC_SIZE; + dram_buf[CODEC_IDX_VOICE] = iram_buf[CODEC_IDX_VOICE] + CODEC_IRAM_SIZE; #endif } else { - /* Allow for 1 codec at end of file buffer */ +#ifdef PLAYBACK_VOICE + /* Allow for 1 codec at end of audio buffer */ filebuflen -= CODEC_IRAM_SIZE + CODEC_SIZE; -#ifdef PLAYBACK_VOICE - iram_buf[0] = &filebuf[filebuflen]; - iram_buf[1] = NULL; - dram_buf[0] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE]; - dram_buf[1] = NULL; + iram_buf[CODEC_IDX_AUDIO] = filebuf + filebuflen; + dram_buf[CODEC_IDX_AUDIO] = iram_buf[CODEC_IDX_AUDIO] + CODEC_IRAM_SIZE; + iram_buf[CODEC_IDX_VOICE] = NULL; + dram_buf[CODEC_IDX_VOICE] = NULL; #endif } + filebuflen -= pcmbuf_init(pcmbufsize, filebuf + filebuflen) + GUARD_BUFSIZE; + /* Ensure that file buffer is aligned */ - offset = (-(size_t)filebuf) & 3; + offset = -(size_t)filebuf & 3; filebuf += offset; filebuflen -= offset; filebuflen &= ~3; /* Clear any references to the file buffer */ + buffer_state = BUFFER_STATE_NORMAL; } - #ifdef ROCKBOX_HAS_LOGF static void audio_test_track_changed_event(struct mp3entry *id3) { @@ -3149,9 +3273,8 @@ static void audio_playback_init(void) logf("playback api init"); pcm_init(); -#if defined(HAVE_RECORDING) && !defined(SIMULATOR) - /* Set the input multiplexer to Line In */ - pcm_rec_mux(0); +#ifdef AUDIO_HAVE_RECORDING + rec_set_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); #endif #ifdef ROCKBOX_HAS_LOGF @@ -3219,8 +3342,9 @@ static void audio_playback_init(void) #endif } - filebuf = (char *)&audiobuf[MALLOC_BUFSIZE]; /* Will be reset by reset_buffer */ - + /* initialize the buffer */ + filebuf = audiobuf; /* must be non-NULL for audio_set_crossfade */ + buffer_state = BUFFER_STATE_TRASHED; /* force it */ audio_set_crossfade(global_settings.crossfade); audio_is_initialized = true; @@ -3358,6 +3482,14 @@ static void audio_thread(void) playlist_update_resume_info(audio_current_track()); break ; +#ifdef AUDIO_HAVE_RECORDING + case Q_AUDIO_LOAD_ENCODER: + LOGFQUEUE("audio < Q_AUDIO_LOAD_ENCODER"); + LOGFQUEUE("audio > codec Q_ENCODER_LOAD_DISK"); + queue_post(&codec_queue, Q_ENCODER_LOAD_DISK, ev.data); + break; +#endif + #ifndef SIMULATOR case SYS_USB_CONNECTED: LOGFQUEUE("audio < SYS_USB_CONNECTED"); @@ -3368,7 +3500,7 @@ static void audio_thread(void) #endif case SYS_TIMEOUT: - LOGFQUEUE("audio < SYS_TIMEOUT"); + LOGFQUEUE_SYS_TIMEOUT("audio < SYS_TIMEOUT"); break; default: diff --git a/apps/playlist.c b/apps/playlist.c index 5a5313b736..134b52ea8b 100644 --- a/apps/playlist.c +++ b/apps/playlist.c @@ -155,7 +155,7 @@ static int recreate_control(struct playlist_info* playlist); static void update_playlist_filename(struct playlist_info* playlist, const char *dir, const char *file); static int add_indices_to_playlist(struct playlist_info* playlist, - char* buffer, int buflen); + char* buffer, size_t buflen); static int add_track_to_playlist(struct playlist_info* playlist, const char *filename, int position, bool queue, int seek_pos); @@ -457,7 +457,7 @@ static void update_playlist_filename(struct playlist_info* playlist, * calculate track offsets within a playlist file */ static int add_indices_to_playlist(struct playlist_info* playlist, - char* buffer, int buflen) + char* buffer, size_t buflen) { unsigned int nread; unsigned int i = 0; @@ -489,8 +489,7 @@ static int add_indices_to_playlist(struct playlist_info* playlist, buflen = (audiobufend - audiobuf); buffer = (char *)audiobuf; #else - buflen = (audiobufend - audiobuf - talk_get_bufsize()); - buffer = (char *)&audiobuf[talk_get_bufsize()]; + buffer = (char *)audio_get_buffer(false, &buflen); #endif } @@ -1853,7 +1852,7 @@ int playlist_resume(void) { struct playlist_info* playlist = ¤t_playlist; char *buffer; - int buflen; + size_t buflen; int nread; int total_read = 0; int control_file_size = 0; @@ -1866,8 +1865,7 @@ int playlist_resume(void) buflen = (audiobufend - audiobuf); buffer = (char *)audiobuf; #else - buflen = (audiobufend - audiobuf - talk_get_bufsize()); - buffer = (char *)&audiobuf[talk_get_bufsize()]; + buffer = (char *)audio_get_buffer(false, &buflen); #endif empty_playlist(playlist, true); diff --git a/apps/plugin.c b/apps/plugin.c index 25f1865c9e..3a893fc537 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -674,12 +674,18 @@ void* plugin_get_buffer(int* buffer_size) } /* Returns a pointer to the mp3 buffer. - Playback gets stopped, to avoid conflicts. */ + Playback gets stopped, to avoid conflicts. + Talk buffer is stolen as well. + */ void* plugin_get_audio_buffer(int* buffer_size) { +#if CONFIG_CODEC == SWCODEC + return audio_get_buffer(true, (size_t *)buffer_size); +#else audio_stop(); talk_buffer_steal(); /* we use the mp3 buffer, need to tell */ *buffer_size = audiobufend - audiobuf; +#endif return audiobuf; } diff --git a/apps/recorder/icons.c b/apps/recorder/icons.c index 46d628e780..ba22bb5a2c 100644 --- a/apps/recorder/icons.c +++ b/apps/recorder/icons.c @@ -30,12 +30,17 @@ const unsigned char bitmap_icons_5x8[][5] = { - [Icon_Lock_Main] ={0x78,0x7f,0x49,0x7f,0x78}, /* Lock Main */ - [Icon_Lock_Remote]={0x78,0x7f,0x49,0x7f,0x78}, /* Lock Remote */ - [Icon_Stereo]={0x7f, 0x1c, 0x00, 0x1c, 0x7f}, /* Stereo recording */ - [Icon_Mono]={0x00, 0x1c, 0x7f, 0x00, 0x00}, /* Mono recording */ + [Icon_Lock_Main] = + {0x78, 0x7f, 0x49, 0x7f, 0x78}, /* Lock Main */ + [Icon_Lock_Remote] = + {0x78, 0x7f, 0x49, 0x7f, 0x78}, /* Lock Remote */ + [Icon_Stereo] = + {0x7f, 0x22, 0x1c, 0x22, 0x7f}, /* Stereo recording */ + [Icon_Mono] = + {0x00, 0x1c, 0x22, 0x7f, 0x00}, /* Mono recording */ #if CONFIG_CODEC != SWCODEC - [Icon_q]={0x1e, 0x21, 0x31, 0x21, 0x5e} /* Q icon */ + [Icon_q] = + {0x1e, 0x21, 0x31, 0x21, 0x5e} /* Q icon */ #endif }; @@ -81,45 +86,52 @@ const unsigned char bitmap_icons_7x8[][7] = {0x7f,0x04,0x4e,0x5f,0x44,0x38,0x7f} /* Repeat-AB playmode */ }; -#if CONFIG_CODEC == SWCODEC -const unsigned char bitmap_icons_18x8[][18] = +#if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) +const unsigned char bitmap_glyphs_4x8[][4] = { - {0x00, 0x00, 0x00, 0x00,0x3e, 0x04, 0x08, 0x04, 0x3e, 0x00, 0x3e, 0x2a, - 0x3a, 0x00, 0x0e, 0x08, 0x3e, 0x00}, /* mp3 64kbps */ - {0x00, 0x00, 0x00, 0x00,0x3e, 0x04, 0x08, 0x04, 0x3e, 0x00, 0x0e, 0x0a, - 0x3e, 0x00, 0x3e, 0x2a, 0x3a, 0x00}, /* mp3 96kbps */ - {0x3e, 0x04, 0x08, 0x04, 0x3e, 0x00, 0x24, 0x3e, 0x20, 0x00, 0x3a, 0x2a, - 0x2e, 0x00, 0x3e, 0x2a, 0x3e, 0x00}, /* mp3 128kbps */ - {0x3e, 0x04, 0x08, 0x04, 0x3e, 0x00, 0x24, 0x3e, 0x20, 0x00, 0x3e, 0x2a, - 0x3a, 0x00, 0x3e, 0x22, 0x3e, 0x00}, /* mp3 160kbps */ - {0x3e, 0x04, 0x08, 0x04, 0x3e, 0x00, 0x24, 0x3e, 0x20, 0x00, 0x0e, 0x0a, - 0x3e, 0x00, 0x3a, 0x2a, 0x2e, 0x00}, /* mp3 192kbps */ - {0x3e, 0x04, 0x08, 0x04, 0x3e, 0x00, 0x3a, 0x2a, 0x2e, 0x00, 0x3a, 0x2a, - 0x2e, 0x00, 0x0e, 0x08, 0x3e, 0x00}, /* mp3 224kbps */ - {0x3e, 0x04, 0x08, 0x04, 0x3e, 0x00, 0x22, 0x2a, 0x36, 0x00, 0x3a, 0x2a, - 0x2e, 0x00, 0x3e, 0x22, 0x3e, 0x00}, /* mp3 320kbps */ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x1e, 0x20, 0x18, 0x20, 0x1e, - 0x00, 0x1e, 0x20, 0x18, 0x06, 0x00}, /* wv */ - {0x00, 0x00, 0x1e, 0x20, 0x18, 0x20, 0x1e, 0x00, 0x3c, 0x12, 0x12, 0x3c, - 0x00, 0x1e, 0x20, 0x18, 0x06, 0x00} /* wav */ + /* Keep digits together and first! */ + [0] = + {0x00, 0x3e, 0x22, 0x3e}, /* 0 */ + [1] = + {0x00, 0x24, 0x3e, 0x20}, /* 1 */ + [2] = + {0x00, 0x3a, 0x2a, 0x2e}, /* 2 */ + [3] = + {0x00, 0x22, 0x2a, 0x36}, /* 3 */ + [4] = + {0x00, 0x0e, 0x08, 0x3e}, /* 4 */ + [5] = + {0x00, 0x2e, 0x2a, 0x3a}, /* 5 */ + [6] = + {0x00, 0x3e, 0x2a, 0x3a}, /* 6 */ + [7] = + {0x00, 0x02, 0x02, 0x3e}, /* 7 */ + [8] = + {0x00, 0x3e, 0x2a, 0x3e}, /* 8 */ + [9] = + {0x00, 0x0e, 0x0a, 0x3e}, /* 9 */ + [10 ... Glyph_4x8Last-1] = + {0x00, 0x00, 0x00, 0x00}, /* auto-blank */ + [Glyph_4x8_k] = + {0x00, 0x3e, 0x10, 0x28}, /* k */ }; -const unsigned char bitmap_icons_12x8[][12] = +const unsigned char bitmap_formats_18x8[Format_18x8Last][18]= { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x2a, 0x3e, 0x00, 0x3e, 0x10, 0x28}, /* 8khz */ - {0x00, 0x24, 0x3e, 0x20, 0x00, 0x24, 0x3e, 0x20, 0x00, 0x3e, 0x10, 0x28}, /* 11khz */ - {0x00, 0x24, 0x3e, 0x20, 0x00, 0x3a, 0x2a, 0x2e, 0x00, 0x3e, 0x10, 0x28}, /* 12khz */ - {0x00, 0x24, 0x3e, 0x20, 0x00, 0x3e, 0x2a, 0x3a, 0x00, 0x3e, 0x10, 0x28}, /* 16khz */ - {0x00, 0x3a, 0x2a, 0x2e, 0x00, 0x3a, 0x2a, 0x2e, 0x00, 0x3e, 0x10, 0x28}, /* 22khz */ - {0x00, 0x3a, 0x2a, 0x2e, 0x00, 0x0e, 0x08, 0x3e, 0x00, 0x3e, 0x10, 0x28}, /* 24khz */ - {0x00, 0x22, 0x2a, 0x36, 0x00, 0x3a, 0x2a, 0x2e, 0x00, 0x3e, 0x10, 0x28}, /* 32khz */ - {0x00, 0x0e, 0x08, 0x3e, 0x00, 0x0e, 0x08, 0x3e, 0x00, 0x3e, 0x10, 0x28}, /* 44.1khz */ - {0x00, 0x0e, 0x08, 0x3e, 0x00, 0x3e, 0x2a, 0x3e, 0x00, 0x3e, 0x10, 0x28}, /* 48khz */ - {0x00, 0x3e, 0x2a, 0x3a, 0x00, 0x0e, 0x08, 0x3e, 0x00, 0x3e, 0x10, 0x28}, /* 64khz */ - {0x00, 0x3e, 0x2a, 0x3e, 0x00, 0x3e, 0x2a, 0x3e, 0x00, 0x3e, 0x10, 0x28}, /* 88.2khz */ - {0x00, 0x0e, 0x0a, 0x3e, 0x00, 0x3e, 0x2a, 0x3a, 0x00, 0x3e, 0x10, 0x28} /* 96khz */ + [0 ... Format_18x8Last-1] = /* auto-blank */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ___ */ + [Format_18x8_MPA_L3] = + {0x00, 0x3e, 0x04, 0x08, 0x04, 0x3e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* M__ */ + [Format_18x8_WAVPACK] = + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x20, + 0x18, 0x20, 0x1e, 0x00, 0x0e, 0x10, 0x20, 0x10, 0x0e}, /* _WV */ + [Format_18x8_PCM_WAV] = + {0x00, 0x1e, 0x20, 0x18, 0x20, 0x1e, 0x00, 0x3c, 0x0a, + 0x0a, 0x0a, 0x3c, 0x00, 0x0e, 0x10, 0x20, 0x10, 0x0e}, /* WAV */ }; -#endif +#endif /* CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) */ /* Disk/MMC activity */ const unsigned char bitmap_icon_disk[12] = diff --git a/apps/recorder/icons.h b/apps/recorder/icons.h index 75401f6f0b..1e7b8dba1e 100644 --- a/apps/recorder/icons.h +++ b/apps/recorder/icons.h @@ -89,44 +89,40 @@ enum icons_7x8 { Icon7x8Last }; -#if CONFIG_CODEC == SWCODEC -enum icons_12x8 { - Icon_8000, - Icon_11025, - Icon_12000, - Icon_16000, - Icon_22050, - Icon_24000, - Icon_32000, - Icon_44100, - Icon_48000, - Icon_64000, - Icon_88200, - Icon_96000, - Icon12x8Last +#if CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) +#define BM_GLYPH_WIDTH 4 +enum Glyphs_4x8 { + Glyph_4x8_0 = 0, + Glyph_4x8_1, + Glyph_4x8_2, + Glyph_4x8_3, + Glyph_4x8_4, + Glyph_4x8_5, + Glyph_4x8_6, + Glyph_4x8_7, + Glyph_4x8_8, + Glyph_4x8_9, + Glyph_4x8_k, + Glyph_4x8Last }; +extern const unsigned char bitmap_glyphs_4x8[Glyph_4x8Last][4]; -enum icons_18x8 { - Icon_mp364, - Icon_mp396, - Icon_mp3128, - Icon_mp3160, - Icon_mp3192, - Icon_mp3224, - Icon_mp3320, - Icon_wv, - Icon_wav, - Icon18x8Last +#define BM_MPA_L3_M_WIDTH 6 +#ifdef ID3_H +/* This enum is redundant but sort of in keeping with the style */ +enum rec_format_18x8 { + Format_18x8_MPA_L3 = REC_FORMAT_MPA_L3, + Format_18x8_WAVPACK = REC_FORMAT_WAVPACK, + Format_18x8_PCM_WAV = REC_FORMAT_PCM_WAV, + Format_18x8Last = REC_NUM_FORMATS }; -#endif +extern const unsigned char bitmap_formats_18x8[Format_18x8Last][18]; +#endif /* ID3_H */ +#endif /* CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) */ extern const unsigned char bitmap_icons_5x8[Icon5x8Last][5]; extern const unsigned char bitmap_icons_6x8[Icon6x8Last][6]; extern const unsigned char bitmap_icons_7x8[Icon7x8Last][7]; -#if CONFIG_CODEC == SWCODEC -extern const unsigned char bitmap_icons_12x8[Icon12x8Last][12]; -extern const unsigned char bitmap_icons_18x8[Icon18x8Last][18]; -#endif extern const unsigned char bitmap_icon_disk[]; #define STATUSBAR_X_POS 0 diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c index 0370f4deea..44be43124a 100644 --- a/apps/recorder/peakmeter.c +++ b/apps/recorder/peakmeter.c @@ -540,10 +540,8 @@ void peak_meter_peek(void) if (pm_playback) pcm_calculate_peaks(&pm_cur_left, &pm_cur_right); #ifdef HAVE_RECORDING - if (!pm_playback) - { - pcm_rec_get_peaks(&pm_cur_left, &pm_cur_right); - } + else + pcm_calculate_rec_peaks(&pm_cur_left, &pm_cur_right); #endif left = pm_cur_left; right = pm_cur_right; diff --git a/apps/recorder/radio.c b/apps/recorder/radio.c index d74437a8c9..7a0cc6543e 100644 --- a/apps/recorder/radio.c +++ b/apps/recorder/radio.c @@ -386,6 +386,7 @@ bool radio_screen(void) unsigned int last_seconds = 0; #if CONFIG_CODEC != SWCODEC int hours, minutes; + struct audio_recording_options rec_options; #endif bool keep_playing = false; bool statusbar = global_settings.statusbar; @@ -436,12 +437,9 @@ bool radio_screen(void) peak_meter_enabled = true; - rec_set_recording_options(global_settings.rec_frequency, - global_settings.rec_quality, - AUDIO_SRC_LINEIN, 0, - global_settings.rec_channels, - global_settings.rec_editable, - global_settings.rec_prerecord_time); + rec_init_recording_options(&rec_options); + rec_options.rec_source = AUDIO_SRC_LINEIN; + rec_set_recording_options(&rec_options); audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN), sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN); @@ -881,7 +879,7 @@ bool radio_screen(void) } else { - if(global_settings.rec_prerecord_time) + if(rec_options.rec_prerecord_time) { snprintf(buf, 32, "%s %02d", str(LANG_RECORD_PRERECORD), seconds%60); @@ -1173,7 +1171,8 @@ bool save_preset_list(void) if(!opendir(FMPRESET_PATH)) /* Check if there is preset folder */ mkdir(FMPRESET_PATH, 0); - create_numbered_filename(filepreset,FMPRESET_PATH,"preset",".fmr",2); + create_numbered_filename(filepreset, FMPRESET_PATH, "preset", + ".fmr", 2 IF_CNFN_NUM_(, NULL)); while(bad_file_name) { @@ -1534,12 +1533,10 @@ static bool fm_recording_settings(void) #if CONFIG_CODEC != SWCODEC if (!ret) { - rec_set_recording_options(global_settings.rec_frequency, - global_settings.rec_quality, - AUDIO_SRC_LINEIN, 0, - global_settings.rec_channels, - global_settings.rec_editable, - global_settings.rec_prerecord_time); + struct audio_recording_options rec_options; + rec_init_recording_options(&rec_options); + rec_options.rec_source = AUDIO_SRC_LINEIN; + rec_set_recording_options(&rec_options); } #endif diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c index 0a21d96566..6a053cd12e 100644 --- a/apps/recorder/recording.c +++ b/apps/recorder/recording.c @@ -30,8 +30,10 @@ #include "mpeg.h" #include "audio.h" #if CONFIG_CODEC == SWCODEC -#include "pcm_record.h" +#include "thread.h" +#include "pcm_playback.h" #include "playback.h" +#include "enc_config.h" #endif #ifdef HAVE_UDA1380 #include "uda1380.h" @@ -73,36 +75,40 @@ #define PM_HEIGHT ((LCD_HEIGHT >= 72) ? 2 : 1) +#if CONFIG_KEYPAD == RECORDER_PAD bool f2_rec_screen(void); bool f3_rec_screen(void); +#endif #define MAX_FILE_SIZE 0x7F800000 /* 2 GB - 4 MB */ int screen_update = NB_SCREENS; bool remote_display_on = true; -const char* const freq_str[6] = -{ - "44.1kHz", - "48kHz", - "32kHz", - "22.05kHz", - "24kHz", - "16kHz" -}; +/** File name creation **/ #if CONFIG_CODEC == SWCODEC -#define REC_ENCODER_ID(q) \ - rec_quality_info_afmt[q] -#define REC_QUALITY_LABEL(q) \ - (audio_formats[REC_ENCODER_ID(q)].label) -#define REC_FILE_ENDING(q) \ - (audio_formats[REC_ENCODER_ID(q)].ext) -#else -/* default record file extension for HWCODEC */ -#define REC_QUALITY_LABEL(q) "MP3" -#define REC_FILE_ENDING(q) ".mp3" -#endif +#ifdef IF_CNFN_NUM +/* current file number to assist in creating unique numbered filenames + without actually having to create the file on disk */ +static int file_number = -1; +#endif /* IF_CNFN_NUM */ + +#define REC_FILE_ENDING(rec_format) \ + (audio_formats[rec_format_afmt[rec_format]].ext_list) + +#else /* CONFIG_CODEC != SWCODEC */ + +/* default record file extension for HWCODEC */ +#define REC_FILE_ENDING(rec_format) \ + (audio_formats[AFMT_MPA_L3].ext_list) + +#endif /* CONFIG_CODEC == SWCODEC */ + +/* path for current file */ +static char path_buffer[MAX_PATH]; + +/** Automatic Gain Control (AGC) **/ #ifdef HAVE_AGC /* Timing counters: * peak_time is incremented every 0.2s, every 2nd run of record screen loop. @@ -496,20 +502,24 @@ void adjust_cursor(void) char *rec_create_filename(char *buffer) { + char ext[16]; + if(global_settings.rec_directory) getcwd(buffer, MAX_PATH); else strncpy(buffer, rec_base_directory, MAX_PATH); + snprintf(ext, sizeof(ext), ".%s", + REC_FILE_ENDING(global_settings.rec_format)); #ifdef CONFIG_RTC - create_datetime_filename(buffer, buffer, "R", - REC_FILE_ENDING(global_settings.rec_quality)); + /* We'll wait at least up to the start of the next second so no duplicate + names are created */ + return create_datetime_filename(buffer, buffer, "R", ext, true); #else - create_numbered_filename(buffer, buffer, "rec_", - REC_FILE_ENDING(global_settings.rec_quality), 4); + return create_numbered_filename(buffer, buffer, "rec_", ext, 4 + IF_CNFN_NUM_(, &file_number)); #endif - return buffer; } int rec_create_directory(void) @@ -557,9 +567,15 @@ static void rec_boost(bool state) /** * Selects an audio source for recording or playback - * powers/unpowers related devices. + * powers/unpowers related devices and sets up monitoring. * Here because it calls app code and used only for HAVE_RECORDING atm. * Would like it in pcm_record.c. + * + * Behaves like a firmware function in that it does not use global settings + * to determine the state. + * + * The order of setting monitoring may need tweaking dependent upon the + * selected source to get the smoothest transition. */ #if defined(HAVE_UDA1380) #define ac_disable_recording uda1380_disable_recording @@ -571,7 +587,13 @@ static void rec_boost(bool state) #define ac_set_monitor tlv320_set_monitor #endif -void rec_set_source(int source, int flags) +#ifdef HAVE_SPDIF_IN +#define rec_spdif_set_monitor(m) audio_spdif_set_monitor(m) +#else +#define rec_spdif_set_monitor(m) +#endif + +void rec_set_source(int source, unsigned flags) { /* Prevent pops from unneeded switching */ static int last_source = AUDIO_SRC_PLAYBACK; @@ -586,7 +608,9 @@ void rec_set_source(int source, int flags) /** Do power up/down of associated device(s) **/ + /** SPDIF **/ #ifdef HAVE_SPDIF_IN + /* Always boost for SPDIF */ if ((source == AUDIO_SRC_SPDIF) != (source == last_source)) rec_boost(source == AUDIO_SRC_SPDIF); @@ -595,10 +619,11 @@ void rec_set_source(int source, int flags) both optical in and out is controlled by the same power source, which is the case on H1x0. */ spdif_power_enable((source == AUDIO_SRC_SPDIF) || - global_settings.spdif_enable); + audio_get_spdif_power_setting()); #endif #endif + /** Tuner **/ #ifdef CONFIG_TUNER /* Switch radio off or on per source and flags. */ if (source != AUDIO_SRC_FMRADIO) @@ -612,12 +637,15 @@ void rec_set_source(int source, int flags) switch (source) { default: /* playback - no recording */ + source = AUDIO_SRC_PLAYBACK; + case AUDIO_SRC_PLAYBACK: pm_playback = true; if (source == last_source) break; ac_disable_recording(); ac_set_monitor(false); pcm_rec_mux(0); /* line in */ + rec_spdif_set_monitor(-1); /* silence it */ break; case AUDIO_SRC_MIC: /* recording only */ @@ -625,6 +653,7 @@ void rec_set_source(int source, int flags) break; ac_enable_recording(true); /* source mic */ pcm_rec_mux(0); /* line in */ + rec_spdif_set_monitor(0); break; case AUDIO_SRC_LINEIN: /* recording only */ @@ -632,29 +661,20 @@ void rec_set_source(int source, int flags) break; pcm_rec_mux(0); /* line in */ ac_enable_recording(false); /* source line */ + rec_spdif_set_monitor(0); break; #ifdef HAVE_SPDIF_IN case AUDIO_SRC_SPDIF: /* recording only */ - if (recording) - { - /* This was originally done in audio_set_recording_options only */ -#ifdef HAVE_SPDIF_POWER - EBU1CONFIG = global_settings.spdif_enable ? (1 << 2) : 0; - /* Input source is EBUin1, Feed-through monitoring if desired */ -#else - EBU1CONFIG = (1 << 2); - /* Input source is EBUin1, Feed-through monitoring */ -#endif - } - - if (source != last_source) - uda1380_disable_recording(); + if (source == last_source) + break; + ac_disable_recording(); + audio_spdif_set_monitor(1); break; #endif /* HAVE_SPDIF_IN */ #ifdef HAVE_FMRADIO_IN - case AUDIO_SRC_FMRADIO: + case AUDIO_SRC_FMRADIO: /* recording and playback */ if (!recording) { audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN), @@ -687,6 +707,8 @@ void rec_set_source(int source, int flags) tlv320_set_monitor(true); /* analog bypass */ } #endif + + rec_spdif_set_monitor(0); break; /* #elif defined(CONFIG_TUNER) */ /* Have radio but cannot record it */ @@ -702,33 +724,50 @@ void rec_set_source(int source, int flags) } /* rec_set_source */ #endif /* CONFIG_CODEC == SWCODEC && !defined(SIMULATOR) */ -/* steal the mp3 buffer then actually set options */ -void rec_set_recording_options(int frequency, int quality, - int source, int source_flags, - int channel_mode, bool editable, - int prerecord_time) +void rec_init_recording_options(struct audio_recording_options *options) +{ + options->rec_source = global_settings.rec_source; + options->rec_frequency = global_settings.rec_frequency; + options->rec_channels = global_settings.rec_channels; + options->rec_prerecord_time = global_settings.rec_prerecord_time; +#if CONFIG_CODEC == SWCODEC + options->rec_source_flags = 0; + options->enc_config.rec_format = global_settings.rec_format; + global_to_encoder_config(&options->enc_config); +#else + options->rec_quality = global_settings.rec_quality; + options->rec_editable = global_settings.rec_editable; +#endif +} + +void rec_set_recording_options(struct audio_recording_options *options) { #if CONFIG_CODEC != SWCODEC if (global_settings.rec_prerecord_time) -#endif talk_buffer_steal(); /* will use the mp3 buffer */ +#endif + +#ifdef HAVE_SPDIF_IN +#ifdef HAVE_SPDIF_POWER + audio_set_spdif_power_setting(global_settings.spdif_enable); +#endif +#endif #if CONFIG_CODEC == SWCODEC - rec_set_source(source, source_flags | SRCF_RECORDING); -#else - (void)source_flags; + rec_set_source(options->rec_source, + options->rec_source_flags | SRCF_RECORDING); #endif - audio_set_recording_options(frequency, quality, source, - channel_mode, editable, prerecord_time); + audio_set_recording_options(options); } -static char path_buffer[MAX_PATH]; - /* steals mp3 buffer, creates unique filename and starts recording */ void rec_record(void) { +#if CONFIG_CODEC != SWCODEC talk_buffer_steal(); /* we use the mp3 buffer */ +#endif + IF_CNFN_NUM_(file_number = -1;) /* Hit disk for number */ audio_record(rec_create_filename(path_buffer)); } @@ -753,7 +792,6 @@ static void trigger_listener(int trigger_status) case TRIG_GO: if((audio_status() & AUDIO_STATUS_RECORD) != AUDIO_STATUS_RECORD) { - talk_buffer_steal(); /* we use the mp3 buffer */ rec_record(); /* give control to mpeg thread so that it can start recording */ @@ -831,6 +869,8 @@ bool recording_screen(bool no_source) ID2P(LANG_GIGABYTE) }; + struct audio_recording_options rec_options; + global_settings.recscreen_on = true; cursor = 0; #if (CONFIG_LED == LED_REAL) && !defined(SIMULATOR) @@ -838,35 +878,26 @@ bool recording_screen(bool no_source) #endif #if CONFIG_CODEC == SWCODEC - audio_stop(); - voice_stop(); /* recording_menu gets messed up: so reset talk_menu */ talk_menu = global_settings.talk_menu; global_settings.talk_menu = 0; + /* audio_init_recording stops anything playing when it takes the audio + buffer */ #else /* Yes, we use the D/A for monitoring */ peak_meter_enabled = true; peak_meter_playback(true); #endif -#if CONFIG_CODEC == SWCODEC - audio_init_recording(talk_get_bufsize()); -#else audio_init_recording(0); -#endif sound_set_volume(global_settings.volume); #ifdef HAVE_AGC peak_meter_get_peakhold(&peak_l, &peak_r); #endif - rec_set_recording_options(global_settings.rec_frequency, - global_settings.rec_quality, - global_settings.rec_source, - 0, - global_settings.rec_channels, - global_settings.rec_editable, - global_settings.rec_prerecord_time); + rec_init_recording_options(&rec_options); + rec_set_recording_options(&rec_options); set_gain(); settings_apply_trigger(); @@ -1025,7 +1056,6 @@ bool recording_screen(bool no_source) { /* manual recording */ have_recorded = true; - talk_buffer_steal(); /* we use the mp3 buffer */ rec_record(); last_seconds = 0; if (talk_menu) @@ -1253,16 +1283,10 @@ bool recording_screen(bool no_source) #if CONFIG_CODEC == SWCODEC /* reinit after submenu exit */ audio_close_recording(); - audio_init_recording(talk_get_bufsize()); + audio_init_recording(0); #endif - rec_set_recording_options( - global_settings.rec_frequency, - global_settings.rec_quality, - global_settings.rec_source, - 0, - global_settings.rec_channels, - global_settings.rec_editable, - global_settings.rec_prerecord_time); + rec_init_recording_options(&rec_options); + rec_set_recording_options(&rec_options); if(rec_create_directory() > 0) have_recorded = true; @@ -1739,11 +1763,7 @@ bool recording_screen(bool no_source) } } /* end while(!done) */ -#if CONFIG_CODEC == SWCODEC - audio_stat = pcm_rec_status(); -#else audio_stat = audio_status(); -#endif if (audio_stat & AUDIO_STATUS_ERROR) { gui_syncsplash(0, true, str(LANG_SYSFONT_DISK_FULL)); @@ -1804,11 +1824,22 @@ bool recording_screen(bool no_source) #if CONFIG_KEYPAD == RECORDER_PAD bool f2_rec_screen(void) { + static const char* const freq_str[6] = + { + "44.1kHz", + "48kHz", + "32kHz", + "22.05kHz", + "24kHz", + "16kHz" + }; + bool exit = false; bool used = false; int w, h, i; char buf[32]; int button; + struct audio_recording_options rec_options; FOR_NB_SCREENS(i) { @@ -1919,13 +1950,8 @@ bool f2_rec_screen(void) } } - rec_set_recording_options(global_settings.rec_frequency, - global_settings.rec_quality, - global_settings.rec_source, - 0, - global_settings.rec_channels, - global_settings.rec_editable, - global_settings.rec_prerecord_time); + rec_init_recording_options(&rec_options); + rec_set_recording_options(&rec_options); set_gain(); @@ -1948,6 +1974,8 @@ bool f3_rec_screen(void) str(LANG_SYSFONT_RECORDING_SRC_LINE), str(LANG_SYSFONT_RECORDING_SRC_DIGITAL) }; + struct audio_recording_options rec_options; + FOR_NB_SCREENS(i) { screens[i].setfont(FONT_SYSFIXED); @@ -2019,13 +2047,8 @@ bool f3_rec_screen(void) } } - rec_set_recording_options(global_settings.rec_frequency, - global_settings.rec_quality, - global_settings.rec_source, - 0, - global_settings.rec_channels, - global_settings.rec_editable, - global_settings.rec_prerecord_time); + rec_init_recording_options(&rec_options); + rec_set_recording_options(&rec_options); set_gain(); @@ -2066,23 +2089,30 @@ unsigned long audio_num_recorded_bytes(void) } #if CONFIG_CODEC == SWCODEC -void rec_set_source(int source, int flags) +void rec_set_source(int source, unsigned flags) { source = source; flags = flags; } -#endif -void audio_set_recording_options(int frequency, int quality, - int source, int channel_mode, - bool editable, int prerecord_time) +#ifdef HAVE_SPDIF_IN +#ifdef HAVE_SPDIF_POWER +void audio_set_spdif_power_setting(bool on) { - frequency = frequency; - quality = quality; - source = source; - channel_mode = channel_mode; - editable = editable; - prerecord_time = prerecord_time; + on = on; +} + +bool audio_get_spdif_power_setting(void) +{ + return true; +} +#endif /* HAVE_SPDIF_POWER */ +#endif /* HAVE_SPDIF_IN */ +#endif /* CONFIG_CODEC == SWCODEC */ + +void audio_set_recording_options(struct audio_recording_options *options) +{ + options = options; } void audio_set_recording_gain(int left, int right, int type) @@ -2104,7 +2134,7 @@ void audio_resume_recording(void) { } -void pcm_rec_get_peaks(int *left, int *right) +void pcm_calculate_rec_peaks(int *left, int *right) { if (left) *left = 0; diff --git a/apps/recorder/recording.h b/apps/recorder/recording.h index aa216e757f..a977efa749 100644 --- a/apps/recorder/recording.h +++ b/apps/recorder/recording.h @@ -32,15 +32,16 @@ int rec_create_directory(void); #define SRCF_FMRADIO_PLAYING 0x0000 /* default */ #define SRCF_FMRADIO_PAUSED 0x2000 #endif -void rec_set_source(int source, int flags); +void rec_set_source(int source, unsigned flags); #endif /* CONFIG_CODEC == SW_CODEC */ +/* Initializes a recording_options structure with global settings. + pass returned data to audio_set_recording_options or + rec_set_recording_options */ +void rec_init_recording_options(struct audio_recording_options *options); /* steals mp3 buffer, sets source and then options */ -/* SRCF_RECORDING is implied */ -void rec_set_recording_options(int frequency, int quality, - int source, int source_flags, - int channel_mode, bool editable, - int prerecord_time); +/* SRCF_RECORDING is implied for SWCODEC */ +void rec_set_recording_options(struct audio_recording_options *options); /* steals mp3 buffer, creates unique filename and starts recording */ void rec_record(void); diff --git a/apps/settings.c b/apps/settings.c index a4320eda7b..ec96cc760b 100644 --- a/apps/settings.c +++ b/apps/settings.c @@ -90,13 +90,16 @@ const char rec_base_directory[] = REC_BASE_DIR; #include "pcmbuf.h" #include "pcm_playback.h" #include "dsp.h" +#ifdef HAVE_RECORDING +#include "enc_config.h" #endif +#endif /* CONFIG_CODEC == SWCODEC */ #ifdef HAVE_WM8758 #include "eq_menu.h" #endif -#define CONFIG_BLOCK_VERSION 55 +#define CONFIG_BLOCK_VERSION 56 #define CONFIG_BLOCK_SIZE 512 #define RTC_BLOCK_SIZE 44 @@ -514,7 +517,7 @@ static const struct bit_entry hd_bits[] = {1, S_O(rec_editable), false, "editable recordings", off_on }, #endif /* CONFIG_CODEC == MAS3587F */ -#if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) +#if CONFIG_CODEC == SWCODEC #ifdef HAVE_UDA1380 {8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */ #endif @@ -524,16 +527,20 @@ static const struct bit_entry hd_bits[] = #endif {8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */ {8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */ -#if 0 /* Till samplerates are added for SWCODEC */ - {3, S_O(rec_frequency), 0, /* 0=44.1kHz */ - "rec frequency", "44,48,32,22,24,16" }, -#else - {3, S_O(rec_frequency), 0, /* 0=44.1kHz */ - "rec frequency", "44" }, -#endif - - {4, S_O(rec_quality), 4 /* MP3 L3 192 kBit/s */, "rec quality", NULL }, -#endif /* CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) */ + {REC_FREQ_CFG_NUM_BITS, S_O(rec_frequency), REC_FREQ_DEFAULT, + "rec frequency", REC_FREQ_CFG_VAL_LIST }, + {REC_FORMAT_CFG_NUM_BITS ,S_O(rec_format), REC_FORMAT_DEFAULT, + "rec format", REC_FORMAT_CFG_VAL_LIST }, + /** Encoder settings start - keep these together **/ + /* mp3_enc */ + {5,S_O(mp3_enc_config.bitrate), MP3_ENC_BITRATE_CFG_DEFAULT, + "mp3_enc bitrate", MP3_ENC_BITRATE_CFG_VALUE_LIST }, + /* wav_enc */ + /* (no settings yet) */ + /* wavpack_enc */ + /* (no settings yet) */ + /** Encoder settings end **/ +#endif /* CONFIG_CODEC == SWCODEC */ /* values for the trigger */ {8 | SIGNED, S_O(rec_start_thres), -35, "trigger start threshold", NULL}, @@ -1301,6 +1308,11 @@ void settings_apply(void) lcd_set_sleep_after_backlight_off(global_settings.lcd_sleep_after_backlight_off); #endif #endif /* CONFIG_BACKLIGHT */ + + /* This should stay last */ +#if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC + enc_global_settings_apply(); +#endif } @@ -1727,13 +1739,13 @@ static void save_cfg_table(const struct bit_entry* p_table, int count, int fd) } } - bool settings_save_config(void) { int fd; char filename[MAX_PATH]; - create_numbered_filename(filename, ROCKBOX_DIR, "config", ".cfg", 2); + create_numbered_filename(filename, ROCKBOX_DIR, "config", ".cfg", 2 + IF_CNFN_NUM_(, NULL)); /* allow user to modify filename */ while (true) { @@ -1887,6 +1899,10 @@ void settings_reset(void) { global_settings.kbd_file[0] = '\0'; #endif global_settings.hold_lr_for_scroll_in_list = true; + +#if defined (HAVE_RECORDING) && CONFIG_CODEC == SWCODEC + enc_global_settings_reset(); +#endif } bool set_bool(const char* string, bool* variable ) diff --git a/apps/settings.h b/apps/settings.h index 435d8e9ce2..09d4974eee 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -29,6 +29,10 @@ #include "tagcache.h" #include "button.h" +#if CONFIG_CODEC == SWCODEC +#include "audio.h" +#endif + #ifdef HAVE_BACKLIGHT_BRIGHTNESS #include "backlight.h" /* for [MIN|MAX]_BRIGHTNESS_SETTING */ #endif @@ -142,18 +146,22 @@ struct user_settings int crossfade_fade_out_mixmode; /* Fade out mode (0=crossfade,1=mix) */ #endif +#if CONFIG_CODEC == SWCODEC + int rec_format; /* record format index */ +#else int rec_quality; /* 0-7 */ - int rec_source; /* 0=mic, 1=line, 2=S/PDIF */ - int rec_frequency; /* 0 = 44.1kHz +#endif /* CONFIG_CODEC == SWCODEC */ + int rec_source; /* 0=mic, 1=line, 2=S/PDIF, 2 or 3=FM Radio */ + int rec_frequency; /* 0 = 44.1kHz (depends on target) 1 = 48kHz 2 = 32kHz 3 = 22.05kHz 4 = 24kHz 5 = 16kHz */ int rec_channels; /* 0=Stereo, 1=Mono */ - int rec_mic_gain; /* 0-15 */ - int rec_left_gain; /* 0-15 */ - int rec_right_gain; /* 0-15 */ + int rec_mic_gain; /* depends on target */ + int rec_left_gain; /* depends on target */ + int rec_right_gain; /* depands on target */ bool rec_editable; /* true means that the bit reservoir is off */ bool recscreen_on; /* true if using the recording screen */ @@ -504,6 +512,20 @@ struct user_settings #endif bool audioscrobbler; /* Audioscrobbler logging */ + /* If values are just added to the end, no need to bump plugin API + version. */ + /* new stuff to be added at the end */ + +#if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC + /* Encoder Settings Start - keep these together */ + struct mp3_enc_config mp3_enc_config; +#if 0 /* These currently contain no members but their places in line + should be held */ + struct wav_enc_config wav_enc_config; + struct wavpack_enc_config wavpack_enc_config; +#endif + /* Encoder Settings End */ +#endif /* CONFIG_CODEC == SWCODEC */ }; enum optiontype { INT, BOOL }; @@ -584,7 +606,7 @@ extern const char rec_base_directory[]; /* argument bits for settings_load() */ #define SETTINGS_RTC 1 /* only the settings from the RTC nonvolatile RAM */ -#define SETTINGS_HD 2 /* only the settings fron the disk sector */ +#define SETTINGS_HD 2 /* only the settings from the disk sector */ #define SETTINGS_ALL 3 /* both */ /* repeat mode options */ diff --git a/apps/sound_menu.c b/apps/sound_menu.c index c10ba9417e..fb766d604c 100644 --- a/apps/sound_menu.c +++ b/apps/sound_menu.c @@ -54,6 +54,10 @@ #include "dsp.h" #include "eq_menu.h" #include "pcmbuf.h" +#ifdef HAVE_RECORDING +#include "enc_config.h" +#endif +#include "general.h" #endif #include "action.h" @@ -308,22 +312,20 @@ static bool recsource(void) { int n_opts = AUDIO_NUM_SOURCES; - struct opt_items names[AUDIO_NUM_SOURCES] = { - { STR(LANG_RECORDING_SRC_MIC) }, - { STR(LANG_RECORDING_SRC_LINE) }, + static const struct opt_items names[AUDIO_NUM_SOURCES] = { + [AUDIO_SRC_MIC] = { STR(LANG_RECORDING_SRC_MIC) }, + [AUDIO_SRC_LINEIN] = { STR(LANG_RECORDING_SRC_LINE) }, #ifdef HAVE_SPDIF_IN - { STR(LANG_RECORDING_SRC_DIGITAL) }, + [AUDIO_SRC_SPDIF] = { STR(LANG_RECORDING_SRC_DIGITAL) }, +#endif +#ifdef HAVE_FMRADIO_IN + [AUDIO_SRC_FMRADIO] = { STR(LANG_FM_RADIO) } #endif }; /* caveat: assumes it's the last item! */ #ifdef HAVE_FMRADIO_IN - if (radio_hardware_present()) - { - names[AUDIO_SRC_FMRADIO].string = ID2P(LANG_FM_RADIO); - names[AUDIO_SRC_FMRADIO].voice_id = LANG_FM_RADIO; - } - else + if (!radio_hardware_present()) n_opts--; #endif @@ -332,28 +334,7 @@ static bool recsource(void) n_opts, NULL ); } -/* To be removed when we add support for sample rates and channel settings */ -#if CONFIG_CODEC == SWCODEC -static bool recquality(void) -{ - static const struct opt_items names[] = { - { "MP3 64 kBit/s", TALK_ID( 64, UNIT_KBIT) }, - { "MP3 96 kBit/s", TALK_ID( 96, UNIT_KBIT) }, - { "MP3 128 kBit/s", TALK_ID( 128, UNIT_KBIT) }, - { "MP3 160 kBit/s", TALK_ID( 160, UNIT_KBIT) }, - { "MP3 192 kBit/s", TALK_ID( 192, UNIT_KBIT) }, - { "MP3 224 kBit/s", TALK_ID( 224, UNIT_KBIT) }, - { "MP3 320 kBit/s", TALK_ID( 320, UNIT_KBIT) }, - { "WV 900 kBit/s", TALK_ID( 900, UNIT_KBIT) }, - { "WAV 1411 kBit/s", TALK_ID(1411, UNIT_KBIT) } - }; - - return set_option(str(LANG_RECORDING_QUALITY), - &global_settings.rec_quality, INT, - names, sizeof (names)/sizeof(struct opt_items), - NULL ); -} -#elif CONFIG_CODEC == MAS3587F +#if CONFIG_CODEC == MAS3587F static bool recquality(void) { return set_int(str(LANG_RECORDING_QUALITY), "", UNIT_INT, @@ -368,32 +349,182 @@ static bool receditable(void) } #endif /* CONFIG_CODEC == MAS3587F */ +#if CONFIG_CODEC == SWCODEC +/* Makes an options list from a source list of options and indexes */ +void make_options_from_indexes(const struct opt_items *src_names, + const long *src_indexes, + int n_indexes, + struct opt_items *dst_names) +{ + while (--n_indexes >= 0) + dst_names[n_indexes] = src_names[src_indexes[n_indexes]]; +} /* make_options_from_indexes */ + +static bool recformat(void) +{ + static const struct opt_items names[REC_NUM_FORMATS] = { + [REC_FORMAT_MPA_L3] = { STR(LANG_AFMT_MPA_L3) }, + [REC_FORMAT_WAVPACK] = { STR(LANG_AFMT_WAVPACK) }, + [REC_FORMAT_PCM_WAV] = { STR(LANG_AFMT_PCM_WAV) }, + }; + + int rec_format = global_settings.rec_format; + bool res = set_option(str(LANG_RECORDING_FORMAT), &rec_format, INT, + names, REC_NUM_FORMATS, NULL ); + + if (rec_format != global_settings.rec_format) + { + global_settings.rec_format = rec_format; + enc_global_settings_apply(); + } + + return res; +} /* recformat */ + +#endif /* CONFIG_CODEC == SWCODEC */ + static bool recfrequency(void) { - static const struct opt_items names[] = { +#if CONFIG_CODEC == MAS3587F + static const struct opt_items names[6] = { { "44.1kHz", TALK_ID(44, UNIT_KHZ) }, -#if CONFIG_CODEC != SWCODEC /* This is temporary */ { "48kHz", TALK_ID(48, UNIT_KHZ) }, { "32kHz", TALK_ID(32, UNIT_KHZ) }, { "22.05kHz", TALK_ID(22, UNIT_KHZ) }, { "24kHz", TALK_ID(24, UNIT_KHZ) }, { "16kHz", TALK_ID(16, UNIT_KHZ) } -#endif }; return set_option(str(LANG_RECORDING_FREQUENCY), &global_settings.rec_frequency, INT, - names, sizeof(names)/sizeof(*names), NULL ); + names, 6, NULL ); +#endif /* CONFIG_CODEC == MAS3587F */ + +#if CONFIG_CODEC == SWCODEC + static const struct opt_items names[REC_NUM_FREQ] = { + REC_HAVE_96_([REC_FREQ_96] = { "96kHz", TALK_ID(96, UNIT_KHZ) },) + REC_HAVE_88_([REC_FREQ_88] = { "88.2kHz", TALK_ID(88, UNIT_KHZ) },) + REC_HAVE_64_([REC_FREQ_64] = { "64kHz", TALK_ID(64, UNIT_KHZ) },) + REC_HAVE_48_([REC_FREQ_48] = { "48kHz", TALK_ID(48, UNIT_KHZ) },) + REC_HAVE_44_([REC_FREQ_44] = { "44.1kHz", TALK_ID(44, UNIT_KHZ) },) + REC_HAVE_32_([REC_FREQ_32] = { "32kHz", TALK_ID(32, UNIT_KHZ) },) + REC_HAVE_24_([REC_FREQ_24] = { "24kHz", TALK_ID(24, UNIT_KHZ) },) + REC_HAVE_22_([REC_FREQ_22] = { "22.05kHz", TALK_ID(22, UNIT_KHZ) },) + REC_HAVE_16_([REC_FREQ_16] = { "16kHz", TALK_ID(16, UNIT_KHZ) },) + REC_HAVE_12_([REC_FREQ_12] = { "12kHz", TALK_ID(12, UNIT_KHZ) },) + REC_HAVE_11_([REC_FREQ_11] = { "11.025kHz", TALK_ID(11, UNIT_KHZ) },) + REC_HAVE_8_( [REC_FREQ_8 ] = { "8kHz", TALK_ID( 8, UNIT_KHZ) },) + }; + + struct opt_items opts[REC_NUM_FREQ]; + unsigned long table[REC_NUM_FREQ]; + int n_opts; + int rec_frequency; + bool ret; + +#ifdef HAVE_SPDIF_IN + if (global_settings.rec_source == AUDIO_SRC_SPDIF) + { + /* Inform user that frequency follows the source's frequency */ + opts[0].string = ID2P(LANG_SOURCE_FREQUENCY); + opts[0].voice_id = LANG_SOURCE_FREQUENCY; + n_opts = 1; + rec_frequency = 0; } + else +#endif + { + struct encoder_caps caps; + struct encoder_config cfg; + + cfg.rec_format = global_settings.rec_format; + global_to_encoder_config(&cfg); + + if (!enc_get_caps(&cfg, &caps, true)) + return false; + + /* Construct samplerate menu based upon encoder settings */ + n_opts = make_list_from_caps32(REC_SAMPR_CAPS, NULL, + caps.samplerate_caps, table); + + if (n_opts == 0) + return false; /* No common flags...?? */ + + make_options_from_indexes(names, table, n_opts, opts); + + /* Find closest rate that the potentially restricted list + comes to */ + make_list_from_caps32(REC_SAMPR_CAPS, rec_freq_sampr, + caps.samplerate_caps, table); + + rec_frequency = round_value_to_list32( + rec_freq_sampr[global_settings.rec_frequency], + table, n_opts, false); + } + + ret = set_option(str(LANG_RECORDING_FREQUENCY), + &rec_frequency, INT, opts, n_opts, NULL ); + + if (!ret +#ifdef HAVE_SPDIF_IN + && global_settings.rec_source != AUDIO_SRC_SPDIF +#endif + ) + { + /* Translate back to full index */ + global_settings.rec_frequency = + round_value_to_list32(table[rec_frequency], + rec_freq_sampr, + REC_NUM_FREQ, + false); + } + + return ret; +#endif /* CONFIG_CODEC == SWCODEC */ +} /* recfrequency */ static bool recchannels(void) { - static const struct opt_items names[] = { - { STR(LANG_CHANNEL_STEREO) }, - { STR(LANG_CHANNEL_MONO) } + static const struct opt_items names[CHN_NUM_MODES] = { + [CHN_MODE_STEREO] = { STR(LANG_CHANNEL_STEREO) }, + [CHN_MODE_MONO] = { STR(LANG_CHANNEL_MONO) } }; +#if CONFIG_CODEC == MAS3587F return set_option(str(LANG_RECORDING_CHANNELS), &global_settings.rec_channels, INT, - names, 2, NULL ); + names, CHN_NUM_MODES, NULL ); +#endif /* CONFIG_CODEC == MAS3587F */ + +#if CONFIG_CODEC == SWCODEC + struct opt_items opts[CHN_NUM_MODES]; + long table[CHN_NUM_MODES]; + struct encoder_caps caps; + struct encoder_config cfg; + int n_opts; + int rec_channels; + bool ret; + + cfg.rec_format = global_settings.rec_format; + global_to_encoder_config(&cfg); + + if (!enc_get_caps(&cfg, &caps, true)) + return false; + + n_opts = make_list_from_caps32(CHN_CAP_ALL, NULL, + caps.channel_caps, table); + + rec_channels = round_value_to_list32(global_settings.rec_channels, + table, n_opts, false); + + make_options_from_indexes(names, table, n_opts, opts); + + ret = set_option(str(LANG_RECORDING_CHANNELS), &rec_channels, + INT, opts, n_opts, NULL ); + + if (!ret) + global_settings.rec_channels = table[rec_channels]; + + return ret; +#endif /* CONFIG_CODEC == SWCODEC */ } static bool rectimesplit(void) @@ -1049,58 +1180,59 @@ bool rectrigger(void) action_signalscreenchange(); return retval; } -#endif +#endif /* !defined(SIMULATOR) && CONFIG_CODEC == MAS3587F */ bool recording_menu(bool no_source) { - int m; - int i = 0; - struct menu_item items[13]; - bool result; - -#if CONFIG_CODEC == MAS3587F || CONFIG_CODEC == SWCODEC - items[i].desc = ID2P(LANG_RECORDING_QUALITY); - items[i++].function = recquality; -#endif - items[i].desc = ID2P(LANG_RECORDING_FREQUENCY); - items[i++].function = recfrequency; - if(!no_source) { - items[i].desc = ID2P(LANG_RECORDING_SOURCE); - items[i++].function = recsource; - } - items[i].desc = ID2P(LANG_RECORDING_CHANNELS); - items[i++].function = recchannels; + static const struct menu_item static_items[] = { #if CONFIG_CODEC == MAS3587F - items[i].desc = ID2P(LANG_RECORDING_EDITABLE); - items[i++].function = receditable; + { ID2P(LANG_RECORDING_QUALITY), recquality }, #endif - items[i].desc = ID2P(LANG_RECORD_TIMESPLIT); - items[i++].function = filesplitoptionsmenu; - items[i].desc = ID2P(LANG_RECORD_PRERECORD_TIME); - items[i++].function = recprerecord; - items[i].desc = ID2P(LANG_RECORD_DIRECTORY); - items[i++].function = recdirectory; - items[i].desc = ID2P(LANG_RECORD_STARTUP); - items[i++].function = reconstartup; +#if CONFIG_CODEC == SWCODEC + { ID2P(LANG_RECORDING_FORMAT), recformat }, + { ID2P(LANG_ENCODER_SETTINGS), enc_global_config_menu }, +#endif + { ID2P(LANG_RECORDING_FREQUENCY), recfrequency }, + { ID2P(LANG_RECORDING_SOURCE), recsource }, /* not shown if no_source */ + { ID2P(LANG_RECORDING_CHANNELS), recchannels }, +#if CONFIG_CODEC == MAS3587F + { ID2P(LANG_RECORDING_EDITABLE), receditable }, +#endif + { ID2P(LANG_RECORD_TIMESPLIT), filesplitoptionsmenu }, + { ID2P(LANG_RECORD_PRERECORD_TIME), recprerecord }, + { ID2P(LANG_RECORD_DIRECTORY), recdirectory }, + { ID2P(LANG_RECORD_STARTUP), reconstartup }, #ifdef CONFIG_BACKLIGHT - items[i].desc = ID2P(LANG_CLIP_LIGHT); - items[i++].function = cliplight; + { ID2P(LANG_CLIP_LIGHT), cliplight }, #endif #if !defined(SIMULATOR) && CONFIG_CODEC == MAS3587F - items[i].desc = ID2P(LANG_RECORD_TRIGGER); - items[i++].function = rectrigger; + { ID2P(LANG_RECORD_TRIGGER), rectrigger }, #endif #ifdef HAVE_AGC - items[i].desc = ID2P(LANG_RECORD_AGC_PRESET); - items[i++].function = agc_preset; - items[i].desc = ID2P(LANG_RECORD_AGC_CLIPTIME); - items[i++].function = agc_cliptime; + { ID2P(LANG_RECORD_AGC_PRESET), agc_preset }, + { ID2P(LANG_RECORD_AGC_CLIPTIME), agc_cliptime }, #endif + }; - m=menu_init( items, i, NULL, NULL, NULL, NULL); + struct menu_item items[ARRAYLEN(static_items)]; + int i, n_items; + int m; + + bool result; + + for (i = 0, n_items = 0; i < (int)ARRAYLEN(items); i++) + { + const struct menu_item *mi = &static_items[i]; + if (no_source && mi->function == recsource) + continue; + items[n_items++] = *mi; + } + + m = menu_init(items, n_items, NULL, NULL, NULL, NULL); result = menu_run(m); menu_exit(m); return result; -} -#endif +} /* recording_menu */ + +#endif /* HAVE_RECORDING */ diff --git a/apps/status.c b/apps/status.c index 2a57db0f89..75219d604c 100644 --- a/apps/status.c +++ b/apps/status.c @@ -46,7 +46,7 @@ #ifdef CONFIG_TUNER #include "radio.h" #endif -#if CONFIG_CODEC == SWCODEC +#if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC #include "pcm_record.h" #endif @@ -87,10 +87,6 @@ int current_playmode(void) } #ifdef HAVE_RECORDING -#if CONFIG_CODEC == SWCODEC - audio_stat = pcm_rec_status(); -#endif - if(audio_stat & AUDIO_STATUS_RECORD) { if(audio_stat & AUDIO_STATUS_PAUSE) diff --git a/apps/talk.c b/apps/talk.c index d81aa082c9..018f6ed5ab 100644 --- a/apps/talk.c +++ b/apps/talk.c @@ -46,13 +46,17 @@ MASCODEC | MASCODEC | SWCODEC (playing) | (stopped) | - audiobuf-----------+-----------+----------- + audiobuf-----------+-----------+------------ audio | voice | thumbnail - |-----------|----------- filebuf + |-----------|------------ | thumbnail | voice - | |----------- + | |------------ + | | filebuf + | |------------ | | audio - audiobufend----------+-----------+----------- + | |------------ + | | codec swap + audiobufend----------+-----------+------------ SWCODEC allocates dedicated buffers, MASCODEC reuses audiobuf. */ @@ -102,7 +106,7 @@ struct queue_entry /* one entry of the internal queue */ /***************** Globals *****************/ -static unsigned char* p_thumbnail; /* buffer for thumbnail */ +static unsigned char* p_thumbnail = NULL; /* buffer for thumbnail */ static long size_for_thumbnail; /* leftover buffer size for it */ static struct voicefile* p_voicefile; /* loaded voicefile */ static bool has_voicefile; /* a voicefile file is present */ @@ -479,11 +483,14 @@ static void reset_state(void) queue_write = queue_read = 0; /* reset the queue */ p_voicefile = NULL; /* indicate no voicefile (trashed) */ #if CONFIG_CODEC == SWCODEC - /* Allocate a dedicated thumbnail buffer */ - size_for_thumbnail = audiobufend - audiobuf; - if (size_for_thumbnail > MAX_THUMBNAIL_BUFSIZE) - size_for_thumbnail = MAX_THUMBNAIL_BUFSIZE; - p_thumbnail = buffer_alloc(size_for_thumbnail); + /* Allocate a dedicated thumbnail buffer - once */ + if (p_thumbnail == NULL) + { + size_for_thumbnail = audiobufend - audiobuf; + if (size_for_thumbnail > MAX_THUMBNAIL_BUFSIZE) + size_for_thumbnail = MAX_THUMBNAIL_BUFSIZE; + p_thumbnail = buffer_alloc(size_for_thumbnail); + } #else /* Just use the audiobuf, without allocating anything */ p_thumbnail = audiobuf; diff --git a/apps/tree.c b/apps/tree.c index 653da791a8..6465b50e6f 100644 --- a/apps/tree.c +++ b/apps/tree.c @@ -58,7 +58,9 @@ #include "misc.h" #include "filetree.h" #include "tagtree.h" +#ifdef HAVE_RECORDING #include "recorder/recording.h" +#endif #include "rtc.h" #include "dircache.h" #ifdef HAVE_TAGCACHE diff --git a/bootloader/main.c b/bootloader/main.c index 99eb449151..0f3d706d7b 100644 --- a/bootloader/main.c +++ b/bootloader/main.c @@ -204,6 +204,7 @@ void main(void) kernel_init(); set_cpu_frequency(CPUFREQ_NORMAL); + coldfire_set_pllcr_audio_bits(DEFAULT_PLLCR_AUDIO_BITS); set_irq_level(0); lcd_init(); @@ -311,6 +312,9 @@ void main(void) #ifdef HAVE_ADJUSTABLE_CPU_FREQ /* Set up waitstates for the peripherals */ set_cpu_frequency(0); /* PLL off */ +#ifdef CPU_COLDFIRE + coldfire_set_pllcr_audio_bits(DEFAULT_PLLCR_AUDIO_BITS); +#endif #endif #ifdef HAVE_UDA1380 diff --git a/firmware/SOURCES b/firmware/SOURCES index 1ec3c82616..df38169be3 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -4,6 +4,7 @@ logf.c #endif backlight.c buffer.c +general.c common/atoi.c common/crc32.c common/ctype.c @@ -45,7 +46,12 @@ target/coldfire/memcpy-coldfire.S target/coldfire/memmove-coldfire.S target/coldfire/memset-coldfire.S target/coldfire/memset16-coldfire.S +#ifndef SIMULATOR +#ifndef BOOTLOADER +target/coldfire/pcm-coldfire.c +#endif target/coldfire/system-coldfire.c +#endif #elif (CONFIG_CPU == SH7034) target/sh/memcpy-sh.S target/sh/memmove-sh.S @@ -207,15 +213,21 @@ drivers/wm8731l.c #elif defined(HAVE_TLV320) && !defined(SIMULATOR) drivers/tlv320.c #endif -#if (CONFIG_CODEC == SWCODEC) && !defined(SIMULATOR) -pcm_playback.c -#endif -#if CONFIG_CODEC == SWCODEC +#if (CONFIG_CODEC == SWCODEC) && !defined(BOOTLOADER) +pcm_sampr.c replaygain.c -#endif -#if defined(CPU_COLDFIRE) && !defined(SIMULATOR) +#ifndef SIMULATOR +pcm_playback.c +#endif /* SIMULATOR */ +#ifdef HAVE_RECORDING +enc_base.c +#if defined(CPU_COLDFIRE) +#ifndef SIMULATOR pcm_record.c -#endif +#endif /* SIMULATOR */ +#endif /* CPU_COLDFIRE */ +#endif /* HAVE_RECORDING */ +#endif /* SWCODEC && !BOOTLOADER */ sound.c #if defined(IRIVER_IFP7XX_SERIES) && defined(STUB) common/sscanf.c diff --git a/firmware/drivers/tlv320.c b/firmware/drivers/tlv320.c index abce31ef81..7c4bbbd1ee 100644 --- a/firmware/drivers/tlv320.c +++ b/firmware/drivers/tlv320.c @@ -82,7 +82,7 @@ void tlv320_init(void) tlv320_write_reg(REG_DAP, 0x00); /* No deemphasis */ tlv320_write_reg(REG_DAIF, DAIF_IWL_16 | DAIF_FOR_I2S); tlv320_write_reg(REG_DIA, DIA_ACT); - tlv320_write_reg(REG_SRC, (1 << 5)); /* 44.1kHz */ + tlv320_set_frequency(-1); /* default */ /* All ON except ADC, MIC and LINE */ tlv320_write_reg(REG_PC, PC_ADC | PC_MIC | PC_LINE); } @@ -95,6 +95,32 @@ void tlv320_reset(void) tlv320_write_reg(REG_RR, RR_RESET); } +/** + * Sets internal sample rate for DAC and ADC relative to MCLK + * Selection for frequency: + * Fs: tlv: with: + * 11025: 0 = MCLK/2 MCLK/2 SCLK, LRCK: Audio Clk / 16 + * 22050: 0 = MCLK/2 MCLK SCLK, LRCK: Audio Clk / 8 + * 44100: 1 = MCLK MCLK SCLK, LRCK: Audio Clk / 4 (default) + * 88200: 2 = MCLK*2 MCLK SCLK, LRCK: Audio Clk / 2 + */ +void tlv320_set_frequency(unsigned fsel) +{ + /* All rates available for 11.2896MHz besides 8.021 */ + unsigned char values_src[3] = + { + /* Fs: */ + (0x8 << 2) | SRC_CLKIN, /* 11025, 22050 */ + (0x8 << 2), /* 44100 */ + (0xf << 2), /* 88200 */ + }; + + if (fsel >= ARRAYLEN(values_src)) + fsel = 1; + + tlv320_write_reg(REG_SRC, values_src[fsel]); +} + /** * Sets left and right headphone volume * @@ -142,7 +168,6 @@ void tlv320_set_recvol(int left, int right, int type) value_aap &= ~AAP_MICB; tlv320_write_reg(REG_AAP, value_aap); - } else if (type == AUDIO_GAIN_LINEIN) { @@ -180,15 +205,17 @@ void tlv320_mute(bool mute) } /* Nice shutdown of TLV320 codec */ -void tlv320_close() +void tlv320_close(void) { + tlv320_mute(true); + sleep(HZ/8); + tlv320_write_reg(REG_PC, PC_OFF | PC_CLK | PC_OSC | PC_OUT | PC_DAC | PC_ADC | PC_MIC | PC_LINE); /* All OFF */ } void tlv320_enable_recording(bool source_mic) { - unsigned value_daif = tlv320_regs[REG_DAIF]; unsigned value_aap, value_pc; if (source_mic) @@ -205,20 +232,12 @@ void tlv320_enable_recording(bool source_mic) tlv320_write_reg(REG_PC, value_pc); tlv320_write_reg(REG_AAP, value_aap); - - /* Enable MASTER mode (start sending I2S data to the CPU) */ - value_daif |= DAIF_MS; - tlv320_write_reg(REG_DAIF, value_daif); } -void tlv320_disable_recording() +void tlv320_disable_recording(void) { unsigned value_pc = tlv320_regs[REG_PC]; unsigned value_aap = tlv320_regs[REG_AAP]; - unsigned value_daif = tlv320_regs[REG_DAIF]; - - value_daif &= ~DAIF_MS; /* disable MASTER mode */ - tlv320_write_reg(REG_DAIF, value_daif); value_aap |= AAP_MICM; /* mute MIC */ tlv320_write_reg(REG_PC, value_aap); diff --git a/firmware/drivers/uda1380.c b/firmware/drivers/uda1380.c index 241a117385..d6dfe6623b 100644 --- a/firmware/drivers/uda1380.c +++ b/firmware/drivers/uda1380.c @@ -49,9 +49,10 @@ short recgain_line; #define NUM_DEFAULT_REGS 13 unsigned short uda1380_defaults[2*NUM_DEFAULT_REGS] = { - REG_0, EN_DAC | EN_INT | EN_DEC | SYSCLK_256FS | WSPLL_25_50, + REG_0, EN_DAC | EN_INT | EN_DEC | ADC_CLK | DAC_CLK | + SYSCLK_256FS | WSPLL_25_50, REG_I2S, I2S_IFMT_IIS, - REG_PWR, PON_BIAS, + REG_PWR, PON_PLL | PON_BIAS, /* PON_HP & PON_DAC is enabled later */ REG_AMIX, AMIX_RIGHT(0x3f) | AMIX_LEFT(0x3f), /* 00=max, 3f=mute */ @@ -60,7 +61,7 @@ unsigned short uda1380_defaults[2*NUM_DEFAULT_REGS] = REG_MIX_VOL, MIX_VOL_CH_1(0) | MIX_VOL_CH_2(0xff), /* 00=max, ff=mute */ REG_EQ, EQ_MODE_MAX, - /* Bass and tremble = 0 dB */ + /* Bass and treble = 0 dB */ REG_MUTE, MUTE_MASTER | MUTE_CH2, /* Mute everything to start with */ REG_MIX_CTL, MIX_CTL_MIX, @@ -192,6 +193,43 @@ void uda1380_reset(void) #endif } +/** + * Sets frequency settings for DAC and ADC relative to MCLK + * + * Selection for frequency ranges: + * Fs: range: with: + * 11025: 0 = 6.25 to 12.5 MCLK/2 SCLK, LRCK: Audio Clk / 16 + * 22050: 1 = 12.5 to 25 MCLK/2 SCLK, LRCK: Audio Clk / 8 + * 44100: 2 = 25 to 50 MCLK SCLK, LRCK: Audio Clk / 4 (default) + * 88200: 3 = 50 to 100 MCLK SCLK, LRCK: Audio Clk / 2 <= TODO: Needs WSPLL + */ +void uda1380_set_frequency(unsigned fsel) +{ + static const unsigned short values_reg[4][2] = + { + /* Fs: */ + { 0, WSPLL_625_125 | SYSCLK_512FS }, /* 11025 */ + { 0, WSPLL_125_25 | SYSCLK_256FS }, /* 22050 */ + { MIX_CTL_SEL_NS, WSPLL_25_50 | SYSCLK_256FS }, /* 44100 */ + { MIX_CTL_SEL_NS, WSPLL_50_100 | SYSCLK_256FS }, /* 88200 */ + }; + + const unsigned short *ent; + + if (fsel >= ARRAYLEN(values_reg)) + fsel = 2; + + ent = values_reg[fsel]; + + /* Set WSPLL input frequency range or SYSCLK divider */ + uda1380_regs[REG_0] &= ~0xf; + uda1380_write_reg(REG_0, uda1380_regs[REG_0] | ent[1]); + + /* Choose 3rd order or 5th order noise shaper */ + uda1380_regs[REG_MIX_CTL] &= ~MIX_CTL_SEL_NS; + uda1380_write_reg(REG_MIX_CTL, uda1380_regs[REG_MIX_CTL] | ent[0]); +} + /* Initialize UDA1380 codec with default register values (uda1380_defaults) */ int uda1380_init(void) { @@ -227,30 +265,34 @@ void uda1380_close(void) */ void uda1380_enable_recording(bool source_mic) { + uda1380_regs[REG_0] &= ~(ADC_CLK | DAC_CLK); uda1380_write_reg(REG_0, uda1380_regs[REG_0] | EN_ADC); if (source_mic) { /* VGA_GAIN: 0=0 dB, F=30dB */ + /* Output of left ADC is fed into right bitstream */ + uda1380_regs[REG_PWR] &= ~(PON_PLL | PON_PGAR | PON_ADCR); uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] | PON_LNA | PON_ADCL); + uda1380_regs[REG_ADC] &= ~SKIP_DCFIL; uda1380_write_reg(REG_ADC, (uda1380_regs[REG_ADC] & VGA_GAIN_MASK) | SEL_LNA | SEL_MIC | EN_DCFIL); uda1380_write_reg(REG_PGA, 0); - } else + } + else { /* PGA_GAIN: 0=0 dB, F=24dB */ + uda1380_regs[REG_PWR] &= ~(PON_PLL | PON_LNA); uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] | PON_PGAL | PON_ADCL | PON_PGAR | PON_ADCR); uda1380_write_reg(REG_ADC, EN_DCFIL); - uda1380_write_reg(REG_PGA, (uda1380_regs[REG_PGA] & PGA_GAIN_MASK) - | PGA_GAINL(0) | PGA_GAINR(0)); + uda1380_write_reg(REG_PGA, uda1380_regs[REG_PGA] & PGA_GAIN_MASK); } sleep(HZ/8); uda1380_write_reg(REG_I2S, uda1380_regs[REG_I2S] | I2S_MODE_MASTER); uda1380_write_reg(REG_MIX_CTL, MIX_MODE(1)); - } /** @@ -262,10 +304,13 @@ void uda1380_disable_recording(void) sleep(HZ/8); uda1380_write_reg(REG_I2S, I2S_IFMT_IIS); - uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] & ~(PON_LNA | PON_ADCL - | PON_ADCR | PON_PGAL - | PON_PGAR)); - uda1380_write_reg(REG_0, uda1380_regs[REG_0] & ~EN_ADC); + + uda1380_regs[REG_PWR] &= ~(PON_LNA | PON_ADCL | PON_ADCR | PON_PGAL | PON_PGAR); + uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] | PON_PLL); + + uda1380_regs[REG_0] &= ~EN_ADC; + uda1380_write_reg(REG_0, uda1380_regs[REG_0] | ADC_CLK | DAC_CLK); + uda1380_write_reg(REG_ADC, SKIP_DCFIL); } @@ -373,20 +418,3 @@ void uda1380_set_monitor(int enable) else /* mute channel 2 */ uda1380_write_reg(REG_MUTE, uda1380_regs[REG_MUTE] | MUTE_CH2); } - -/* Change the order of the noise chaper, - 5th order is recommended above 32kHz */ -void uda1380_set_nsorder(int order) -{ - switch(order) - { - case 5: - uda1380_write_reg(REG_MIX_CTL, uda1380_regs[REG_MIX_CTL] - | MIX_CTL_SEL_NS); - break; - case 3: - default: - uda1380_write_reg(REG_MIX_CTL, uda1380_regs[REG_MIX_CTL] - & ~MIX_CTL_SEL_NS); - } -} diff --git a/firmware/export/audio.h b/firmware/export/audio.h index 9099cb3765..d3f544de94 100644 --- a/firmware/export/audio.h +++ b/firmware/export/audio.h @@ -20,6 +20,20 @@ #define AUDIO_H #include +#include +/* These must always be included with audio.h for this to compile under + cetain conditions. Do it here or else spread the complication around to + many files. */ +#if CONFIG_CODEC == SWCODEC +#include "pcm_sampr.h" +#include "pcm_playback.h" +#ifdef HAVE_RECORDING +#include "pcm_record.h" +#include "id3.h" +#include "enc_base.h" +#endif /* HAVE_RECORDING */ +#endif /* CONFIG_CODEC == SWCODEC */ + #ifdef SIMULATOR #define audio_play(x) sim_audio_play(x) @@ -31,9 +45,6 @@ #define AUDIO_STATUS_PRERECORD 8 #define AUDIO_STATUS_ERROR 16 -#define AUDIO_STATUS_STAYON_FLAGS \ - (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE | AUDIO_STATUS_RECORD | AUDIO_) - #define AUDIOERR_DISK_FULL 1 #define AUDIO_GAIN_LINEIN 0 @@ -72,10 +83,11 @@ void audio_resume(void); void audio_next(void); void audio_prev(void); int audio_status(void); -bool audio_query_poweroff(void); +#if CONFIG_CODEC == SWCODEC int audio_track_count(void); /* SWCODEC only */ long audio_filebufused(void); /* SWCODEC only */ void audio_pre_ff_rewind(void); /* SWCODEC only */ +#endif /* CONFIG_CODEC == SWCODEC */ void audio_ff_rewind(long newtime); void audio_flush_and_reload_tracks(void); struct mp3entry* audio_current_track(void); @@ -89,18 +101,28 @@ void audio_error_clear(void); int audio_get_file_pos(void); void audio_beep(int duration); void audio_init_playback(void); +unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size); -/* audio recording functions */ -void audio_init_recording(unsigned int buffer_offset); -void audio_close_recording(void); -void audio_record(const char *filename); -void audio_stop_recording(void); -void audio_pause_recording(void); -void audio_resume_recording(void); -void audio_new_file(const char *filename); +/* channel modes */ +enum rec_channel_modes +{ + __CHN_MODE_START_INDEX = -1, + + CHN_MODE_STEREO, + CHN_MODE_MONO, + + CHN_NUM_MODES +}; + +#if CONFIG_CODEC == SWCODEC +/* channel mode capability bits */ +#define CHN_CAP_STEREO (1 << CHN_MODE_STEREO) +#define CHN_CAP_MONO (1 << CHN_MODE_MONO) +#define CHN_CAP_ALL (CHN_CAP_STEREO | CHN_CAP_MONO) +#endif /* CONFIG_CODEC == SWCODEC */ /* audio sources */ -enum +enum audio_sources { AUDIO_SRC_PLAYBACK = -1, /* for audio playback (default) */ AUDIO_SRC_MIC, /* monitor mic */ @@ -123,33 +145,57 @@ enum AUDIO_SRC_MAX = AUDIO_NUM_SOURCES-1 }; -/* channel modes */ -enum +#ifdef HAVE_RECORDING +/* parameters for audio_set_recording_options */ +struct audio_recording_options { - CHN_MODE_MONO = 1, - CHN_MODE_STEREO, + int rec_source; + int rec_frequency; + int rec_channels; + int rec_prerecord_time; +#if CONFIG_CODEC == SWCODEC + int rec_source_flags; /* for rec_set_source */ + struct encoder_config enc_config; +#else + int rec_quality; + bool rec_editable; +#endif }; -void audio_set_recording_options(int frequency, int quality, - int source, int channel_mode, - bool editable, int prerecord_time); + +/* audio recording functions */ +void audio_init_recording(unsigned int buffer_offset); +void audio_close_recording(void); +void audio_record(const char *filename); +void audio_stop_recording(void); +void audio_pause_recording(void); +void audio_resume_recording(void); +void audio_new_file(const char *filename); +void audio_set_recording_options(struct audio_recording_options *options); void audio_set_recording_gain(int left, int right, int type); unsigned long audio_recorded_time(void); unsigned long audio_num_recorded_bytes(void); -#if 0 + +#if CONFIG_CODEC == SWCODEC +/* SWCODEC recoring functions */ +/* playback.c */ +bool audio_load_encoder(int afmt); +void audio_remove_encoder(void); +unsigned char *audio_get_recording_buffer(size_t *buffer_size); +#endif /* CONFIG_CODEC == SWCODEC */ +#endif /* HAVE_RECORDING */ + +#ifdef HAVE_SPDIF_IN #ifdef HAVE_SPDIF_POWER void audio_set_spdif_power_setting(bool on); +bool audio_get_spdif_power_setting(void); #endif -#endif -unsigned long audio_get_spdif_sample_rate(void); +/* returns index into rec_master_sampr_list */ +int audio_get_spdif_sample_rate(void); +/* > 0: monitor EBUin, 0: Monitor IISrecv, <0: reset only */ +void audio_spdif_set_monitor(int monitor_spdif); +#endif /* HAVE_SPDIF_IN */ + unsigned long audio_prev_elapsed(void); -#if CONFIG_CODEC == SWCODEC -/* audio encoder functions (defined in playback.c) */ -int audio_get_encoder_id(void); -void audio_load_encoder(int enc_id); -void audio_remove_encoder(void); -#endif /* CONFIG_CODEC == SWCODEC */ - - /***********************************************************************/ diff --git a/firmware/export/config-h100.h b/firmware/export/config-h100.h index 6f74078e1e..285ab88930 100644 --- a/firmware/export/config-h100.h +++ b/firmware/export/config-h100.h @@ -84,6 +84,12 @@ /* define this if you have recording possibility */ #define HAVE_RECORDING 1 +/* define hardware samples rate caps mask */ +#define HW_SAMPR_CAPS (SAMPR_CAP_88 | SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11) + +/* define the bitmask of recording sample rates */ +#define REC_SAMPR_CAPS (SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11) + #define HAVE_AGC #ifndef SIMULATOR diff --git a/firmware/export/config-h120.h b/firmware/export/config-h120.h index 1476102100..b22ff0eb22 100644 --- a/firmware/export/config-h120.h +++ b/firmware/export/config-h120.h @@ -77,6 +77,12 @@ /* define this if you have recording possibility */ #define HAVE_RECORDING 1 +/* define hardware samples rate caps mask */ +#define HW_SAMPR_CAPS (SAMPR_CAP_88 | SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11) + +/* define the bitmask of recording sample rates */ +#define REC_SAMPR_CAPS (SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11) + #define HAVE_AGC #define BATTERY_CAPACITY_DEFAULT 1300 /* default battery capacity */ diff --git a/firmware/export/config-h300.h b/firmware/export/config-h300.h index 31f0f6729f..748635dcb4 100644 --- a/firmware/export/config-h300.h +++ b/firmware/export/config-h300.h @@ -72,6 +72,12 @@ /* define this if you have recording possibility */ #define HAVE_RECORDING 1 +/* define hardware samples rate caps mask */ +#define HW_SAMPR_CAPS (SAMPR_CAP_88 | SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11) + +/* define the bitmask of recording sample rates */ +#define REC_SAMPR_CAPS (SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11) + #define HAVE_AGC #define BATTERY_CAPACITY_DEFAULT 1300 /* default battery capacity */ @@ -157,4 +163,3 @@ /* Define this for FM radio input available */ #define HAVE_FMRADIO_IN - diff --git a/firmware/export/config-iaudiox5.h b/firmware/export/config-iaudiox5.h index 80b010a6b0..d4c904ed23 100644 --- a/firmware/export/config-iaudiox5.h +++ b/firmware/export/config-iaudiox5.h @@ -9,6 +9,12 @@ /* define this if you have recording possibility */ #define HAVE_RECORDING 1 +/* define the bitmask of hardware sample rates */ +#define HW_SAMPR_CAPS (SAMPR_CAP_88 | SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11) + +/* define the bitmask of recording sample rates */ +#define REC_SAMPR_CAPS (SAMPR_CAP_88 | SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11) + /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP 1 diff --git a/firmware/export/id3.h b/firmware/export/id3.h index 1d07affbfa..dd099e0204 100644 --- a/firmware/export/id3.h +++ b/firmware/export/id3.h @@ -24,13 +24,19 @@ #include "file.h" /* Audio file types. */ -enum { +enum +{ AFMT_UNKNOWN = 0, /* Unknown file format */ + /* start formats */ + AFMT_MPA_L1, /* MPEG Audio layer 1 */ AFMT_MPA_L2, /* MPEG Audio layer 2 */ AFMT_MPA_L3, /* MPEG Audio layer 3 */ + AFMT_AIFF, /* Audio Interchange File Format */ + +#if CONFIG_CODEC == SWCODEC AFMT_PCM_WAV, /* Uncompressed PCM in a WAV file */ AFMT_OGG_VORBIS, /* Ogg Vorbis */ AFMT_FLAC, /* FLAC */ @@ -40,54 +46,91 @@ enum { AFMT_ALAC, /* Apple Lossless Audio Codec */ AFMT_AAC, /* Advanced Audio Coding (AAC) in M4A container */ AFMT_SHN, /* Shorten */ - AFMT_AIFF, /* Audio Interchange File Format */ AFMT_SID, /* SID File Format */ - AFMT_ADX, /* ADX */ + AFMT_ADX, /* ADX File Format */ +#endif - /* New formats must be added to the end of this list */ + /* add new formats at any index above this line to have a sensible order - + specified array index inits are used */ + /* format arrays defined in id3.c */ AFMT_NUM_CODECS, -#if CONFIG_CODEC == SWCODEC +#if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) /* masks to decompose parts */ CODEC_AFMT_MASK = 0x0fff, CODEC_TYPE_MASK = 0x7000, /* switch for specifying codec type when requesting a filename */ CODEC_TYPE_DECODER = (0 << 12), /* default */ - CODEC_TYPE_ENCODER = (1 << 12) -#endif + CODEC_TYPE_ENCODER = (1 << 12), +#endif /* CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) */ }; #if CONFIG_CODEC == SWCODEC -#define AFMT_ENTRY(label, codec_fname, codec_enc_fname, enc_ext) \ - { label, codec_fname, codec_enc_fname, enc_ext } -#else -#define AFMT_ENTRY(label, codec_fname, codec_enc_fname, enc_ext) \ - { label } -#endif +#define CODEC_EXTENSION "codec" + +#ifdef HAVE_RECORDING +#define ENCODER_SUFFIX "_enc" +enum rec_format_indexes +{ + __REC_FORMAT_START_INDEX = -1, + + /* start formats */ + + REC_FORMAT_PCM_WAV, + REC_FORMAT_WAVPACK, + REC_FORMAT_MPA_L3, + + /* add new formats at any index above this line to have a sensible order - + specified array index inits are used + REC_FORMAT_CFG_NUM_BITS should allocate enough bits to hold the range + REC_FORMAT_CFG_VALUE_LIST should be in same order as indexes + */ + + REC_NUM_FORMATS, + + REC_FORMAT_DEFAULT = REC_FORMAT_PCM_WAV, + REC_FORMAT_CFG_NUM_BITS = 2 +}; + +#define REC_FORMAT_CFG_VAL_LIST "wave,wvpk,mpa3" + +/* get REC_FORMAT_* corresponding AFMT_* */ +extern const int rec_format_afmt[REC_NUM_FORMATS]; +/* get AFMT_* corresponding REC_FORMAT_* */ +extern const int afmt_rec_format[AFMT_NUM_CODECS]; + +#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \ + { label, root_fname, enc_root_fname, ext_list } +#else /* !HAVE_RECORDING */ +#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \ + { label, root_fname, ext_list } +#endif /* HAVE_RECORDING */ +#else /* !SWCODEC */ + +#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \ + { label, ext_list } +#endif /* CONFIG_CODEC == SWCODEC */ /* record describing the audio format */ struct afmt_entry { -#if CONFIG_CODEC == SWCODEC char label[8]; /* format label */ - char *codec_fn; /* filename of decoder codec */ - char *codec_enc_fn; /* filename of encoder codec */ - char *ext; /* default extension for file (enc only for now) */ -#else - char label[4]; +#if CONFIG_CODEC == SWCODEC + char *codec_root_fn; /* root codec filename (sans _enc and .codec) */ +#ifdef HAVE_RECORDING + char *codec_enc_root_fn; /* filename of encoder codec */ #endif +#endif + char *ext_list; /* double NULL terminated extension + list for type with the first as + the default for recording */ }; /* database of labels and codecs. add formats per above enum */ extern const struct afmt_entry audio_formats[AFMT_NUM_CODECS]; -#if CONFIG_CODEC == SWCODEC -/* recording quality to AFMT_* */ -extern const int rec_quality_info_afmt[9]; -#endif - struct mp3entry { char path[MAX_PATH]; char* title; diff --git a/firmware/export/pcm_playback.h b/firmware/export/pcm_playback.h index a4cd93969b..9c3e96ba63 100644 --- a/firmware/export/pcm_playback.h +++ b/firmware/export/pcm_playback.h @@ -19,11 +19,23 @@ #ifndef PCM_PLAYBACK_H #define PCM_PLAYBACK_H +#include + +/* Typedef for registered callback (play and record) */ +typedef void (*pcm_more_callback_type)(unsigned char **start, + size_t *size); + void pcm_init(void); + +/* set the pcm frequency - use values in hw_sampr_list + * use -1 for the default frequency + */ void pcm_set_frequency(unsigned int frequency); +/* apply settings to hardware immediately */ +void pcm_apply_settings(bool reset); /* This is for playing "raw" PCM data */ -void pcm_play_data(void (*get_more)(unsigned char** start, size_t* size), +void pcm_play_data(pcm_more_callback_type get_more, unsigned char* start, size_t size); void pcm_calculate_peaks(int *left, int *right); @@ -35,4 +47,4 @@ void pcm_play_pause(bool play); bool pcm_is_paused(void); bool pcm_is_playing(void); -#endif +#endif /* PCM_PLAYBACK_H */ diff --git a/firmware/export/pcm_record.h b/firmware/export/pcm_record.h index b217335340..c1187a4c6c 100644 --- a/firmware/export/pcm_record.h +++ b/firmware/export/pcm_record.h @@ -20,24 +20,44 @@ #ifndef PCM_RECORD_H #define PCM_RECORD_H -void enc_set_parameters(int chunk_size, int num_chunks, - int samp_per_chunk, char *head_ptr, int head_size, - int enc_id); -void enc_get_inputs(int *buffer_size, int *channels, int *quality); -unsigned int* enc_alloc_chunk(void); -void enc_free_chunk(void); -int enc_wavbuf_near_empty(void); -char* enc_get_wav_data(int size); -extern void (*enc_set_header_callback)(void *head_buffer, int head_size, - int num_pcm_samples, bool is_file_header); +#define DMA_REC_ERROR_DMA ((size_t)-1) +#ifdef HAVE_SPDIF_IN +#define DMA_REC_ERROR_SPDIF ((size_t)-2) +#endif +/* Use AUDIO_SRC_* enumeration values */ +void pcm_set_monitor(int monitor); +void pcm_set_rec_source(int source); +/** + * RAW pcm data recording + * These calls are nescessary only when using the raw pcm apis directly. + */ + +/* Initialize pcm recording interface */ +void pcm_init_recording(void); +/* Uninitialze pcm recording interface */ +void pcm_close_recording(void); + +/* Start recording "raw" PCM data */ +void pcm_record_data(pcm_more_callback_type more_ready, + unsigned char *start, size_t size); + +/* Stop tranferring data into supplied buffer */ +void pcm_stop_recording(void); + +void pcm_calculate_rec_peaks(int *left, int *right); + +/** General functions for high level codec recording **/ +void pcm_rec_error_clear(void); unsigned long pcm_rec_status(void); void pcm_rec_init(void); void pcm_rec_mux(int source); int pcm_rec_current_bitrate(void); +int pcm_rec_encoder_afmt(void); /* AFMT_* value, AFMT_UNKNOWN if none */ +int pcm_rec_rec_format(void); /* Format index or -1 otherwise */ +unsigned long pcm_rec_sample_rate(void); int pcm_get_num_unprocessed(void); -void pcm_rec_get_peaks(int *left, int *right); -/* audio.h contains audio recording functions */ +/* audio.h contains audio_* recording functions */ -#endif +#endif /* PCM_RECORD_H */ diff --git a/firmware/export/system.h b/firmware/export/system.h index 4a33d80466..9b90a6e80c 100644 --- a/firmware/export/system.h +++ b/firmware/export/system.h @@ -21,7 +21,6 @@ #define __SYSTEM_H__ #include "cpu.h" -#include "config.h" #include "stdbool.h" extern void system_reboot (void); @@ -111,6 +110,23 @@ const char *get_cpu_boost_tracker(void); #define MAX(a, b) (((a)>(b))?(a):(b)) #endif +/* return number of elements in array a */ +#define ARRAYLEN(a) (sizeof(a)/sizeof((a)[0])) + +/* return p incremented by specified number of bytes */ +#define SKIPBYTES(p, count) ((typeof (p))((char *)(p) + (count))) + +#define P2_M1(p2) ((1 << (p2))-1) + +/* align up or down to nearest 2^p2 */ +#define ALIGN_DOWN_P2(n, p2) ((n) & ~P2_M1(p2)) +#define ALIGN_UP_P2(n, p2) ALIGN_DOWN_P2((n) + P2_M1(p2),p2) + +/* align up or down to nearest integer multiple of a */ +#define ALIGN_DOWN(n, a) ((n)/(a)*(a)) +#define ALIGN_UP(n, a) ALIGN_DOWN((n)+((a)-1),a) + +/* live endianness conversion */ #ifdef ROCKBOX_LITTLE_ENDIAN #define letoh16(x) (x) #define letoh32(x) (x) @@ -120,6 +136,8 @@ const char *get_cpu_boost_tracker(void); #define betoh32(x) swap32(x) #define htobe16(x) swap16(x) #define htobe32(x) swap32(x) +#define swap_odd_even_be32(x) (x) +#define swap_odd_even_le32(x) swap_odd_even32(x) #else #define letoh16(x) swap16(x) #define letoh32(x) swap32(x) @@ -129,6 +147,37 @@ const char *get_cpu_boost_tracker(void); #define betoh32(x) (x) #define htobe16(x) (x) #define htobe32(x) (x) +#define swap_odd_even_be32(x) swap_odd_even32(x) +#define swap_odd_even_le32(x) (x) +#endif + +/* static endianness conversion */ +#define SWAP_16(x) ((typeof(x))(unsigned short)(((unsigned short)(x) >> 8) | \ + ((unsigned short)(x) << 8))) + +#define SWAP_32(x) ((typeof(x))(unsigned long)( ((unsigned long)(x) >> 24) | \ + (((unsigned long)(x) & 0xff0000ul) >> 8) | \ + (((unsigned long)(x) & 0xff00ul) << 8) | \ + ((unsigned long)(x) << 24))) + +#ifdef ROCKBOX_LITTLE_ENDIAN +#define LE_TO_H16(x) (x) +#define LE_TO_H32(x) (x) +#define H_TO_LE16(x) (x) +#define H_TO_LE32(x) (x) +#define BE_TO_H16(x) SWAP_16(x) +#define BE_TO_H32(x) SWAP_32(x) +#define H_TO_BE16(x) SWAP_16(x) +#define H_TO_BE32(x) SWAP_32(x) +#else +#define LE_TO_H16(x) SWAP_16(x) +#define LE_TO_H32(x) SWAP_32(x) +#define H_TO_LE16(x) SWAP_16(x) +#define H_TO_LE32(x) SWAP_32(x) +#define BE_TO_H16(x) (x) +#define BE_TO_H32(x) (x) +#define H_TO_BE16(x) (x) +#define H_TO_BE32(x) (x) #endif @@ -181,6 +230,7 @@ enum { : /* %0 */ I_CONSTRAINT((char)(mask)), \ /* %1 */ "z"(address-GBR)) + #endif /* CONFIG_CPU == SH7034 */ #ifndef SIMULATOR @@ -388,7 +438,20 @@ static inline unsigned long swap32(unsigned long value) #define invalidate_icache() #endif -#else + +#ifndef CPU_COLDFIRE +static inline unsigned long swap_odd_even32(unsigned long value) +{ + /* + result[31..24],[15.. 8] = value[23..16],[ 7.. 0] + result[23..16],[ 7.. 0] = value[31..24],[15.. 8] + */ + unsigned long t = value & 0xff00ff00; + return (t >> 8) | ((t ^ value) << 8); +} +#endif + +#else /* SIMULATOR */ static inline unsigned short swap16(unsigned short value) /* @@ -412,8 +475,18 @@ static inline unsigned long swap32(unsigned long value) return (lo << 16) | hi; } +static inline unsigned long swap_odd_even32(unsigned long value) +{ + /* + result[31..24],[15.. 8] = value[23..16],[ 7.. 0] + result[23..16],[ 7.. 0] = value[31..24],[15.. 8] + */ + unsigned long t = value & 0xff00ff00; + return (t >> 8) | ((t ^ value) << 8); +} + #define invalidate_icache() -#endif +#endif /* !SIMULATOR */ -#endif +#endif /* __SYSTEM_H__ */ diff --git a/firmware/export/thread.h b/firmware/export/thread.h index 17e6e3aa88..72c692ec3b 100644 --- a/firmware/export/thread.h +++ b/firmware/export/thread.h @@ -142,7 +142,10 @@ void switch_thread(bool save_context, struct thread_entry **blocked_list); void sleep_thread(int ticks); void block_thread(struct thread_entry **thread, int timeout); void wakeup_thread(struct thread_entry **thread); +#ifdef HAVE_PRIORITY_SCHEDULING int thread_set_priority(struct thread_entry *thread, int priority); +int thread_get_priority(struct thread_entry *thread); +#endif void init_threads(void); int thread_stack_usage(const struct thread_entry *thread); int thread_get_status(const struct thread_entry *thread); diff --git a/firmware/export/tlv320.h b/firmware/export/tlv320.h index dfcbec4373..023ec9d874 100644 --- a/firmware/export/tlv320.h +++ b/firmware/export/tlv320.h @@ -24,6 +24,16 @@ extern void tlv320_init(void); extern void tlv320_reset(void); +/** + * Sets internal sample rate for DAC and ADC relative to MCLK + * Selection for frequency: + * Fs: tlv: with: + * 11025: 0 = MCLK/2 MCLK/2 SCLK, LRCK: Audio Clk / 16 + * 22050: 0 = MCLK/2 MCLK SCLK, LRCK: Audio Clk / 8 + * 44100: 1 = MCLK MCLK SCLK, LRCK: Audio Clk / 4 (default) + * 88200: 2 = MCLK*2 MCLK SCLK, LRCK: Audio Clk / 2 + */ +extern void tlv320_set_frequency(unsigned fsel); extern void tlv320_enable_output(bool enable); extern void tlv320_set_headphone_vol(int vol_l, int vol_r); extern void tlv320_set_recvol(int left, int right, int type); diff --git a/firmware/export/uda1380.h b/firmware/export/uda1380.h index 9c761c6a7d..639ca8aa5c 100644 --- a/firmware/export/uda1380.h +++ b/firmware/export/uda1380.h @@ -28,8 +28,17 @@ extern void uda1380_set_bass(int value); extern void uda1380_set_treble(int value); extern int uda1380_mute(int mute); extern void uda1380_close(void); -extern void uda1380_set_nsorder(int order); - +/** + * Sets frequency settings for DAC and ADC relative to MCLK + * + * Selection for frequency ranges: + * Fs: range: with: + * 11025: 0 = 6.25 to 12.5 SCLK, LRCK: Audio Clk / 16 + * 22050: 1 = 12.5 to 25 SCLK, LRCK: Audio Clk / 8 + * 44100: 2 = 25 to 50 SCLK, LRCK: Audio Clk / 4 (default) + * 88200: 3 = 50 to 100 SCLK, LRCK: Audio Clk / 2 + */ +extern void uda1380_set_frequency(unsigned fsel); extern void uda1380_enable_recording(bool source_mic); extern void uda1380_disable_recording(void); extern void uda1380_set_recvol(int left, int right, int type); diff --git a/firmware/id3.c b/firmware/id3.c index 92f60a2095..7d03c75331 100644 --- a/firmware/id3.c +++ b/firmware/id3.c @@ -44,6 +44,89 @@ #include "replaygain.h" #include "rbunicode.h" +/** Database of audio formats **/ +const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = +{ + /* Unknown file format */ + [AFMT_UNKNOWN] = + AFMT_ENTRY("???", NULL, NULL, NULL ), + + /* MPEG Audio layer 1 */ + [AFMT_MPA_L1] = + AFMT_ENTRY("MP1", "mpa", NULL, "mp1\0" ), + /* MPEG Audio layer 2 */ + [AFMT_MPA_L2] = + AFMT_ENTRY("MP2", "mpa", NULL, "mpa\0mp2\0" ), + /* MPEG Audio layer 3 */ + [AFMT_MPA_L3] = + AFMT_ENTRY("MP3", "mpa", "mp3_enc", "mp3\0" ), + + /* Audio Interchange File Format */ + [AFMT_AIFF] = + AFMT_ENTRY("AIFF", "aiff", NULL, "aiff\0aif\0"), + +#if CONFIG_CODEC == SWCODEC + /* Uncompressed PCM in a WAV file */ + [AFMT_PCM_WAV] = + AFMT_ENTRY("WAV", "wav", "wav_enc", "wav\0" ), + /* Ogg Vorbis */ + [AFMT_OGG_VORBIS] = + AFMT_ENTRY("Ogg", "vorbis", NULL, "ogg\0" ), + /* FLAC */ + [AFMT_FLAC] = + AFMT_ENTRY("FLAC", "flac", NULL, "flac\0" ), + /* Musepack */ + [AFMT_MPC] = + AFMT_ENTRY("MPC", "mpc", NULL, "mpc\0" ), + /* A/52 (aka AC3) audio */ + [AFMT_A52] = + AFMT_ENTRY("AC3", "a52", NULL, "a52\0ac3\0" ), + /* WavPack */ + [AFMT_WAVPACK] = + AFMT_ENTRY("WV", "wavpack", "wavpack_enc", "wv\0" ), + /* Apple Lossless Audio Codec */ + [AFMT_ALAC] = + AFMT_ENTRY("ALAC", "alac", NULL, "m4a\0" ), + /* Advanced Audio Coding in M4A container */ + [AFMT_AAC] = + AFMT_ENTRY("AAC", "aac", NULL, "mp4\0" ), + /* Shorten */ + [AFMT_SHN] = + AFMT_ENTRY("SHN", "shorten", NULL, "shn\0" ), + /* SID File Format */ + [AFMT_SID] = + AFMT_ENTRY("SID", "sid", NULL, "sid\0" ), + /* ADX File Format */ + [AFMT_ADX] = + AFMT_ENTRY("ADX", "adx", NULL, "adx\0" ), +#endif +}; + +#if CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) +/* get REC_FORMAT_* corresponding AFMT_* */ +const int rec_format_afmt[REC_NUM_FORMATS] = +{ + /* give AFMT_UNKNOWN by default */ + [0 ... REC_NUM_FORMATS-1] = AFMT_UNKNOWN, + /* add new entries below this line */ + [REC_FORMAT_MPA_L3] = AFMT_MPA_L3, + [REC_FORMAT_WAVPACK] = AFMT_WAVPACK, + [REC_FORMAT_PCM_WAV] = AFMT_PCM_WAV, +}; + +/* get AFMT_* corresponding REC_FORMAT_* */ +const int afmt_rec_format[AFMT_NUM_CODECS] = +{ + /* give -1 by default */ + [0 ... AFMT_NUM_CODECS-1] = -1, + /* add new entries below this line */ + [AFMT_MPA_L3] = REC_FORMAT_MPA_L3, + [AFMT_WAVPACK] = REC_FORMAT_WAVPACK, + [AFMT_PCM_WAV] = REC_FORMAT_PCM_WAV, +}; +#endif /* CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) */ +/****/ + #define UNSYNC(b0,b1,b2,b3) (((long)(b0 & 0x7F) << (3*7)) | \ ((long)(b1 & 0x7F) << (2*7)) | \ ((long)(b2 & 0x7F) << (1*7)) | \ @@ -85,61 +168,6 @@ static const char* const genres[] = { "Synthpop" }; -/* database of audio formats */ -const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = -{ - /* Unknown file format */ - AFMT_ENTRY("???", NULL, NULL, NULL ), - /* MPEG Audio layer 1 */ - AFMT_ENTRY("MP1", "mpa.codec", NULL, NULL ), - /* MPEG Audio layer 2 */ - AFMT_ENTRY("MP2", "mpa.codec", NULL, NULL ), - /* MPEG Audio layer 3 */ - AFMT_ENTRY("MP3", "mpa.codec", "mp3_enc.codec", ".mp3"), -#if CONFIG_CODEC == SWCODEC - /* Uncompressed PCM in a WAV file */ - AFMT_ENTRY("WAV", "wav.codec", "wav_enc.codec", ".wav"), - /* Ogg Vorbis */ - AFMT_ENTRY("Ogg", "vorbis.codec", NULL, NULL ), - /* FLAC */ - AFMT_ENTRY("FLAC", "flac.codec", NULL, NULL ), - /* Musepack */ - AFMT_ENTRY("MPC", "mpc.codec", NULL, NULL ), - /* A/52 (aka AC3) audio */ - AFMT_ENTRY("AC3", "a52.codec", NULL, NULL ), - /* WavPack */ - AFMT_ENTRY("WV", "wavpack.codec", "wavpack_enc.codec", ".wv" ), - /* Apple Lossless Audio Codec */ - AFMT_ENTRY("ALAC", "alac.codec", NULL, NULL ), - /* Advanced Audio Coding in M4A container */ - AFMT_ENTRY("AAC", "aac.codec", NULL, NULL ), - /* Shorten */ - AFMT_ENTRY("SHN", "shorten.codec", NULL, NULL ), - /* Audio Interchange File Format */ - AFMT_ENTRY("AIFF", "aiff.codec", NULL, NULL ), - /* SID File Format */ - AFMT_ENTRY("SID", "sid.codec", NULL, NULL ), - /* ADX File Format */ - AFMT_ENTRY("ADX", "adx.codec", NULL, NULL ), -#endif -}; - -#if CONFIG_CODEC == SWCODEC -/* recording quality to AFMT_* */ -const int rec_quality_info_afmt[9] = -{ - AFMT_MPA_L3, /* MPEG L3 64 kBit/s */ - AFMT_MPA_L3, /* MPEG L3 96 kBit/s */ - AFMT_MPA_L3, /* MPEG L3 128 kBit/s */ - AFMT_MPA_L3, /* MPEG L3 160 kBit/s */ - AFMT_MPA_L3, /* MPEG L3 192 kBit/s */ - AFMT_MPA_L3, /* MPEG L3 224 kBit/s */ - AFMT_MPA_L3, /* MPEG L3 320 kBit/s */ - AFMT_WAVPACK, /* WavPack 909 kBit/s */ - AFMT_PCM_WAV, /* PCM Wav 1411 kBit/s */ -}; -#endif /* SWCODEC */ - char* id3_get_genre(const struct mp3entry* id3) { if( id3->genre_string ) diff --git a/firmware/mpeg.c b/firmware/mpeg.c index ce1d995461..bb438a3ab4 100644 --- a/firmware/mpeg.c +++ b/firmware/mpeg.c @@ -2453,34 +2453,32 @@ static void stop_recording(void) resume_recording(); } -void audio_set_recording_options(int frequency, int quality, - int source, int channel_mode, - bool editable, int prerecord_time) +void audio_set_recording_options(struct audio_recording_options *options) { bool is_mpeg1; - is_mpeg1 = (frequency < 3)?true:false; + is_mpeg1 = (options->rec_frequency < 3)?true:false; rec_version_index = is_mpeg1?3:2; - rec_frequency_index = frequency % 3; + rec_frequency_index = options->rec_frequency % 3; - shadow_encoder_control = (quality << 17) | + shadow_encoder_control = (options->rec_quality << 17) | (rec_frequency_index << 10) | ((is_mpeg1?1:0) << 9) | - (((channel_mode * 2 + 1) & 3) << 6) | + (((options->rec_channels * 2 + 1) & 3) << 6) | (1 << 5) /* MS-stereo */ | (1 << 2) /* Is an original */; mas_writemem(MAS_BANK_D0, MAS_D0_ENCODER_CONTROL, &shadow_encoder_control,1); DEBUGF("mas_writemem(MAS_BANK_D0, ENCODER_CONTROL, %x)\n", shadow_encoder_control); - shadow_soft_mute = editable?4:0; + shadow_soft_mute = options->rec_editable?4:0; mas_writemem(MAS_BANK_D0, MAS_D0_SOFT_MUTE, &shadow_soft_mute,1); DEBUGF("mas_writemem(MAS_BANK_D0, SOFT_MUTE, %x)\n", shadow_soft_mute); shadow_io_control_main = ((1 << 10) | /* Monitoring ON */ - ((source < 2)?1:2) << 8) | /* Input select */ + ((options->rec_source < 2)?1:2) << 8) | /* Input select */ (1 << 5) | /* SDO strobe invert */ ((is_mpeg1?0:1) << 3) | (1 << 2) | /* Inverted SIBC clock signal */ @@ -2489,7 +2487,7 @@ void audio_set_recording_options(int frequency, int quality, DEBUGF("mas_writemem(MAS_BANK_D0, IO_CONTROL_MAIN, %x)\n", shadow_io_control_main); - if(source == AUDIO_SRC_MIC) + if(options->rec_source == AUDIO_SRC_MIC) { /* Copy left channel to right (mono mode) */ mas_codec_writereg(8, 0x8000); @@ -2500,7 +2498,7 @@ void audio_set_recording_options(int frequency, int quality, mas_codec_writereg(8, 0); } - prerecording_max_seconds = prerecord_time; + prerecording_max_seconds = options->rec_prerecord_time; if(prerecording_max_seconds) { prerecording = true; diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c index cd14f123d1..b7ea96f3d3 100644 --- a/firmware/pcm_playback.c +++ b/firmware/pcm_playback.c @@ -16,260 +16,87 @@ * KIND, either express or implied. * ****************************************************************************/ -#include -#include "config.h" -#include "debug.h" -#include "panic.h" -#include -#include "cpu.h" -#include "i2c.h" -#if defined(HAVE_UDA1380) -#include "uda1380.h" -#elif defined(HAVE_WM8975) +#include "system.h" +#include "kernel.h" +#include "logf.h" +#include "audio.h" +#if defined(HAVE_WM8975) #include "wm8975.h" #elif defined(HAVE_WM8758) #include "wm8758.h" -#elif defined(HAVE_TLV320) -#include "tlv320.h" #elif defined(HAVE_WM8731) || defined(HAVE_WM8721) #include "wm8731l.h" #elif CONFIG_CPU == PNX0101 +#include "string.h" #include "pnx0101.h" #endif -#include "system.h" -#include "logf.h" -#include -#include -#include -#include "pcm_playback.h" -#include "lcd.h" -#include "button.h" -#include "file.h" -#include "buffer.h" -#include "sprintf.h" -#include "button.h" -#include +/** + * APIs implemented in the target-specific portion: + * Public - + * pcm_init + * pcm_get_bytes_waiting + * pcm_calculate_peaks + * Semi-private - + * pcm_play_dma_start + * pcm_play_dma_stop + * pcm_play_pause_pause + * pcm_play_pause_unpause + */ -static bool pcm_playing; -static bool pcm_paused; +/** These items may be implemented target specifically or need to + be shared semi-privately **/ /* the registered callback function to ask for more mp3 data */ -static void (*callback_for_more)(unsigned char**, size_t*) IDATA_ATTR = NULL; +pcm_more_callback_type pcm_callback_for_more = NULL; +bool pcm_playing = false; +bool pcm_paused = false; + +void pcm_play_dma_start(const void *addr, size_t size); +void pcm_play_dma_stop(void); +void pcm_play_pause_pause(void); +void pcm_play_pause_unpause(void); + +/** Functions that require targeted implementation **/ + +#ifndef CPU_COLDFIRE #if (CONFIG_CPU == S3C2440) /* TODO: Implement for Gigabeat For now, just implement some dummy functions. */ - void pcm_init(void) { - } -static void dma_start(const void *addr, size_t size) +void pcm_play_dma_start(const void *addr, size_t size) { (void)addr; (void)size; } +void pcm_play_dma_stop(void) +{ +} + +void pcm_play_pause_pause(void) +{ +} + +void pcm_play_pause_unpause(void) +{ +} + void pcm_set_frequency(unsigned int frequency) { (void)frequency; } -void pcm_play_stop(void) -{ -} - size_t pcm_get_bytes_waiting(void) { return 0; } -#else -#ifdef CPU_COLDFIRE - -#ifdef HAVE_SPDIF_OUT -#define EBU_DEFPARM ((7 << 12) | (3 << 8) | (1 << 5) | (5 << 2)) -#endif -#define IIS_DEFPARM(freq) ((freq << 12) | 0x300 | 4 << 2) -#define IIS_RESET 0x800 - -#ifdef IAUDIO_X5 -#define SET_IIS_CONFIG(x) IIS1CONFIG = (x); -#else -#define SET_IIS_CONFIG(x) IIS2CONFIG = (x); -#endif - -static int pcm_freq = 0x6; /* 44.1 is default */ - -int peak_left = 0, peak_right = 0; - -/* Set up the DMA transfer that kicks in when the audio FIFO gets empty */ -static void dma_start(const void *addr, size_t size) -{ - pcm_playing = true; - - addr = (void *)((unsigned long)addr & ~3); /* Align data */ - size &= ~3; /* Size must be multiple of 4 */ - - /* Reset the audio FIFO */ -#ifdef HAVE_SPDIF_OUT - EBU1CONFIG = IIS_RESET | EBU_DEFPARM; -#endif - - /* Set up DMA transfer */ - SAR0 = (unsigned long)addr; /* Source address */ - DAR0 = (unsigned long)&PDOR3; /* Destination address */ - BCR0 = size; /* Bytes to transfer */ - - /* Enable the FIFO and force one write to it */ - SET_IIS_CONFIG(IIS_DEFPARM(pcm_freq)); - /* Also send the audio to S/PDIF */ -#ifdef HAVE_SPDIF_OUT - EBU1CONFIG = EBU_DEFPARM; -#endif - DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_SINC | (3 << 20) | DMA_START; -} - -/* Stops the DMA transfer and interrupt */ -static void dma_stop(void) -{ - pcm_playing = false; - - DCR0 = 0; - DSR0 = 1; - /* Reset the FIFO */ - SET_IIS_CONFIG(IIS_RESET | IIS_DEFPARM(pcm_freq)); -#ifdef HAVE_SPDIF_OUT - EBU1CONFIG = IIS_RESET | EBU_DEFPARM; -#endif -} - -/* sets frequency of input to DAC */ -void pcm_set_frequency(unsigned int frequency) -{ - switch(frequency) - { - case 11025: - pcm_freq = 0x2; -#ifdef HAVE_UDA1380 - uda1380_set_nsorder(3); -#endif - break; - case 22050: - pcm_freq = 0x4; -#ifdef HAVE_UDA1380 - uda1380_set_nsorder(3); -#endif - break; - case 44100: - default: - pcm_freq = 0x6; -#ifdef HAVE_UDA1380 - uda1380_set_nsorder(5); -#endif - break; - } -} - -size_t pcm_get_bytes_waiting(void) -{ - return (BCR0 & 0xffffff); -} - -/* DMA0 Interrupt is called when the DMA has finished transfering a chunk */ -void DMA0(void) __attribute__ ((interrupt_handler, section(".icode"))); -void DMA0(void) -{ - int res = DSR0; - - DSR0 = 1; /* Clear interrupt */ - DCR0 &= ~DMA_EEXT; - - /* Stop on error */ - if(res & 0x70) - { - dma_stop(); - logf("DMA Error:0x%04x", res); - } - else - { - size_t next_size; - unsigned char *next_start; - { - void (*get_more)(unsigned char**, size_t*) = callback_for_more; - if (get_more) - get_more(&next_start, &next_size); - else - { - next_size = 0; - next_start = NULL; - } - } - if(next_size) - { - SAR0 = (unsigned long)next_start; /* Source address */ - BCR0 = next_size; /* Bytes to transfer */ - DCR0 |= DMA_EEXT; - } - else - { - /* Finished playing */ - dma_stop(); - logf("DMA No Data:0x%04x", res); - } - } - - IPR |= (1<<14); /* Clear pending interrupt request */ -} - -void pcm_init(void) -{ - pcm_playing = false; - pcm_paused = false; - - MPARK = 0x81; /* PARK[1,0]=10 + BCR24BIT */ - DIVR0 = 54; /* DMA0 is mapped into vector 54 in system.c */ - DMAROUTE = (DMAROUTE & 0xffffff00) | DMA0_REQ_AUDIO_1; - DMACONFIG = 1; /* DMA0Req = PDOR3 */ - - /* Reset the audio FIFO */ - SET_IIS_CONFIG(IIS_RESET); - - /* Enable interrupt at level 7, priority 0 */ - ICR6 = 0x1c; - IMR &= ~(1<<14); /* bit 14 is DMA0 */ - - pcm_set_frequency(44100); - - /* Prevent pops (resets DAC to zero point) */ - SET_IIS_CONFIG(IIS_DEFPARM(pcm_freq) | IIS_RESET); - -#if defined(HAVE_UDA1380) - /* Initialize default register values. */ - uda1380_init(); - - /* Sleep a while so the power can stabilize (especially a long - delay is needed for the line out connector). */ - sleep(HZ); - - /* Power on FSDAC and HP amp. */ - uda1380_enable_output(true); - - /* Unmute the master channel (DAC should be at zero point now). */ - uda1380_mute(false); - -#elif defined(HAVE_TLV320) - tlv320_init(); - sleep(HZ/4); - tlv320_mute(false); -#endif - - /* Call dma_stop to initialize everything. */ - dma_stop(); -} #elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \ || defined(HAVE_WM8731) || defined(HAVE_WM8721) @@ -286,14 +113,14 @@ void pcm_init(void) #define FIFO_FREE_COUNT 4 /* TODO: make this sensible */ #endif -static int pcm_freq = 44100; /* 44.1 is default */ +static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ /* NOTE: The order of these two variables is important if you use the iPod assembler optimised fiq handler, so don't change it. */ unsigned short* p IBSS_ATTR; size_t p_size IBSS_ATTR; -static void dma_start(const void *addr, size_t size) +void pcm_play_dma_start(const void *addr, size_t size) { p=(unsigned short*)addr; p_size=size; @@ -341,7 +168,7 @@ static void dma_start(const void *addr, size_t size) } /* Stops the DMA transfer and interrupt */ -static void dma_stop(void) +void pcm_play_dma_stop(void) { pcm_playing = false; @@ -365,9 +192,58 @@ static void dma_stop(void) disable_fiq(); } +void pcm_play_pause_pause(void) +{ +#if CONFIG_CPU == PP5020 + /* Disable the interrupt */ + IISCONFIG &= ~0x2; + /* Disable playback FIFO */ + IISCONFIG &= ~0x20000000; +#elif CONFIG_CPU == PP5002 + /* Disable the interrupt */ + IISFIFO_CFG &= ~(1<<9); + /* Disable playback FIFO */ + IISCONFIG &= ~0x4; +#endif + disable_fiq(); +} + +void pcm_play_pause_unpause(void) +{ + /* Enable the FIFO and fill it */ + + enable_fiq(); + + /* Enable playback FIFO */ +#if CONFIG_CPU == PP5020 + IISCONFIG |= 0x20000000; +#elif CONFIG_CPU == PP5002 + IISCONFIG |= 0x4; +#endif + + /* Fill the FIFO - we assume there are enough bytes in the + pcm buffer to fill the 32-byte FIFO. */ + while (p_size > 0) { + if (FIFO_FREE_COUNT < 2) { + /* Enable interrupt */ +#if CONFIG_CPU == PP5020 + IISCONFIG |= 0x2; +#elif CONFIG_CPU == PP5002 + IISFIFO_CFG |= (1<<9); +#endif + return; + } + + IISFIFO_WR = (*(p++))<<16; + IISFIFO_WR = (*(p++))<<16; + p_size-=4; + } +} + void pcm_set_frequency(unsigned int frequency) { - pcm_freq=frequency; + (void)frequency; + pcm_freq = HW_SAMPR_DEFAULT; } size_t pcm_get_bytes_waiting(void) @@ -378,8 +254,8 @@ size_t pcm_get_bytes_waiting(void) /* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode has registers r8-r14 banked, and so does not need to be saved. This routine uses only these registers, and so will never touch the stack unless it - actually needs to do so when calling callback_for_more. C version is still - included below for reference. + actually needs to do so when calling pcm_callback_for_more. C version is + still included below for reference. */ #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 void fiq(void) ICODE_ATTR __attribute__((naked)); @@ -433,10 +309,10 @@ void fiq(void) "add r1, r11, #4 \n\t" /* r1 = &p_size */ "str r9, [r0] \n\t" /* save internal copies of variables back */ "str r8, [r1] \n\t" - "ldr r2, =callback_for_more\n\t" + "ldr r2, =pcm_callback_for_more\n\t" "ldr r2, [r2] \n\t" /* get callback address */ "cmp r2, #0 \n\t" /* check for null pointer */ - "movne lr, pc \n\t" /* call callback_for_more */ + "movne lr, pc \n\t" /* call pcm_callback_for_more */ "bxne r2 \n\t" "ldmia sp!, { r0-r3, r12, lr}\n\t" "ldr r8, [r11, #4] \n\t" /* reload p_size and p */ @@ -477,7 +353,7 @@ void fiq(void) "b .exit \n\t" ); } -#else +#else /* !(CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002) */ void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); void fiq(void) { @@ -507,20 +383,21 @@ void fiq(void) } /* p is empty, get some more data */ - if (callback_for_more) { - callback_for_more((unsigned char**)&p,&p_size); + if (pcm_callback_for_more) { + pcm_callback_for_more((unsigned char**)&p,&p_size); } } while (p_size); /* No more data, so disable the FIFO/FIQ */ - dma_stop(); + pcm_play_dma_stop(); } -#endif +#endif /* CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 */ void pcm_init(void) { pcm_playing = false; pcm_paused = false; + pcm_callback_for_more = NULL; /* Initialize default register values. */ wmcodec_init(); @@ -531,8 +408,8 @@ void pcm_init(void) /* Unmute the master channel (DAC should be at zero point now). */ wmcodec_mute(false); - /* Call dma_stop to initialize everything. */ - dma_stop(); + /* Call pcm_play_dma_stop to initialize everything. */ + pcm_play_dma_stop(); } #elif (CONFIG_CPU == PNX0101) @@ -542,12 +419,16 @@ void pcm_init(void) short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES]; short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES]; -static int pcm_freq = 44100; /* 44.1 is default */ +static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ unsigned short* p IBSS_ATTR; size_t p_size IBSS_ATTR; -static void dma_start(const void *addr, size_t size) +void pcm_init(void) +{ +} + +void pcm_play_dma_start(const void *addr, size_t size) { p = (unsigned short*)addr; p_size = size; @@ -555,11 +436,19 @@ static void dma_start(const void *addr, size_t size) pcm_playing = true; } -static void dma_stop(void) +void pcm_play_dma_stop(void) { pcm_playing = false; } +void pcm_play_pause_pause(void) +{ +} + +void pcm_play_pause_unpause(void) +{ +} + static inline void fill_dma_buf(int offset) { short *l, *r, *lend; @@ -611,8 +500,8 @@ static inline void fill_dma_buf(int offset) p = tmp_p; if (l >= lend) return; - else if (callback_for_more) - callback_for_more((unsigned char**)&p, + else if (pcm_callback_for_more) + pcm_callback_for_more((unsigned char**)&p, &p_size); } while (p_size); @@ -647,9 +536,10 @@ unsigned long physical_address(void *p) void pcm_init(void) { int i; - callback_for_more = NULL; + pcm_playing = false; pcm_paused = false; + pcm_callback_for_more = NULL; memset(dma_buf_left, 0, sizeof(dma_buf_left)); memset(dma_buf_right, 0, sizeof(dma_buf_right)); @@ -691,271 +581,37 @@ void pcm_init(void) void pcm_set_frequency(unsigned int frequency) { - pcm_freq=frequency; + (void)frequency; + pcm_freq = HW_SAMPR_DEFAULT; } size_t pcm_get_bytes_waiting(void) { return p_size; } -#endif +#endif /* CONFIG_CPU == */ -void pcm_play_stop(void) +/* dummy functions for those not actually supporting all this yet */ +void pcm_apply_settings(bool reset) { - if (pcm_playing) { - dma_stop(); - } + (void)reset; } -#endif - -void pcm_play_data(void (*get_more)(unsigned char** start, size_t* size), - unsigned char* start, size_t size) +void pcm_set_monitor(int monitor) { - callback_for_more = get_more; - - if (!(start && size)) - { - if (get_more) - get_more(&start, &size); - else - return; - } - if (start && size) - { - dma_start(start, size); - if (pcm_paused) { - pcm_paused = false; - pcm_play_pause(false); - } - } + (void)monitor; } +/** **/ void pcm_mute(bool mute) { -#ifdef HAVE_UDA1380 - uda1380_mute(mute); -#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \ +#if defined(HAVE_WM8975) || defined(HAVE_WM8758) \ || defined(HAVE_WM8731) || defined(HAVE_WM8721) wmcodec_mute(mute); -#elif defined(HAVE_TLV320) - tlv320_mute(mute); #endif if (mute) sleep(HZ/16); } -void pcm_play_pause(bool play) -{ - bool needs_change = pcm_paused == play; - - /* This needs to be done ahead of the rest to prevent infinite - * recursion from dma_start */ - pcm_paused = !play; - if (pcm_playing && needs_change) { - if(play) { - if (pcm_get_bytes_waiting()) { - logf("unpause"); - -#ifdef CPU_COLDFIRE - /* Enable the FIFO and force one write to it */ - SET_IIS_CONFIG(IIS_DEFPARM(pcm_freq)); -#ifdef HAVE_SPDIF_OUT - EBU1CONFIG = EBU_DEFPARM; -#endif - DCR0 |= DMA_EEXT | DMA_START; -#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \ - || defined(HAVE_WM8731) || defined(HAVE_WM8721) - /* Enable the FIFO and fill it */ - - enable_fiq(); - - /* Enable playback FIFO */ -#if CONFIG_CPU == PP5020 - IISCONFIG |= 0x20000000; -#elif CONFIG_CPU == PP5002 - IISCONFIG |= 0x4; -#endif - - /* Fill the FIFO - we assume there are enough bytes in the - pcm buffer to fill the 32-byte FIFO. */ - while (p_size > 0) { - if (FIFO_FREE_COUNT < 2) { - /* Enable interrupt */ -#if CONFIG_CPU == PP5020 - IISCONFIG |= 0x2; -#elif CONFIG_CPU == PP5002 - IISFIFO_CFG |= (1<<9); -#endif - return; - } - - IISFIFO_WR = (*(p++))<<16; - IISFIFO_WR = (*(p++))<<16; - p_size-=4; - } -#elif (CONFIG_CPU == PNX0101 || CONFIG_CPU == S3C2440) /* End wmcodecs */ - /* nothing yet */ -#endif - } else { -#if (CONFIG_CPU != PNX0101 && CONFIG_CPU != S3C2440) - size_t next_size; - unsigned char *next_start; - void (*get_more)(unsigned char**, size_t*) = callback_for_more; - logf("unpause, no data waiting"); - if (get_more) - get_more(&next_start, &next_size); - if (next_start && next_size) - dma_start(next_start, next_size); - else - { - dma_stop(); - logf("unpause attempted, no data"); - } -#endif - } - } else { - logf("pause"); - -#ifdef CPU_COLDFIRE - /* Disable DMA peripheral request. */ - DCR0 &= ~DMA_EEXT; - SET_IIS_CONFIG(IIS_RESET | IIS_DEFPARM(pcm_freq)); -#ifdef HAVE_SPDIF_OUT - EBU1CONFIG = IIS_RESET | EBU_DEFPARM; -#endif -#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \ - || defined(HAVE_WM8731) || defined(HAVE_WM8721) -#if CONFIG_CPU == PP5020 - /* Disable the interrupt */ - IISCONFIG &= ~0x2; - /* Disable playback FIFO */ - IISCONFIG &= ~0x20000000; -#elif CONFIG_CPU == PP5002 - /* Disable the interrupt */ - IISFIFO_CFG &= ~(1<<9); - /* Disable playback FIFO */ - IISCONFIG &= ~0x4; -#endif - - disable_fiq(); -#elif (CONFIG_CPU == PNX0101 || CONFIG_CPU == S3C2440) /* End wmcodecs */ - /* nothing yet */ -#endif - } - } /* pcm_playing && needs_change */ -} - -bool pcm_is_playing(void) { - return pcm_playing; -} - -bool pcm_is_paused(void) { - return pcm_paused; -} - - -#if defined(CPU_COLDFIRE) -/* Peaks ahead in the DMA buffer based upon the calling period to - attempt to compensate for the delay. Keeps a moving average of - length four. */ -void pcm_calculate_peaks(int *left, int *right) -{ - unsigned long samples; - unsigned long *addr, *end; - long peak_p, peak_n; - - static unsigned long last_peak_tick = 0; - static unsigned long frame_period = 0; - - /* Throttled peak ahead based on calling period */ - unsigned long period = current_tick - last_peak_tick; - - /* Keep reasonable limits on period */ - if (period < 1) - period = 1; - else if (period > HZ/5) - period = HZ/5; - - frame_period = (3*frame_period + period) >> 2; - - last_peak_tick = current_tick; - - if (!pcm_playing || pcm_paused) - { - peak_left = peak_right = 0; - goto peak_done; - } - - samples = (BCR0 & 0xffffff) >> 2; - addr = (long *)(SAR0 & ~3); - samples = MIN(frame_period*44100/HZ, samples); - end = addr + samples; - peak_p = peak_n = 0; - - if (left && right) - { - if (samples > 0) - { - long peak_rp = 0, peak_rn = 0; - - do - { - long value = *addr; - long ch; - - ch = value >> 16; - if (ch > peak_p) peak_p = ch; - else if (ch < peak_n) peak_n = ch; - - ch = (short)value; - if (ch > peak_rp) peak_rp = ch; - else if (ch < peak_rn) peak_rn = ch; - - addr += 4; - } - while (addr < end); - - peak_left = MAX(peak_p, -peak_n); - peak_right = MAX(peak_rp, -peak_rn); - } - } - else if (left || right) - { - if (samples > 0) - { - if (left) - { - /* Put left channel in low word */ - addr = (long *)((short *)addr - 1); - end = (long *)((short *)end - 1); - } - - do - { - long value = *(short *)addr; - - if (value > peak_p) peak_p = value; - else if (value < peak_n) peak_n = value; - - addr += 4; - } - while (addr < end); - - if (left) - peak_left = MAX(peak_p, -peak_n); - else - peak_right = MAX(peak_p, -peak_n); - } - } - -peak_done: - if (left) - *left = peak_left; - - if (right) - *right = peak_right; -} -#else /* * This function goes directly into the DMA buffer to calculate the left and * right peak values. To avoid missing peaks it tries to look forward two full @@ -1037,4 +693,94 @@ void pcm_calculate_peaks(int *left, int *right) } #endif } + #endif /* CPU_COLDFIRE */ + +/**************************************************************************** + * Functions that do not require targeted implementation but only a targeted + * interface + */ + +/* Common code to pcm_play_data and pcm_play_pause + Returns true if DMA playback was started, else false. */ +bool pcm_play_data_start(pcm_more_callback_type get_more, + unsigned char *start, size_t size) +{ + if (!(start && size)) + { + size = 0; + if (get_more) + get_more(&start, &size); + } + + if (start && size) + { + pcm_play_dma_start(start, size); + return true; + } + + return false; +} + +void pcm_play_data(pcm_more_callback_type get_more, + unsigned char *start, size_t size) +{ + pcm_callback_for_more = get_more; + + if (pcm_play_data_start(get_more, start, size) && pcm_paused) + { + pcm_paused = false; + pcm_play_pause(false); + } +} + +void pcm_play_pause(bool play) +{ + bool needs_change = pcm_paused == play; + + /* This needs to be done ahead of the rest to prevent infinite + recursion from pcm_play_data */ + pcm_paused = !play; + + if (pcm_playing && needs_change) + { + if (play) + { + if (pcm_get_bytes_waiting()) + { + logf("unpause"); + pcm_play_pause_unpause(); + } + else + { + logf("unpause, no data waiting"); + if (!pcm_play_data_start(pcm_callback_for_more, NULL, 0)) + { + pcm_play_dma_stop(); + logf("unpause attempted, no data"); + } + } + } + else + { + logf("pause"); + pcm_play_pause_pause(); + } + } /* pcm_playing && needs_change */ +} + +void pcm_play_stop(void) +{ + if (pcm_playing) + pcm_play_dma_stop(); +} + +bool pcm_is_playing(void) +{ + return pcm_playing; +} + +bool pcm_is_paused(void) +{ + return pcm_paused; +} diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c index 2785d4b1b1..25f1f1ef64 100644 --- a/firmware/pcm_record.c +++ b/firmware/pcm_record.c @@ -16,199 +16,238 @@ * KIND, either express or implied. * ****************************************************************************/ - -#include "config.h" -#include "debug.h" +#include "system.h" +#include "kernel.h" +#include "logf.h" #include "panic.h" #include "thread.h" - -#include -#include #include -#include -#include - -#include "cpu.h" -#include "i2c.h" -#include "power.h" -#ifdef HAVE_UDA1380 +#include "ata.h" +#include "usb.h" +#if defined(HAVE_UDA1380) #include "uda1380.h" -#endif -#ifdef HAVE_TLV320 +#include "general.h" +#elif defined(HAVE_TLV320) #include "tlv320.h" #endif -#include "system.h" -#include "usb.h" - #include "buffer.h" #include "audio.h" -#include "button.h" -#include "file.h" -#include "sprintf.h" -#include "logf.h" -#include "button.h" -#include "lcd.h" -#include "lcd-remote.h" -#include "pcm_playback.h" #include "sound.h" #include "id3.h" -#include "pcm_record.h" - -extern int boost_counter; /* used for boost check */ /***************************************************************************/ +/** + * APIs implemented in the target tree portion: + * Public - + * pcm_init_recording + * pcm_close_recording + * pcm_rec_mux + * Semi-private - + * pcm_rec_dma_start + * pcm_rec_dma_stop + */ + +/** These items may be implemented target specifically or need to + be shared semi-privately **/ + +/* the registered callback function for when more data is available */ +pcm_more_callback_type pcm_callback_more_ready = NULL; +/* DMA transfer in is currently active */ +bool pcm_recording = false; + +/* APIs implemented in the target-specific portion */ +void pcm_rec_dma_start(const void *addr, size_t size); +void pcm_rec_dma_stop(void); + +/** General recording state **/ static bool is_recording; /* We are recording */ static bool is_paused; /* We have paused */ +static bool is_stopping; /* We are currently stopping */ static bool is_error; /* An error has occured */ -static unsigned long num_rec_bytes; /* Num bytes recorded */ -static unsigned long num_file_bytes; /* Num bytes written to current file */ -static int error_count; /* Number of DMA errors */ -static unsigned long num_pcm_samples; /* Num pcm samples written to current file */ +/** Stats on encoded data for current file **/ +static size_t num_rec_bytes; /* Num bytes recorded */ +static unsigned long num_rec_samples; /* Number of PCM samples recorded */ -static long record_start_time; /* current_tick when recording was started */ -static long pause_start_time; /* current_tick when pause was started */ -static unsigned int sample_rate; /* Sample rate at time of recording start */ -static int rec_source; /* Current recording source */ +/** Stats on encoded data for all files from start to stop **/ +static unsigned long long accum_rec_bytes; /* total size written to chunks */ +static unsigned long long accum_pcm_samples; /* total pcm count processed */ -static int wav_file; -static char recording_filename[MAX_PATH]; +/* Keeps data about current file and is sent as event data for codec */ +static struct enc_file_event_data rec_fdata IDATA_ATTR = +{ + .chunk = NULL, + .new_enc_size = 0, + .new_num_pcm = 0, + .rec_file = -1, + .num_pcm_samples = 0 +}; -static volatile bool init_done, close_done, record_done; -static volatile bool stop_done, pause_done, resume_done, new_file_done; - -static int peak_left, peak_right; - -#ifdef IAUDIO_X5 -#define SET_IIS_PLAY(x) IIS1CONFIG = (x); -#define SET_IIS_REC(x) IIS1CONFIG = (x); -#else -#define SET_IIS_PLAY(x) IIS2CONFIG = (x); -#define SET_IIS_REC(x) IIS1CONFIG = (x); -#endif +/** These apply to current settings **/ +static int rec_source; /* current rec_source setting */ +static int rec_frequency; /* current frequency setting */ +static unsigned long sample_rate; /* Sample rate in HZ */ +static int num_channels; /* Current number of channels */ +static struct encoder_config enc_config; /* Current encoder configuration */ /**************************************************************************** - use 2 circular buffers of same size: - rec_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data + use 2 circular buffers: + pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data enc_buffer=encoded audio buffer: storage for encoder output data Flow: - 1. when entering recording_screen DMA feeds the ringbuffer rec_buffer + 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer 2. if enough pcm data are available the encoder codec does encoding of pcm chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk - Functions calls: - 1.main: codec_load_encoder(); start the encoder - 2.encoder: enc_get_inputs(); get encoder buffsize, mono/stereo, quality - 3.encoder: enc_set_parameters(); set the encoder parameters (max.chunksize) - 4.encoder: enc_get_wav_data(); get n bytes of unprocessed pcm data - 5.encoder: enc_wavbuf_near_empty();if true: reduce cpu_boost + Functions calls (basic encoder steps): + 1.main: audio_load_encoder(); start the encoder + 2.encoder: enc_get_inputs(); get encoder recording settings + 3.encoder: enc_set_parameters(); set the encoder parameters + 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data + 5.encoder: enc_pcm_buf_near_empty(); if 1: reduce cpu_boost 6.encoder: enc_alloc_chunk(); get a ptr to next enc chunk 7.encoder: compress and store data to enc chunk 8.encoder: enc_free_chunk(); inform main about chunk process finished 9.encoder: repeat 4. to 8. - A.main: enc_set_header_callback(); create the current format header (file) + A.pcmrec: enc_events_callback(); called for certain events ****************************************************************************/ -#define NUM_CHUNKS 256 /* Power of 2 */ -#define CHUNK_SIZE 8192 /* Power of 2 */ -#define MAX_FEED_SIZE 20000 /* max pcm size passed to encoder */ -#define CHUNK_MASK (NUM_CHUNKS * CHUNK_SIZE - 1) -#define WRITE_THRESHOLD (44100 * 5 / enc_samp_per_chunk) /* 5sec */ -#define GET_CHUNK(x) (long*)(&rec_buffer[x]) -#define GET_ENC_CHUNK(x) (long*)(&enc_buffer[enc_chunk_size*(x)]) -static int audio_enc_id; /* current encoder id */ -static unsigned char *rec_buffer; /* Circular recording buffer */ -static unsigned char *enc_buffer; /* Circular encoding buffer */ -static unsigned char *enc_head_buffer; /* encoder header buffer */ -static int enc_head_size; /* used size in header buffer */ -static int write_pos; /* Current chunk pos for DMA writing */ -static int read_pos; /* Current chunk pos for encoding */ -static long pre_record_ticks;/* pre-record time expressed in ticks */ -static int enc_wr_index; /* Current encoding chunk write index */ -static int enc_rd_index; /* Current encoding chunk read index */ -static int enc_chunk_size; /* maximum encoder chunk size */ +/** buffer parameters where incoming PCM data is placed **/ +#define PCM_NUM_CHUNKS 256 /* Power of 2 */ +#define PCM_CHUNK_SIZE 8192 /* Power of 2 */ +#define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1) + +#define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset))) +#define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index)) + +#define INC_ENC_INDEX(index) \ + { if (++index >= enc_num_chunks) index = 0; } +#define DEC_ENC_INDEX(index) \ + { if (--index < 0) index = enc_num_chunks - 1; } + +static size_t rec_buffer_size; /* size of available buffer */ +static unsigned char *pcm_buffer; /* circular recording buffer */ +static unsigned char *enc_buffer; /* circular encoding buffer */ +static volatile int dma_wr_pos; /* current DMA write pos */ +static int pcm_rd_pos; /* current PCM read pos */ +static volatile bool dma_lock; /* lock DMA write position */ +static unsigned long pre_record_ticks;/* pre-record time in ticks */ +static int enc_wr_index; /* encoder chunk write index */ +static int enc_rd_index; /* encoder chunk read index */ static int enc_num_chunks; /* number of chunks in ringbuffer */ -static int enc_buffer_size; /* encode buffer size */ -static int enc_channels; /* 1=mono 2=stereo */ -static int enc_quality; /* mp3: 64,96,128,160,192,320 kBit */ -static int enc_samp_per_chunk;/* pcm samples per encoder chunk */ +static size_t enc_chunk_size; /* maximum encoder chunk size */ +static size_t enc_data_size; /* maximum data size for encoder */ +static unsigned long enc_sample_rate; /* sample rate used by encoder */ static bool wav_queue_empty; /* all wav chunks processed? */ -static unsigned long avrg_bit_rate; /* average bit rates from chunks */ -static unsigned long curr_bit_rate; /* cumulated bit rates from chunks */ -static unsigned long curr_chunk_cnt; /* number of processed chunks */ -void (*enc_set_header_callback)(void *head_buffer, int head_size, - int num_pcm_samples, bool is_file_header); +/** file flushing **/ +static int write_threshold; /* max chunk limit for data flush */ +static int panic_threshold; /* boost thread prio when here */ +static int spinup_time = -1;/* last ata_spinup_time */ + +/** encoder events **/ +static void (*enc_events_callback)(enum enc_events event, void *data); + +/** Path queue for files to write **/ +#define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */ +static unsigned char *fn_queue; /* pointer to first filename */ +static ssize_t fnq_size; /* capacity of queue in bytes */ +static int fnq_rd_pos; /* current read position */ +static int fnq_wr_pos; /* current write position */ /***************************************************************************/ static struct event_queue pcmrec_queue; -static long pcmrec_stack[2*DEFAULT_STACK_SIZE/sizeof(long)]; +static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)]; static const char pcmrec_thread_name[] = "pcmrec"; static void pcmrec_thread(void); -static void pcmrec_dma_start(void); -static void pcmrec_dma_stop(void); -static void close_wave(void); -/* Event IDs */ -#define PCMREC_INIT 1 /* Enable recording */ -#define PCMREC_CLOSE 2 +/* Event values which are also single-bit flags */ +#define PCMREC_INIT 0x00000001 /* enable recording */ +#define PCMREC_CLOSE 0x00000002 -#define PCMREC_START 3 /* Start a new recording */ -#define PCMREC_STOP 4 /* Stop the current recording */ -#define PCMREC_PAUSE 10 -#define PCMREC_RESUME 11 -#define PCMREC_NEW_FILE 12 -#define PCMREC_SET_GAIN 13 +#define PCMREC_START 0x00000004 /* start recording (when stopped) */ +#define PCMREC_STOP 0x00000008 /* stop the current recording */ +#define PCMREC_PAUSE 0x00000010 /* pause the current recording */ +#define PCMREC_RESUME 0x00000020 /* resume the current recording */ +#define PCMREC_NEW_FILE 0x00000040 /* start new file (when recording) */ +#define PCMREC_SET_GAIN 0x00000080 +#define PCMREC_FLUSH_NUM 0x00000100 /* flush a number of files out */ +#define PCMREC_FINISH_STOP 0x00000200 /* finish the stopping recording */ + +/* mask for signaling events */ +static volatile long pcm_thread_event_mask; + +static void pcm_thread_sync_post(long event, void *data) +{ + pcm_thread_event_mask &= ~event; + queue_post(&pcmrec_queue, event, data); + while(!(event & pcm_thread_event_mask)) + yield(); +} /* pcm_thread_sync_post */ + +static inline void pcm_thread_signal_event(long event) +{ + pcm_thread_event_mask |= event; +} /* pcm_thread_signal_event */ + +static inline void pcm_thread_unsignal_event(long event) +{ + pcm_thread_event_mask &= ~event; +} /* pcm_thread_unsignal_event */ + +static inline bool pcm_thread_event_state(long signaled, long unsignaled) +{ + return ((signaled | unsignaled) & pcm_thread_event_mask) == signaled; +} /* pcm_thread_event_state */ + +static void pcm_thread_wait_for_stop(void) +{ + if (is_stopping) + { + logf("waiting for stop to finish"); + while (is_stopping) + yield(); + } +} /* pcm_thread_wait_for_stop */ /*******************************************************************/ /* Functions that are not executing in the pcmrec_thread first */ /*******************************************************************/ - -/* Creates pcmrec_thread */ -void pcm_rec_init(void) -{ - queue_init(&pcmrec_queue, true); - create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), - pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)); -} - - -int audio_get_encoder_id(void) -{ - return audio_enc_id; -} - -/* Initializes recording: - * - Set up the UDA1380/TLV320 for recording - * - Prepare for DMA transfers - */ - -void audio_init_recording(unsigned int buffer_offset) -{ - (void)buffer_offset; - - init_done = false; - queue_post(&pcmrec_queue, PCMREC_INIT, 0); - while(!init_done) - sleep_thread(1); -} - -void audio_close_recording(void) +/* Callback for when more data is ready */ +static void pcm_rec_have_more(unsigned char **data, size_t *size) { - close_done = false; - queue_post(&pcmrec_queue, PCMREC_CLOSE, 0); + if (*size != 0) + { + /* some error condition */ + if (*size == DMA_REC_ERROR_DMA) + { + /* Flush recorded data to disk and stop recording */ + queue_post(&pcmrec_queue, PCMREC_STOP, NULL); + return; + } + /* else try again next transmission */ + } + else if (!dma_lock) + { + /* advance write position */ + dma_wr_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK; + } - while(!close_done) - sleep_thread(1); + *data = (unsigned char *)GET_PCM_CHUNK(dma_wr_pos); + *size = PCM_CHUNK_SIZE; +} /* pcm_rec_have_more */ - audio_remove_encoder(); -} +/** pcm_rec_* group **/ +void pcm_rec_error_clear(void) +{ + is_error = false; +} /* pcm_rec_error_clear */ unsigned long pcm_rec_status(void) { @@ -216,165 +255,223 @@ unsigned long pcm_rec_status(void) if (is_recording) ret |= AUDIO_STATUS_RECORD; + if (is_paused) ret |= AUDIO_STATUS_PAUSE; + if (is_error) ret |= AUDIO_STATUS_ERROR; - if (!is_recording && pre_record_ticks && init_done && !close_done) + + if (!is_recording && pre_record_ticks && + pcm_thread_event_state(PCMREC_INIT, PCMREC_CLOSE)) ret |= AUDIO_STATUS_PRERECORD; return ret; -} +} /* pcm_rec_status */ int pcm_rec_current_bitrate(void) { - return avrg_bit_rate; -} + if (accum_pcm_samples == 0) + return 0; + + return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples)); +} /* pcm_rec_current_bitrate */ + +int pcm_rec_encoder_afmt(void) +{ + return enc_config.afmt; +} /* pcm_rec_encoder_afmt */ + +int pcm_rec_rec_format(void) + { + return afmt_rec_format[enc_config.afmt]; +} /* pcm_rec_rec_format */ + +unsigned long pcm_rec_sample_rate(void) +{ + /* Which is better ?? */ +#if 0 + return enc_sample_rate; +#endif + return sample_rate; +} /* audio_get_sample_rate */ + +/** + * Creates pcmrec_thread + */ +void pcm_rec_init(void) +{ + queue_init(&pcmrec_queue, true); + create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), + pcmrec_thread_name, PRIORITY_RECORDING); +} /* pcm_rec_init */ + +/** audio_* group **/ + +void audio_init_recording(unsigned int buffer_offset) +{ + (void)buffer_offset; + pcm_thread_wait_for_stop(); + pcm_thread_sync_post(PCMREC_INIT, NULL); +} /* audio_init_recording */ + +void audio_close_recording(void) +{ + pcm_thread_wait_for_stop(); + pcm_thread_sync_post(PCMREC_CLOSE, NULL); + /* reset pcm to defaults (playback only) */ + pcm_set_frequency(-1); + pcm_set_monitor(-1); + pcm_set_rec_source(-1); +#ifdef HAVE_TLV320 + /* tlv320 screeches if left at 88.2 with no inputs */ + pcm_apply_settings(true); +#endif + audio_remove_encoder(); +} /* audio_close_recording */ unsigned long audio_recorded_time(void) { - if (is_recording) - { - if (is_paused) - return pause_start_time - record_start_time; - else - return current_tick - record_start_time; - } - + if (!is_recording || enc_sample_rate == 0) return 0; -} + + /* return actual recorded time a la encoded data even if encoder rate + doesn't match the pcm rate */ + return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate); +} /* audio_recorded_time */ unsigned long audio_num_recorded_bytes(void) { - if (is_recording) - return num_rec_bytes; + if (!is_recording) + return 0; - return 0; -} - -#ifdef HAVE_SPDIF_IN -/* Only the last six of these are standard rates, but all sample rates are - * possible, so we support some other common ones as well. - */ -static unsigned long spdif_sample_rates[] = { - 8000, 11025, 12000, 16000, 22050, 24000, - 32000, 44100, 48000, 64000, 88200, 96000 -}; - -/* Return SPDIF sample rate. Since we base our reading on the actual SPDIF - * sample rate (which might be a bit inaccurate), we round off to the closest - * sample rate that is supported by SPDIF. - */ -unsigned long audio_get_spdif_sample_rate(void) -{ - int i = 0; - unsigned long measured_rate; - const int upper_bound = sizeof(spdif_sample_rates)/sizeof(long) - 1; + return num_rec_bytes; +} /* audio_num_recorded_bytes */ +#ifdef HAVE_SPDIF_IN +/* Return current SPDIF sample rate */ +static unsigned long measure_spdif_sample_rate(void) +{ /* The following formula is specified in MCF5249 user's manual section - * 17.6.1. The 3*(1 << 13) part will need changing if the setup of the - * PHASECONFIG register is ever changed. The 128 divide is because of the - * fact that the SPDIF clock is the sample rate times 128. + * 17.6.1. The 128 divide is because of the fact that the SPDIF clock is + * the sample rate times 128. Keep "3*(1 << 13)" part in sync with + * PHASECONFIG setup in pcm_init_recording in pcm-coldfire.c. */ - measured_rate = (unsigned long)((unsigned long long)FREQMEAS*CPU_FREQ/ + return (unsigned long)((unsigned long long)FREQMEAS*CPU_FREQ / ((1 << 15)*3*(1 << 13))/128); +} /* measure_spdif_sample_rate */ + +/** + * Return SPDIF sample rate index in audio_master_sampr_list. Since we base + * our reading on the actual SPDIF sample rate (which might be a bit + * inaccurate), we round off to the closest sample rate that is supported by + * SPDIF. + */ +int audio_get_spdif_sample_rate(void) +{ + unsigned long measured_rate = measure_spdif_sample_rate(); /* Find which SPDIF sample rate we're closest to. */ - while (spdif_sample_rates[i] < measured_rate && i < upper_bound) ++i; - if (i > 0 && i < upper_bound) - { - long diff1 = measured_rate - spdif_sample_rates[i - 1]; - long diff2 = spdif_sample_rates[i] - measured_rate; + return round_value_to_list32(measured_rate, audio_master_sampr_list, + SAMPR_NUM_FREQ, false); +} /* audio_get_spdif_sample_rate */ - if (diff2 > diff1) --i; - } - return i; -} -#endif - -#if 0 -/* not needed atm */ #ifdef HAVE_SPDIF_POWER static bool spdif_power_setting; void audio_set_spdif_power_setting(bool on) { spdif_power_setting = on; -} -#endif -#endif +} /* audio_set_spdif_power_setting */ -/** - * Sets recording parameters - * - * This functions starts feeding the CPU with audio data over the I2S bus - */ -void audio_set_recording_options(int frequency, int quality, - int source, int channel_mode, - bool editable, int prerecord_time) +bool audio_get_spdif_power_setting(void) { - /* TODO: */ - (void)editable; - - /* NOTE: Coldfire UDA based recording does not yet support anything other - * than 44.1kHz sampling rate, so we limit it to that case here now. SPDIF - * based recording will overwrite this value with the proper sample rate in - * audio_record(), and will not be affected by this. - */ - frequency = 44100; - enc_quality = quality; - rec_source = source; - enc_channels = channel_mode == CHN_MODE_MONO ? 1 : 2; - pre_record_ticks = prerecord_time * HZ; - - switch (source) - { - case AUDIO_SRC_MIC: - case AUDIO_SRC_LINEIN: -#ifdef HAVE_FMRADIO_IN - case AUDIO_SRC_FMRADIO: + return spdif_power_setting; +} /* audio_get_spdif_power_setting */ #endif - /* Generate int. when 6 samples in FIFO, PDIR2 src = IIS1recv */ - DATAINCONTROL = 0xc020; - break; -#ifdef HAVE_SPDIF_IN - case AUDIO_SRC_SPDIF: - /* Int. when 6 samples in FIFO. PDIR2 source = ebu1RcvData */ - DATAINCONTROL = 0xc038; - break; -#endif /* HAVE_SPDIF_IN */ - } +void audio_spdif_set_monitor(int monitor_spdif) +{ + EBU1CONFIG = 0x800; /* Reset before reprogram */ - sample_rate = frequency; - - /* Monitoring: route the signals through the coldfire audio interface. */ - - SET_IIS_PLAY(0x800); /* Reset before reprogram */ - -#ifdef HAVE_SPDIF_IN - if (source == AUDIO_SRC_SPDIF) + if (monitor_spdif > 0) { - /* SCLK2 = Audioclk/4 (can't use EBUin clock), TXSRC = EBU1rcv, 64 bclk/wclk */ - IIS2CONFIG = (6 << 12) | (7 << 8) | (4 << 2); - /* S/PDIF feed-through already configured */ +#ifdef HAVE_SPDIF_POWER + EBU1CONFIG = spdif_power_setting ? (1 << 2) : 0; + /* Input source is EBUin1, Feed-through monitoring if desired */ +#else + EBU1CONFIG = (1 << 2); + /* Input source is EBUin1, Feed-through monitoring */ +#endif } - else + else if (monitor_spdif == 0) { - /* SCLK2 follow IIS1 (UDA clock), TXSRC = IIS1rcv, 64 bclk/wclk */ - IIS2CONFIG = (8 << 12) | (4 << 8) | (4 << 2); - - EBU1CONFIG = 0x800; /* Reset before reprogram */ /* SCLK2, TXSRC = IIS1recv, validity, normal operation */ EBU1CONFIG = (7 << 12) | (4 << 8) | (1 << 5) | (5 << 2); } -#else - /* SCLK2 follow IIS1 (UDA clock), TXSRC = IIS1rcv, 64 bclk/wclk */ - SET_IIS_PLAY( (8 << 12) | (4 << 8) | (4 << 2) ); +} /* audio_spdif_set_monitor */ + +#endif /* HAVE_SPDIF_IN */ + +/** + * Sets recording parameters + */ +void audio_set_recording_options(struct audio_recording_options *options) +{ + pcm_thread_wait_for_stop(); + + /* stop DMA transfer */ + dma_lock = true; + pcm_stop_recording(); + + rec_frequency = options->rec_frequency; + rec_source = options->rec_source; + num_channels = options->rec_channels == 1 ? 1 : 2; + pre_record_ticks = options->rec_prerecord_time * HZ; + enc_config = options->enc_config; + enc_config.afmt = rec_format_afmt[enc_config.rec_format]; + +#ifdef HAVE_SPDIF_IN + if (rec_source == AUDIO_SRC_SPDIF) + { + /* must measure SPDIF sample rate before configuring codecs */ + unsigned long sr = measure_spdif_sample_rate(); + /* round to master list for SPDIF rate */ + int index = round_value_to_list32(sr, audio_master_sampr_list, + SAMPR_NUM_FREQ, false); + sample_rate = audio_master_sampr_list[index]; + /* round to HW playback rates for monitoring */ + index = round_value_to_list32(sr, hw_freq_sampr, + HW_NUM_FREQ, false); + pcm_set_frequency(hw_freq_sampr[index]); + /* encoders with a limited number of rates do their own rounding */ + } + else #endif + { + /* set sample rate from frequency selection */ + sample_rate = rec_freq_sampr[rec_frequency]; + pcm_set_frequency(sample_rate); + } - audio_load_encoder(rec_quality_info_afmt[quality]); + pcm_set_monitor(rec_source); + pcm_set_rec_source(rec_source); + + /* apply pcm settings to hardware */ + pcm_apply_settings(true); + + if (audio_load_encoder(enc_config.afmt)) + { + /* start DMA transfer */ + pcm_record_data(pcm_rec_have_more, NULL, 0); + /* do unlock after starting to prevent preincrement of dma_wr_pos */ + dma_lock = pre_record_ticks == 0; + } + else + { + logf("set rec opt: enc load failed"); + is_error = true; } - +} /* audio_set_recording_options */ /** * Note that microphone is mono, only left value is used @@ -391,8 +488,7 @@ void audio_set_recording_gain(int left, int right, int type) #elif defined (HAVE_TLV320) tlv320_set_recvol(left, right, type); #endif -} - +} /* audio_set_recording_gain */ /** * Start recording @@ -401,585 +497,882 @@ void audio_set_recording_gain(int left, int right, int type) */ void audio_record(const char *filename) { - if (is_recording) - { - logf("record while recording"); - return; - } - - strncpy(recording_filename, filename, MAX_PATH - 1); - recording_filename[MAX_PATH - 1] = 0; + logf("audio_record: %s", filename); -#ifdef HAVE_SPDIF_IN - if (rec_source == AUDIO_SRC_SPDIF) - sample_rate = audio_get_spdif_sample_rate(); -#endif - - record_done = false; - queue_post(&pcmrec_queue, PCMREC_START, 0); - - while(!record_done) - sleep_thread(1); -} + pcm_thread_wait_for_stop(); + pcm_thread_sync_post(PCMREC_START, (void *)filename); + logf("audio_record_done"); +} /* audio_record */ void audio_new_file(const char *filename) { - logf("pcm_new_file"); - - new_file_done = false; - - strncpy(recording_filename, filename, MAX_PATH - 1); - recording_filename[MAX_PATH - 1] = 0; - - queue_post(&pcmrec_queue, PCMREC_NEW_FILE, 0); - - while(!new_file_done) - sleep_thread(1); - - logf("pcm_new_file done"); -} + logf("audio_new_file: %s", filename); + + pcm_thread_wait_for_stop(); + pcm_thread_sync_post(PCMREC_NEW_FILE, (void *)filename); + + logf("audio_new_file done"); +} /* audio_new_file */ -/** - * - */ void audio_stop_recording(void) { - if (!is_recording) - return; + logf("audio_stop_recording"); - logf("pcm_stop"); - - is_paused = true; /* fix pcm write ptr at current position */ - stop_done = false; - queue_post(&pcmrec_queue, PCMREC_STOP, 0); + pcm_thread_wait_for_stop(); - while(!stop_done) - sleep_thread(1); + if (is_recording) + dma_lock = true; /* fix DMA write ptr at current position */ - logf("pcm_stop done"); -} + pcm_thread_sync_post(PCMREC_STOP, NULL); + + logf("audio_stop_recording done"); +} /* audio_stop_recording */ void audio_pause_recording(void) { - if (!is_recording) - { - logf("pause when not recording"); - return; - } - if (is_paused) - { - logf("pause when paused"); - return; - } + logf("audio_pause_recording"); - pause_done = false; - queue_post(&pcmrec_queue, PCMREC_PAUSE, 0); - - while(!pause_done) - sleep_thread(1); -} + pcm_thread_wait_for_stop(); + if (is_recording) + dma_lock = true; /* fix DMA write ptr at current position */ + + pcm_thread_sync_post(PCMREC_PAUSE, NULL); + logf("audio_pause_recording done"); +} /* audio_pause_recording */ + void audio_resume_recording(void) { - if (!is_paused) + logf("audio_resume_recording"); + + pcm_thread_wait_for_stop(); + pcm_thread_sync_post(PCMREC_RESUME, NULL); + + logf("audio_resume_recording done"); +} /* audio_resume_recording */ + +/***************************************************************************/ +/* */ +/* Functions that execute in the context of pcmrec_thread */ +/* */ +/***************************************************************************/ + +/** Filename Queue **/ + +/* returns true if the queue is empty */ +static inline bool pcmrec_fnq_is_empty(void) +{ + return fnq_rd_pos == fnq_wr_pos; +} /* pcmrec_fnq_is_empty */ + +/* empties the filename queue */ +static inline void pcmrec_fnq_set_empty(void) +{ + fnq_rd_pos = fnq_wr_pos; +} /* pcmrec_fnq_set_empty */ + +/* returns true if the queue is full */ +static bool pcmrec_fnq_is_full(void) +{ + ssize_t size = fnq_wr_pos - fnq_rd_pos; + if (size < 0) + size += fnq_size; + + return size >= fnq_size - MAX_PATH; +} /* pcmrec_fnq_is_full */ + +/* queue another filename - will overwrite oldest one if full */ +static bool pcmrec_fnq_add_filename(const char *filename) +{ + strncpy(fn_queue + fnq_wr_pos, filename, MAX_PATH); + + if ((fnq_wr_pos += MAX_PATH) >= fnq_size) + fnq_wr_pos = 0; + + if (fnq_rd_pos != fnq_wr_pos) + return true; + + /* queue full */ + if ((fnq_rd_pos += MAX_PATH) >= fnq_size) + fnq_rd_pos = 0; + + return true; +} /* pcmrec_fnq_add_filename */ + +/* replace the last filename added */ +static bool pcmrec_fnq_replace_tail(const char *filename) +{ + int pos; + + if (pcmrec_fnq_is_empty()) + return false; + + pos = fnq_wr_pos - MAX_PATH; + if (pos < 0) + pos = fnq_size - MAX_PATH; + + strncpy(fn_queue + pos, filename, MAX_PATH); + + return true; +} /* pcmrec_fnq_replace_tail */ + +/* pulls the next filename from the queue */ +static bool pcmrec_fnq_get_filename(char *filename) +{ + if (pcmrec_fnq_is_empty()) + return false; + + if (filename) + strncpy(filename, fn_queue + fnq_rd_pos, MAX_PATH); + + if ((fnq_rd_pos += MAX_PATH) >= fnq_size) + fnq_rd_pos = 0; + + return true; +} /* pcmrec_fnq_get_filename */ + +/* close the file number pointed to by fd_p */ +static void pcmrec_close_file(int *fd_p) +{ + if (*fd_p < 0) + return; /* preserve error */ + + close(*fd_p); + *fd_p = -1; +} /* pcmrec_close_file */ + +/** Data Flushing **/ + +/** + * called after callback to update sizes if codec changed the amount of data + * a chunk represents + */ +static inline void pcmrec_update_sizes_inl(size_t prev_enc_size, + unsigned long prev_num_pcm) +{ + if (rec_fdata.new_enc_size != prev_enc_size) { - logf("resume when not paused"); - return; + ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size; + num_rec_bytes += size_diff; + accum_rec_bytes += size_diff; + } + + if (rec_fdata.new_num_pcm != prev_num_pcm) + { + unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm; + num_rec_samples += pcm_diff; + accum_pcm_samples += pcm_diff; + } +} /* pcmrec_update_sizes_inl */ + +/* don't need to inline every instance */ +static void pcmrec_update_sizes(size_t prev_enc_size, + unsigned long prev_num_pcm) +{ + pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm); +} /* pcmrec_update_sizes */ + +static void pcmrec_start_file(void) +{ + size_t enc_size = rec_fdata.new_enc_size; + unsigned long num_pcm = rec_fdata.new_num_pcm; + int curr_rec_file = rec_fdata.rec_file; + char filename[MAX_PATH]; + + /* must always pull the filename that matches with this queue */ + if (!pcmrec_fnq_get_filename(filename)) + { + logf("start file: fnq empty"); + *filename = '\0'; + is_error = true; + } + else if (is_error) + { + logf("start file: is_error already"); + } + else if (curr_rec_file >= 0) + { + /* Any previous file should have been closed */ + logf("start file: file already open"); + is_error = true; } - resume_done = false; - queue_post(&pcmrec_queue, PCMREC_RESUME, 0); + if (is_error) + rec_fdata.chunk->flags |= CHUNKF_ERROR; - while(!resume_done) - sleep_thread(1); -} + /* encoder can set error flag here and should increase + enc_new_size and pcm_new_size to reflect additional + data written if any */ + rec_fdata.filename = filename; + enc_events_callback(ENC_START_FILE, &rec_fdata); -/* return peaks as int, so convert from short first - note that peak values are always positive */ -void pcm_rec_get_peaks(int *left, int *right) + if (!is_error && (rec_fdata.chunk->flags & CHUNKF_ERROR)) + { + logf("start file: enc error"); + is_error = true; + } + + if (is_error) + { + pcmrec_close_file(&curr_rec_file); + /* Write no more to this file */ + rec_fdata.chunk->flags |= CHUNKF_END_FILE; + } + else + { + pcmrec_update_sizes(enc_size, num_pcm); + } + + rec_fdata.chunk->flags &= ~CHUNKF_START_FILE; +} /* pcmrec_start_file */ + +static inline void pcmrec_write_chunk(void) { - if (left) - *left = peak_left; - if (right) - *right = peak_right; - peak_left = 0; - peak_right = 0; -} + size_t enc_size = rec_fdata.new_enc_size; + unsigned long num_pcm = rec_fdata.new_num_pcm; -/***************************************************************************/ -/* Functions that executes in the context of pcmrec_thread */ -/***************************************************************************/ + if (is_error) + rec_fdata.chunk->flags |= CHUNKF_ERROR; + + enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata); + + if ((long)rec_fdata.chunk->flags >= 0) + { + pcmrec_update_sizes_inl(enc_size, num_pcm); + } + else if (!is_error) + { + logf("wr chk enc error %d %d", + rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm); + is_error = true; + } +} /* pcmrec_write_chunk */ + +static void pcmrec_end_file(void) +{ + /* all data in output buffer for current file will have been + written and encoder can now do any nescessary steps to + finalize the written file */ + size_t enc_size = rec_fdata.new_enc_size; + unsigned long num_pcm = rec_fdata.new_num_pcm; + + enc_events_callback(ENC_END_FILE, &rec_fdata); + + if (!is_error) + { + if (rec_fdata.chunk->flags & CHUNKF_ERROR) + { + logf("end file: enc error"); + is_error = true; + } + else + { + pcmrec_update_sizes(enc_size, num_pcm); + } + } + + /* Force file close if error */ + if (is_error) + pcmrec_close_file(&rec_fdata.rec_file); + + rec_fdata.chunk->flags &= ~CHUNKF_END_FILE; +} /* pcmrec_end_file */ /** * Process the chunks * * This function is called when queue_get_w_tmo times out. * - * Other functions can also call this function with flush = true when - * they want to save everything in the buffers to disk. + * Set flush_num to the number of files to flush to disk. + * flush_num = -1 to flush all available chunks to disk. + * flush_num = 0 normal write thresholding + * flush_num = 1 or greater - all available chunks of current file plus + * flush_num file starts if first chunk has been processed. * */ -static void pcmrec_callback(bool flush) +static void pcmrec_flush(unsigned flush_num) { - int i, num_ready, size_yield; - long *enc_chunk, chunk_size; - - if (!is_recording && !flush) - return; + static unsigned long last_flush_tick = 0; + unsigned long start_tick; + int num_ready, num; + int prio; + int i; num_ready = enc_wr_index - enc_rd_index; if (num_ready < 0) num_ready += enc_num_chunks; - /* calculate an estimate of recorded bytes */ - num_rec_bytes = num_file_bytes + num_ready * /* enc_chunk_size */ - ((avrg_bit_rate * 1000 / 8 * enc_samp_per_chunk + 22050) / 44100); + num = num_ready; - /* near full state reached: less than 5sec remaining space */ - if (enc_num_chunks - num_ready < WRITE_THRESHOLD || flush) + if (flush_num == 0) { - logf("writing: %d (%d)", num_ready, flush); - - cpu_boost_id(true, CPUBOOSTID_PCMRECORD); + if (!is_recording) + return; - size_yield = 0; - for (i=0; i limit */ - if (chunk_size > (long)(enc_chunk_size - sizeof(long))) - chunk_size = enc_chunk_size - sizeof(long); + /* write at 5s + st remaining in enc_buffer */ + if (st < 2*HZ) + st = 2*HZ; /* my drive is usually < 250 ticks :) */ + else if (st > 10*HZ) + st = 10*HZ; - if (enc_set_header_callback != NULL) - enc_set_header_callback(enc_chunk, enc_chunk_size, - num_pcm_samples, false); + write_threshold = enc_num_chunks - + (int)(((5ull*HZ + st)*4ull*sample_rate + (enc_chunk_size-1)) / + (enc_chunk_size*HZ)); - if (write(wav_file, enc_chunk, chunk_size) != chunk_size) - { - close_wave(); - logf("pcmrec: write err"); - is_error = true; - break; - } + if (write_threshold < 0) + write_threshold = 0; + else if (write_threshold > panic_threshold) + write_threshold = panic_threshold; - num_file_bytes += chunk_size; - num_pcm_samples += enc_samp_per_chunk; - size_yield += chunk_size; - - if (size_yield >= 32768) - { /* yield when 32kB written */ - size_yield = 0; - yield(); - } - - enc_rd_index = (enc_rd_index + 1) % enc_num_chunks; + logf("new wr thresh: %d", write_threshold); } + if (num_ready < write_threshold) + return; + + /* if we're getting called too much and this isn't forced, + boost stat */ + if (current_tick - last_flush_tick < HZ/2) + num = panic_threshold; + } + + start_tick = current_tick; + prio = -1; + + logf("writing: %d (%d)", num_ready, flush_num); + + cpu_boost_id(true, CPUBOOSTID_PCMRECORD); + + for (i=0; i= panic_threshold || + current_tick - start_tick > 10*HZ)) + { + /* losing ground - boost priority until finished */ + logf("pcmrec: boost priority"); + prio = thread_set_priority(NULL, thread_get_priority(NULL)-1); + } + + rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index); + rec_fdata.new_enc_size = rec_fdata.chunk->enc_size; + rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm; + + if (rec_fdata.chunk->flags & CHUNKF_START_FILE) + { + pcmrec_start_file(); + if (--flush_num == 0) + i = num_ready; /* stop on next loop - must write this + chunk if it has data */ + } + + pcmrec_write_chunk(); + + if (rec_fdata.chunk->flags & CHUNKF_END_FILE) + pcmrec_end_file(); + + INC_ENC_INDEX(enc_rd_index); + + if (is_error) + break; + + if (prio == -1) + { + num = enc_wr_index - enc_rd_index; + if (num < 0) + num += enc_num_chunks; + } + + /* no yielding, the file apis called in the codecs do that */ + } /* end for */ + /* sync file */ - fsync(wav_file); + if (rec_fdata.rec_file >= 0) + fsync(rec_fdata.rec_file); cpu_boost_id(false, CPUBOOSTID_PCMRECORD); - logf("done"); + if (prio != -1) + { + /* return to original priority */ + logf("pcmrec: unboost priority"); + thread_set_priority(NULL, prio); } -} -/* Abort dma transfer */ -static void pcmrec_dma_stop(void) + last_flush_tick = current_tick; /* save tick when we left */ + logf("done"); +} /* pcmrec_flush */ + +/** + * Marks a new stream in the buffer and gives the encoder a chance for special + * handling of transition from one to the next. The encoder may change the + * chunk that ends the old stream by requesting more chunks and similiarly for + * the new but must always advance the position though the interface. It can + * later reject any data it cares to when writing the file but should mark the + * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept + * a NULL data pointer without error as well. + */ +static void pcmrec_new_stream(const char *filename, /* next file name */ + unsigned long flags, /* CHUNKF_* flags */ + int pre_index) /* index for prerecorded data */ { - DCR1 = 0; + logf("pcmrec_new_stream"); - error_count++; + struct enc_buffer_event_data data; + bool (*fnq_add_fn)(const char *) = NULL; + struct enc_chunk_hdr *start = NULL; - DSR1 = 1; /* Clear interrupt */ - IPR |= (1<<15); /* Clear pending interrupt request */ - - logf("dma1 stopped"); -} - -static void pcmrec_dma_start(void) -{ - DAR1 = (unsigned long)GET_CHUNK(write_pos); /* Destination address */ - SAR1 = (unsigned long)&PDIR2; /* Source address */ - BCR1 = CHUNK_SIZE; /* Bytes to transfer */ - - /* Start the DMA transfer.. */ -#ifdef HAVE_SPDIF_IN - INTERRUPTCLEAR = 0x03c00000; -#endif - - /* 16Byte transfers prevents from sporadic errors during cpu_boost() */ - DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_DSIZE(3) | DMA_START; - - logf("dma1 started"); -} - -/* DMA1 Interrupt is called when the DMA has finished transfering a chunk */ -void DMA1(void) __attribute__ ((interrupt_handler, section(".icode"))); -void DMA1(void) -{ - int res = DSR1; - - DSR1 = 1; /* Clear interrupt */ - - if (res & 0x70) + int get_chunk_index(struct enc_chunk_hdr *chunk) { - DCR1 = 0; /* Stop DMA transfer */ - error_count++; - - logf("dma1 err: 0x%x", res); - - DAR1 = (unsigned long)GET_CHUNK(write_pos); /* Destination address */ - BCR1 = CHUNK_SIZE; - DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START; - - /* Flush recorded data to disk and stop recording */ - queue_post(&pcmrec_queue, PCMREC_STOP, NULL); - } -#ifdef HAVE_SPDIF_IN - else if ((rec_source == AUDIO_SRC_SPDIF) && - (INTERRUPTSTAT & 0x01c00000)) /* valnogood, symbolerr, parityerr */ - { - INTERRUPTCLEAR = 0x03c00000; - error_count++; - - logf("spdif err"); - - DAR1 = (unsigned long)GET_CHUNK(write_pos); /* Destination address */ - BCR1 = CHUNK_SIZE; + return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size; } -#endif - else + + struct enc_chunk_hdr * get_prev_chunk(int index) { - long peak_l, peak_r; - long *ptr, j; + DEC_ENC_INDEX(index); + return GET_ENC_CHUNK(index); + } - ptr = GET_CHUNK(write_pos); + data.pre_chunk = NULL; + data.chunk = GET_ENC_CHUNK(enc_wr_index); - if (!is_paused) /* advance write position */ - write_pos = (write_pos + CHUNK_SIZE) & CHUNK_MASK; + /* end chunk */ + if (flags & CHUNKF_END_FILE) + { + data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE; - DAR1 = (unsigned long)GET_CHUNK(write_pos); /* Destination address */ - BCR1 = CHUNK_SIZE; - - peak_l = peak_r = 0; - - /* only peak every 4th sample */ - for (j=0; jflags & CHUNKF_START_FILE) { - long value = ptr[j]; -#ifdef ROCKBOX_BIG_ENDIAN - if (value > peak_l) peak_l = value; - else if (-value > peak_l) peak_l = -value; + /* cannot start and end on same unprocessed chunk */ + logf("file end on start"); + flags &= ~CHUNKF_END_FILE; + } + else if (enc_rd_index == enc_wr_index) + { + /* all data flushed but file not ended - chunk will be left + empty */ + logf("end on dead end"); + data.chunk->flags = 0; + data.chunk->enc_size = 0; + data.chunk->num_pcm = 0; + data.chunk->enc_data = NULL; + INC_ENC_INDEX(enc_wr_index); + data.chunk = GET_ENC_CHUNK(enc_wr_index); + } + else + { + struct enc_chunk_hdr *last = get_prev_chunk(enc_wr_index); - value <<= 16; - if (value > peak_r) peak_r = value; - else if (-value > peak_r) peak_r = -value; -#else - if (value > peak_r) peak_r = value; - else if (-value > peak_r) peak_r = -value; + if (last->flags & CHUNKF_END_FILE) + { + /* end already processed and marked - can't end twice */ + logf("file end again"); + flags &= ~CHUNKF_END_FILE; + } + } + } - value <<= 16; - if (value > peak_l) peak_l = value; - else if (-value > peak_l) peak_l = -value; -#endif + /* start chunk */ + if (flags & CHUNKF_START_FILE) + { + bool pre = flags & CHUNKF_PRERECORD; + + if (pre) + { + logf("stream prerecord start"); + start = data.pre_chunk = GET_ENC_CHUNK(pre_index); + start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD; + } + else + { + logf("stream normal start"); + start = data.chunk; + start->flags &= CHUNKF_START_FILE; } - peak_left = (int)(peak_l >> 16); - peak_right = (int)(peak_r >> 16); + /* if encoder hasn't yet processed the last start - abort the start + of the previous file queued or else it will be empty and invalid */ + if (start->flags & CHUNKF_START_FILE) + { + logf("replacing fnq tail: %s", filename); + fnq_add_fn = pcmrec_fnq_replace_tail; + } + else + { + logf("adding filename: %s", filename); + fnq_add_fn = pcmrec_fnq_add_filename; + } } - IPR |= (1<<15); /* Clear pending interrupt request */ -} + data.flags = flags; + enc_events_callback(ENC_REC_NEW_STREAM, &data); -/* Create WAVE file and write header */ -/* Sets returns 0 if success, -1 on failure */ -static int start_wave(void) -{ - wav_file = open(recording_filename, O_RDWR|O_CREAT|O_TRUNC); - - if (wav_file < 0) + if (flags & CHUNKF_END_FILE) { - wav_file = -1; - logf("rec: create failed: %d", wav_file); - is_error = true; - return -1; + int i = get_chunk_index(data.chunk); + get_prev_chunk(i)->flags |= CHUNKF_END_FILE; } + + if (start) + { + if (!(flags & CHUNKF_PRERECORD)) + { + /* get stats on data added to start - sort of a prerecord operation */ + int i = get_chunk_index(data.chunk); + struct enc_chunk_hdr *chunk = data.chunk; + + logf("start data: %d %d", i, enc_wr_index); + + num_rec_bytes = 0; + num_rec_samples = 0; + + while (i != enc_wr_index) + { + num_rec_bytes += chunk->enc_size; + num_rec_samples += chunk->num_pcm; + INC_ENC_INDEX(i); + chunk = GET_ENC_CHUNK(i); + } + + start->flags &= ~CHUNKF_START_FILE; + start = data.chunk; + } + + start->flags |= CHUNKF_START_FILE; + + /* flush one file out if full and adding */ + if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full()) + { + logf("fnq full: flushing 1"); + pcmrec_flush(1); + } - /* add main file header (enc_head_size=0 for encoders without) */ - if (enc_head_size != write(wav_file, enc_head_buffer, enc_head_size)) - { - close(wav_file); - wav_file = -1; - logf("rec: write failed"); - is_error = true; - return -1; + fnq_add_fn(filename); } +} /* pcmrec_new_stream */ - return 0; -} +/** event handlers for pcmrec thread */ -/* Update header and set correct length values */ -static void close_wave(void) +/* PCMREC_INIT */ +static void pcmrec_init(void) { - unsigned char head[100]; /* assume maximum 100 bytes for file header */ - int size_read; + rec_fdata.rec_file = -1; - if (wav_file != -1) - { - /* update header before closing the file (wav+wv encoder will do) */ - if (enc_set_header_callback != NULL) - { - lseek(wav_file, 0, SEEK_SET); - /* try to read the head size (but we'll accept less) */ - size_read = read(wav_file, head, sizeof(head)); + /* pcm FIFO */ + dma_lock = true; + pcm_rd_pos = 0; + dma_wr_pos = 0; - enc_set_header_callback(head, size_read, num_pcm_samples, true); - lseek(wav_file, 0, SEEK_SET); - write(wav_file, head, size_read); - } - close(wav_file); - wav_file = -1; - } -} + /* encoder FIFO */ + enc_wr_index = 0; + enc_rd_index = 0; -static void pcmrec_start(void) + /* filename queue */ + fnq_rd_pos = 0; + fnq_wr_pos = 0; + + /* stats */ + num_rec_bytes = 0; + num_rec_samples = 0; + accum_rec_bytes = 0; + accum_pcm_samples = 0; + + pcm_thread_unsignal_event(PCMREC_CLOSE); + is_recording = false; + is_paused = false; + is_stopping = false; + is_error = false; + + pcm_buffer = audio_get_recording_buffer(&rec_buffer_size); + /* Line align pcm_buffer 2^4=16 bytes */ + pcm_buffer = (unsigned char *)ALIGN_UP_P2((unsigned)pcm_buffer, 4); + enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE + + PCM_MAX_FEED_SIZE, 2); + + pcm_init_recording(); + pcm_thread_signal_event(PCMREC_INIT); +} /* pcmrec_init */ + +/* PCMREC_CLOSE */ +static void pcmrec_close(void) { - long max_pre_chunks, pre_ticks, max_pre_ticks; + dma_lock = true; + pcm_close_recording(); + pcm_thread_unsignal_event(PCMREC_INIT); + pcm_thread_signal_event(PCMREC_CLOSE); +} /* pcmrec_close */ - logf("pcmrec_start"); +/* PCMREC_START */ +static void pcmrec_start(const char *filename) +{ + unsigned long pre_sample_ticks; + int rd_start; + + logf("pcmrec_start: %s", filename); if (is_recording) { logf("already recording"); - record_done = true; - return; + goto already_recording; } - if (wav_file != -1) - close_wave(); + /* reset stats */ + num_rec_bytes = 0; + num_rec_samples = 0; + accum_rec_bytes = 0; + accum_pcm_samples = 0; + spinup_time = -1; - if (start_wave() != 0) + rd_start = enc_wr_index; + pre_sample_ticks = 0; + + if (pre_record_ticks) { - /* failed to create the file */ - record_done = true; - return; + int i; + + /* calculate number of available chunks */ + unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index + + enc_num_chunks) % enc_num_chunks; + /* overflow at 974 seconds of prerecording at 44.1kHz */ + unsigned long pre_record_sample_ticks = enc_sample_rate*pre_record_ticks; + + /* Get exact measure of recorded data as number of samples aren't + nescessarily going to be the max for each chunk */ + for (i = rd_start; avail_pre_chunks-- > 0;) + { + struct enc_chunk_hdr *chunk; + unsigned long chunk_sample_ticks; + + DEC_ENC_INDEX(i); + + chunk = GET_ENC_CHUNK(i); + + /* must have data to be counted */ + if (chunk->enc_data == NULL) + continue; + + chunk_sample_ticks = chunk->num_pcm*HZ; + + rd_start = i; + pre_sample_ticks += chunk_sample_ticks; + num_rec_bytes += chunk->enc_size; + num_rec_samples += chunk->num_pcm; + + /* stop here if enough already */ + if (pre_sample_ticks >= pre_record_sample_ticks) + break; + } + + accum_rec_bytes = num_rec_bytes; + accum_pcm_samples = num_rec_samples; } - /* calculate maximum available chunks & resulting ticks */ - max_pre_chunks = (enc_wr_index - enc_rd_index + - enc_num_chunks) % enc_num_chunks; - if (max_pre_chunks > enc_num_chunks - WRITE_THRESHOLD) - max_pre_chunks = enc_num_chunks - WRITE_THRESHOLD; - max_pre_ticks = max_pre_chunks * HZ * enc_samp_per_chunk / 44100; + enc_rd_index = rd_start; - /* limit prerecord if not enough data available */ - pre_ticks = pre_record_ticks > max_pre_ticks ? - max_pre_ticks : pre_record_ticks; - max_pre_chunks = 44100 * pre_ticks / HZ / enc_samp_per_chunk; - enc_rd_index = (enc_wr_index - max_pre_chunks + - enc_num_chunks) % enc_num_chunks; - - record_start_time = current_tick - pre_ticks; - - num_rec_bytes = enc_num_chunks * CHUNK_SIZE; - num_file_bytes = 0; - num_pcm_samples = 0; - pause_start_time = 0; + /* filename queue should be empty */ + if (!pcmrec_fnq_is_empty()) + { + logf("fnq: not empty!"); + pcmrec_fnq_set_empty(); + } + dma_lock = false; is_paused = false; is_recording = true; - record_done = true; -} + pcmrec_new_stream(filename, + CHUNKF_START_FILE | + (pre_sample_ticks > 0 ? CHUNKF_PRERECORD : 0), + enc_rd_index); + +already_recording: + pcm_thread_signal_event(PCMREC_START); + logf("pcmrec_start done"); +} /* pcmrec_start */ + +/* PCMREC_STOP */ static void pcmrec_stop(void) { logf("pcmrec_stop"); - if (is_recording) - { - /* wait for encoding finish */ - is_paused = true; - while(!wav_queue_empty) - sleep_thread(1); - - is_recording = false; - - /* Flush buffers to file */ - pcmrec_callback(true); - close_wave(); - } - - is_paused = false; - stop_done = true; - - logf("pcmrec_stop done"); -} - -static void pcmrec_new_file(void) -{ - logf("pcmrec_new_file"); - if (!is_recording) { logf("not recording"); - new_file_done = true; - return; + goto not_recording_or_stopping; + } + else if (is_stopping) + { + logf("already stopping"); + goto not_recording_or_stopping; + } + + is_stopping = true; + dma_lock = true; /* lock dma write position */ + queue_post(&pcmrec_queue, PCMREC_FINISH_STOP, NULL); + +not_recording_or_stopping: + pcm_thread_signal_event(PCMREC_STOP); + logf("pcmrec_stop done"); +} /* pcmrec_stop */ + +/* PCMREC_FINISH_STOP */ +static void pcmrec_finish_stop(void) +{ + logf("pcmrec_finish_stop"); + + if (!is_stopping) + { + logf("not stopping"); + goto not_stopping; } - /* Since pcmrec_callback() blocks until the data has been written, - here is a good approximation when recording to the new file starts - */ - record_start_time = current_tick; + /* flush all available data first to avoid overflow while waiting + for encoding to finish */ + pcmrec_flush(-1); - if (is_paused) - pause_start_time = record_start_time; - - /* Flush what we got in buffers to file */ - pcmrec_callback(true); - - close_wave(); - - num_rec_bytes = 0; - num_file_bytes = 0; - num_pcm_samples = 0; - - /* start the new file */ - if (start_wave() != 0) + /* wait for encoder to finish remaining data */ + if (!is_error) { - logf("new_file failed"); - pcmrec_stop(); + while (!wav_queue_empty) + yield(); + } + + /* end stream at last data */ + pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0); + + /* flush anything else encoder added */ + pcmrec_flush(-1); + + /* remove any pending file start not yet processed - should be at + most one at enc_wr_index */ + pcmrec_fnq_get_filename(NULL); + /* encoder should abort any chunk it was in midst of processing */ + GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT; + + /* filename queue should be empty */ + if (!pcmrec_fnq_is_empty()) + { + logf("fnq: not empty!"); + pcmrec_fnq_set_empty(); } - new_file_done = true; - logf("pcmrec_new_file done"); -} + /* be absolutely sure the file is closed */ + if (is_error) + pcmrec_close_file(&rec_fdata.rec_file); + rec_fdata.rec_file = -1; + is_recording = false; + is_paused = false; + is_stopping = false; + dma_lock = pre_record_ticks == 0; + +not_stopping: + logf("pcmrec_finish_stop done"); +} /* pcmrec_finish_stop */ + +/* PCMREC_PAUSE */ static void pcmrec_pause(void) { logf("pcmrec_pause"); if (!is_recording) { - logf("pause: not recording"); - pause_done = true; - return; + logf("not recording"); + goto not_recording_or_paused; + } + else if (is_paused) + { + logf("already paused"); + goto not_recording_or_paused; } - pause_start_time = current_tick; + dma_lock = true; /* fix DMA write pointer at current position */ is_paused = true; - pause_done = true; +not_recording_or_paused: + pcm_thread_signal_event(PCMREC_PAUSE); logf("pcmrec_pause done"); -} - +} /* pcmrec_pause */ +/* PCMREC_RESUME */ static void pcmrec_resume(void) { logf("pcmrec_resume"); - if (!is_paused) + if (!is_recording) { - logf("resume: not paused"); - resume_done = true; - return; + logf("not recording"); + goto not_recording_or_not_paused; + } + else if (!is_paused) + { + logf("not paused"); + goto not_recording_or_not_paused; } is_paused = false; is_recording = true; + dma_lock = false; - /* Compensate for the time we have been paused */ - if (pause_start_time) +not_recording_or_not_paused: + pcm_thread_signal_event(PCMREC_RESUME); + logf("pcmrec_resume done"); +} /* pcmrec_resume */ + +/* PCMREC_NEW_FILE */ +static void pcmrec_new_file(const char *filename) +{ + logf("pcmrec_new_file: %s", filename); + + if (!is_recording) { - record_start_time += current_tick - pause_start_time; - pause_start_time = 0; + logf("not recording"); + goto not_recording; } - resume_done = true; - logf("pcmrec_resume done"); -} - -/** - * audio_init_recording calls this function using PCMREC_INIT - * - */ -static void pcmrec_init(void) -{ - wav_file = -1; - read_pos = 0; - write_pos = 0; - enc_wr_index = 0; - enc_rd_index = 0; - - avrg_bit_rate = 0; - curr_bit_rate = 0; - curr_chunk_cnt = 0; - - peak_left = 0; - peak_right = 0; - num_rec_bytes = 0; - num_file_bytes = 0; - num_pcm_samples = 0; - record_start_time = 0; - pause_start_time = 0; + num_rec_samples = 0; - close_done = false; - is_recording = false; - is_paused = false; - is_error = false; + pcmrec_new_stream(filename, + CHUNKF_START_FILE | CHUNKF_END_FILE, + 0); - rec_buffer = (unsigned char*)(((long)audiobuf + 15) & ~15); - enc_buffer = rec_buffer + NUM_CHUNKS * CHUNK_SIZE + MAX_FEED_SIZE; - /* 8000Bytes at audiobufend */ - enc_buffer_size = audiobufend - enc_buffer - 8000; - - SET_IIS_PLAY(0x800); /* Stop any playback */ - AUDIOGLOB |= 0x180; /* IIS1 fifo auto sync = on, PDIR2 auto sync = on */ - DATAINCONTROL = 0xc000; /* Generate Interrupt when 6 samples in fifo */ - - DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */ - DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */ - DMAROUTE = (DMAROUTE & 0xffff00ff) | DMA1_REQ_AUDIO_2; - ICR7 = 0x1c; /* Enable interrupt at level 7, priority 0 */ - IMR &= ~(1<<15); /* bit 15 is DMA1 */ - -#ifdef HAVE_SPDIF_IN - PHASECONFIG = 0x34; /* Gain = 3*2^13, source = EBUIN */ -#endif - pcmrec_dma_start(); - - init_done = 1; -} - -static void pcmrec_close(void) -{ - DMAROUTE = (DMAROUTE & 0xffff00ff); - ICR7 = 0x00; /* Disable interrupt */ - IMR |= (1<<15); /* bit 15 is DMA1 */ - - pcmrec_dma_stop(); - - /* Reset PDIR2 data flow */ - DATAINCONTROL = 0x200; - close_done = true; - init_done = false; -} +not_recording: + pcm_thread_signal_event(PCMREC_NEW_FILE); + logf("pcmrec_new_file done"); +} /* pcmrec_new_file */ +static void pcmrec_thread(void) __attribute__((noreturn)); static void pcmrec_thread(void) { struct event ev; logf("thread pcmrec start"); - error_count = 0; - while(1) { - queue_wait_w_tmo(&pcmrec_queue, &ev, HZ / 4); + if (is_recording) + { + /* Poll periodically to flush data */ + queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5); + + if (ev.id == SYS_TIMEOUT) + { + pcmrec_flush(0); /* flush if getting full */ + continue; + } + } + else + { + /* Not doing anything - sit and wait for commands */ + queue_wait(&pcmrec_queue, &ev); + } switch (ev.id) { @@ -992,13 +1385,17 @@ static void pcmrec_thread(void) break; case PCMREC_START: - pcmrec_start(); + pcmrec_start((const char *)ev.data); break; case PCMREC_STOP: pcmrec_stop(); break; + case PCMREC_FINISH_STOP: + pcmrec_finish_stop(); + break; + case PCMREC_PAUSE: pcmrec_pause(); break; @@ -1008,11 +1405,11 @@ static void pcmrec_thread(void) break; case PCMREC_NEW_FILE: - pcmrec_new_file(); + pcmrec_new_file((const char *)ev.data); break; - case SYS_TIMEOUT: - pcmrec_callback(false); + case PCMREC_FLUSH_NUM: + pcmrec_flush((unsigned)ev.data); break; case SYS_USB_CONNECTED: @@ -1023,45 +1420,9 @@ static void pcmrec_thread(void) usb_wait_for_disconnect(&pcmrec_queue); } break; - } - } - - logf("thread pcmrec done"); -} - -/* Select VINL & VINR source: 0=Line-in, 1=FM Radio */ -void pcm_rec_mux(int source) -{ -#ifdef IRIVER_H300_SERIES - if(source == 0) - and_l(~0x40000000, &GPIO_OUT); /* Line In */ - else - or_l(0x40000000, &GPIO_OUT); /* FM radio */ - - or_l(0x40000000, &GPIO_ENABLE); - or_l(0x40000000, &GPIO_FUNCTION); -#elif defined(IRIVER_H100_SERIES) - if(source == 0) - and_l(~0x00800000, &GPIO_OUT); /* Line In */ - else - or_l(0x00800000, &GPIO_OUT); /* FM radio */ - - or_l(0x00800000, &GPIO_ENABLE); - or_l(0x00800000, &GPIO_FUNCTION); - -#elif defined(IAUDIO_X5) - if(source == 0) - or_l((1<<29), &GPIO_OUT); /* Line In */ - else - and_l(~(1<<29), &GPIO_OUT); /* FM radio */ - - or_l((1<<29), &GPIO_ENABLE); - or_l((1<<29), &GPIO_FUNCTION); - - /* iAudio x5 */ -#endif -} - + } /* end switch */ + } /* end while */ +} /* pcmrec_thread */ /****************************************************************************/ /* */ @@ -1069,94 +1430,257 @@ void pcm_rec_mux(int source) /* */ /****************************************************************************/ -/* pass the encoder buffer pointer/size, mono/stereo, quality to the encoder */ -void enc_get_inputs(int *buffer_size, int *channels, int *quality) +/* pass the encoder settings to the encoder */ +void enc_get_inputs(struct enc_inputs *inputs) { - *buffer_size = enc_buffer_size; - *channels = enc_channels; - *quality = enc_quality; -} - -/* set the encoder dimensions (called by encoder codec at initialization) */ -void enc_set_parameters(int chunk_size, int num_chunks, int samp_per_chunk, - char *head_ptr, int head_size, int enc_id) + inputs->sample_rate = sample_rate; + inputs->num_channels = num_channels; + inputs->config = &enc_config; +} /* enc_get_inputs */ + +/* set the encoder dimensions (called by encoder codec at initialization and + termination) */ +void enc_set_parameters(struct enc_parameters *params) { - /* set read_pos just in front of current write_pos */ - read_pos = (write_pos - CHUNK_SIZE) & CHUNK_MASK; - - enc_rd_index = 0; /* reset */ - enc_wr_index = 0; /* reset */ - enc_chunk_size = chunk_size; /* max chunk size */ - enc_num_chunks = num_chunks; /* total number of chunks */ - enc_samp_per_chunk = samp_per_chunk; /* pcm samples / encoderchunk */ - enc_head_buffer = head_ptr; /* optional file header data (wav) */ - enc_head_size = head_size; /* optional file header data (wav) */ - audio_enc_id = enc_id; /* AFMT_* id */ -} - -/* allocate encoder chunk */ -unsigned int *enc_alloc_chunk(void) -{ - return (unsigned int*)(enc_buffer + enc_wr_index * enc_chunk_size); -} - -/* free previously allocated encoder chunk */ -void enc_free_chunk(void) -{ - unsigned long *enc_chunk; - - enc_chunk = GET_ENC_CHUNK(enc_wr_index); - curr_chunk_cnt++; -/* curr_bit_rate += *enc_chunk * 44100 * 8 / (enc_samp_per_chunk * 1000); */ - curr_bit_rate += *enc_chunk * 441 * 8 / (enc_samp_per_chunk * 10 ); - avrg_bit_rate = (curr_bit_rate + curr_chunk_cnt / 2) / curr_chunk_cnt; - - /* advance enc_wr_index to the next chunk */ - enc_wr_index = (enc_wr_index + 1) % enc_num_chunks; - - /* buffer full: advance enc_rd_index (for prerecording purpose) */ - if (enc_rd_index == enc_wr_index) + size_t bufsize, resbytes; + + logf("enc_set_parameters"); + + if (!params) { - enc_rd_index = (enc_rd_index + 1) % enc_num_chunks; + logf("reset"); + /* Encoder is terminating */ + memset(&enc_config, 0, sizeof (enc_config)); + enc_sample_rate = 0; + return; } -} -/* checks near empty state on wav input buffer */ -int enc_wavbuf_near_empty(void) + enc_sample_rate = params->enc_sample_rate; + logf("enc sampr:%d", enc_sample_rate); + + pcm_rd_pos = dma_wr_pos; + + enc_config.afmt = params->afmt; + /* addition of the header is always implied - chunk size 4-byte aligned */ + enc_chunk_size = + ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2); + enc_data_size = enc_chunk_size - ENC_CHUNK_HDR_SIZE; + enc_events_callback = params->events_callback; + + logf("chunk size:%d", enc_chunk_size); + + /*** Configure the buffers ***/ + + /* Layout of recording buffer: + * [ax] = possible alignment x multiple + * [sx] = possible size alignment of x multiple + * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|-> + * |[[s4]:Reserved Bytes]|Filename Queue->|[space]| + */ + resbytes = ALIGN_UP_P2(params->reserve_bytes, 2); + logf("resbytes:%d", resbytes); + + bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) - + resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH; + + enc_num_chunks = bufsize / enc_chunk_size; + logf("num chunks:%d", enc_num_chunks); + + /* get real amount used by encoder chunks */ + bufsize = enc_num_chunks*enc_chunk_size; + logf("enc size:%d", bufsize); + + /* panic boost thread priority at 1 second remaining */ + panic_threshold = enc_num_chunks - + (4*sample_rate + (enc_chunk_size-1)) / enc_chunk_size; + if (panic_threshold < 0) + panic_threshold = 0; + + logf("panic thr:%d", panic_threshold); + + /** set OUT parameters **/ + params->enc_buffer = enc_buffer; + params->buf_chunk_size = enc_chunk_size; + params->num_chunks = enc_num_chunks; + + /* calculate reserve buffer start and return pointer to encoder */ + params->reserve_buffer = NULL; + if (resbytes > 0) + { + params->reserve_buffer = enc_buffer + bufsize; + bufsize += resbytes; + } + + /* place filename queue at end of buffer using up whatever remains */ + fnq_rd_pos = 0; /* reset */ + fnq_wr_pos = 0; /* reset */ + fn_queue = enc_buffer + bufsize; + fnq_size = pcm_buffer + rec_buffer_size - fn_queue; + fnq_size = ALIGN_DOWN(fnq_size, MAX_PATH); + logf("fnq files: %d", fnq_size / MAX_PATH); + +#if 0 + logf("ab :%08X", (unsigned long)audiobuf); + logf("pcm:%08X", (unsigned long)pcm_buffer); + logf("enc:%08X", (unsigned long)enc_buffer); + logf("res:%08X", (unsigned long)params->reserve_buffer); + logf("fnq:%08X", (unsigned long)fn_queue); + logf("end:%08X", (unsigned long)fn_queue + fnq_size); + logf("abe:%08X", (unsigned long)audiobufend); +#endif + + /* init all chunk headers and reset indexes */ + enc_rd_index = 0; + for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; ) + GET_ENC_CHUNK(--enc_wr_index)->flags = 0; + + logf("enc_set_parameters done"); +} /* enc_set_parameters */ + +/* return encoder chunk at current write position */ +struct enc_chunk_hdr * enc_get_chunk(void) +{ + struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); + chunk->flags &= CHUNKF_START_FILE; + + if (!is_recording) + chunk->flags |= CHUNKF_PRERECORD; + + return chunk; +} /* enc_get_chunk */ + +/* releases the current chunk into the available chunks */ +void enc_finish_chunk(void) +{ + struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); + + /* encoder may have set error flag or written too much data */ + if ((long)chunk->flags < 0 || chunk->enc_size > enc_data_size) + { + is_error = true; + +#ifdef ROCKBOX_HAS_LOGF + if (chunk->enc_size > enc_data_size) + { + /* illegal to scribble over next chunk */ + logf("finish chk ovf: %d>%d", chunk->enc_size, enc_data_size); + } + else + { + /* encoder set error flag */ + logf("finish chk enc error"); + } +#endif + } + + /* advance enc_wr_index to the next encoder chunk */ + INC_ENC_INDEX(enc_wr_index); + + if (enc_rd_index != enc_wr_index) + { + num_rec_bytes += chunk->enc_size; + accum_rec_bytes += chunk->enc_size; + num_rec_samples += chunk->num_pcm; + accum_pcm_samples += chunk->num_pcm; + } + else if (is_recording) /* buffer full */ + { + /* keep current position */ + logf("enc_buffer ovf"); + DEC_ENC_INDEX(enc_wr_index); + } + else + { + /* advance enc_rd_index for prerecording */ + INC_ENC_INDEX(enc_rd_index); + } +} /* enc_finish_chunk */ + +/* checks near empty state on pcm input buffer */ +int enc_pcm_buf_near_empty(void) { /* less than 1sec raw data? => unboost encoder */ - if (((write_pos - read_pos) & CHUNK_MASK) < 44100*4) - return 1; - else - return 0; -} + size_t avail = (dma_wr_pos - pcm_rd_pos) & PCM_CHUNK_MASK; + return avail < (sample_rate << 2) ? 1 : 0; +} /* enc_pcm_buf_near_empty */ /* passes a pointer to next chunk of unprocessed wav data */ -char *enc_get_wav_data(int size) +/* TODO: this really should give the actual size returned */ +unsigned char * enc_get_pcm_data(size_t size) { - char *ptr; - int avail; + size_t avail = (dma_wr_pos - pcm_rd_pos) & PCM_CHUNK_MASK; /* limit the requested pcm data size */ - if(size > MAX_FEED_SIZE) - size = MAX_FEED_SIZE; - - avail = (write_pos - read_pos) & CHUNK_MASK; + if (size > PCM_MAX_FEED_SIZE) + size = PCM_MAX_FEED_SIZE; if (avail >= size) { - ptr = rec_buffer + read_pos; - read_pos = (read_pos + size) & CHUNK_MASK; + unsigned char *ptr = pcm_buffer + pcm_rd_pos; + pcm_rd_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK; /* ptr must point to continous data at wraparound position */ - if (read_pos < size) - memcpy(rec_buffer + NUM_CHUNKS * CHUNK_SIZE, - rec_buffer, read_pos); + if ((size_t)pcm_rd_pos < size) + memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE, + pcm_buffer, pcm_rd_pos); wav_queue_empty = false; return ptr; } + /* not enough data available - encoder should idle */ wav_queue_empty = true; return NULL; -} +} /* enc_get_pcm_data */ + +/* puts some pcm data back in the queue */ +size_t enc_unget_pcm_data(size_t size) +{ + /* can't let DMA advance write position when doing this */ + int level = set_irq_level(HIGHEST_IRQ_LEVEL); + + if (pcm_rd_pos != dma_wr_pos) + { + /* disallow backing up into current DMA write chunk */ + size_t old_avail = (pcm_rd_pos - dma_wr_pos - PCM_CHUNK_SIZE) + & PCM_CHUNK_MASK; + + /* limit size to amount of old data remaining */ + if (size > old_avail) + size = old_avail; + + pcm_rd_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK; + } + + set_irq_level(level); + + return size; +} /* enc_unget_pcm_data */ + +/** Low level pcm recording apis **/ + +/**************************************************************************** + * Functions that do not require targeted implementation but only a targeted + * interface + */ +void pcm_record_data(pcm_more_callback_type more_ready, + unsigned char *start, size_t size) +{ + pcm_callback_more_ready = more_ready; + + if (!(start && size)) + { + size = 0; + if (more_ready) + more_ready(&start, &size); + } + + if (start && size) + pcm_rec_dma_start(start, size); +} /* pcm_record_data */ + +void pcm_stop_recording(void) +{ + if (pcm_recording) + pcm_rec_dma_stop(); +} /* pcm_stop_recording */ diff --git a/firmware/system.c b/firmware/system.c index 242d84d16c..96d5f96602 100644 --- a/firmware/system.c +++ b/firmware/system.c @@ -390,8 +390,7 @@ int system_memory_guard(int newmode) (void)newmode; return 0; } -#elif defined(CPU_COLDFIRE) -/* system code is in target tree for all coldfire targets */ + #elif CONFIG_CPU == SH7034 #include "led.h" #include "system.h" diff --git a/firmware/target/coldfire/iaudio/x5/system-x5.c b/firmware/target/coldfire/iaudio/x5/system-x5.c index 6be6d25ce0..30a4f6e71b 100644 --- a/firmware/target/coldfire/iaudio/x5/system-x5.c +++ b/firmware/target/coldfire/iaudio/x5/system-x5.c @@ -42,7 +42,7 @@ void set_cpu_frequency(long frequency) PLLCR &= ~1; /* Bypass mode */ timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, false); RECALC_DELAYS(CPUFREQ_MAX); - PLLCR = 0x13442045; + PLLCR = 0x03042045 | (PLLCR & 0x70C00000); CSCR0 = 0x00001180; /* Flash: 4 wait states */ CSCR1 = 0x00000980; /* LCD: 2 wait states */ while(!(PLLCR & 0x80000000)) {}; /* Wait until the PLL has locked. @@ -60,7 +60,7 @@ void set_cpu_frequency(long frequency) PLLCR &= ~1; /* Bypass mode */ timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, false); RECALC_DELAYS(CPUFREQ_NORMAL); - PLLCR = 0x16430045; + PLLCR = 0x06030045 | (PLLCR & 0x70C00000); CSCR0 = 0x00000580; /* Flash: 1 wait state */ CSCR1 = 0x00000180; /* LCD: 0 wait states */ while(!(PLLCR & 0x80000000)) {}; /* Wait until the PLL has locked. @@ -77,7 +77,8 @@ void set_cpu_frequency(long frequency) PLLCR &= ~1; /* Bypass mode */ timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, true); RECALC_DELAYS(CPUFREQ_DEFAULT); - PLLCR = 0x10400200; /* Power down PLL, but keep CLSEL and CRSEL */ + /* Power down PLL, but keep CLSEL and CRSEL */ + PLLCR = 0x00000200 | (PLLCR & 0x70C00000); CSCR0 = 0x00000180; /* Flash: 0 wait states */ CSCR1 = 0x00000180; /* LCD: 0 wait states */ DCR = (0x8000 | DEFAULT_REFRESH_TIMER); /* Refresh timer */ diff --git a/firmware/target/coldfire/iriver/system-iriver.c b/firmware/target/coldfire/iriver/system-iriver.c index 3517788641..43ba4eeed4 100644 --- a/firmware/target/coldfire/iriver/system-iriver.c +++ b/firmware/target/coldfire/iriver/system-iriver.c @@ -81,7 +81,7 @@ void set_cpu_frequency(long frequency) PLLCR &= ~1; /* Bypass mode */ timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, false); RECALC_DELAYS(CPUFREQ_MAX); - PLLCR = 0x11c56005; + PLLCR = 0x01056005 | (PLLCR & 0x70c00000); CSCR0 = 0x00001180; /* Flash: 4 wait states */ CSCR1 = 0x00001580; /* LCD: 5 wait states */ #if CONFIG_USBOTG == USBOTG_ISP1362 @@ -108,7 +108,7 @@ void set_cpu_frequency(long frequency) PLLCR &= ~1; /* Bypass mode */ timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, false); RECALC_DELAYS(CPUFREQ_NORMAL); - PLLCR = 0x13c5e005; + PLLCR = 0x0305e005 | (PLLCR & 0x70c00000); CSCR0 = 0x00000580; /* Flash: 1 wait state */ CSCR1 = 0x00000180; /* LCD: 0 wait states */ #if CONFIG_USBOTG == USBOTG_ISP1362 @@ -134,7 +134,8 @@ void set_cpu_frequency(long frequency) PLLCR &= ~1; /* Bypass mode */ timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, true); RECALC_DELAYS(CPUFREQ_DEFAULT); - PLLCR = 0x10c00200; /* Power down PLL, but keep CLSEL and CRSEL */ + /* Power down PLL, but keep CLSEL and CRSEL */ + PLLCR = 0x00000200 | (PLLCR & 0x70c00000); CSCR0 = 0x00000180; /* Flash: 0 wait states */ CSCR1 = 0x00000180; /* LCD: 0 wait states */ #if CONFIG_USBOTG == USBOTG_ISP1362 diff --git a/firmware/target/coldfire/system-coldfire.c b/firmware/target/coldfire/system-coldfire.c index 66e4feb154..2fc81496db 100644 --- a/firmware/target/coldfire/system-coldfire.c +++ b/firmware/target/coldfire/system-coldfire.c @@ -310,3 +310,10 @@ int system_memory_guard(int newmode) return oldmode; } + +/* allow setting of audio clock related bits */ +void coldfire_set_pllcr_audio_bits(long bits) +{ + PLLCR = (PLLCR & ~0x70c00000) | (bits & 0x70c00000); +} + diff --git a/firmware/target/coldfire/system-target.h b/firmware/target/coldfire/system-target.h index 03852115ad..24e3fb8705 100644 --- a/firmware/target/coldfire/system-target.h +++ b/firmware/target/coldfire/system-target.h @@ -110,6 +110,28 @@ static inline unsigned long swap32(unsigned long value) return value; } +static inline unsigned long swap_odd_even32(unsigned long value) +{ + /* + result[31..24],[15.. 8] = value[23..16],[ 7.. 0] + result[23..16],[ 7.. 0] = value[31..24],[15.. 8] + */ + unsigned long mask = 0x00FF00FF; + + asm ( /* val = ABCD */ + "and.l %[val],%[mask] \n" /* mask = .B.D */ + "eor.l %[mask],%[val] \n" /* val = A.C. */ + "lsl.l #8,%[mask] \n" /* mask = B.D. */ + "lsr.l #8,%[val] \n" /* val = .A.C */ + "or.l %[mask],%[val] \n" /* val = BADC */ + : /* outputs */ + [val] "+d"(value), + [mask]"+d"(mask) + ); + + return value; +} + static inline void invalidate_icache(void) { asm volatile ("move.l #0x01000000,%d0\n" @@ -118,6 +140,13 @@ static inline void invalidate_icache(void) "movec.l %d0,%cacr"); } +#ifdef IAUDIO_X5 +#define DEFAULT_PLLCR_AUDIO_BITS 0x10400000 +#else +#define DEFAULT_PLLCR_AUDIO_BITS 0x10c00000 +#endif +void coldfire_set_pllcr_audio_bits(long bits); + /* 11.2896 MHz */ #define CPUFREQ_DEFAULT_MULT 1 #define CPUFREQ_DEFAULT (CPUFREQ_DEFAULT_MULT * CPU_FREQ) diff --git a/firmware/thread.c b/firmware/thread.c index 6a94a52333..4094877742 100644 --- a/firmware/thread.c +++ b/firmware/thread.c @@ -711,6 +711,14 @@ int thread_set_priority(struct thread_entry *thread, int priority) return old_priority; } + +int thread_get_priority(struct thread_entry *thread) +{ + if (thread == NULL) + thread = cores[CURRENT_CORE].running; + + return thread->priority; +} #endif void init_threads(void) diff --git a/uisimulator/sdl/lcd-charcell.c b/uisimulator/sdl/lcd-charcell.c index 8b93653a19..eb94ebf67f 100644 --- a/uisimulator/sdl/lcd-charcell.c +++ b/uisimulator/sdl/lcd-charcell.c @@ -181,7 +181,8 @@ void screen_dump(void) int x, y; static unsigned char line[BMP_LINESIZE]; - create_numbered_filename(filename, "", "dump_", ".bmp", 4); + create_numbered_filename(filename, "", "dump_", ".bmp", 4, + IF_CNFN_NUM_(, NULL)); DEBUGF("screen_dump\n"); fd = sim_creat(filename, O_WRONLY);