Fix resampling clicking as much as possible at the moment. 1) Upsampling clicked because of size inaccuracies returned by DSP. Fix by simplifying audio system to use per-channel sample count from codec to pcm buffer. 2) Downsampling affected by 1) and was often starting passed the end of the data when not enough was available to generate an output sample. Fix by clamping input range to last sample in buffer and using the last sample value in the buffer. A perfect fix will require a double buffering scheme on the resampler to sufficient data during small data transients on both ends at all times of the down ratio on input and the up ratio on output.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12218 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sevakis 2007-02-07 00:51:50 +00:00
parent dd50c863e6
commit aba6ca0881
21 changed files with 170 additions and 256 deletions

View file

@ -78,7 +78,6 @@ struct codec_api ci = {
0, /* seek_time */ 0, /* seek_time */
NULL, /* get_codec_memory */ NULL, /* get_codec_memory */
NULL, /* pcmbuf_insert */ NULL, /* pcmbuf_insert */
NULL, /* pcmbuf_insert_split */
NULL, /* set_elapsed */ NULL, /* set_elapsed */
NULL, /* read_filebuf */ NULL, /* read_filebuf */
NULL, /* request_buffer */ NULL, /* request_buffer */

View file

@ -90,12 +90,12 @@
#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */ #define CODEC_ENC_MAGIC 0x52454E43 /* RENC */
/* increase this every time the api struct changes */ /* increase this every time the api struct changes */
#define CODEC_API_VERSION 12 #define CODEC_API_VERSION 13
/* update this to latest version if a change to the api struct breaks /* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any backwards compatibility (and please take the opportunity to sort in any
new function which are "waiting" at the end of the function table) */ new function which are "waiting" at the end of the function table) */
#define CODEC_MIN_API_VERSION 12 #define CODEC_MIN_API_VERSION 13
/* codec return codes */ /* codec return codes */
enum codec_status { enum codec_status {
@ -133,8 +133,7 @@ struct codec_api {
void* (*get_codec_memory)(size_t *size); void* (*get_codec_memory)(size_t *size);
/* Insert PCM data into audio buffer for playback. Playback will start /* Insert PCM data into audio buffer for playback. Playback will start
automatically. */ automatically. */
bool (*pcmbuf_insert)(const char *data, size_t length); bool (*pcmbuf_insert)(const void *ch1, const void *ch2, int count);
bool (*pcmbuf_insert_split)(const void *ch1, const void *ch2, size_t length);
/* Set song position in WPS (value in ms). */ /* Set song position in WPS (value in ms). */
void (*set_elapsed)(unsigned int value); void (*set_elapsed)(unsigned int value);

View file

@ -35,12 +35,9 @@ unsigned long frequency;
/* used outside liba52 */ /* used outside liba52 */
static uint8_t buf[3840] IBSS_ATTR; static uint8_t buf[3840] IBSS_ATTR;
void output_audio(sample_t *samples) static inline void output_audio(sample_t *samples)
{ {
do { ci->pcmbuf_insert(&samples[0], &samples[256], 256);
ci->yield();
} while (!ci->pcmbuf_insert_split(&samples[0], &samples[256],
256*sizeof(sample_t)));
} }
void a52_decode_data(uint8_t *start, uint8_t *end) void a52_decode_data(uint8_t *start, uint8_t *end)

View file

@ -183,12 +183,8 @@ next_track:
/* Output the audio */ /* Output the audio */
ci->yield(); ci->yield();
while (!ci->pcmbuf_insert_split(decoder->time_out[0], ci->pcmbuf_insert(decoder->time_out[0], decoder->time_out[1],
decoder->time_out[1], frame_info.samples >> 1);
frame_info.samples * 2))
{
ci->sleep(1);
}
/* Update the elapsed-time indicator */ /* Update the elapsed-time indicator */
sound_samples_done += sample_duration; sound_samples_done += sample_duration;

View file

@ -320,9 +320,10 @@ next_track:
} }
} }
/* 2 bytes per sample */ if (channels == 2)
while (!ci->pcmbuf_insert((char *)samples, sampleswritten*2)) sampleswritten >>= 1; /* make samples/channel */
ci->yield();
ci->pcmbuf_insert(samples, NULL, sampleswritten);
ci->set_elapsed( ci->set_elapsed(
((end_adr-start_adr)*loop_count + bufoff-chanstart)* ((end_adr-start_adr)*loop_count + bufoff-chanstart)*

View file

@ -51,7 +51,8 @@ enum codec_status codec_main(void)
uint16_t sample_size = 0; uint16_t sample_size = 0;
uint32_t sample_rate = 0; uint32_t sample_rate = 0;
uint32_t i; uint32_t i;
size_t n, bufsize; size_t n;
int bufcount;
int endofstream; int endofstream;
unsigned char *buf; unsigned char *buf;
uint8_t *aifbuf; uint8_t *aifbuf;
@ -229,25 +230,27 @@ next_track:
samples[i/4] = (SE(aifbuf[i])<<21)|(aifbuf[i + 1]<<13) samples[i/4] = (SE(aifbuf[i])<<21)|(aifbuf[i + 1]<<13)
|(aifbuf[i + 2]<<5)|(aifbuf[i + 3]>>3); |(aifbuf[i + 2]<<5)|(aifbuf[i + 3]>>3);
} }
bufsize = n; bufcount = n >> 2;
} else if (sample_size > 16) { } else if (sample_size > 16) {
for (i = 0; i < n; i += 3) { for (i = 0; i < n; i += 3) {
samples[i/3] = (SE(aifbuf[i])<<21)|(aifbuf[i + 1]<<13) samples[i/3] = (SE(aifbuf[i])<<21)|(aifbuf[i + 1]<<13)
|(aifbuf[i + 2]<<5); |(aifbuf[i + 2]<<5);
} }
bufsize = n*4/3; bufcount = n/3;
} else if (sample_size > 8) { } else if (sample_size > 8) {
for (i = 0; i < n; i += 2) for (i = 0; i < n; i += 2)
samples[i/2] = (SE(aifbuf[i])<<21)|(aifbuf[i + 1]<<13); samples[i/2] = (SE(aifbuf[i])<<21)|(aifbuf[i + 1]<<13);
bufsize = n*2; bufcount = n >> 1;
} else { } else {
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
samples[i] = SE(aifbuf[i]) << 21; samples[i] = SE(aifbuf[i]) << 21;
bufsize = n*4; bufcount = n;
} }
while (!ci->pcmbuf_insert((char *)samples, bufsize)) if (num_channels == 2)
ci->yield(); bufcount >>= 1;
ci->pcmbuf_insert(samples, NULL, bufcount);
ci->advance_buffer(n); ci->advance_buffer(n);
bytesdone += n; bytesdone += n;

View file

@ -121,10 +121,7 @@ enum codec_status codec_main(void)
/* Output the audio */ /* Output the audio */
ci->yield(); ci->yield();
while(!ci->pcmbuf_insert_split(outputbuffer[0], ci->pcmbuf_insert(outputbuffer[0], outputbuffer[1], samplesdecoded);
outputbuffer[1],
samplesdecoded*sizeof(int32_t)))
ci->yield();
/* Update the elapsed-time indicator */ /* Update the elapsed-time indicator */
samplesdone+=sample_duration; samplesdone+=sample_duration;

View file

@ -489,11 +489,8 @@ enum codec_status codec_main(void)
frame++; frame++;
ci->yield(); ci->yield();
while(!ci->pcmbuf_insert_split((char*)&decoded0[fc.sample_skip], ci->pcmbuf_insert(&decoded0[fc.sample_skip], &decoded1[fc.sample_skip],
(char*)&decoded1[fc.sample_skip], fc.blocksize - fc.sample_skip);
(fc.blocksize-fc.sample_skip)*4)) {
ci->yield();
}
fc.sample_skip = 0; fc.sample_skip = 0;

View file

@ -199,9 +199,9 @@ next_track:
loop we will need to process the final frame that was decoded. */ loop we will need to process the final frame that was decoded. */
if (framelength > 0) { if (framelength > 0) {
/* In case of a mono file, the second array will be ignored. */ /* In case of a mono file, the second array will be ignored. */
ci->pcmbuf_insert_split(&synth.pcm.samples[0][samples_to_skip], ci->pcmbuf_insert(&synth.pcm.samples[0][samples_to_skip],
&synth.pcm.samples[1][samples_to_skip], &synth.pcm.samples[1][samples_to_skip],
framelength * 4); framelength);
/* Only skip samples for the first frame added. */ /* Only skip samples for the first frame added. */
samples_to_skip = 0; samples_to_skip = 0;
@ -244,8 +244,8 @@ next_track:
/* Finish the remaining decoded frame. /* Finish the remaining decoded frame.
Cut the required samples from the end. */ Cut the required samples from the end. */
if (framelength > stop_skip) if (framelength > stop_skip)
ci->pcmbuf_insert_split(synth.pcm.samples[0], synth.pcm.samples[1], ci->pcmbuf_insert(synth.pcm.samples[0], synth.pcm.samples[1],
(framelength - stop_skip) * 4); framelength - stop_skip);
stream.error = 0; stream.error = 0;

View file

@ -168,10 +168,9 @@ next_track:
retval = CODEC_ERROR; retval = CODEC_ERROR;
goto done; goto done;
} else { } else {
while (!ci->pcmbuf_insert_split(sample_buffer, ci->pcmbuf_insert(sample_buffer,
sample_buffer + MPC_FRAME_LENGTH, sample_buffer + MPC_FRAME_LENGTH,
status*sizeof(MPC_SAMPLE_FORMAT))) status);
ci->yield();
samplesdone += status; samplesdone += status;
ci->set_elapsed(samplesdone/frequency); ci->set_elapsed(samplesdone/frequency);
} }

View file

@ -4445,9 +4445,7 @@ init_nsf:
goto init_nsf; goto init_nsf;
} }
while (!ci->pcmbuf_insert((char *)samples, written)) ci->pcmbuf_insert(samples, NULL, written >> 1);
ci->yield();
} }
print_timers(last_path,track); print_timers(last_path,track);

View file

@ -134,11 +134,8 @@ seek_start:
/* Insert decoded samples in pcmbuf */ /* Insert decoded samples in pcmbuf */
if (nsamples) { if (nsamples) {
ci->yield(); ci->yield();
while (!ci->pcmbuf_insert_split((char*)(decoded0 + sc.nwrap), ci->pcmbuf_insert(decoded0 + sc.nwrap, decoded1 + sc.nwrap,
(char*)(decoded1 + sc.nwrap), nsamples);
4*nsamples)) {
ci->yield();
}
/* Update the elapsed-time indicator */ /* Update the elapsed-time indicator */
samplesdone += nsamples; samplesdone += nsamples;

View file

@ -1309,8 +1309,7 @@ next_track:
} }
} }
while (!ci->pcmbuf_insert((char *)samples, CHUNK_SIZE*4)) ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE);
ci->yield();
} }
if (ci->request_next_track()) if (ci->request_next_track())

View file

@ -217,10 +217,7 @@ next_track:
} else if (n < 0) { } else if (n < 0) {
DEBUGF("Error decoding frame\n"); DEBUGF("Error decoding frame\n");
} else { } else {
while (!ci->pcmbuf_insert_split(pcm[0], pcm[1], ci->pcmbuf_insert(pcm[0], pcm[1], n);
n*sizeof(ogg_int32_t))) {
ci->sleep(1);
}
ci->set_offset(ov_raw_tell(&vf)); ci->set_offset(ov_raw_tell(&vf));
ci->set_elapsed(ov_time_tell(&vf)); ci->set_elapsed(ov_time_tell(&vf));
} }

View file

@ -212,7 +212,8 @@ enum codec_status codec_main(void)
int bytespersample = 0; int bytespersample = 0;
uint16_t bitspersample; uint16_t bitspersample;
uint32_t i; uint32_t i;
size_t n, bufsize; size_t n;
int bufcount;
int endofstream; int endofstream;
unsigned char *buf; unsigned char *buf;
uint8_t *wavbuf; uint8_t *wavbuf;
@ -466,34 +467,39 @@ next_track:
(wavbuf[i + 1]<<5)|(wavbuf[i + 2]<<13)| (wavbuf[i + 1]<<5)|(wavbuf[i + 2]<<13)|
(SE(wavbuf[i + 3])<<21); (SE(wavbuf[i + 3])<<21);
} }
bufsize = n; bufcount = n >> 2;
} else if (bitspersample > 16) { } else if (bitspersample > 16) {
for (i = 0; i < n; i += 3) { for (i = 0; i < n; i += 3) {
samples[i/3] = (wavbuf[i]<<5)| samples[i/3] = (wavbuf[i]<<5)|
(wavbuf[i + 1]<<13)|(SE(wavbuf[i + 2])<<21); (wavbuf[i + 1]<<13)|(SE(wavbuf[i + 2])<<21);
} }
bufsize = n*4/3; bufcount = n/3;
} else if (bitspersample > 8) { } else if (bitspersample > 8) {
for (i = 0; i < n; i += 2) { for (i = 0; i < n; i += 2) {
samples[i/2] = (wavbuf[i]<<13)|(SE(wavbuf[i + 1])<<21); samples[i/2] = (wavbuf[i]<<13)|(SE(wavbuf[i + 1])<<21);
} }
bufsize = n*2; bufcount = n >> 1;
} else { } else {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
samples[i] = (wavbuf[i] - 0x80)<<21; samples[i] = (wavbuf[i] - 0x80)<<21;
} }
bufsize = n*4; bufcount = n;
} }
if (channels == 2)
bufcount >>= 1;
} else if (formattag == WAVE_FORMAT_ALAW } else if (formattag == WAVE_FORMAT_ALAW
|| formattag == IBM_FORMAT_ALAW) { || formattag == IBM_FORMAT_ALAW) {
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
samples[i] = alaw2linear16[wavbuf[i]] << 13; samples[i] = alaw2linear16[wavbuf[i]] << 13;
bufsize = n*4;
bufcount = (channels == 2) ? (n >> 1) : n;
} else if (formattag == WAVE_FORMAT_MULAW } else if (formattag == WAVE_FORMAT_MULAW
|| formattag == IBM_FORMAT_MULAW) { || formattag == IBM_FORMAT_MULAW) {
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
samples[i] = ulaw2linear16[wavbuf[i]] << 13; samples[i] = ulaw2linear16[wavbuf[i]] << 13;
bufsize = n*4;
bufcount = (channels == 2) ? (n >> 1) : n;
} }
else if (formattag == WAVE_FORMAT_DVI_ADPCM) { else if (formattag == WAVE_FORMAT_DVI_ADPCM) {
unsigned int nblocks = chunksize/blockalign; unsigned int nblocks = chunksize/blockalign;
@ -508,15 +514,14 @@ next_track:
goto done; goto done;
} }
} }
bufsize = nblocks*samplesperblock*channels*4; bufcount = nblocks*samplesperblock;
} else { } else {
DEBUGF("CODEC_ERROR: unsupported format %x\n", formattag); DEBUGF("CODEC_ERROR: unsupported format %x\n", formattag);
i = CODEC_ERROR; i = CODEC_ERROR;
goto done; goto done;
} }
while (!ci->pcmbuf_insert((char *)samples, bufsize)) ci->pcmbuf_insert(samples, NULL, bufcount);
ci->yield();
ci->advance_buffer(n); ci->advance_buffer(n);
bytesdone += n; bytesdone += n;

View file

@ -118,8 +118,7 @@ enum codec_status codec_main(void)
if (ci->stop_codec || ci->new_track) if (ci->stop_codec || ci->new_track)
break; break;
while (!ci->pcmbuf_insert ((char *) temp_buffer, nsamples * nchans * 4)) ci->pcmbuf_insert (temp_buffer, NULL, nsamples);
ci->sleep (1);
ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10); ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10);
ci->yield (); ci->yield ();

View file

@ -40,8 +40,8 @@
#define WORD_FRACBITS 27 #define WORD_FRACBITS 27
#define NATIVE_DEPTH 16 #define NATIVE_DEPTH 16
#define SAMPLE_BUF_SIZE 256 #define SAMPLE_BUF_COUNT 256
#define RESAMPLE_BUF_SIZE (256 * 4) /* Enough for 11,025 Hz -> 44,100 Hz*/ #define RESAMPLE_BUF_COUNT (256 * 4) /* Enough for 11,025 Hz -> 44,100 Hz*/
#define DEFAULT_GAIN 0x01000000 #define DEFAULT_GAIN 0x01000000
struct dsp_config struct dsp_config
@ -116,8 +116,8 @@ static struct dsp_config *dsp;
* of copying needed is minimized for that case. * of copying needed is minimized for that case.
*/ */
static int32_t sample_buf[SAMPLE_BUF_SIZE] IBSS_ATTR; static int32_t sample_buf[SAMPLE_BUF_COUNT] IBSS_ATTR;
static int32_t resample_buf[RESAMPLE_BUF_SIZE] IBSS_ATTR; static int32_t resample_buf[RESAMPLE_BUF_COUNT] IBSS_ATTR;
int sound_get_pitch(void) int sound_get_pitch(void)
{ {
@ -139,14 +139,14 @@ void sound_set_pitch(int permille)
*/ */
static int convert_to_internal(const char* src[], int count, int32_t* dst[]) static int convert_to_internal(const char* src[], int count, int32_t* dst[])
{ {
count = MIN(SAMPLE_BUF_SIZE / 2, count); count = MIN(SAMPLE_BUF_COUNT / 2, count);
if ((dsp->sample_depth <= NATIVE_DEPTH) if ((dsp->sample_depth <= NATIVE_DEPTH)
|| (dsp->stereo_mode == STEREO_INTERLEAVED)) || (dsp->stereo_mode == STEREO_INTERLEAVED))
{ {
dst[0] = &sample_buf[0]; dst[0] = &sample_buf[0];
dst[1] = (dsp->stereo_mode == STEREO_MONO) dst[1] = (dsp->stereo_mode == STEREO_MONO)
? dst[0] : &sample_buf[SAMPLE_BUF_SIZE / 2]; ? dst[0] : &sample_buf[SAMPLE_BUF_COUNT / 2];
} }
else else
{ {
@ -231,7 +231,7 @@ static void resampler_set_delta(int frequency)
/* TODO: we really should have a separate set of resample functions for both /* TODO: we really should have a separate set of resample functions for both
mono and stereo to avoid all this internal branching and looping. */ mono and stereo to avoid all this internal branching and looping. */
static long downsample(int32_t **dst, int32_t **src, int count, static int downsample(int32_t **dst, int32_t **src, int count,
struct resample_data *r) struct resample_data *r)
{ {
long phase = r->phase; long phase = r->phase;
@ -246,11 +246,14 @@ static long downsample(int32_t **dst, int32_t **src, int count,
last_sample = r->last_sample[j]; last_sample = r->last_sample[j];
/* Do we need last sample of previous frame for interpolation? */ /* Do we need last sample of previous frame for interpolation? */
if (pos > 0) if (pos > 0)
{
last_sample = src[j][pos - 1]; last_sample = src[j][pos - 1];
}
*d[j]++ = last_sample + FRACMUL((phase & 0xffff) << 15, /* Be sure starting position isn't passed the available data */
src[j][pos] - last_sample); if (pos < count)
*d[j]++ = last_sample + FRACMUL((phase & 0xffff) << 15,
src[j][pos] - last_sample);
else /* This is kinda nasty but works somewhat well for now */
*d[j]++ = src[j][count - 1];
} }
phase += delta; phase += delta;
@ -316,7 +319,7 @@ static inline int resample(int32_t* src[], int count)
if (dsp->frequency != NATIVE_FREQUENCY) if (dsp->frequency != NATIVE_FREQUENCY)
{ {
int32_t* dst[2] = {&resample_buf[0], &resample_buf[RESAMPLE_BUF_SIZE / 2]}; int32_t* dst[2] = {&resample_buf[0], &resample_buf[RESAMPLE_BUF_COUNT / 2]};
if (dsp->frequency < NATIVE_FREQUENCY) if (dsp->frequency < NATIVE_FREQUENCY)
{ {
@ -619,7 +622,7 @@ static void apply_gain(int32_t* _src[], int _count)
if (s0 != s1) if (s0 != s1)
{ {
d = &sample_buf[SAMPLE_BUF_SIZE / 2]; d = &sample_buf[SAMPLE_BUF_COUNT / 2];
src[1] = d; src[1] = d;
s = *s1++; s = *s1++;
@ -736,18 +739,17 @@ static void write_samples(short* dst, int32_t* src[], int count)
} }
/* Process and convert src audio to dst based on the DSP configuration, /* Process and convert src audio to dst based on the DSP configuration,
* reading size bytes of audio data. dst is assumed to be large enough; use * reading count number of audio samples. dst is assumed to be large
* dst_get_dest_size() to get the required size. src is an array of * enough; use dsp_output_count() to get the required number. src is an
* pointers; for mono and interleaved stereo, it contains one pointer to the * array of pointers; for mono and interleaved stereo, it contains one
* start of the audio data; for non-interleaved stereo, it contains two * pointer to the start of the audio data and the other is ignored; for
* pointers, one for each audio channel. Returns number of bytes written to * non-interleaved stereo, it contains two pointers, one for each audio
* dest. * channel. Returns number of bytes written to dst.
*/ */
long dsp_process(char* dst, const char* src[], long size) int dsp_process(char *dst, const char *src[], int count)
{ {
int32_t* tmp[2]; int32_t* tmp[2];
long written = 0; int written = 0;
long factor;
int samples; int samples;
#if defined(CPU_COLDFIRE) && !defined(SIMULATOR) #if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
@ -759,14 +761,12 @@ long dsp_process(char* dst, const char* src[], long size)
dsp = &dsp_conf[current_codec]; dsp = &dsp_conf[current_codec];
factor = (dsp->stereo_mode != STEREO_MONO) ? 2 : 1;
size /= dsp->sample_bytes * factor;
dsp_set_replaygain(false); dsp_set_replaygain(false);
while (size > 0) while (count > 0)
{ {
samples = convert_to_internal(src, size, tmp); samples = convert_to_internal(src, count, tmp);
size -= samples; count -= samples;
apply_gain(tmp, samples); apply_gain(tmp, samples);
samples = resample(tmp, samples); samples = resample(tmp, samples);
if (dsp->crossfeed_enabled && dsp->stereo_mode != STEREO_MONO) if (dsp->crossfeed_enabled && dsp->stereo_mode != STEREO_MONO)
@ -780,85 +780,61 @@ long dsp_process(char* dst, const char* src[], long size)
dst += samples * sizeof(short) * 2; dst += samples * sizeof(short) * 2;
yield(); yield();
} }
#if defined(CPU_COLDFIRE) && !defined(SIMULATOR) #if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
/* set old macsr again */ /* set old macsr again */
coldfire_set_macsr(old_macsr); coldfire_set_macsr(old_macsr);
#endif #endif
return written * sizeof(short) * 2; return written;
} }
/* Given size bytes of input data, calculate the maximum number of bytes of /* Given count number of input samples, calculate the maximum number of
* output data that would be generated (the calculation is not entirely * samples of output data that would be generated (the calculation is not
* exact and rounds upwards to be on the safe side; during resampling, * entirely exact and rounds upwards to be on the safe side; during
* the number of samples generated depends on the current state of the * resampling, the number of samples generated depends on the current state
* resampler). * of the resampler).
*/ */
/* dsp_input_size MUST be called afterwards */ /* dsp_input_size MUST be called afterwards */
long dsp_output_size(long size) int dsp_output_count(int count)
{ {
dsp = &dsp_conf[current_codec]; dsp = &dsp_conf[current_codec];
if (dsp->sample_depth > NATIVE_DEPTH)
{
size /= 2;
}
if (dsp->frequency != NATIVE_FREQUENCY) if (dsp->frequency != NATIVE_FREQUENCY)
{ {
size = (long) ((((unsigned long) size * NATIVE_FREQUENCY) count = (int)(((unsigned long)count * NATIVE_FREQUENCY
+ (dsp->frequency - 1)) / dsp->frequency); + (dsp->frequency - 1)) / dsp->frequency);
} }
/* round to the next multiple of 2 (these are shorts) */ /* Now we have the resampled sample count which must not exceed
size = (size + 1) & ~1; * RESAMPLE_BUF_COUNT/2 to avoid resample buffer overflow. One
* must call dsp_input_count() to get the correct input sample
* count.
*/
if (count > RESAMPLE_BUF_COUNT/2)
count = RESAMPLE_BUF_COUNT/2;
if (dsp->stereo_mode == STEREO_MONO) return count;
{
size *= 2;
}
/* now we have the size in bytes for two resampled channels,
* and the size in (short) must not exceed RESAMPLE_BUF_SIZE to
* avoid resample buffer overflow. One must call dsp_input_size()
* to get the correct input buffer size. */
if (size > RESAMPLE_BUF_SIZE*2)
size = RESAMPLE_BUF_SIZE*2;
return size;
} }
/* Given size bytes of output buffer, calculate number of bytes of input /* Given count output samples, calculate number of input samples
* data that would be consumed in order to fill the output buffer. * that would be consumed in order to fill the output buffer.
*/ */
long dsp_input_size(long size) int dsp_input_count(int count)
{ {
dsp = &dsp_conf[current_codec]; dsp = &dsp_conf[current_codec];
/* convert to number of output stereo samples. */
size /= 2;
/* Mono means we need half input samples to fill the output buffer */ /* count is now the number of resampled input samples. Convert to
if (dsp->stereo_mode == STEREO_MONO)
size /= 2;
/* size is now the number of resampled input samples. Convert to
original input samples. */ original input samples. */
if (dsp->frequency != NATIVE_FREQUENCY) if (dsp->frequency != NATIVE_FREQUENCY)
{ {
/* Use the real resampling delta = /* Use the real resampling delta =
* (unsigned long) dsp->frequency * 65536 / NATIVE_FREQUENCY, and * dsp->frequency * 65536 / NATIVE_FREQUENCY, and
* round towards zero to avoid buffer overflows. */ * round towards zero to avoid buffer overflows. */
size = ((unsigned long)size * count = (int)(((unsigned long)count *
resample_data[current_codec].delta) >> 16; resample_data[current_codec].delta) >> 16);
} }
/* Convert back to bytes. */ return count;
if (dsp->sample_depth > NATIVE_DEPTH)
size *= 4;
else
size *= 2;
return size;
} }
int dsp_stereo_mode(void) int dsp_stereo_mode(void)

View file

@ -206,9 +206,9 @@ enum {
#define DIV64(x, y, z) (long)(((long long)(x) << (z))/(y)) #define DIV64(x, y, z) (long)(((long long)(x) << (z))/(y))
long dsp_process(char *dest, const char *src[], long size); int dsp_process(char *dest, const char *src[], int count);
long dsp_input_size(long size); int dsp_input_count(int count);
long dsp_output_size(long size); int dsp_output_count(int count);
int dsp_stereo_mode(void); int dsp_stereo_mode(void);
bool dsp_configure(int setting, void *value); bool dsp_configure(int setting, void *value);
void dsp_set_replaygain(bool always); void dsp_set_replaygain(bool always);

View file

@ -791,21 +791,20 @@ static bool prepare_insert(size_t length)
return true; return true;
} }
void* pcmbuf_request_buffer(size_t length, size_t *realsize) void* pcmbuf_request_buffer(int *count)
{ {
if (crossfade_init) if (crossfade_init)
crossfade_start(); crossfade_start();
if (crossfade_active) { if (crossfade_active) {
*realsize = MIN(length, PCMBUF_MIX_CHUNK); *count = MIN(*count, PCMBUF_MIX_CHUNK/4);
return fadebuf; return fadebuf;
} }
else else
{ {
if(prepare_insert(length)) if(prepare_insert(*count << 2))
{ {
size_t audiobuffer_index = audiobuffer_pos + audiobuffer_fillpos; size_t audiobuffer_index = audiobuffer_pos + audiobuffer_fillpos;
*realsize = length;
if (pcmbuf_size - audiobuffer_index >= PCMBUF_MIN_CHUNK) if (pcmbuf_size - audiobuffer_index >= PCMBUF_MIN_CHUNK)
{ {
/* Usual case, there's space here */ /* Usual case, there's space here */
@ -821,34 +820,31 @@ void* pcmbuf_request_buffer(size_t length, size_t *realsize)
} }
else else
{ {
*realsize = 0;
return NULL; return NULL;
} }
} }
} }
void* pcmbuf_request_voice_buffer(size_t length, size_t *realsize, bool mix) void* pcmbuf_request_voice_buffer(int *count, bool mix)
{ {
if (mix) if (mix)
{ {
if (pcmbuf_read == NULL) if (pcmbuf_read == NULL)
{ {
*realsize = 0;
return NULL; return NULL;
} }
else if (pcmbuf_mix_chunk || pcmbuf_read->link) else if (pcmbuf_mix_chunk || pcmbuf_read->link)
{ {
*realsize = MIN(length, PCMBUF_MIX_CHUNK); *count = MIN(*count, PCMBUF_MIX_CHUNK/4);
return voicebuf; return voicebuf;
} }
else else
{ {
*realsize = 0;
return NULL; return NULL;
} }
} }
else else
return pcmbuf_request_buffer(length, realsize); return pcmbuf_request_buffer(count);
} }
bool pcmbuf_is_crossfade_active(void) bool pcmbuf_is_crossfade_active(void)
@ -856,8 +852,10 @@ bool pcmbuf_is_crossfade_active(void)
return crossfade_active || crossfade_init; return crossfade_active || crossfade_init;
} }
void pcmbuf_write_complete(size_t length) void pcmbuf_write_complete(int count)
{ {
size_t length = (size_t)(unsigned int)count << 2;
if (crossfade_active) if (crossfade_active)
{ {
flush_crossfade(fadebuf, length); flush_crossfade(fadebuf, length);
@ -874,8 +872,10 @@ void pcmbuf_write_complete(size_t length)
} }
#if 0 #if 0
bool pcmbuf_insert_buffer(char *buf, size_t length) bool pcmbuf_insert_buffer(char *buf, int count)
{ {
size_t length = (size_t)(unsigned int)count << 2;
if (crossfade_active) if (crossfade_active)
{ {
flush_crossfade(buf, length); flush_crossfade(buf, length);
@ -980,7 +980,7 @@ int pcmbuf_mix_free(void)
return 100; return 100;
} }
void pcmbuf_mix_voice(size_t length) void pcmbuf_mix_voice(int count)
{ {
short *ibuf = (short *)voicebuf; short *ibuf = (short *)voicebuf;
short *obuf; short *obuf;
@ -998,9 +998,9 @@ void pcmbuf_mix_voice(size_t length)
obuf = (short *)pcmbuf_mix_chunk->addr; obuf = (short *)pcmbuf_mix_chunk->addr;
chunk_samples = pcmbuf_mix_chunk->size / 2; chunk_samples = pcmbuf_mix_chunk->size / 2;
length /= 2; count <<= 1;
while (length-- > 0) { while (count-- > 0) {
int sample = *ibuf++; int sample = *ibuf++;
if (pcmbuf_mix_sample >= chunk_samples) if (pcmbuf_mix_sample >= chunk_samples)
{ {

View file

@ -63,16 +63,16 @@ void pcmbuf_set_position_callback(void (*callback)(size_t size));
size_t pcmbuf_free(void); size_t pcmbuf_free(void);
unsigned int pcmbuf_get_latency(void); unsigned int pcmbuf_get_latency(void);
void pcmbuf_set_low_latency(bool state); void pcmbuf_set_low_latency(bool state);
void pcmbuf_write_complete(size_t length); void pcmbuf_write_complete(int count);
void* pcmbuf_request_buffer(size_t length, size_t *realsize); void* pcmbuf_request_buffer(int *count);
void* pcmbuf_request_voice_buffer(size_t length, size_t *realsize, bool mix); void* pcmbuf_request_voice_buffer(int *count, bool mix);
bool pcmbuf_is_crossfade_enabled(void); bool pcmbuf_is_crossfade_enabled(void);
void pcmbuf_crossfade_enable(bool on_off); void pcmbuf_crossfade_enable(bool on_off);
int pcmbuf_usage(void); int pcmbuf_usage(void);
int pcmbuf_mix_free(void); int pcmbuf_mix_free(void);
void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude); void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
void pcmbuf_mix_voice(size_t length); void pcmbuf_mix_voice(int count);
int pcmbuf_used_descs(void); int pcmbuf_used_descs(void);
int pcmbuf_descs(void); int pcmbuf_descs(void);

View file

@ -1022,81 +1022,60 @@ static const char * get_codec_filename(int cod_spec)
#ifdef PLAYBACK_VOICE #ifdef PLAYBACK_VOICE
static bool voice_pcmbuf_insert_split_callback( static bool voice_pcmbuf_insert_callback(
const void *ch1, const void *ch2, size_t length) const void *ch1, const void *ch2, int count)
{ {
const char* src[2]; const char *src[2] = { ch1, ch2 };
char *dest;
long input_size;
size_t output_size;
src[0] = ch1; while (count > 0)
src[1] = ch2;
if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
length *= 2; /* Length is per channel */
while (length)
{ {
long est_output_size = dsp_output_size(length); int out_count = dsp_output_count(count);
int inp_count;
while ((dest = pcmbuf_request_voice_buffer(est_output_size, char *dest;
&output_size, playing)) == NULL)
while ((dest = pcmbuf_request_voice_buffer(
&out_count, playing)) == NULL)
{ {
if (playing && audio_codec_loaded) if (playing && audio_codec_loaded)
swap_codec(); swap_codec();
else else
yield(); yield();
} }
/* Get the real input_size for output_size bytes, guarding /* Get the real input_size for output_size bytes, guarding
* against resampling buffer overflows. */ * against resampling buffer overflows. */
input_size = dsp_input_size(output_size); inp_count = dsp_input_count(out_count);
if (input_size <= 0) if (inp_count <= 0)
{ {
DEBUGF("Error: dsp_input_size(%ld=dsp_output_size(%ld))=%ld<=0\n", DEBUGF("Error: dsp_input_count(%ld=dsp_output_count(%ld))=%ld<=0\n",
output_size, length, input_size); out_count, count, inp_count);
/* If this happens, there are samples of codec data that don't /* If this happens, there are samples of codec data that don't
* become a number of pcm samples, and something is broken */ * become a number of pcm samples, and something is broken */
return false; return false;
} }
/* Input size has grown, no error, just don't write more than length */ /* Input size has grown, no error, just don't write more than length */
if ((size_t)input_size > length) if (inp_count > count)
input_size = length; inp_count = count;
output_size = dsp_process(dest, src, input_size); out_count = dsp_process(dest, src, inp_count);
if (playing) if (playing)
{ {
pcmbuf_mix_voice(output_size); pcmbuf_mix_voice(out_count);
if ((pcmbuf_usage() < 10 || pcmbuf_mix_free() < 30) && if ((pcmbuf_usage() < 10 || pcmbuf_mix_free() < 30) &&
audio_codec_loaded) audio_codec_loaded)
swap_codec(); swap_codec();
} }
else else
pcmbuf_write_complete(output_size); pcmbuf_write_complete(out_count);
length -= input_size; count -= inp_count;
} }
return true; return true;
} /* voice_pcmbuf_insert_split_callback */ } /* voice_pcmbuf_insert_callback */
static bool voice_pcmbuf_insert_callback(const char *buf, size_t length)
{
/* TODO: The audiobuffer API should probably be updated, and be based on
* pcmbuf_insert_split(). */
long real_length = length;
if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
length /= 2; /* Length is per channel */
/* Second channel is only used for non-interleaved stereo. */
return voice_pcmbuf_insert_split_callback(buf, buf + (real_length / 2),
length);
}
static void* voice_get_memory_callback(size_t *size) static void* voice_get_memory_callback(size_t *size)
{ {
@ -1321,30 +1300,22 @@ static void voice_thread(void)
#endif /* PLAYBACK_VOICE */ #endif /* PLAYBACK_VOICE */
/* --- Codec thread --- */ /* --- Codec thread --- */
static bool codec_pcmbuf_insert_callback(
static bool codec_pcmbuf_insert_split_callback( const void *ch1, const void *ch2, int count)
const void *ch1, const void *ch2, size_t length)
{ {
const char* src[2]; const char *src[2] = { ch1, ch2 };
char *dest;
long input_size;
size_t output_size;
src[0] = ch1; while (count > 0)
src[1] = ch2;
if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
length *= 2; /* Length is per channel */
while (length)
{ {
long est_output_size = dsp_output_size(length); int out_count = dsp_output_count(count);
int inp_count;
char *dest;
/* Prevent audio from a previous track from playing */ /* Prevent audio from a previous track from playing */
if (ci.new_track || ci.stop_codec) if (ci.new_track || ci.stop_codec)
return true; return true;
while ((dest = pcmbuf_request_buffer(est_output_size, while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
&output_size)) == NULL)
{ {
sleep(1); sleep(1);
if (ci.seek_time || ci.new_track || ci.stop_codec) if (ci.seek_time || ci.new_track || ci.stop_codec)
@ -1353,24 +1324,24 @@ static bool codec_pcmbuf_insert_split_callback(
/* Get the real input_size for output_size bytes, guarding /* Get the real input_size for output_size bytes, guarding
* against resampling buffer overflows. */ * against resampling buffer overflows. */
input_size = dsp_input_size(output_size); inp_count = dsp_input_count(out_count);
if (input_size <= 0) if (inp_count <= 0)
{ {
DEBUGF("Error: dsp_input_size(%ld=dsp_output_size(%ld))=%ld<=0\n", DEBUGF("Error: dsp_input_count(%ld=dsp_output_count(%ld))=%ld<=0\n",
output_size, length, input_size); out_count, count, inp_count);
/* If this happens, there are samples of codec data that don't /* If this happens, there are samples of codec data that don't
* become a number of pcm samples, and something is broken */ * become a number of pcm samples, and something is broken */
return false; return false;
} }
/* Input size has grown, no error, just don't write more than length */ /* Input size has grown, no error, just don't write more than length */
if ((size_t)input_size > length) if (inp_count > count)
input_size = length; inp_count = count;
output_size = dsp_process(dest, src, input_size); out_count = dsp_process(dest, src, inp_count);
pcmbuf_write_complete(output_size); pcmbuf_write_complete(out_count);
#ifdef PLAYBACK_VOICE #ifdef PLAYBACK_VOICE
if ((voice_is_playing || voice_thread_start) if ((voice_is_playing || voice_thread_start)
@ -1381,26 +1352,12 @@ static bool codec_pcmbuf_insert_split_callback(
swap_codec(); swap_codec();
} }
#endif #endif
length -= input_size; count -= inp_count;
} }
return true; return true;
} /* codec_pcmbuf_insert_split_callback */ } /* codec_pcmbuf_insert_callback */
static bool codec_pcmbuf_insert_callback(const char *buf, size_t length)
{
/* TODO: The audiobuffer API should probably be updated, and be based on
* pcmbuf_insert_split(). */
long real_length = length;
if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
length /= 2; /* Length is per channel */
/* Second channel is only used for non-interleaved stereo. */
return codec_pcmbuf_insert_split_callback(buf, buf + (real_length / 2),
length);
}
static void* codec_get_memory_callback(size_t *size) static void* codec_get_memory_callback(size_t *size)
{ {
@ -3523,7 +3480,6 @@ static void audio_playback_init(void)
/* Initialize codec api. */ /* Initialize codec api. */
ci.read_filebuf = codec_filebuf_callback; ci.read_filebuf = codec_filebuf_callback;
ci.pcmbuf_insert = codec_pcmbuf_insert_callback; ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
ci.pcmbuf_insert_split = codec_pcmbuf_insert_split_callback;
ci.get_codec_memory = codec_get_memory_callback; ci.get_codec_memory = codec_get_memory_callback;
ci.request_buffer = codec_request_buffer_callback; ci.request_buffer = codec_request_buffer_callback;
ci.advance_buffer = codec_advance_buffer_callback; ci.advance_buffer = codec_advance_buffer_callback;
@ -3543,7 +3499,6 @@ static void audio_playback_init(void)
memset(&id3_voice, 0, sizeof(struct mp3entry)); memset(&id3_voice, 0, sizeof(struct mp3entry));
ci_voice.read_filebuf = voice_filebuf_callback; ci_voice.read_filebuf = voice_filebuf_callback;
ci_voice.pcmbuf_insert = voice_pcmbuf_insert_callback; ci_voice.pcmbuf_insert = voice_pcmbuf_insert_callback;
ci_voice.pcmbuf_insert_split = voice_pcmbuf_insert_split_callback;
ci_voice.get_codec_memory = voice_get_memory_callback; ci_voice.get_codec_memory = voice_get_memory_callback;
ci_voice.request_buffer = voice_request_buffer_callback; ci_voice.request_buffer = voice_request_buffer_callback;
ci_voice.advance_buffer = voice_advance_buffer_callback; ci_voice.advance_buffer = voice_advance_buffer_callback;