ReplayGain support for Ogg Vorbis files (also called VorbisGain) added.
Note that there is a small delay from leaving a setting until the change can be heard (due to audio data buffering). git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7234 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
6bd8e5db08
commit
4a53787992
17 changed files with 404 additions and 38 deletions
|
@ -121,6 +121,8 @@ extern vorbis_comment *ov_comment(OggVorbis_File *vf,int link);
|
|||
|
||||
extern long ov_read(OggVorbis_File *vf,char *buffer,int length,
|
||||
int *bitstream);
|
||||
extern long ov_read_fixed(OggVorbis_File *vf,ogg_int32_t ***pcm_channels,
|
||||
int length,int *bitstream);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1604,3 +1604,47 @@ long ov_read(OggVorbis_File *vf,char *buffer,int bytes_req,int *bitstream){
|
|||
return(samples);
|
||||
}
|
||||
}
|
||||
|
||||
/* input values: pcm_channels) a float vector per channel of output
|
||||
length) the sample length being read by the app
|
||||
|
||||
return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL)
|
||||
0) EOF
|
||||
n) number of samples of PCM actually returned. The
|
||||
below works on a packet-by-packet basis, so the
|
||||
return length is not related to the 'length' passed
|
||||
in, just guaranteed to fit.
|
||||
|
||||
*section) set to the logical bitstream number */
|
||||
|
||||
long ov_read_fixed(OggVorbis_File *vf,ogg_int32_t ***pcm_channels,int length,
|
||||
int *bitstream){
|
||||
if(vf->ready_state<OPENED)return(OV_EINVAL);
|
||||
|
||||
#if CONFIG_CPU == MCF5249
|
||||
mcf5249_init_mac();
|
||||
#endif
|
||||
|
||||
while(1){
|
||||
if(vf->ready_state==INITSET){
|
||||
ogg_int32_t **pcm;
|
||||
long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
|
||||
if(samples){
|
||||
if(pcm_channels)*pcm_channels=pcm;
|
||||
if(samples>length)samples=length;
|
||||
vorbis_synthesis_read(&vf->vd,samples);
|
||||
vf->pcm_offset+=samples;
|
||||
if(bitstream)*bitstream=vf->current_link;
|
||||
return samples;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* suck in another packet */
|
||||
{
|
||||
int ret=_fetch_and_process_packet(vf,1,1);
|
||||
if(ret==OV_EOF)return(0);
|
||||
if(ret<=0)return(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "playback.h"
|
||||
#include "codeclib.h"
|
||||
#include "xxx2wav.h"
|
||||
#include "id3.h"
|
||||
|
||||
struct codec_api *local_rb;
|
||||
|
||||
|
@ -34,3 +35,11 @@ int codec_init(struct codec_api* rb)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void codec_set_replaygain(struct mp3entry* id3)
|
||||
{
|
||||
local_rb->configure(DSP_SET_TRACK_GAIN, (long *) id3->track_gain);
|
||||
local_rb->configure(DSP_SET_ALBUM_GAIN, (long *) id3->album_gain);
|
||||
local_rb->configure(DSP_SET_TRACK_PEAK, (long *) id3->track_peak);
|
||||
local_rb->configure(DSP_SET_ALBUM_PEAK, (long *) id3->album_peak);
|
||||
}
|
||||
|
|
|
@ -37,4 +37,4 @@ int memcmp(const void *s1, const void *s2, size_t n);
|
|||
void* memmove(const void *s1, const void *s2, size_t n);
|
||||
|
||||
int codec_init(struct codec_api* rb);
|
||||
|
||||
void codec_set_replaygain(struct mp3entry* id3);
|
||||
|
|
|
@ -105,16 +105,11 @@ bool vorbis_set_codec_parameters(OggVorbis_File *vf)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (rb->id3->frequency != NATIVE_FREQUENCY) {
|
||||
rb->configure(CODEC_DSP_ENABLE, (bool *)true);
|
||||
} else {
|
||||
rb->configure(CODEC_DSP_ENABLE, (bool *)false);
|
||||
}
|
||||
|
||||
rb->configure(DSP_SET_FREQUENCY, (int *)rb->id3->frequency);
|
||||
codec_set_replaygain(rb->id3);
|
||||
|
||||
if (vi->channels == 2) {
|
||||
rb->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED);
|
||||
rb->configure(DSP_SET_STEREO_MODE, (int *)STEREO_NONINTERLEAVED);
|
||||
} else if (vi->channels == 1) {
|
||||
rb->configure(DSP_SET_STEREO_MODE, (int *)STEREO_MONO);
|
||||
}
|
||||
|
@ -129,14 +124,12 @@ extern char iramend[];
|
|||
#endif
|
||||
|
||||
|
||||
/* reserve the PCM buffer in the IRAM area */
|
||||
static char pcmbuf[4096] IDATA_ATTR;
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_start(struct codec_api* api)
|
||||
{
|
||||
ov_callbacks callbacks;
|
||||
OggVorbis_File vf;
|
||||
ogg_int32_t** pcm;
|
||||
|
||||
int error;
|
||||
long n;
|
||||
|
@ -157,10 +150,12 @@ enum codec_status codec_start(struct codec_api* api)
|
|||
#ifdef USE_IRAM
|
||||
rb->memcpy(iramstart, iramcopy, iramend-iramstart);
|
||||
#endif
|
||||
|
||||
rb->configure(DSP_DITHER, (bool *)false);
|
||||
rb->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16));
|
||||
|
||||
rb->configure(CODEC_DSP_ENABLE, (bool *)true);
|
||||
rb->configure(DSP_DITHER, (bool *)false);
|
||||
rb->configure(DSP_SET_SAMPLE_DEPTH, (long *) (24));
|
||||
rb->configure(DSP_SET_CLIP_MAX, (long *) ((1 << 24) - 1));
|
||||
rb->configure(DSP_SET_CLIP_MIN, (long *) -((1 << 24) - 1));
|
||||
/* Note: These are sane defaults for these values. Perhaps
|
||||
* they should be set differently based on quality setting
|
||||
*/
|
||||
|
@ -244,9 +239,9 @@ enum codec_status codec_start(struct codec_api* api)
|
|||
}
|
||||
rb->seek_time = 0;
|
||||
}
|
||||
|
||||
/* Read host-endian signed 16 bit PCM samples */
|
||||
n=ov_read(&vf,pcmbuf,sizeof(pcmbuf),¤t_section);
|
||||
|
||||
/* Read host-endian signed 24-bit PCM samples */
|
||||
n=ov_read_fixed(&vf,&pcm,1024,¤t_section);
|
||||
|
||||
/* Change DSP and buffer settings for this bitstream */
|
||||
if ( current_section != previous_section ) {
|
||||
|
@ -262,9 +257,10 @@ enum codec_status codec_start(struct codec_api* api)
|
|||
} else if (n < 0) {
|
||||
DEBUGF("Error decoding frame\n");
|
||||
} else {
|
||||
while (!rb->pcmbuf_insert(pcmbuf, n)) {
|
||||
while (!rb->pcmbuf_insert_split(pcm[0], pcm[1],
|
||||
n * sizeof(ogg_int32_t))) {
|
||||
rb->sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
rb->set_offset(ov_raw_tell(&vf));
|
||||
rb->set_elapsed(ov_time_tell(&vf));
|
||||
|
|
138
apps/dsp.c
138
apps/dsp.c
|
@ -16,11 +16,14 @@
|
|||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include "dsp.h"
|
||||
#include "kernel.h"
|
||||
#include "playback.h"
|
||||
#include "system.h"
|
||||
#include "settings.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* The "dither" code to convert the 24-bit samples produced by libmad was
|
||||
* taken from the coolplayer project - coolplayer.sourceforge.net
|
||||
|
@ -35,6 +38,7 @@
|
|||
#define NATIVE_DEPTH 16
|
||||
#define SAMPLE_BUF_SIZE 256
|
||||
#define RESAMPLE_BUF_SIZE (256 * 4) /* Enough for 11,025 Hz -> 44,100 Hz*/
|
||||
#define DEFAULT_REPLAYGAIN 0x01000000
|
||||
|
||||
#if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
|
||||
|
||||
|
@ -45,16 +49,31 @@
|
|||
#define FRACMUL(x, y) \
|
||||
({ \
|
||||
long t; \
|
||||
asm volatile ("mac.l %[a], %[b], %%acc0\n\t" \
|
||||
asm volatile ("mac.l %[a], %[b], %%acc0\n\t" \
|
||||
"movclr.l %%acc0, %[t]\n\t" \
|
||||
: [t] "=r" (t) : [a] "r" (x), [b] "r" (y)); \
|
||||
t; \
|
||||
})
|
||||
/* Multiply 2 32-bit integers and of the 40 most significat bits of the
|
||||
* result, return the 32 least significant bits. I.e., like FRACMUL with one
|
||||
* of the arguments shifted 8 bits to the right.
|
||||
*/
|
||||
#define FRACMUL_8(x, y) \
|
||||
({ \
|
||||
long t; \
|
||||
long u; \
|
||||
asm volatile ("mac.l %[a], %[b], %%acc0\n\t" \
|
||||
"move.l %%accext01, %[u]\n\t" \
|
||||
"movclr.l %%acc0, %[t]\n\t" \
|
||||
: [t] "=r" (t), [u] "=r" (u) : [a] "r" (x), [b] "r" (y)); \
|
||||
(t << 8) | (u & 0xff); \
|
||||
})
|
||||
|
||||
#else
|
||||
|
||||
#define INIT()
|
||||
#define FRACMUL(x, y) (long) (((((long long) (x)) * ((long long) (y))) >> 32))
|
||||
#define FRACMUL_8(x, y) (long) (((((long long) (x)) * ((long long) (y))) >> 24))
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -63,11 +82,17 @@ struct dsp_config
|
|||
long frequency;
|
||||
long clip_min;
|
||||
long clip_max;
|
||||
long track_gain;
|
||||
long album_gain;
|
||||
long track_peak;
|
||||
long album_peak;
|
||||
long replaygain;
|
||||
int sample_depth;
|
||||
int sample_bytes;
|
||||
int stereo_mode;
|
||||
int frac_bits;
|
||||
bool dither_enabled;
|
||||
bool new_gain;
|
||||
};
|
||||
|
||||
struct resample_data
|
||||
|
@ -197,8 +222,6 @@ static long downsample(long *dst, long *src, int count,
|
|||
int pos = phase >> 16;
|
||||
int i = 1;
|
||||
|
||||
INIT();
|
||||
|
||||
/* Do we need last sample of previous frame for interpolation? */
|
||||
if (pos > 0)
|
||||
{
|
||||
|
@ -232,8 +255,6 @@ static long upsample(long *dst, long *src, int count, struct resample_data *r)
|
|||
int i = 0;
|
||||
int pos;
|
||||
|
||||
INIT();
|
||||
|
||||
while ((pos = phase >> 16) == 0)
|
||||
{
|
||||
*dst++ = last_sample + FRACMUL((phase & 0xffff) << 15,
|
||||
|
@ -352,6 +373,40 @@ static long dither_sample(long sample, long bias, long mask,
|
|||
return output;
|
||||
}
|
||||
|
||||
/* Apply a constant gain to the samples (e.g., for ReplayGain). May update
|
||||
* the src array if gain was applied.
|
||||
* Note that this must be called before the resampler.
|
||||
*/
|
||||
static void apply_gain(long* src[], int count)
|
||||
{
|
||||
if (dsp.replaygain)
|
||||
{
|
||||
long* s0 = src[0];
|
||||
long* s1 = src[1];
|
||||
long* d0 = &sample_buf[0];
|
||||
long* d1 = (s0 == s1) ? d0 : &sample_buf[SAMPLE_BUF_SIZE / 2];
|
||||
long gain = dsp.replaygain;
|
||||
long i;
|
||||
|
||||
|
||||
src[0] = d0;
|
||||
src[1] = d1;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
*d0++ = FRACMUL_8(*s0++, gain);
|
||||
}
|
||||
|
||||
if (s0 != s1)
|
||||
{
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
*d1++ = FRACMUL_8(*s1++, gain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_samples(short* dst, long* src[], int count)
|
||||
{
|
||||
long* s0 = src[0];
|
||||
|
@ -397,11 +452,14 @@ long dsp_process(char* dst, char* src[], long size)
|
|||
int samples;
|
||||
|
||||
size /= dsp.sample_bytes * factor;
|
||||
INIT();
|
||||
dsp_set_replaygain(false);
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
samples = convert_to_internal(src, size, tmp);
|
||||
size -= samples;
|
||||
apply_gain(tmp, samples);
|
||||
samples = resample(tmp, samples);
|
||||
write_samples((short*) dst, tmp, samples);
|
||||
written += samples;
|
||||
|
@ -514,9 +572,14 @@ bool dsp_configure(int setting, void *value)
|
|||
dsp.stereo_mode = STEREO_NONINTERLEAVED;
|
||||
dsp.clip_max = ((1 << WORD_FRACBITS) - 1);
|
||||
dsp.clip_min = -((1 << WORD_FRACBITS));
|
||||
dsp.track_gain = 0;
|
||||
dsp.album_gain = 0;
|
||||
dsp.track_peak = 0;
|
||||
dsp.album_peak = 0;
|
||||
dsp.frequency = NATIVE_FREQUENCY;
|
||||
dsp.sample_depth = NATIVE_DEPTH;
|
||||
dsp.frac_bits = WORD_FRACBITS;
|
||||
dsp.new_gain = true;
|
||||
break;
|
||||
|
||||
case DSP_DITHER:
|
||||
|
@ -524,9 +587,74 @@ bool dsp_configure(int setting, void *value)
|
|||
dsp.dither_enabled = (bool) value;
|
||||
break;
|
||||
|
||||
case DSP_SET_TRACK_GAIN:
|
||||
dsp.track_gain = (long) value;
|
||||
dsp.new_gain = true;
|
||||
break;
|
||||
|
||||
case DSP_SET_ALBUM_GAIN:
|
||||
dsp.album_gain = (long) value;
|
||||
dsp.new_gain = true;
|
||||
break;
|
||||
|
||||
case DSP_SET_TRACK_PEAK:
|
||||
dsp.track_peak = (long) value;
|
||||
dsp.new_gain = true;
|
||||
break;
|
||||
|
||||
case DSP_SET_ALBUM_PEAK:
|
||||
dsp.album_peak = (long) value;
|
||||
dsp.new_gain = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void dsp_set_replaygain(bool always)
|
||||
{
|
||||
if (always || dsp.new_gain)
|
||||
{
|
||||
long gain = 0;
|
||||
|
||||
dsp.new_gain = false;
|
||||
|
||||
if (global_settings.replaygain || global_settings.replaygain_noclip)
|
||||
{
|
||||
long peak;
|
||||
|
||||
if (global_settings.replaygain)
|
||||
{
|
||||
gain = (global_settings.replaygain_track || !dsp.album_gain)
|
||||
? dsp.track_gain : dsp.album_gain;
|
||||
}
|
||||
|
||||
peak = (global_settings.replaygain_track || !dsp.album_peak)
|
||||
? dsp.track_peak : dsp.album_peak;
|
||||
|
||||
if (gain == 0)
|
||||
{
|
||||
/* So that noclip can work even with no gain information. */
|
||||
gain = DEFAULT_REPLAYGAIN;
|
||||
}
|
||||
|
||||
if (global_settings.replaygain_noclip && (peak != 0)
|
||||
&& ((((int64_t) gain * peak) >> 24) >= DEFAULT_REPLAYGAIN))
|
||||
{
|
||||
gain = (((int64_t) DEFAULT_REPLAYGAIN << 24) / peak);
|
||||
}
|
||||
|
||||
if (gain == DEFAULT_REPLAYGAIN)
|
||||
{
|
||||
/* Nothing to do, disable processing. */
|
||||
gain = 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
dsp.replaygain = gain;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,5 +33,6 @@ long dsp_input_size(long size);
|
|||
long dsp_output_size(long size);
|
||||
int dsp_stereo_mode(void);
|
||||
bool dsp_configure(int setting, void *value);
|
||||
void dsp_set_replaygain(bool always);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3185,3 +3185,56 @@ eng: "Restarting playback..."
|
|||
voice: "Restarting playback..."
|
||||
new:
|
||||
|
||||
id: LANG_REPLAYGAIN
|
||||
desc: in replaygain
|
||||
eng: "Replaygain"
|
||||
voice "Replaygain"
|
||||
new:
|
||||
|
||||
id: LANG_REPLAYGAIN_ENABLE
|
||||
desc: in replaygain
|
||||
eng: "Enable replaygain"
|
||||
voice "Enable replaygain"
|
||||
new:
|
||||
|
||||
id: LANG_REPLAYGAIN_NOCLIP
|
||||
desc: in replaygain
|
||||
eng: "Prevent clipping"
|
||||
voice "Prevent clipping"
|
||||
new:
|
||||
|
||||
id: LANG_REPLAYGAIN_MODE
|
||||
desc: in replaygain
|
||||
eng: "Replaygain type"
|
||||
voice "Replaygain type"
|
||||
new:
|
||||
|
||||
id: LANG_TRACK_GAIN
|
||||
desc: in replaygain
|
||||
eng: "Track gain"
|
||||
voice "Track gain"
|
||||
new:
|
||||
|
||||
id: LANG_ALBUM_GAIN
|
||||
desc: in replaygain
|
||||
eng: "Album gain"
|
||||
voice "Album gain"
|
||||
new:
|
||||
|
||||
id: LANG_ID3_TRACK_GAIN
|
||||
desc: in browse_id3
|
||||
eng: "[Track gain]"
|
||||
voice ""
|
||||
new:
|
||||
|
||||
id: LANG_ID3_ALBUM_GAIN
|
||||
desc: in browse_id3
|
||||
eng: "[Album gain]"
|
||||
voice ""
|
||||
new:
|
||||
|
||||
id: LANG_ID3_NO_GAIN
|
||||
desc: in browse_id3
|
||||
eng: "<No gain>"
|
||||
voice ""
|
||||
new:
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include "mp3_playback.h"
|
||||
#include "logf.h"
|
||||
#include "atoi.h"
|
||||
#include "replaygain.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* Simple file type probing by looking filename extension. */
|
||||
int probe_file_format(const char *filename)
|
||||
|
@ -271,7 +273,7 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
|
|||
channels=buf[39];
|
||||
|
||||
if ( !get_vorbis_comments(&(track->id3), fd) ) {
|
||||
logf("get_vorbis_comments failed");
|
||||
logf("get_vorbis_comments failed");
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
@ -283,7 +285,7 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
|
|||
/* We now need to search for the last page in the file - identified by
|
||||
by ('O','g','g','S',0) and retrieve totalsamples */
|
||||
|
||||
lseek(fd, -32*1024, SEEK_END);
|
||||
lseek(fd, -64*1024, SEEK_END); /* A page is always < 64 kB */
|
||||
eof=0;
|
||||
j=0; /* The number of bytes currently in buffer */
|
||||
i=0;
|
||||
|
@ -300,6 +302,7 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
|
|||
while (i < (j-5)) {
|
||||
if (memcmp(&buf[i],"OggS",5)==0) {
|
||||
if (i < (j-17)) {
|
||||
/* Note that this only reads the low 32 bits of a 64 bit value */
|
||||
totalsamples=(buf[i+6])|(buf[i+7]<<8)|(buf[i+8]<<16)|(buf[i+9]<<24);
|
||||
last_serialno=(buf[i+14])|(buf[i+15]<<8)|(buf[i+16]<<16)|(buf[i+17]<<24);
|
||||
j=0; /* We can discard the rest of the buffer */
|
||||
|
@ -761,7 +764,7 @@ static bool get_vorbis_comments (struct mp3entry *entry, int fd)
|
|||
int comment_length;
|
||||
int i = 0;
|
||||
unsigned char temp[300];
|
||||
int buffer_remaining = sizeof(entry->id3v2buf);
|
||||
int buffer_remaining = sizeof(entry->id3v2buf) + sizeof(entry->id3v1buf);
|
||||
char *buffer = entry->id3v2buf;
|
||||
char **p = NULL;
|
||||
int segments;
|
||||
|
@ -884,8 +887,36 @@ static bool get_vorbis_comments (struct mp3entry *entry, int fd)
|
|||
} else if (strncasecmp(temp, "TRACKNUMBER=", 12) == 0) {
|
||||
name_length = 11;
|
||||
p = &(entry->track_string);
|
||||
} else if ((strncasecmp(temp, "RG_RADIO=", 9) == 0)
|
||||
&& !entry->track_gain) {
|
||||
entry->track_gain = get_replaygain(&temp[9]);
|
||||
name_length = 8;
|
||||
p = &(entry->track_gain_str);
|
||||
} else if (strncasecmp(temp, "REPLAYGAIN_TRACK_GAIN=", 22) == 0) {
|
||||
entry->track_gain = get_replaygain(&temp[22]);
|
||||
name_length = 21;
|
||||
p = &(entry->track_gain_str);
|
||||
} else if ((strncasecmp(temp, "RG_AUDIOPHILE=", 14) == 0)
|
||||
&& !entry->album_gain) {
|
||||
entry->album_gain = get_replaygain(&temp[14]);
|
||||
name_length = 13;
|
||||
p = &(entry->album_gain_str);
|
||||
} else if (strncasecmp(temp, "REPLAYGAIN_ALBUM_GAIN=", 22) == 0) {
|
||||
entry->album_gain = get_replaygain(&temp[22]);
|
||||
name_length = 21;
|
||||
p = &(entry->album_gain_str);
|
||||
} else if ((strncasecmp(temp, "RG_PEAK=", 8) == 0)
|
||||
&& !entry->track_peak) {
|
||||
entry->track_peak = get_replaypeak(&temp[8]);
|
||||
p = NULL;
|
||||
} else if (strncasecmp(temp, "REPLAYGAIN_TRACK_PEAK=", 22) == 0) {
|
||||
entry->track_peak = get_replaypeak(&temp[22]);
|
||||
p = NULL;
|
||||
} else if (strncasecmp(temp, "REPLAYGAIN_ALBUM_PEAK=", 22) == 0) {
|
||||
entry->album_peak = get_replaypeak(&temp[22]);
|
||||
p = NULL;
|
||||
} else {
|
||||
p = NULL;
|
||||
p = NULL;
|
||||
}
|
||||
|
||||
if (p) {
|
||||
|
@ -899,7 +930,6 @@ static bool get_vorbis_comments (struct mp3entry *entry, int fd)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,11 @@ enum {
|
|||
DSP_SET_SAMPLE_DEPTH,
|
||||
DSP_SET_STEREO_MODE,
|
||||
DSP_RESET,
|
||||
DSP_DITHER
|
||||
DSP_DITHER,
|
||||
DSP_SET_TRACK_GAIN,
|
||||
DSP_SET_ALBUM_GAIN,
|
||||
DSP_SET_TRACK_PEAK,
|
||||
DSP_SET_ALBUM_PEAK
|
||||
};
|
||||
|
||||
/* Not yet implemented. */
|
||||
|
|
|
@ -1274,7 +1274,11 @@ bool browse_id3(void)
|
|||
struct mp3entry* id3 = audio_current_track();
|
||||
int button;
|
||||
int menu_pos = 0;
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
int menu_max = 12;
|
||||
#else
|
||||
int menu_max = 10;
|
||||
#endif
|
||||
bool exit = false;
|
||||
char scroll_text[MAX_PATH];
|
||||
|
||||
|
@ -1381,6 +1385,21 @@ bool browse_id3(void)
|
|||
lcd_puts(0, 0, str(LANG_ID3_PATH));
|
||||
lcd_puts_scroll(0, 1, id3->path);
|
||||
break;
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
case 11:
|
||||
lcd_puts(0, 0, str(LANG_ID3_TRACK_GAIN));
|
||||
lcd_puts(0, 1, id3->track_gain_str
|
||||
? id3->track_gain_str
|
||||
: (char*) str(LANG_ID3_NO_GAIN));
|
||||
break;
|
||||
|
||||
case 12:
|
||||
lcd_puts(0, 0, str(LANG_ID3_ALBUM_GAIN));
|
||||
lcd_puts(0, 1, id3->album_gain_str
|
||||
? id3->album_gain_str
|
||||
: (char*) str(LANG_ID3_NO_GAIN));
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
lcd_update();
|
||||
|
||||
|
|
|
@ -416,6 +416,9 @@ static const struct bit_entry hd_bits[] =
|
|||
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
{2, S_O(crossfade), 0, "crossfade type", "off,crossfade,mix"},
|
||||
{1, S_O(replaygain), false, "replaygain", off_on },
|
||||
{1, S_O(replaygain_track), false, "replaygain type", "track,album" },
|
||||
{1, S_O(replaygain_noclip), false, "replaygain noclip", off_on },
|
||||
#endif
|
||||
|
||||
/* new stuff to be added at the end */
|
||||
|
|
|
@ -328,6 +328,12 @@ struct user_settings
|
|||
|
||||
bool next_folder; /* move to next folder */
|
||||
bool runtimedb; /* runtime database active? */
|
||||
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
bool replaygain; /* enable replaygain */
|
||||
bool replaygain_track; /* true for track gain, false for album gain */
|
||||
bool replaygain_noclip; /* scale to prevent clips */
|
||||
#endif
|
||||
};
|
||||
|
||||
enum optiontype { INT, BOOL };
|
||||
|
|
|
@ -66,6 +66,7 @@ void dac_line_in(bool enable);
|
|||
#if CONFIG_HWCODEC == MASNONE
|
||||
#include "pcmbuf.h"
|
||||
#include "pcm_playback.h"
|
||||
#include "dsp.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CHARGING
|
||||
|
@ -1187,6 +1188,56 @@ static bool runtimedb(void)
|
|||
return rc;
|
||||
}
|
||||
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
static bool replaygain(void)
|
||||
{
|
||||
bool result = set_bool(str(LANG_REPLAYGAIN_ENABLE),
|
||||
&global_settings.replaygain);
|
||||
|
||||
dsp_set_replaygain(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool replaygain_mode(void)
|
||||
{
|
||||
bool result = set_bool_options(str(LANG_REPLAYGAIN_MODE),
|
||||
&global_settings.replaygain_track,
|
||||
STR(LANG_TRACK_GAIN),
|
||||
STR(LANG_ALBUM_GAIN),
|
||||
NULL);
|
||||
|
||||
dsp_set_replaygain(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool replaygain_noclip(void)
|
||||
{
|
||||
bool result = set_bool(str(LANG_REPLAYGAIN_NOCLIP),
|
||||
&global_settings.replaygain_noclip);
|
||||
|
||||
dsp_set_replaygain(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool replaygain_settings_menu(void)
|
||||
{
|
||||
int m;
|
||||
bool result;
|
||||
|
||||
static const struct menu_item items[] = {
|
||||
{ ID2P(LANG_REPLAYGAIN_ENABLE), replaygain },
|
||||
{ ID2P(LANG_REPLAYGAIN_NOCLIP), replaygain_noclip },
|
||||
{ ID2P(LANG_REPLAYGAIN_MODE), replaygain_mode },
|
||||
};
|
||||
|
||||
m=menu_init( items, sizeof(items) / sizeof(*items), NULL,
|
||||
NULL, NULL, NULL);
|
||||
result = menu_run(m);
|
||||
menu_exit(m);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool playback_settings_menu(void)
|
||||
{
|
||||
int m;
|
||||
|
@ -1203,6 +1254,7 @@ static bool playback_settings_menu(void)
|
|||
#if CONFIG_HWCODEC == MASNONE
|
||||
{ ID2P(LANG_CROSSFADE), crossfade },
|
||||
{ ID2P(LANG_CROSSFADE_DURATION), crossfade_duration },
|
||||
{ ID2P(LANG_REPLAYGAIN), replaygain_settings_menu },
|
||||
#endif
|
||||
#ifdef HAVE_SPDIF_POWER
|
||||
{ ID2P(LANG_SPDIF_ENABLE), spdif },
|
||||
|
|
|
@ -129,6 +129,9 @@ drivers/uda1380.c
|
|||
#if (CONFIG_HWCODEC == MASNONE) && !defined(SIMULATOR)
|
||||
pcm_playback.c
|
||||
#endif
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
replaygain.c
|
||||
#endif
|
||||
#if defined(HAVE_UDA1380) && !defined(SIMULATOR)
|
||||
pcm_record.c
|
||||
#endif
|
||||
|
|
|
@ -179,6 +179,11 @@ SECTIONS
|
|||
_iramcopy = .;
|
||||
} > DRAM
|
||||
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.eh_frame)
|
||||
}
|
||||
|
||||
.iram IRAMORIG : AT ( _iramcopy)
|
||||
{
|
||||
_iramstart = .;
|
||||
|
|
|
@ -53,13 +53,13 @@ enum {
|
|||
|
||||
struct mp3entry {
|
||||
char path[MAX_PATH];
|
||||
char *title;
|
||||
char *artist;
|
||||
char *album;
|
||||
char* genre_string ;
|
||||
char* track_string ;
|
||||
char* year_string ;
|
||||
char* composer ;
|
||||
char* title;
|
||||
char* artist;
|
||||
char* album;
|
||||
char* genre_string;
|
||||
char* track_string;
|
||||
char* year_string;
|
||||
char* composer;
|
||||
int tracknum;
|
||||
int version;
|
||||
int layer;
|
||||
|
@ -115,6 +115,17 @@ struct mp3entry {
|
|||
short voladjust;
|
||||
long playcount;
|
||||
long lastplayed;
|
||||
|
||||
/* replaygain support */
|
||||
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
char* track_gain_str;
|
||||
char* album_gain_str;
|
||||
long track_gain; /* 7.24 signed fixed point. 0 for no gain. */
|
||||
long album_gain;
|
||||
long track_peak; /* 7.24 signed fixed point. 0 for no peak. */
|
||||
long album_peak;
|
||||
#endif
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
Loading…
Reference in a new issue