Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / audioodevice.C
diff --git a/cinelerra-5.1/cinelerra/audioodevice.C b/cinelerra-5.1/cinelerra/audioodevice.C
new file mode 100644 (file)
index 0000000..32be129
--- /dev/null
@@ -0,0 +1,404 @@
+
+/*
+ * 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;
+}
+
+