/* 2004.02.01 first released source code for IOMP */ /* * Copyright (C) 2000-2003 the xine project * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * 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.2 2003/11/25 04:21:47 georgedon Exp $ * * 22-8-2001 James imported some useful AC3 sections from the previous alsa driver. * (c) 2001 Andy Lo A Foe * 20-8-2001 First implementation of Audio sync and Audio driver separation. * (c) 2001 James Courtier-Dutton James@superbug.demon.co.uk */ /* * General Programming Guidelines: - * New concept of an "audio_frame". * An audio_frame consists of all the samples required to fill every * audio channel to a full amount of bits. * So, it does not mater how many bits per sample, or how many audio channels * are being used, the number of audio_frames is the same. * E.g. 16 bit stereo is 4 bytes, but one frame. * 16 bit 5.1 surround is 12 bytes, but one frame. * The purpose of this is to make the audio_sync code a lot more readable, * rather than having to multiply by the amount of channels all the time * when dealing with audio_bytes instead of audio_frames. * * The number of samples passed to/from the audio driver is also sent * in units of audio_frames. * * Currently, James has tested with OSS: Standard stereo out, SPDIF PCM, SPDIF AC3 * ALSA: Standard stereo out * No testing has been done of ALSA SPDIF AC3 or any 4,5,5.1 channel output. * Currently, I don't think resampling functions, as I cannot test it. * * equalizer based on * * PCM time-domain equalizer * * Copyright (C) 2002 Felipe Rivera * * heavily modified by guenter bartsch 2003 for use in libxine * */ extern char *XINE_SEG_ERROR_FILE; //#define LOG_SEG_ERROR sprintf(XINE_SEG_ERROR_FILE,"%s (%d)",__FILE__,__LINE__); #define LOG_SEG_ERROR #ifndef __sun /* required for swab() */ #define _XOPEN_SOURCE 500 #endif /* required for FNDELAY decl */ #define _BSD_SOURCE 1 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #define XINE_ENABLE_EXPERIMENTAL_FEATURES /********** logging **********/ #define LOG_MODULE "audio_out" /* #define LOG_VERBOSE */ /* #define LOG */ #define LOG_RESAMPLE_SYNC 0 #include "xine_internal.h" #include "xineutils.h" #include "audio_out.h" #include "resample.h" #include "metronom.h" #define NUM_AUDIO_BUFFERS 32 #define AUDIO_BUF_SIZE 32768 #define ZERO_BUF_SIZE 5000 /* By adding gap errors (difference between reported and expected * sound card clock) into metronom's vpts_offset we can use its * smoothing algorithms to correct sound card clock drifts. * obs: previously this error was added to xine scr. * * audio buf ---> metronom --> audio fifo --> (buf->vpts - hw_vpts) * (vpts_offset + error) gap * <---------- control --------------| * * Unfortunately audio fifo adds a large delay to our closed loop. * * The defines below are designed to avoid updating the metronom too fast. * - it will only be updated 1 time per second (so it has a chance of * distributing the error for several frames). * - it will only be updated 2 times for the whole audio fifo size * length (so the control will wait to see the feedback effect) * - each update will be of gap/SYNC_GAP_RATE. * * Sound card clock correction can only provide smooth playback for * errors < 1% nominal rate. For bigger errors (bad streams) audio * buffers may be dropped or gaps filled with silence. */ #define SYNC_TIME_INVERVAL (1 * 90000) #define SYNC_BUF_INTERVAL NUM_AUDIO_BUFFERS / 2 #define SYNC_GAP_RATE 4 /* Alternative for metronom feedback: fix sound card clock drift * by resampling all audio data, so that the sound card keeps in * sync with the system clock. This may help, if one uses a DXR3/H+ * decoder board. Those have their own clock (which serves as xine's * master clock) and can only operate at fixed frame rates (if you * want smooth playback). Resampling then avoids A/V sync problems, * gaps filled with 0-frames and jerky video playback due to different * clock speeds of the sound card and DXR3/H+. */ #define RESAMPLE_SYNC_WINDOW 50 #define RESAMPLE_MAX_GAP_DIFF 150 #define RESAMPLE_REDUCE_GAP_THRESHOLD 200 typedef struct { double last_factor; int window; int reduce_gap; uint64_t window_duration, last_vpts; int64_t recent_gap[8], last_avg_gap; int valid; } resample_sync_t; /* * equalizer stuff */ #define EQ_BANDS 10 #define EQ_CHANNELS 8 #define FP_FRBITS 28 #define EQ_REAL(x) ((int)((x) * (1 << FP_FRBITS))) typedef struct { int beta; int alpha; int gamma; } sIIRCoefficients; /* Coefficient history for the IIR filter */ typedef struct { int x[3]; /* x[n], x[n-1], x[n-2] */ int y[3]; /* y[n], y[n-1], y[n-2] */ }sXYData; static sIIRCoefficients iir_cf[] = { /* 31 Hz*/ { EQ_REAL(9.9691562441e-01), EQ_REAL(1.5421877947e-03), EQ_REAL(1.9968961468e+00) }, /* 62 Hz*/ { EQ_REAL(9.9384077546e-01), EQ_REAL(3.0796122698e-03), EQ_REAL(1.9937629855e+00) }, /* 125 Hz*/ { EQ_REAL(9.8774277725e-01), EQ_REAL(6.1286113769e-03), EQ_REAL(1.9874275518e+00) }, /* 250 Hz*/ { EQ_REAL(9.7522112569e-01), EQ_REAL(1.2389437156e-02), EQ_REAL(1.9739682661e+00) }, /* 500 Hz*/ { EQ_REAL(9.5105628526e-01), EQ_REAL(2.4471857368e-02), EQ_REAL(1.9461077269e+00) }, /* 1k Hz*/ { EQ_REAL(9.0450844499e-01), EQ_REAL(4.7745777504e-02), EQ_REAL(1.8852109613e+00) }, /* 2k Hz*/ { EQ_REAL(8.1778971701e-01), EQ_REAL(9.1105141497e-02), EQ_REAL(1.7444877599e+00) }, /* 4k Hz*/ { EQ_REAL(6.6857185264e-01), EQ_REAL(1.6571407368e-01), EQ_REAL(1.4048592171e+00) }, /* 8k Hz*/ { EQ_REAL(4.4861333678e-01), EQ_REAL(2.7569333161e-01), EQ_REAL(6.0518718075e-01) }, /* 16k Hz*/ { EQ_REAL(2.4201241845e-01), EQ_REAL(3.7899379077e-01), EQ_REAL(-8.0847117831e-01) }, }; typedef struct { xine_audio_port_t ao; /* public part */ /* private stuff */ ao_driver_t *driver; pthread_mutex_t driver_lock; int driver_open; metronom_clock_t *clock; xine_t *xine; xine_list_t *streams; pthread_mutex_t streams_lock; int audio_loop_running; int grab_only; /* => do not start thread, frontend will consume samples */ int audio_paused; pthread_t audio_thread; int64_t audio_step; /* pts per 32 768 samples (sample = #bytes/2) */ int32_t frames_per_kpts; /* frames per 1024/90000 sec */ ao_format_t input, output; /* format conversion done at audio_out.c */ double frame_rate_factor; double output_frame_excess; /* used to keep track of 'half' frames */ int av_sync_method_conf; resample_sync_t resample_sync_info; int resample_sync_method; /* fix sound card clock drift by resampling */ double resample_sync_factor; /* correct buffer length by this factor * to sync audio hardware to (dxr3) clock */ int resample_conf; uint32_t force_rate; /* force audio output rate to this value if non-zero */ int do_resample; int gap_tolerance; audio_fifo_t *free_fifo; audio_fifo_t *out_fifo; int64_t last_audio_vpts; audio_buffer_t *frame_buf[2]; /* two buffers for "stackable" conversions */ int16_t *zero_space; int64_t passthrough_offset; int flush_audio_driver; pthread_mutex_t flush_audio_driver_lock; pthread_cond_t flush_audio_driver_reached; int discard_buffers; /* some built-in audio filters */ int do_compress; double compression_factor; /* current compression */ double compression_factor_max; /* user limit on compression */ int do_amp; double amp_factor; /* 10-band equalizer */ int do_equ; int eq_gain[EQ_BANDS]; int eq_preamp; int eq_i; int eq_j; int eq_k; sXYData eq_data_history[EQ_BANDS][EQ_CHANNELS]; } aos_t; struct audio_fifo_s { audio_buffer_t *first; audio_buffer_t *last; int num_buffers; pthread_mutex_t mutex; pthread_cond_t not_empty; pthread_cond_t empty; }; static audio_fifo_t *fifo_new (void) { audio_fifo_t *fifo; fifo = (audio_fifo_t *) xine_xmalloc (sizeof (audio_fifo_t)); if (!fifo) { printf ("audio_out: out of memory!\n"); return NULL; } fifo->first = NULL; fifo->last = NULL; fifo->num_buffers = 0; pthread_mutex_init (&fifo->mutex, NULL); pthread_cond_init (&fifo->not_empty, NULL); pthread_cond_init (&fifo->empty, NULL); return fifo; } static void fifo_append_int (audio_fifo_t *fifo, audio_buffer_t *buf) { /* buf->next = NULL; */ XINE_ASSERT(!buf->next, "Next audio buffer is not NULL."); if (!fifo->first) { fifo->first = buf; fifo->last = buf; fifo->num_buffers = 1; } else { fifo->last->next = buf; fifo->last = buf; fifo->num_buffers++; } pthread_cond_signal (&fifo->not_empty); } static void fifo_append (audio_fifo_t *fifo, audio_buffer_t *buf) { pthread_mutex_lock (&fifo->mutex); fifo_append_int (fifo, buf); pthread_mutex_unlock (&fifo->mutex); } static audio_buffer_t *fifo_remove_int (audio_fifo_t *fifo) { audio_buffer_t *buf; while (!fifo->first) { pthread_cond_signal (&fifo->empty); pthread_cond_wait (&fifo->not_empty, &fifo->mutex); } buf = fifo->first; if (buf) { fifo->first = buf->next; if (!fifo->first) { fifo->last = NULL; fifo->num_buffers = 0; } else fifo->num_buffers--; } buf->next = NULL; return buf; } static audio_buffer_t *fifo_remove (audio_fifo_t *fifo) { audio_buffer_t *buf; pthread_mutex_lock (&fifo->mutex); buf = fifo_remove_int(fifo); pthread_mutex_unlock (&fifo->mutex); return buf; } /* This function is currently not needed */ #if 0 static int fifo_num_buffers (audio_fifo_t *fifo) { int ret; pthread_mutex_lock (&fifo->mutex); ret = fifo->num_buffers; pthread_mutex_unlock (&fifo->mutex); return ret; } #endif static void fifo_wait_empty (audio_fifo_t *fifo) { pthread_mutex_lock (&fifo->mutex); pthread_cond_signal (&fifo->not_empty); pthread_cond_wait (&fifo->empty, &fifo->mutex); pthread_mutex_unlock (&fifo->mutex); } static void write_pause_burst(aos_t *this, uint32_t num_frames) { int error = 0; unsigned char buf[8192]; unsigned short *sbuf = (unsigned short *)&buf[0]; sbuf[0] = 0xf872; sbuf[1] = 0x4e1f; if (error == 0) /* Audio ES Channel empty, wait for DD Decoder or pause */ sbuf[2] = 0x0003; else /* user stop, skip or error */ sbuf[2] = 0x0103; sbuf[3] = 0x0020; sbuf[4] = 0x0000; sbuf[5] = 0x0000; memset(&sbuf[6], 0, 6144 - 96); while (num_frames > 1536) { pthread_mutex_lock( &this->driver_lock ); if(this->driver_open) this->driver->write(this->driver, sbuf, 1536); pthread_mutex_unlock( &this->driver_lock ); num_frames -= 1536; } } static void ao_fill_gap (aos_t *this, int64_t pts_len) { int num_frames ; num_frames = pts_len * this->frames_per_kpts / 1024; xprintf (this->xine, XINE_VERBOSITY_LOG, "inserting %d 0-frames to fill a gap of %" PRId64 " pts\n", num_frames, pts_len); if ((this->output.mode == AO_CAP_MODE_A52) || (this->output.mode == AO_CAP_MODE_AC5)) { write_pause_burst(this,num_frames); return; } while (num_frames > 0 && !this->discard_buffers) { if (num_frames > ZERO_BUF_SIZE) { pthread_mutex_lock( &this->driver_lock ); if(this->driver_open) this->driver->write(this->driver, this->zero_space, ZERO_BUF_SIZE); pthread_mutex_unlock( &this->driver_lock ); num_frames -= ZERO_BUF_SIZE; } else { pthread_mutex_lock( &this->driver_lock ); if(this->driver_open) this->driver->write(this->driver, this->zero_space, num_frames); pthread_mutex_unlock( &this->driver_lock ); num_frames = 0; } } } static void ensure_buffer_size (audio_buffer_t *buf, int bytes_per_frame, int frames) { 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 ( aos_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_4_1CHANNEL: case AO_CAP_MODE_5CHANNEL: case AO_CAP_MODE_5_1CHANNEL: return 6; } return 0; } static void audio_filter_compress (aos_t *this, int16_t *mem, int num_frames) { int i, maxs; double f_max; int num_channels; num_channels = mode_channels (this->input.mode); if (!num_channels) return; maxs = 0; /* measure */ for (i=0; imaxs) maxs = sample; } /* calc maximum possible & allowed factor */ if (maxs>0) { f_max = 32767.0 / maxs; this->compression_factor = this->compression_factor * 0.999 + f_max * 0.001; if (this->compression_factor > f_max) this->compression_factor = f_max; if (this->compression_factor > this->compression_factor_max) this->compression_factor = this->compression_factor_max; } else f_max = 1.0; lprintf ("max=%d f_max=%f compression_factor=%f\n", maxs, f_max, this->compression_factor); /* apply it */ for (i=0; icompression_factor * this->amp_factor; } } static void audio_filter_amp (aos_t *this, int16_t *mem, int num_frames) { int i; int num_channels; num_channels = mode_channels (this->input.mode); if (!num_channels) return; for (i=0; iamp_factor; } } static void audio_filter_equalize (aos_t *this, int16_t *data, int num_frames) { int index, band, channel; int halflength, length; int out[EQ_CHANNELS], scaledpcm[EQ_CHANNELS]; int64_t l; int num_channels; num_channels = mode_channels (this->input.mode); if (!num_channels) return; halflength = num_frames * 2; length = num_frames * 4; for (index = 0; index < halflength; index+=2) { for (channel = 0; channel < num_channels; channel++) { /* Convert the PCM sample to a fixed fraction */ scaledpcm[channel] = ((int)data[index+channel]) << (FP_FRBITS-16-1); out[channel] = 0; /* For each band */ for (band = 0; band < EQ_BANDS; band++) { this->eq_data_history[band][channel].x[this->eq_i] = scaledpcm[channel]; l = (int64_t)iir_cf[band].alpha * (int64_t)(this->eq_data_history[band][channel].x[this->eq_i] - this->eq_data_history[band][channel].x[this->eq_k]) + (int64_t)iir_cf[band].gamma * (int64_t)this->eq_data_history[band][channel].y[this->eq_j] - (int64_t)iir_cf[band].beta * (int64_t)this->eq_data_history[band][channel].y[this->eq_k]; this->eq_data_history[band][channel].y[this->eq_i] = (int)(l >> FP_FRBITS); l = (int64_t)this->eq_data_history[band][channel].y[this->eq_i] * (int64_t)this->eq_gain[band]; out[channel] += (int)(l >> FP_FRBITS); } /* Volume scaling adjustment by 2^-2 */ out[channel] += (scaledpcm[channel] >> 2); /* Adjust the fixed point fraction value to a PCM sample */ /* Scale back to a 16bit signed int */ out[channel] >>= (FP_FRBITS-16); /* Limit the output */ if (out[channel] < -32768) data[index+channel] = -32768; else if (out[channel] > 32767) data[index+channel] = 32767; else data[index+channel] = out[channel]; } this->eq_i++; this->eq_j++; this->eq_k++; if (this->eq_i == 3) this->eq_i = 0; else if (this->eq_j == 3) this->eq_j = 0; else this->eq_k = 0; } } static audio_buffer_t* prepare_samples( aos_t *this, audio_buffer_t *buf) { double acc_output_frames; int num_output_frames ; /* * volume / compressor / equalizer filter */ if (this->input.bits == 16) { if (this->do_equ) audio_filter_equalize (this, buf->mem, buf->num_frames); if (this->do_compress) audio_filter_compress (this, buf->mem, buf->num_frames); if (this->do_amp) audio_filter_amp (this, buf->mem, buf->num_frames); } /* * resample and output audio data */ /* calculate number of output frames (after resampling) */ acc_output_frames = (double) buf->num_frames * this->frame_rate_factor * this->resample_sync_factor + this->output_frame_excess; /* Truncate to an integer */ num_output_frames = acc_output_frames; /* Keep track of the amount truncated */ this->output_frame_excess = acc_output_frames - (double) num_output_frames; if ( this->output_frame_excess != 0 && !this->do_resample && !this->resample_sync_method) this->output_frame_excess = 0; lprintf ("outputting %d frames\n", num_output_frames); /* convert 8 bit samples as needed */ if ( this->input.bits == 8 && (this->resample_sync_method || 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->resample_sync_method || 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_4_1CHANNEL: case AO_CAP_MODE_5CHANNEL: 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->resample_sync_method || 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); } return buf; } static int resample_rate_adjust(aos_t *this, int64_t gap, audio_buffer_t *buf) { /* Calculates the drift factor used to resample the audio data to * keep in sync with system (or dxr3) clock. * * To compensate the sound card drift it is necessary to know, how many audio * frames need to be added (or removed) via resampling. This function waits for * RESAMPLE_SYNC_WINDOW audio buffers to be sent to the card and keeps track * of their total duration in vpts. With the measured gap difference between * the reported gap values at the beginning and at the end of this window the * required resampling factor is calculated: * * resample_factor = (duration + gap_difference) / duration * * This factor is then used in prepare_samples() to resample the audio * buffers as needed so we keep in sync with the system (or dxr3) clock. */ resample_sync_t *info = &this->resample_sync_info; int64_t avg_gap = 0; double factor; double diff; double duration; int i; if (abs(gap) > AO_MAX_GAP) { /* drop buffers or insert 0-frames in audio out loop */ info->valid = 0; return -1; } if ( ! info->valid) { this->resample_sync_factor = 1.0; info->window = 0; info->reduce_gap = 0; info->last_avg_gap = gap; info->last_factor = 0; info->window_duration = info->last_vpts = 0; info->valid = 1; } /* calc average gap (to compensate small errors during measurement) */ for (i = 0; i < 7; i++) info->recent_gap[i] = info->recent_gap[i + 1]; info->recent_gap[i] = gap; for (i = 0; i < 8; i++) avg_gap += info->recent_gap[i]; avg_gap /= 8; /* gap too big? Change sample rate so that gap converges towards 0. */ if (abs(avg_gap) > RESAMPLE_REDUCE_GAP_THRESHOLD && !info->reduce_gap) { info->reduce_gap = 1; this->resample_sync_factor = (avg_gap < 0) ? 0.995 : 1.005; llprintf (LOG_RESAMPLE_SYNC, "sample rate adjusted to reduce gap: gap=%" PRId64 "\n", avg_gap); return 0; } else if (info->reduce_gap && abs(avg_gap) < 50) { info->reduce_gap = 0; info->valid = 0; llprintf (LOG_RESAMPLE_SYNC, "gap successfully reduced\n"); return 0; } else if (info->reduce_gap) { /* re-check, because the gap might suddenly change its sign, * also slow down, when getting close to zero (-300 300) this->resample_sync_factor = (avg_gap < 0) ? 0.995 : 1.005; else this->resample_sync_factor = (avg_gap < 0) ? 0.998 : 1.002; return 0; } if (info->window > RESAMPLE_SYNC_WINDOW) { /* adjust drift correction */ int64_t gap_diff = avg_gap - info->last_avg_gap; if (gap_diff < RESAMPLE_MAX_GAP_DIFF) { #if LOG_RESAMPLE_SYNC int num_frames; /* if we are already resampling to a different output rate, consider * this during calculation */ num_frames = (this->do_resample) ? (buf->num_frames * this->frame_rate_factor) : buf->num_frames; printf("audio_out: gap=%5" PRId64 "; gap_diff=%5" PRId64 "; frame_diff=%3.0f; drift_factor=%f\n", avg_gap, gap_diff, num_frames * info->window * info->last_factor, this->resample_sync_factor); #endif /* we want to add factor * num_frames to each buffer */ diff = gap_diff; duration = (int64_t)info->window_duration + (int64_t)info->last_factor; factor = diff / duration; info->last_factor = factor; this->resample_sync_factor = 1.0 + factor; info->last_avg_gap = avg_gap; info->window_duration = 0; info->window = 0; } else info->valid = 0; } else { /* collect data for next adjustment */ if (info->window > 0) info->window_duration += buf->vpts - info->last_vpts; info->last_vpts = buf->vpts; info->window++; } return 0; } static int ao_change_settings(aos_t *this, uint32_t bits, uint32_t rate, int mode); /* Audio output loop: - * 1) Check for pause. * 2) Make sure audio hardware is in RUNNING state. * 3) Get delay * 4) Do drop, 0-fill or output samples. * 5) Go round loop again. */ static void *ao_loop (void *this_gen) { aos_t *this = (aos_t *) this_gen; int64_t hw_vpts; audio_buffer_t *in_buf, *out_buf; int64_t gap; int64_t delay; int64_t cur_time; int64_t last_sync_time; int bufs_since_sync; int result; last_sync_time = bufs_since_sync = 0; in_buf = NULL; while ((this->audio_loop_running) || (!this->audio_loop_running && this->out_fifo->first)) { /* * get buffer to process for this loop iteration */ if (!in_buf) { lprintf ("loop: get buf from fifo\n"); in_buf = fifo_remove (this->out_fifo); bufs_since_sync++; lprintf ("got a buffer\n"); } pthread_mutex_lock(&this->flush_audio_driver_lock); if (this->flush_audio_driver) { this->ao.control(&this->ao, AO_CTRL_FLUSH_BUFFERS); this->flush_audio_driver--; pthread_cond_broadcast(&this->flush_audio_driver_reached); } if (this->discard_buffers) { fifo_append (this->free_fifo, in_buf); in_buf = NULL; pthread_mutex_unlock(&this->flush_audio_driver_lock); continue; } pthread_mutex_unlock(&this->flush_audio_driver_lock); /* * wait until user unpauses stream * audio_paused == 1 means we are playing at a different speed * them we must process buffers otherwise the entire engine will stop. */ if ( this->audio_paused && this->audio_loop_running ) { if (this->audio_paused == 1) { LOG_SEG_ERROR cur_time = this->clock->get_current_time (this->clock); LOG_SEG_ERROR if (in_buf->vpts < cur_time ) { lprintf ("loop: next fifo\n"); fifo_append (this->free_fifo, in_buf); in_buf = NULL; LOG_SEG_ERROR continue; } } LOG_SEG_ERROR lprintf ("loop:pause: I feel sleepy (%d buffers).\n", this->out_fifo->num_buffers); xine_usec_sleep (10000); lprintf ("loop:pause: I wake up.\n"); continue; } LOG_SEG_ERROR /* change driver's settings as needed */ pthread_mutex_lock( &this->driver_lock ); if( in_buf && in_buf->num_frames ) { if( !this->driver_open || in_buf->format.bits != this->input.bits || in_buf->format.rate != this->input.rate || in_buf->format.mode != this->input.mode ) { lprintf("audio format has changed\n"); ao_change_settings(this, in_buf->format.bits, in_buf->format.rate, in_buf->format.mode); } } LOG_SEG_ERROR if(this->driver_open) { delay = this->driver->delay(this->driver); while (delay < 0 && this->audio_loop_running) { /* Get the audio card into RUNNING state. */ pthread_mutex_unlock( &this->driver_lock ); ao_fill_gap (this, 10000); /* FIXME, this PTS of 1000 should == period size */ pthread_mutex_lock( &this->driver_lock ); delay = this->driver->delay(this->driver); } } else delay = 0; pthread_mutex_unlock( &this->driver_lock ); LOG_SEG_ERROR cur_time = this->clock->get_current_time (this->clock); LOG_SEG_ERROR /* we update current_extra_info if either there is no video stream that could do that * or if the current_extra_info is getting too much out of date */ if( in_buf && in_buf->stream && (!in_buf->stream->video_decoder_plugin || (cur_time - in_buf->stream->current_extra_info->vpts) > 30000 )) { pthread_mutex_lock( &in_buf->stream->current_extra_info_lock ); extra_info_merge( in_buf->stream->current_extra_info, in_buf->extra_info ); pthread_mutex_unlock( &in_buf->stream->current_extra_info_lock ); } LOG_SEG_ERROR /* * where, in the timeline is the "end" of the * hardware audio buffer at the moment? */ hw_vpts = cur_time; lprintf ("current delay is %" PRId64 ", current time is %" PRId64 "\n", delay, cur_time); LOG_SEG_ERROR /* External A52 decoder delay correction */ if ((this->output.mode==AO_CAP_MODE_A52) || (this->output.mode==AO_CAP_MODE_AC5)) delay += this->passthrough_offset; if(this->frames_per_kpts) hw_vpts += (delay * 1024) / this->frames_per_kpts; LOG_SEG_ERROR /* * calculate gap: */ gap = in_buf->vpts - hw_vpts; lprintf ("hw_vpts : %" PRId64 " buffer_vpts : %" PRId64 " gap : %" PRId64 "\n", hw_vpts, in_buf->vpts, gap); if (this->resample_sync_method) { /* Correct sound card drift via resampling. If gap is too big to * be corrected this way, we use the fallback: drop/insert frames. * This function only calculates the drift correction factor. The * actual resampling is done by prepare_samples(). */ resample_rate_adjust(this, gap, in_buf); } else { this->resample_sync_factor = 1.0; } LOG_SEG_ERROR /* * output audio data synced to master clock */ if (gap < (-1 * AO_MAX_GAP) || !in_buf->num_frames ) { /* drop package */ lprintf ("loop: drop package, next fifo\n"); fifo_append (this->free_fifo, in_buf); lprintf ("audio package (vpts = %" PRId64 ", gap = %" PRId64 ") dropped\n", in_buf->vpts, gap); in_buf = NULL; LOG_SEG_ERROR /* for small gaps ( tolerance < abs(gap) < AO_MAX_GAP ) * feedback them into metronom's vpts_offset (when using * metronom feedback for A/V sync) */ } else if ( abs(gap) < AO_MAX_GAP && abs(gap) > this->gap_tolerance && cur_time > (last_sync_time + SYNC_TIME_INVERVAL) && bufs_since_sync >= SYNC_BUF_INTERVAL && !this->resample_sync_method ) { xine_stream_t *stream; lprintf ("audio_loop: ADJ_VPTS\n"); pthread_mutex_lock(&this->streams_lock); for (stream = xine_list_first_content(this->streams); stream; stream = xine_list_next_content(this->streams)) { stream->metronom->set_option(stream->metronom, METRONOM_ADJ_VPTS_OFFSET, -gap/SYNC_GAP_RATE ); last_sync_time = cur_time; bufs_since_sync = 0; } pthread_mutex_unlock(&this->streams_lock); LOG_SEG_ERROR } else if ( gap > AO_MAX_GAP ) { /* for big gaps output silence */ ao_fill_gap (this, gap); LOG_SEG_ERROR } else { #if 0 { int count; printf("Audio data\n"); for (count=0;count < 10;count++) { printf("%x ",buf->mem[count]); } printf("\n"); } #endif LOG_SEG_ERROR out_buf = prepare_samples (this, in_buf); #if 0 { int count; printf("Audio data2\n"); for (count=0;count < 10;count++) { printf("%x ",out_buf->mem[count]); } printf("\n"); } #endif LOG_SEG_ERROR lprintf ("loop: writing %d samples to sound device\n", out_buf->num_frames); pthread_mutex_lock( &this->driver_lock ); result = this->driver->write (this->driver, out_buf->mem, out_buf->num_frames ); pthread_mutex_unlock( &this->driver_lock ); /* FIXME: USB device unplugged. * We should get the card into a closed state here, that involves closing * the PCM as well as the MIXER. * Maybe we should pause the stream until the USB device is plugged in again. * Return values 0 happen even if usb not unplugged, so needs further investigation. */ XINE_ASSERT(result >= 0, "write to sound card failed. Was a USB device unplugged?"); LOG_SEG_ERROR lprintf ("loop: next buf from fifo\n"); fifo_append (this->free_fifo, in_buf); in_buf = NULL; } } LOG_SEG_ERROR if (in_buf) fifo_append (this->free_fifo, in_buf); LOG_SEG_ERROR return NULL; } /* * public a/v processing interface */ int xine_get_next_audio_frame (xine_audio_port_t *this_gen, xine_audio_frame_t *frame) { aos_t *this = (aos_t *) this_gen; audio_buffer_t *in_buf, *out_buf; xine_stream_t *stream; lprintf ("get_next_audio_frame\n"); do { stream = xine_list_first_content(this->streams); if (!stream) xine_usec_sleep (1000); } while( !stream ); pthread_mutex_lock (&this->out_fifo->mutex); in_buf = this->out_fifo->first; /* FIXME: ugly, use conditions and locks instead */ while (!in_buf && (stream->demux_plugin->get_status (stream->demux_plugin)==DEMUX_OK)) { pthread_mutex_unlock(&this->out_fifo->mutex); xine_usec_sleep (1000); pthread_mutex_lock(&this->out_fifo->mutex); in_buf = this->out_fifo->first; } if (!in_buf) { pthread_mutex_unlock(&this->out_fifo->mutex); printf ("audio_audio: EOS\n"); return 0; } in_buf = fifo_remove_int (this->out_fifo); pthread_mutex_unlock(&this->out_fifo->mutex); out_buf = prepare_samples (this, in_buf); if (out_buf != in_buf) { fifo_append (this->free_fifo, in_buf); frame->xine_frame = NULL; } else frame->xine_frame = out_buf; frame->vpts = out_buf->vpts; frame->num_samples = out_buf->num_frames; frame->sample_rate = this->input.rate; frame->num_channels = mode_channels (this->input.mode); frame->bits_per_sample = this->input.bits; frame->pos_stream = out_buf->extra_info->input_pos; frame->pos_time = out_buf->extra_info->input_time; frame->data = (uint8_t *) out_buf->mem; return 1; } void xine_free_audio_frame (xine_audio_port_t *this_gen, xine_audio_frame_t *frame) { aos_t *this = (aos_t *) this_gen; audio_buffer_t *buf; buf = (audio_buffer_t *) frame->xine_frame; if (buf) fifo_append (this->free_fifo, buf); } static int ao_change_settings(aos_t *this, uint32_t bits, uint32_t rate, int mode) { int output_sample_rate; if(this->driver_open) this->driver->close(this->driver); this->driver_open = 0; this->input.mode = mode; this->input.rate = rate; this->input.bits = bits; if (!this->grab_only) { /* not all drivers/cards support 8 bits */ if( this->input.bits == 8 && !(this->driver->get_capabilities(this->driver) & AO_CAP_8BITS) ) { bits = 16; xprintf (this->xine, XINE_VERBOSITY_LOG, "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; xprintf (this->xine, XINE_VERBOSITY_LOG, "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; xprintf (this->xine, XINE_VERBOSITY_LOG, "stereo not supported by driver, converting to mono.\n"); } output_sample_rate=this->driver->open(this->driver,bits,(this->force_rate ? this->force_rate : rate),mode); this->driver_open = 1; } else output_sample_rate = this->input.rate; if ( output_sample_rate == 0) { this->driver_open = 0; xprintf (this->xine, XINE_VERBOSITY_LOG, "open failed!\n"); return 0; }; xprintf (this->xine, XINE_VERBOSITY_LOG, "output sample rate %d\n", output_sample_rate); this->last_audio_vpts = 0; this->output.mode = mode; this->output.rate = output_sample_rate; this->output.bits = bits; switch (this->resample_conf) { case 1: /* force off */ this->do_resample = 0; break; case 2: /* force on */ this->do_resample = 1; break; default: /* AUTO */ this->do_resample = this->output.rate != this->input.rate; } if (this->do_resample) xprintf (this->xine, XINE_VERBOSITY_DEBUG, "will resample audio from %d to %d\n", this->input.rate, this->output.rate); this->frame_rate_factor = ((double)(this->output.rate)) / ((double)(this->input.rate)); /* FIXME: If this->frames_per_kpts line goes after this->audio_step line, * xine crashes with FPE, when compiled with gcc 3.0.1!!! Why? */ this->frames_per_kpts = (this->output.rate * 1024) / 90000; this->audio_step = ((int64_t)90000 * (int64_t)32768) / (int64_t)this->input.rate; lprintf ("audio_step %" PRId64 " pts per 32768 frames\n", this->audio_step); return this->output.rate; } /* * open the audio device for writing to */ static int ao_open(xine_audio_port_t *this_gen, xine_stream_t *stream, uint32_t bits, uint32_t rate, int mode) { aos_t *this = (aos_t *) this_gen; if( !this->driver_open || bits != this->input.bits || rate != this->input.rate || mode != this->input.mode ) { int ret; if (this->audio_loop_running) { /* make sure there are no more buffers on queue */ fifo_wait_empty(this->out_fifo); } pthread_mutex_lock( &this->driver_lock ); ret = ao_change_settings(this, bits, rate, mode); pthread_mutex_unlock( &this->driver_lock ); if( !ret ) return 0; } pthread_mutex_lock(&this->streams_lock); xine_list_append_content(this->streams, stream); pthread_mutex_unlock(&this->streams_lock); /* * set metainfo */ stream->stream_info[XINE_STREAM_INFO_AUDIO_MODE] = mode; switch (mode) { case AO_CAP_MODE_MONO: stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] = 1; break; case AO_CAP_MODE_STEREO: stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] = 2; break; case AO_CAP_MODE_4CHANNEL: stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] = 4; break; case AO_CAP_MODE_4_1CHANNEL: case AO_CAP_MODE_5CHANNEL: case AO_CAP_MODE_5_1CHANNEL: stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] = 6; break; case AO_CAP_MODE_A52: case AO_CAP_MODE_AC5: default: stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] = 255; /* unknown */ } stream->stream_info[XINE_STREAM_INFO_AUDIO_BITS] = bits; stream->stream_info[XINE_STREAM_INFO_AUDIO_SAMPLERATE] = rate; stream->metronom->set_audio_rate(stream->metronom, this->audio_step); return this->output.rate; } static audio_buffer_t *ao_get_buffer (xine_audio_port_t *this_gen) { aos_t *this = (aos_t *) this_gen; audio_buffer_t *buf; buf = fifo_remove (this->free_fifo); extra_info_reset( buf->extra_info ); return buf; } static void ao_put_buffer (xine_audio_port_t *this_gen, audio_buffer_t *buf, xine_stream_t *stream) { aos_t *this = (aos_t *) this_gen; int64_t pts; if (buf->num_frames == 0) { fifo_append (this->free_fifo, buf); return; } buf->stream = stream; buf->format.bits = stream->stream_info[XINE_STREAM_INFO_AUDIO_BITS]; buf->format.rate = stream->stream_info[XINE_STREAM_INFO_AUDIO_SAMPLERATE]; buf->format.mode = stream->stream_info[XINE_STREAM_INFO_AUDIO_MODE]; extra_info_merge( buf->extra_info, stream->audio_decoder_extra_info ); pts = buf->vpts; buf->vpts = stream->metronom->got_audio_samples (stream->metronom, pts, buf->num_frames); buf->extra_info->vpts = buf->vpts; lprintf ("ao_put_buffer, pts=%" PRId64 ", vpts=%" PRId64 ", flushmode=%d\n", pts, buf->vpts, this->discard_buffers); if (!this->discard_buffers) fifo_append (this->out_fifo, buf); else fifo_append (this->free_fifo, buf); this->last_audio_vpts = buf->vpts; lprintf ("ao_put_buffer done\n"); } static void ao_close(xine_audio_port_t *this_gen, xine_stream_t *stream) { aos_t *this = (aos_t *) this_gen; xine_stream_t *cur; xprintf (this->xine, XINE_VERBOSITY_DEBUG, "ao_close\n"); /* unregister stream */ pthread_mutex_lock(&this->streams_lock); for (cur = xine_list_first_content(this->streams); cur; cur = xine_list_next_content(this->streams)) if (cur == stream) { xine_list_delete_current(this->streams); break; } cur = xine_list_first_content(this->streams); pthread_mutex_unlock(&this->streams_lock); /* close driver if no streams left */ if (!cur && !this->grab_only) { xprintf (this->xine, XINE_VERBOSITY_DEBUG, "no streams left, closing driver\n"); if (this->audio_loop_running) { /* make sure there are no more buffers on queue */ fifo_wait_empty(this->out_fifo); } pthread_mutex_lock( &this->driver_lock ); if(this->driver_open) this->driver->close(this->driver); this->driver_open = 0; pthread_mutex_unlock( &this->driver_lock ); } } static void ao_exit(xine_audio_port_t *this_gen) { aos_t *this = (aos_t *) this_gen; int vol; int prop = 0; audio_buffer_t *buf, *next; if (this->audio_loop_running) { void *p; this->audio_loop_running = 0; this->audio_paused = 0; buf = fifo_remove(this->free_fifo); buf->num_frames = 0; buf->stream = NULL; fifo_append (this->out_fifo, buf); pthread_join (this->audio_thread, &p); this->audio_thread = 0; } if (!this->grab_only) { pthread_mutex_lock( &this->driver_lock ); if((this->driver->get_capabilities(this->driver)) & AO_CAP_MIXER_VOL) prop = AO_PROP_MIXER_VOL; else if((this->driver->get_capabilities(this->driver)) & AO_CAP_PCM_VOL) prop = AO_PROP_PCM_VOL; vol = this->driver->get_property(this->driver, prop); this->xine->config->update_num(this->xine->config, "audio.mixer_volume", vol); if(this->driver_open) this->driver->close(this->driver); this->driver->exit(this->driver); pthread_mutex_unlock( &this->driver_lock ); } pthread_mutex_destroy(&this->driver_lock); pthread_mutex_destroy(&this->streams_lock); xine_list_free(this->streams); free (this->frame_buf[0]->mem); free (this->frame_buf[0]->extra_info); free (this->frame_buf[0]); free (this->frame_buf[1]->mem); free (this->frame_buf[1]->extra_info); free (this->frame_buf[1]); free (this->zero_space); pthread_mutex_destroy(&this->flush_audio_driver_lock); pthread_cond_destroy(&this->flush_audio_driver_reached); buf = this->free_fifo->first; while (buf != NULL) { next = buf->next; free (buf->mem); free (buf->extra_info); free (buf); buf = next; } buf = this->out_fifo->first; while (buf != NULL) { next = buf->next; free (buf->mem); free (buf->extra_info); free (buf); buf = next; } pthread_mutex_destroy(&this->free_fifo->mutex); pthread_cond_destroy(&this->free_fifo->empty); pthread_cond_destroy(&this->free_fifo->not_empty); pthread_mutex_destroy(&this->out_fifo->mutex); pthread_cond_destroy(&this->out_fifo->empty); pthread_cond_destroy(&this->out_fifo->not_empty); free (this->free_fifo); free (this->out_fifo); free (this); } static uint32_t ao_get_capabilities (xine_audio_port_t *this_gen) { aos_t *this = (aos_t *) this_gen; uint32_t result; if (this->grab_only) { return AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO ; /* FIXME: make configurable | AO_CAP_MODE_4CHANNEL | AO_CAP_MODE_5CHANNEL | AO_CAP_MODE_5_1CHANNEL | AO_CAP_8BITS; */ } else { pthread_mutex_lock( &this->driver_lock ); result=this->driver->get_capabilities(this->driver); pthread_mutex_unlock( &this->driver_lock ); } return result; } static int ao_get_property (xine_audio_port_t *this_gen, int property) { aos_t *this = (aos_t *) this_gen; int ret; switch (property) { case AO_PROP_COMPRESSOR: ret = this->compression_factor_max*100; break; case AO_PROP_AMP: ret = this->amp_factor*100; break; case AO_PROP_EQ_30HZ: case AO_PROP_EQ_60HZ: case AO_PROP_EQ_125HZ: case AO_PROP_EQ_250HZ: case AO_PROP_EQ_500HZ: case AO_PROP_EQ_1000HZ: case AO_PROP_EQ_2000HZ: case AO_PROP_EQ_4000HZ: case AO_PROP_EQ_8000HZ: case AO_PROP_EQ_16000HZ: ret = (100 * this->eq_gain[property - AO_PROP_EQ_30HZ]) / (1 << FP_FRBITS) ; break; case AO_PROP_DISCARD_BUFFERS: ret = this->discard_buffers; break; case AO_PROP_PAUSED: ret = this->audio_paused; break; default: pthread_mutex_lock( &this->driver_lock ); ret = this->driver->get_property(this->driver, property); pthread_mutex_unlock( &this->driver_lock ); } return ret; } static int ao_set_property (xine_audio_port_t *this_gen, int property, int value) { aos_t *this = (aos_t *) this_gen; int ret; switch (property) { case AO_PROP_COMPRESSOR: this->compression_factor_max = (double) value / 100.0; this->do_compress = (this->compression_factor_max >1.0); ret = this->compression_factor_max*100; break; case AO_PROP_AMP: this->amp_factor = (double) value / 100.0; this->do_amp = (this->amp_factor != 1.0); ret = this->amp_factor*100; break; case AO_PROP_EQ_30HZ: case AO_PROP_EQ_60HZ: case AO_PROP_EQ_125HZ: case AO_PROP_EQ_250HZ: case AO_PROP_EQ_500HZ: case AO_PROP_EQ_1000HZ: case AO_PROP_EQ_2000HZ: case AO_PROP_EQ_4000HZ: case AO_PROP_EQ_8000HZ: case AO_PROP_EQ_16000HZ: { int min_gain, max_gain, i; this->eq_gain[property - AO_PROP_EQ_30HZ] = EQ_REAL(((float)value / 100.0)) ; /* calc pregain, find out if any gain != 0.0 - enable eq if that is the case */ min_gain = EQ_REAL(0.0); max_gain = EQ_REAL(0.0); for (i=0; ieq_gain[i] < min_gain) min_gain = this->eq_gain[i]; if (this->eq_gain[i] > max_gain) max_gain = this->eq_gain[i]; } lprintf ("eq min_gain=%d, max_gain=%d\n", min_gain, max_gain); this->do_equ = ((min_gain != EQ_REAL(0.0)) || (max_gain != EQ_REAL(0.0))); ret = value; } break; case AO_PROP_DISCARD_BUFFERS: /* recursive discard buffers setting */ if(value) this->discard_buffers++; else this->discard_buffers--; ret = this->discard_buffers; /* discard buffers here because we have no output thread */ if (this->grab_only && this->discard_buffers) { audio_buffer_t *buf; pthread_mutex_lock(&this->out_fifo->mutex); while ((buf = this->out_fifo->first)) { lprintf ("flushing out frame\n"); buf = fifo_remove_int (this->out_fifo); fifo_append (this->free_fifo, buf); } pthread_mutex_unlock (&this->out_fifo->mutex); } break; case AO_PROP_PAUSED: this->audio_paused = value; ret = this->audio_paused; break; case AO_PROP_CLOSE_DEVICE: pthread_mutex_lock( &this->driver_lock ); if(this->driver_open) this->driver->close(this->driver); this->driver_open = 0; pthread_mutex_unlock( &this->driver_lock ); break; default: if (!this->grab_only) { /* Let the sound driver lock it's own mixer */ ret = this->driver->set_property(this->driver, property, value); } else ret = 0; } return ret; } static int ao_control (xine_audio_port_t *this_gen, int cmd, ...) { aos_t *this = (aos_t *) this_gen; va_list args; void *arg; int rval = 0; if (this->grab_only) return 0; pthread_mutex_lock( &this->driver_lock ); if(this->driver_open) { va_start(args, cmd); arg = va_arg(args, void*); rval = this->driver->control(this->driver, cmd, arg); va_end(args); } pthread_mutex_unlock( &this->driver_lock ); return rval; } static void ao_flush (xine_audio_port_t *this_gen) { aos_t *this = (aos_t *) this_gen; audio_buffer_t *buf; xprintf (this->xine, XINE_VERBOSITY_DEBUG, "ao_flush (loop running: %d)\n", this->audio_loop_running); if( this->audio_loop_running ) { pthread_mutex_lock(&this->flush_audio_driver_lock); this->discard_buffers++; this->flush_audio_driver++; /* do not try this in paused mode */ while( this->flush_audio_driver && !this->audio_paused) { struct timeval tv; struct timespec ts; /* release mutex to get a buffer, otherwise a deadlock may happen */ pthread_mutex_unlock(&this->flush_audio_driver_lock); buf = fifo_remove (this->free_fifo); pthread_mutex_lock(&this->flush_audio_driver_lock); buf->num_frames = 0; buf->stream = NULL; fifo_append (this->out_fifo, buf); /* cond_timedwait was not supposed be needed here, but somehow it may still * get stuck when using normal cond_wait. probably the signal is missed when * we release the mutex above. */ gettimeofday(&tv, NULL); ts.tv_sec = tv.tv_sec + 1; ts.tv_nsec = tv.tv_usec * 1000; pthread_cond_timedwait(&this->flush_audio_driver_reached, &this->flush_audio_driver_lock, &ts); } this->discard_buffers--; pthread_mutex_unlock(&this->flush_audio_driver_lock); fifo_wait_empty(this->out_fifo); } } static int ao_status (xine_audio_port_t *this_gen, xine_stream_t *stream, uint32_t *bits, uint32_t *rate, int *mode) { aos_t *this = (aos_t *) this_gen; xine_stream_t *cur; int ret = 0; pthread_mutex_lock(&this->streams_lock); for (cur = xine_list_first_content(this->streams); cur; cur = xine_list_next_content(this->streams)) if (cur == stream || !stream) { *bits = this->input.bits; *rate = this->input.rate; *mode = this->input.mode; ret = 1; break; } pthread_mutex_unlock(&this->streams_lock); return ret; } static void ao_update_av_sync_method(void *this_gen, xine_cfg_entry_t *entry) { aos_t *this = (aos_t *) this_gen; lprintf ("av_sync_method = %d\n", entry->num_value); this->av_sync_method_conf = entry->num_value; switch (this->av_sync_method_conf) { case 0: this->resample_sync_method = 0; break; case 1: this->resample_sync_method = 1; break; default: this->resample_sync_method = 0; break; } this->resample_sync_info.valid = 0; } xine_audio_port_t *ao_new_port (xine_t *xine, ao_driver_t *driver, int grab_only) { config_values_t *config = xine->config; aos_t *this; int i, err; pthread_attr_t pth_attrs; static char *resample_modes[] = {"auto", "off", "on", NULL}; static char *av_sync_methods[] = {"metronom_feedback", "resample", NULL}; this = xine_xmalloc (sizeof (aos_t)) ; this->driver = driver; this->xine = xine; this->clock = xine->clock; this->streams = xine_list_new(); pthread_mutex_init( &this->streams_lock, NULL ); pthread_mutex_init( &this->driver_lock, NULL ); this->ao.open = ao_open; this->ao.get_buffer = ao_get_buffer; this->ao.put_buffer = ao_put_buffer; this->ao.close = ao_close; this->ao.exit = ao_exit; this->ao.get_capabilities = ao_get_capabilities; this->ao.get_property = ao_get_property; this->ao.set_property = ao_set_property; this->ao.control = ao_control; this->ao.flush = ao_flush; this->ao.status = ao_status; this->audio_loop_running = 0; this->grab_only = grab_only; this->audio_paused = 0; this->flush_audio_driver = 0; this->discard_buffers = 0; this->zero_space = xine_xmalloc (ZERO_BUF_SIZE * 2 * 6); pthread_mutex_init( &this->flush_audio_driver_lock, NULL ); pthread_cond_init( &this->flush_audio_driver_reached, NULL ); if (!grab_only) this->gap_tolerance = driver->get_gap_tolerance (this->driver); this->av_sync_method_conf = config->register_enum(config, "audio.av_sync_method", 0, av_sync_methods, _("choose method to sync audio and video"), _("'resample' might be better if you use a " "DXR3/H+ card and (analog) audio is " "processed by your sound card"), 30, ao_update_av_sync_method, this); config->update_num(config,"audio.av_sync_method",this->av_sync_method_conf); this->resample_conf = config->register_enum (config, "audio.resample_mode", 0, resample_modes, _("adjust whether resampling is done or not"), NULL, 20, NULL, NULL); this->force_rate = config->register_num (config, "audio.force_rate", 0, _("if !=0 always resample to given rate"), NULL, 20, NULL, NULL); this->passthrough_offset = config->register_num (config, "audio.passthrough_offset", 0, _("adjust if audio is offsync"), NULL, 10, NULL, NULL); this->compression_factor = 2.0; this->compression_factor_max = 0.0; this->do_compress = 0; this->amp_factor = 1.0; this->do_amp = 0; this->do_equ = 0; this->eq_gain[0] = 0; this->eq_gain[1] = 0; this->eq_gain[2] = 0; this->eq_gain[3] = 0; this->eq_gain[4] = 0; this->eq_gain[5] = 0; this->eq_gain[6] = 0; this->eq_gain[7] = 0; this->eq_gain[8] = 0; this->eq_gain[9] = 0; this->eq_preamp = EQ_REAL(1.0); this->eq_i = 0; this->eq_j = 2; this->eq_k = 1; bzero (this->eq_data_history, sizeof(sXYData) * EQ_BANDS * EQ_CHANNELS); /* * pre-allocate memory for samples */ this->free_fifo = fifo_new (); this->out_fifo = fifo_new (); for (i=0; imem = xine_xmalloc (AUDIO_BUF_SIZE); buf->mem_size = AUDIO_BUF_SIZE; buf->extra_info = malloc(sizeof(extra_info_t)); 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 *) xine_xmalloc (sizeof (audio_buffer_t)); buf->mem = xine_xmalloc (4*AUDIO_BUF_SIZE); buf->mem_size = 4*AUDIO_BUF_SIZE; buf->extra_info = malloc(sizeof(extra_info_t)); this->frame_buf[i] = buf; } /* * Set audio volume to latest used one ? */ if(this->driver){ int vol; vol = config->register_range (config, "audio.mixer_volume", 50, 0, 100, _("Audio volume"), NULL, 0, NULL, NULL); if(config->register_bool (config, "audio.remember_volume", 0, _("restore volume level at startup"), _("if this not set, xine will not touch any mixer settings at startup"), 0, NULL, NULL)) { int prop = 0; if((ao_get_capabilities(&this->ao)) & AO_CAP_MIXER_VOL) prop = AO_PROP_MIXER_VOL; else if((ao_get_capabilities(&this->ao)) & AO_CAP_PCM_VOL) prop = AO_PROP_PCM_VOL; ao_set_property(&this->ao, prop, vol); } } if (!this->grab_only) { /* * start output thread */ this->audio_loop_running = 1; pthread_attr_init(&pth_attrs); pthread_attr_setscope(&pth_attrs, PTHREAD_SCOPE_SYSTEM); if ((err = pthread_create (&this->audio_thread, &pth_attrs, ao_loop, this)) != 0) { printf ("audio_out: can't create thread (%s)\n", strerror(err)); printf ("audio_out: sorry, this should not happen. please restart xine.\n"); abort(); } else xprintf (this->xine, XINE_VERBOSITY_DEBUG, "thread created\n"); pthread_attr_destroy(&pth_attrs); } return &this->ao; }