--- /dev/null
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "audiodevice.h"
+#include "playbackconfig.h"
+#include "recordconfig.h"
+#include "bctimer.h"
+#include "condition.h"
+#include "mutex.h"
+#include "samples.h"
+#include "sema.h"
+
+#include <string.h>
+
+#define NO_DITHER 0
+#define DITHER dither()
+
+// count of on bits
+inline static unsigned int on_bits(unsigned int n) {
+ n = (n & 0x55555555) + ((n >> 1) & 0x55555555);
+ n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
+ n = (n & 0x0f0f0f0f) + ((n >> 4) & 0x0f0f0f0f);
+ n += n >> 8; n += n >> 16; //ok, fldsz > 5 bits
+ return n & 0x1f;
+}
+
+// assumes RAND_MAX is (2**n)-1 and n<=32 bits
+static int random_bits = on_bits(RAND_MAX);
+static int dither_random = 0;
+static long dither_bits = 0;
+
+static inline int dither()
+{
+ if( --dither_bits < 0 ) {
+ dither_random = random();
+ dither_bits = random_bits;
+ }
+ return (dither_random>>dither_bits) & 1;
+}
+
+static inline int audio_clip(int64_t v, int mx)
+{
+ if( v > mx ) return mx;
+ if( v < -mx-1 ) return -mx-1;
+ return v;
+}
+
+#define FETCH1(m,dither) \
+ audio_clip((output_channel[j]*gain)-dither, m)
+
+#define FETCH51(m, dither) \
+ audio_clip((gain*(center[j] + 2*front[j] + \
+ 2*back[j] + subwoof[j]))-dither, m)
+
+#define PUT_8BIT_SAMPLES(n,dither) { \
+ sample = FETCH##n(0x7f,dither); \
+ *(int8_t*)&buffer[k] = sample; \
+}
+
+#define PUT_16BIT_SAMPLES(n,dither) { \
+ sample = FETCH##n(0x7fff,dither); \
+ *(int16_t*)&buffer[k] = sample; \
+}
+
+#define PUT_24BIT_SAMPLES(n,dither) { \
+ sample = FETCH##n(0x7fffff,dither); \
+ if( (k&1) ) { \
+ *(int8_t*)&buffer[k] = sample; \
+ *(int16_t*)&buffer[k+1] = (sample >> 8); \
+ } else { \
+ *(int16_t*)&buffer[k] = sample; \
+ *(int8_t*)&buffer[k+2] = (sample >> 16); \
+ } \
+}
+
+#define PUT_32BIT_SAMPLES(n,dither) { \
+ sample = FETCH##n(0x7fffffff,dither); \
+ *(int32_t*)&buffer[k] = sample; \
+}
+
+int AudioDevice::write_buffer(double **data,
+ int channels, int samples, double bfr_time)
+{
+// find free buffer to fill
+ if(playback_interrupted) return 0;
+ int64_t sample_position = total_samples_written;
+ total_samples_written += samples;
+ int buffer_num = arm_buffer_num;
+ if( ++arm_buffer_num >= TOTAL_AUDIO_BUFFERS ) arm_buffer_num = 0;
+ arm_buffer(buffer_num, data, channels, samples,
+ sample_position, bfr_time);
+ return 0;
+}
+
+int AudioDevice::set_last_buffer()
+{
+ output_buffer_t *obfr = &output[arm_buffer_num];
+ if( !obfr ) return 0;
+ if( ++arm_buffer_num >= TOTAL_AUDIO_BUFFERS ) arm_buffer_num = 0;
+ obfr->arm_lock->lock("AudioDevice::set_last_buffer");
+ obfr->last_buffer = 1;
+ obfr->play_lock->unlock();
+ return 0;
+}
+
+// little endian byte order
+int AudioDevice::arm_buffer(int buffer_num, double **data, int channels,
+ int samples, int64_t sample_position, double bfr_time)
+{
+ if(!is_playing_back || playback_interrupted) return 1;
+// wait for buffer to become available for writing
+ output_buffer_t *obfr = &output[buffer_num];
+ obfr->arm_lock->lock("AudioDevice::arm_buffer");
+ if(!is_playing_back || playback_interrupted) return 1;
+
+ int bits = get_obits();
+ int device_channels = get_ochannels();
+ int frame = device_channels * (bits / 8);
+ int fragment_size = frame * samples;
+ if( fragment_size > obfr->allocated ) {
+ delete [] obfr->buffer;
+ obfr->buffer = new char[fragment_size];
+ obfr->allocated = fragment_size;
+ }
+
+ char *buffer = obfr->buffer;
+ obfr->size = fragment_size;
+ obfr->bfr_time = bfr_time;
+ obfr->sample_position = sample_position;
+ int map51_2 = out51_2 && channels == 6 && device_channels == 2;
+ double gain = ((1u<<(bits-1))-1) * play_gain;
+ if( map51_2 ) gain *= 0.2;
+
+ for( int och=0; och<device_channels; ++och ) {
+ int sample;
+ int k = och * bits / 8;
+
+ if( map51_2 ) {
+ double *center = data[0];
+ double *front = data[och+1];
+ double *back = data[och+3];
+ double *subwoof = data[5];
+ if( play_dither ) {
+ switch( bits ) {
+ case 8: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_8BIT_SAMPLES(51,DITHER)
+ break;
+ case 16: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_16BIT_SAMPLES(51,DITHER)
+ break;
+ case 24: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_24BIT_SAMPLES(51,DITHER)
+ break;
+ case 32: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_32BIT_SAMPLES(51,DITHER)
+ break;
+ }
+ }
+ else {
+ switch( bits ) {
+ case 8: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_8BIT_SAMPLES(51,NO_DITHER)
+ break;
+ case 16: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_16BIT_SAMPLES(51,NO_DITHER)
+ break;
+ case 24: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_24BIT_SAMPLES(51,NO_DITHER)
+ break;
+ case 32: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_32BIT_SAMPLES(51,NO_DITHER)
+ break;
+ }
+ }
+ }
+ else {
+ double *output_channel = data[och % channels];
+ if( play_dither ) {
+ switch( bits ) {
+ case 8: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_8BIT_SAMPLES(1,DITHER)
+ break;
+ case 16: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_16BIT_SAMPLES(1,DITHER)
+ break;
+ case 24: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_24BIT_SAMPLES(1,DITHER)
+ break;
+ case 32: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_32BIT_SAMPLES(1,DITHER)
+ break;
+ }
+ }
+ else {
+ switch( bits ) {
+ case 8: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_8BIT_SAMPLES(1,NO_DITHER)
+ break;
+ case 16: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_16BIT_SAMPLES(1,NO_DITHER)
+ break;
+ case 24: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_24BIT_SAMPLES(1,NO_DITHER)
+ break;
+ case 32: for( int j=0; j<samples; ++j,k+=frame )
+ PUT_32BIT_SAMPLES(1,NO_DITHER)
+ break;
+ }
+ }
+ }
+ }
+// make buffer available for playback
+ obfr->play_lock->unlock();
+ return 0;
+}
+
+void AudioDevice::end_output()
+{
+ is_playing_back = 0;
+ monitoring = 0;
+ if( lowlevel_out )
+ lowlevel_out->interrupt_playback();
+ for( int i=0; i<TOTAL_AUDIO_BUFFERS; ++i ) {
+ output_buffer_t *obfr = &output[i];
+ obfr->play_lock->unlock();
+ obfr->arm_lock->unlock();
+ }
+}
+
+int AudioDevice::reset_output()
+{
+ for( int i=0; i<TOTAL_AUDIO_BUFFERS; ++i ) {
+ output_buffer_t *obfr = &output[i];
+ if( obfr->buffer ) {
+ delete [] obfr->buffer;
+ obfr->buffer = 0;
+ }
+ obfr->allocated = 0;
+ obfr->size = 0;
+ obfr->last_buffer = 0;
+ obfr->arm_lock->reset();
+ obfr->play_lock->reset();
+ }
+ is_playing_back = 0;
+ playback_interrupted = 0;
+ monitoring = 0;
+ monitor_open = 0;
+ output_buffer_num = 0;
+ arm_buffer_num = 0;
+ last_position = 0;
+ return 0;
+}
+
+
+void AudioDevice::set_play_gain(double gain)
+{
+ play_gain = gain * out_config->play_gain;
+}
+
+void AudioDevice::set_play_dither(int status)
+{
+ play_dither = status;
+}
+
+void AudioDevice::set_software_positioning(int status)
+{
+ software_position_info = status;
+}
+
+int AudioDevice::start_playback()
+{
+// arm buffer before doing this
+ is_playing_back = 1;
+ playback_interrupted = 0;
+ total_samples_written = 0;
+ total_samples_output = 0;
+// zero timers
+ playback_timer->update();
+ last_position = 0;
+// start output thread
+ audio_out = new AudioThread(this,
+ &AudioDevice::run_output,&AudioDevice::end_output);
+ audio_out->set_realtime(get_orealtime());
+ audio_out->startup();
+ return 0;
+}
+
+int AudioDevice::interrupt_playback()
+{
+//printf("AudioDevice::interrupt_playback\n");
+ stop_output(0);
+ return 0;
+}
+
+int64_t AudioDevice::samples_output()
+{
+ int64_t result = !lowlevel_out ? -1 : lowlevel_out->samples_output();
+ if( result < 0 ) result = total_samples_output;
+ return result;
+}
+
+
+int64_t AudioDevice::current_position()
+{
+// try to get OSS position
+ int64_t result = 0;
+
+ if(w) {
+ int frame = (get_obits() * get_ochannels()) / 8;
+ if( !frame ) return 0;
+
+// get hardware position
+ if(!software_position_info && lowlevel_out) {
+ result = lowlevel_out->device_position();
+ int64_t sample_count = samples_output();
+ if( result > sample_count )
+ result = sample_count;
+ }
+
+// get software position
+ if(result < 0 || software_position_info) {
+ timer_lock->lock("AudioDevice::current_position");
+ result = total_samples_output - device_buffer / frame;
+ result += playback_timer->get_scaled_difference(get_orate());
+ timer_lock->unlock();
+ int64_t sample_count = samples_output();
+ if( result > sample_count )
+ result = sample_count;
+ }
+
+ if(result < last_position)
+ result = last_position;
+ else
+ last_position = result;
+
+ result -= (int64_t)(get_orate() * out_config->audio_offset);
+
+ }
+ else if(r) {
+ result = total_samples_read +
+ record_timer->get_scaled_difference(get_irate());
+ }
+
+ return result;
+}
+
+void AudioDevice::run_output()
+{
+ output_buffer_num = 0;
+ playback_timer->update();
+
+//printf("AudioDevice::run 1 %d\n", Thread::calculate_realtime());
+ while( is_playing_back && !playback_interrupted ) {
+// wait for buffer to become available
+ int buffer_num = output_buffer_num;
+ if( ++output_buffer_num >= TOTAL_AUDIO_BUFFERS ) output_buffer_num = 0;
+ output_buffer_t *obfr = &output[buffer_num];
+ obfr->play_lock->lock("AudioDevice::run 1");
+ if( !is_playing_back || playback_interrupted ) break;
+ if( obfr->last_buffer ) { lowlevel_out->flush_device(); break; }
+// get size for position information
+// write converted buffer synchronously
+ double bfr_time = obfr->bfr_time;
+ int result = lowlevel_out->write_buffer(obfr->buffer, obfr->size);
+// allow writing to the buffer
+ obfr->arm_lock->unlock();
+ if( !result ) {
+ timer_lock->lock("AudioDevice::run 3");
+ int frame = get_ochannels() * get_obits() / 8;
+ int samples = frame ? obfr->size / frame : 0;
+ total_samples_output += samples;
+ last_buffer_time = bfr_time + (double)samples/out_samplerate;
+ playback_timer->update();
+ timer_lock->unlock();
+ }
+// inform user if the buffer write failed
+ else if( result < 0 ) {
+ perror("AudioDevice::write_buffer");
+ usleep(250000);
+ }
+ }
+
+ is_playing_back = 0;
+}
+
+