diff options
author | Miguel Freitas <miguelfreitas@users.sourceforge.net> | 2002-07-01 13:51:26 +0000 |
---|---|---|
committer | Miguel Freitas <miguelfreitas@users.sourceforge.net> | 2002-07-01 13:51:26 +0000 |
commit | 152d605773745cf59f89a3575ed0aed3c8bd5b2e (patch) | |
tree | c9aed4af04b8293b45ed770aba10a87641dc90c7 /src/xine-engine/audio_out.c | |
parent | cd0b9cf91b6b7f14c085c2fc9f564ccfb65203b0 (diff) | |
download | xine-lib-152d605773745cf59f89a3575ed0aed3c8bd5b2e.tar.gz xine-lib-152d605773745cf59f89a3575ed0aed3c8bd5b2e.tar.bz2 |
improved audio resampling for limited featured sound cards (like the ones found in
laptops, supporting only 16 bits, only stereo etc). as added bonus of new "stackable"
conversion scheme fixes resampling for 8 bit streams.
(thanks Damien Clermonté for discussion and ideas)
note1: alsa patch untested.
note2: pass-through untested.
CVS patchset: 2187
CVS date: 2002/07/01 13:51:26
Diffstat (limited to 'src/xine-engine/audio_out.c')
-rw-r--r-- | src/xine-engine/audio_out.c | 253 |
1 files changed, 180 insertions, 73 deletions
diff --git a/src/xine-engine/audio_out.c b/src/xine-engine/audio_out.c index 47f440933..9767ba26b 100644 --- a/src/xine-engine/audio_out.c +++ b/src/xine-engine/audio_out.c @@ -17,7 +17,7 @@ * along with self program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: audio_out.c,v 1.59 2002/06/22 13:40:38 mroi Exp $ + * $Id: audio_out.c,v 1.60 2002/07/01 13:51:27 miguelfreitas Exp $ * * 22-8-2001 James imported some useful AC3 sections from the previous alsa driver. * (c) 2001 Andy Lo A Foe <andy@alsaplayer.org> @@ -231,7 +231,7 @@ static void ao_fill_gap (ao_instance_t *this, int64_t pts_len) { printf ("audio_out: inserting %d 0-frames to fill a gap of %lld pts\n", num_frames, pts_len); - if ((this->mode == AO_CAP_MODE_A52) || (this->mode == AO_CAP_MODE_AC5)) { + if ((this->output.mode == AO_CAP_MODE_A52) || (this->output.mode == AO_CAP_MODE_AC5)) { write_pause_burst(this,num_frames); return; } @@ -247,19 +247,48 @@ static void ao_fill_gap (ao_instance_t *this, int64_t pts_len) { } } -static void ensure_buffer_size (ao_instance_t *this, int size) +static void ensure_buffer_size (audio_buffer_t *buf, int bytes_per_frame, + int frames) { - if (this->frame_buffer_size < size) { - this->frame_buffer = realloc( this->frame_buffer, size ); - this->frame_buffer_size = size; + int size = bytes_per_frame * frames; + + if (buf->mem_size < size) { + buf->mem = realloc( buf->mem, size ); + buf->mem_size = size; } + buf->num_frames = frames; +} + +static audio_buffer_t * swap_frame_buffers ( ao_instance_t *this ) { + audio_buffer_t *tmp; + + tmp = this->frame_buf[1]; + this->frame_buf[1] = this->frame_buf[0]; + this->frame_buf[0] = tmp; + return this->frame_buf[0]; } +static int mode_channels( int mode ) { + switch( mode ) { + case AO_CAP_MODE_MONO: + return 1; + case AO_CAP_MODE_STEREO: + return 2; + case AO_CAP_MODE_4CHANNEL: + return 4; + case AO_CAP_MODE_5CHANNEL: + return 5; + case AO_CAP_MODE_5_1CHANNEL: + return 6; + } + return 0; +} + static void *ao_loop (void *this_gen) { ao_instance_t *this = (ao_instance_t *) this_gen; int64_t hw_vpts; - audio_buffer_t *buf; + audio_buffer_t *buf, *in_buf; int64_t gap; int delay; int64_t cur_time; @@ -274,7 +303,7 @@ static void *ao_loop (void *this_gen) { while ((this->audio_loop_running) || (!this->audio_loop_running && this->out_fifo->first)) { - buf = fifo_remove (this->out_fifo); + in_buf = buf = fifo_remove (this->out_fifo); bufs_since_sync++; #ifdef LOG @@ -300,7 +329,7 @@ static void *ao_loop (void *this_gen) { #endif /* External A52 decoder delay correction */ - if ((this->mode==AO_CAP_MODE_A52) || (this->mode==AO_CAP_MODE_AC5)) + if ((this->output.mode==AO_CAP_MODE_A52) || (this->output.mode==AO_CAP_MODE_AC5)) delay+=10; hw_vpts += delay * 1024 / this->frames_per_kpts; @@ -387,53 +416,104 @@ static void *ao_loop (void *this_gen) { printf ("audio_out: outputting %d frames\n", num_output_frames); #endif - /* check if resample is needed */ - if (((!this->do_resample) || (buf->num_frames == num_output_frames)) - && (this->mode != AO_CAP_MODE_A52) - && (this->mode != AO_CAP_MODE_AC5)) { - - this->driver->write (this->driver, buf->mem, - buf->num_frames ); - } else switch (this->mode) { - case AO_CAP_MODE_MONO: - ensure_buffer_size(this, 2*num_output_frames); - audio_out_resample_mono (buf->mem, buf->num_frames, - this->frame_buffer, num_output_frames); - this->driver->write(this->driver, this->frame_buffer, num_output_frames); - break; - case AO_CAP_MODE_STEREO: - ensure_buffer_size(this, 4*num_output_frames); - audio_out_resample_stereo (buf->mem, buf->num_frames, - this->frame_buffer, num_output_frames); - this->driver->write(this->driver, this->frame_buffer, num_output_frames); - break; - case AO_CAP_MODE_4CHANNEL: - ensure_buffer_size(this, 8*num_output_frames); - audio_out_resample_4channel (buf->mem, buf->num_frames, - this->frame_buffer, num_output_frames); - this->driver->write(this->driver, this->frame_buffer, num_output_frames); - break; - case AO_CAP_MODE_5CHANNEL: - ensure_buffer_size(this, 10*num_output_frames); - audio_out_resample_5channel (buf->mem, buf->num_frames, - this->frame_buffer, num_output_frames); - this->driver->write(this->driver, this->frame_buffer, num_output_frames); - break; - case AO_CAP_MODE_5_1CHANNEL: - ensure_buffer_size(this, 12*num_output_frames); - audio_out_resample_6channel (buf->mem, buf->num_frames, - this->frame_buffer, num_output_frames); - this->driver->write(this->driver, this->frame_buffer, num_output_frames); - break; - case AO_CAP_MODE_A52: - case AO_CAP_MODE_AC5: - this->driver->write(this->driver, buf->mem, buf->num_frames); - break; + /* convert 8 bit samples as needed */ + if( this->input.bits == 8 && + (this->do_resample || this->output.bits != 8 || + this->input.mode != this->output.mode ) ) { + ensure_buffer_size(this->frame_buf[1], 2*mode_channels(this->input.mode), + buf->num_frames ); + audio_out_resample_8to16((int8_t *)buf->mem, this->frame_buf[1]->mem, + mode_channels(this->input.mode) * buf->num_frames ); + buf = swap_frame_buffers(this); + } + + /* check if resampling may be skipped */ + if ( this->do_resample && + buf->num_frames != num_output_frames ) { + switch (this->input.mode) { + case AO_CAP_MODE_MONO: + ensure_buffer_size(this->frame_buf[1], 2, num_output_frames); + audio_out_resample_mono (buf->mem, buf->num_frames, + this->frame_buf[1]->mem, num_output_frames); + buf = swap_frame_buffers(this); + break; + case AO_CAP_MODE_STEREO: + ensure_buffer_size(this->frame_buf[1], 4, num_output_frames); + audio_out_resample_stereo (buf->mem, buf->num_frames, + this->frame_buf[1]->mem, num_output_frames); + buf = swap_frame_buffers(this); + break; + case AO_CAP_MODE_4CHANNEL: + ensure_buffer_size(this->frame_buf[1], 8, num_output_frames); + audio_out_resample_4channel (buf->mem, buf->num_frames, + this->frame_buf[1]->mem, num_output_frames); + buf = swap_frame_buffers(this); + break; + case AO_CAP_MODE_5CHANNEL: + ensure_buffer_size(this->frame_buf[1], 10, num_output_frames); + audio_out_resample_5channel (buf->mem, buf->num_frames, + this->frame_buf[1]->mem, num_output_frames); + buf = swap_frame_buffers(this); + break; + case AO_CAP_MODE_5_1CHANNEL: + ensure_buffer_size(this->frame_buf[1], 12, num_output_frames); + audio_out_resample_6channel (buf->mem, buf->num_frames, + this->frame_buf[1]->mem, num_output_frames); + buf = swap_frame_buffers(this); + break; + case AO_CAP_MODE_A52: + case AO_CAP_MODE_AC5: + /* pass-through modes: no resampling */ + break; + } + } + + /* mode conversion */ + if ( this->input.mode != this->output.mode ) { + switch (this->input.mode) { + case AO_CAP_MODE_MONO: + if( this->output.mode == AO_CAP_MODE_STEREO ) { + ensure_buffer_size(this->frame_buf[1], 4, buf->num_frames ); + audio_out_resample_monotostereo(buf->mem, this->frame_buf[1]->mem, + buf->num_frames ); + buf = swap_frame_buffers(this); + } + break; + case AO_CAP_MODE_STEREO: + if( this->output.mode == AO_CAP_MODE_MONO ) { + ensure_buffer_size(this->frame_buf[1], 2, buf->num_frames ); + audio_out_resample_stereotomono(buf->mem, this->frame_buf[1]->mem, + buf->num_frames ); + buf = swap_frame_buffers(this); + } + break; + case AO_CAP_MODE_4CHANNEL: + break; + case AO_CAP_MODE_5CHANNEL: + break; + case AO_CAP_MODE_5_1CHANNEL: + break; + case AO_CAP_MODE_A52: + case AO_CAP_MODE_AC5: + break; + } } + + /* convert back to 8 bits after resampling */ + if( this->output.bits == 8 && (this->do_resample || + this->input.mode != this->output.mode) ) { + ensure_buffer_size(this->frame_buf[1], 1*mode_channels(this->output.mode), + buf->num_frames ); + audio_out_resample_16to8(buf->mem, (int8_t *)this->frame_buf[1]->mem, + mode_channels(this->output.mode) * buf->num_frames ); + buf = swap_frame_buffers(this); + } + + this->driver->write (this->driver, buf->mem, buf->num_frames ); } pthread_mutex_unlock( &this->driver_lock ); - fifo_append (this->free_fifo, buf); + fifo_append (this->free_fifo, in_buf); } @@ -455,6 +535,29 @@ static int ao_open(ao_instance_t *this, "audio_out: stream audio format is %d kHz sampling rate, %d bits. mode is %d.\n", rate, bits, mode); + this->input.mode = mode; + this->input.rate = rate; + this->input.bits = bits; + + /* not all drivers/cards support 8 bits */ + if( this->input.bits == 8 && + !(this->driver->get_capabilities(this->driver) & AO_CAP_8BITS) ) { + bits = 16; + printf("audio_out: 8 bits not supported by driver, converting to 16 bits.\n"); + } + + /* provide mono->stereo and stereo->mono conversions */ + if( this->input.mode == AO_CAP_MODE_MONO && + !(this->driver->get_capabilities(this->driver) & AO_CAP_MODE_MONO) ) { + mode = AO_CAP_MODE_STEREO; + printf("audio_out: mono not supported by driver, converting to stereo.\n"); + } + if( this->input.mode == AO_CAP_MODE_STEREO && + !(this->driver->get_capabilities(this->driver) & AO_CAP_MODE_STEREO) ) { + mode = AO_CAP_MODE_MONO; + printf("audio_out: stereo not supported by driver, converting to mono.\n"); + } + pthread_mutex_lock( &this->driver_lock ); output_sample_rate=this->driver->open(this->driver,bits,(this->force_rate ? this->force_rate : rate),mode); pthread_mutex_unlock( &this->driver_lock ); @@ -466,11 +569,10 @@ static int ao_open(ao_instance_t *this, printf("audio_out: output sample rate %d\n", output_sample_rate); - this->mode = mode; - this->input_frame_rate = rate; - this->bits = bits; this->last_audio_vpts = 0; - this->output_frame_rate = output_sample_rate; + this->output.mode = mode; + this->output.rate = output_sample_rate; + this->output.bits = bits; switch (this->resample_conf) { case 1: /* force off */ @@ -480,24 +582,16 @@ static int ao_open(ao_instance_t *this, this->do_resample = 1; break; default: /* AUTO */ - this->do_resample = this->output_frame_rate != this->input_frame_rate; + this->do_resample = this->output.rate != this->input.rate; } - /* HACK: we do not have resample functions for 8-bit audio */ - if (this->bits==8) - this->do_resample = 0; - if (this->do_resample) printf("audio_out: will resample audio from %d to %d\n", - this->input_frame_rate, this->output_frame_rate); - - pthread_mutex_lock( &this->driver_lock ); - this->num_channels = this->driver->num_channels(this->driver); - pthread_mutex_unlock( &this->driver_lock ); + this->input.rate, this->output.rate); - this->frame_rate_factor = (double) this->output_frame_rate / (double) this->input_frame_rate; - this->audio_step = (uint32_t) 90000 * (uint32_t) 32768 / this->input_frame_rate; - this->frames_per_kpts = this->output_frame_rate * 1024 / 90000; + this->frame_rate_factor = (double) this->output.rate / (double) this->input.rate; + this->audio_step = (uint32_t) 90000 * (uint32_t) 32768 / this->input.rate; + this->frames_per_kpts = this->output.rate * 1024 / 90000; #ifdef LOG printf ("audio_out : audio_step %d pts per 32768 frames\n", this->audio_step); #endif @@ -525,7 +619,7 @@ static int ao_open(ao_instance_t *this, } else printf ("audio_out: thread created\n"); - return this->output_frame_rate; + return this->output.rate; } static audio_buffer_t *ao_get_buffer (ao_instance_t *this) { @@ -616,7 +710,10 @@ static void ao_exit(ao_instance_t *this) { this->driver->exit(this->driver); pthread_mutex_unlock( &this->driver_lock ); - free (this->frame_buffer); + free (this->frame_buf[0]->mem); + free (this->frame_buf[0]); + free (this->frame_buf[1]->mem); + free (this->frame_buf[1]); free (this->zero_space); buf = this->free_fifo->first; @@ -719,8 +816,6 @@ ao_instance_t *ao_new_instance (ao_driver_t *driver, xine_t *xine) { this->control = ao_control; this->audio_loop_running = 0; this->audio_paused = 0; - this->frame_buffer = xine_xmalloc (4 * AUDIO_BUF_SIZE); - this->frame_buffer_size = 4 * AUDIO_BUF_SIZE; this->zero_space = xine_xmalloc (ZERO_BUF_SIZE * 2 * 6); this->gap_tolerance = driver->get_gap_tolerance (this->driver); @@ -749,6 +844,18 @@ ao_instance_t *ao_new_instance (ao_driver_t *driver, xine_t *xine) { fifo_append (this->free_fifo, buf); } + + /* buffers used for audio conversions */ + for (i=0; i<2; i++) { + + audio_buffer_t *buf; + + buf = (audio_buffer_t *) malloc (sizeof (audio_buffer_t)); + buf->mem = malloc (4*AUDIO_BUF_SIZE); + buf->mem_size = 4*AUDIO_BUF_SIZE; + + this->frame_buf[i] = buf; + } /* * Set audio volume to latest used one ? |