Selaa lähdekoodia

implementation code for the linux OSS driver

Cary Sandvig 25 vuotta sitten
vanhempi
sitoutus
1b3ed5dbda
1 muutettua tiedostoa jossa 280 lisäystä ja 0 poistoa
  1. 280 0
      panda/src/audio/audio_linux_traits.cxx

+ 280 - 0
panda/src/audio/audio_linux_traits.cxx

@@ -0,0 +1,280 @@
+// Filename: audio_linux_traits.C
+// Created by:  cary (02Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "audio_linux_traits.h"
+#include "audio_manager.h"
+#include "config_audio.h"
+#include <ipc_thread.h>
+#include <ipc_mutex.h>
+#include <set>
+
+typedef set<Buffer*> BufferSet;
+
+static bool have_initialized = false;
+static mutex buffer_mutex;
+static byte* buffer1;
+static byte* buffer2;
+static byte* current_buffer;
+static byte* back_buffer;
+static byte* zero_buffer;
+static byte* scratch_buffer;
+static byte* fetch_buffer;
+static int want_buffers, have_buffers;
+static bool initializing = false;
+static int output_fd;
+static thread* update_thread;
+static int sample_size = sizeof(short);
+
+BufferSet buffers;
+
+static void swap_buffers(void) {
+  byte *tmp = current_buffer;
+  current_buffer = back_buffer;
+  back_buffer = tmp;
+}
+
+INLINE static signed short read_buffer(byte* buf, int idx) {
+  signed short ret = 0;
+  switch (sample_size) {
+  case 1:
+    ret = *((signed char *)(&buf[idx]));
+    break;
+  case 2:
+    ret = *((signed short *)(&buf[idx*2]));
+    break;
+  default:
+    audio_cat->debug() << "unknown sample size (" << sample_size << ")"
+		       << endl;
+    break;
+  }
+  return ret;
+}
+
+INLINE static void write_buffer(byte* buf, int idx, signed short val) {
+  switch (sample_size) {
+  case 1:
+    *((signed char *)(&buf[idx])) = val & 0xff;
+    break;
+  case 2:
+    *((signed short *)(&buf[idx*2])) = val;
+    break;
+  default:
+    audio_cat->debug() << "unknown sample size (" << sample_size << ")"
+		       << endl;
+    break;
+  }
+}
+
+INLINE static signed short sound_clamp(signed int value) {
+  signed short ret = 0;
+  switch (sample_size) {
+  case 1:
+    ret = (value > 127)?127:((value < -128)?(-128):(value));
+    break;
+  case 2:
+    ret = (value > 32767)?32767:((value < -32768)?(-32768):(value));
+    break;
+  default:
+    audio_cat->debug() << "unknown sample size (" << sample_size << ")"
+		       << endl;
+    break;
+  }
+  return ret;
+}
+
+static void mix_in(byte* buf, byte* from, float vol, float pan) {
+  int done = audio_buffer_size / sample_size;
+  for (int i=0; i<done; i+=2) {
+    signed short left = read_buffer(buf, i);
+    signed short right = read_buffer(buf, i+1);
+
+    // now get the incoming data
+    signed short in_left = read_buffer(from, i);
+    signed short in_right = read_buffer(from, i+1);
+
+    // figure out panning at some point
+    in_left *= vol;
+    in_right *= vol;
+
+    // compute mixed values
+    left = sound_clamp(left+in_left);
+    right = sound_clamp(right+in_right);
+
+    // write it back to the buffer
+    write_buffer(buf, i, left);
+    write_buffer(buf, i+1, right);
+  }
+}
+
+static void mix_buffer(byte* buf) {
+  memcpy(scratch_buffer, zero_buffer, audio_buffer_size);
+  // do stuff
+  for (BufferSet::iterator i=buffers.begin(); i!=buffers.end(); ++i)
+    min_in(scratch, (*i)->get_buffer(fetch_buffer), 1., 0.);
+  BufferSet to_del;
+  for (BufferSet::iterator j=buffers.begin(); j!=buffers.end(); ++j)
+    if ((*j)->is_done())
+      to_del.insert(*j);
+  {
+    mutex_lock m(buffer_mutex);
+    memcpy(buf, scratch_buffer, audio_buffer_size);
+    --want_buffers;
+    ++have_buffers;
+    for (BufferSet::iterator k=to_del.begin(); k!=to_del.end(); ++k) {
+      buffers.erase(*k);
+      (*k)->reset();
+    }
+  }
+}
+
+static void update_linux(void) {
+  if (buffers.empty())
+    return;
+  switch (want_buffers) {
+  case 0:
+    // all buffers are full right now.  This is a good state.
+    break;
+  case 1:
+    // mix a buffer and put it in place.
+    mix_buffer(back_buffer);
+    break;
+  case 2:
+    if (!initializing)
+      audio_cat->warning() << "audio buffers are being starved" << endl;
+    // mix 2 buffers and put them in place.
+    mix_buffer(current_buffer);
+    mix_buffer(back_buffer);
+    initializing = false;
+    break;
+  default:
+    audio_cat->error() << "audio system wants more then 2 buffers!" << endl;
+  }
+}
+
+static void internal_update(void*) {
+  if ((output_fd = open(audio_device, O_WRONLY, 0)) == -1) {
+    audio_cat->error() << "could not open '" << audio_device << "'" << endl;
+    return;
+  }
+  // this one I don't know about
+  int fragsize = 0x0004000c;
+  if (ioctl(output_fd, SNDCTL_DSP_SETFRAGMENT, &fragsize) == -1) {
+    audio_cat->error() << "faied to set fragment size" << endl;
+    return;
+  }
+  // for now signed, 16-bit, little endian
+  int format = AFMT_S16_LE;
+  if (ioctl(output_fd, SNDCTL_DSP_SETFMT, &format) == -1) {
+    audio_cat->error() << "failed to set format on the dsp" << endl;
+    return;
+  }
+  // set stereo
+  int stereo = 1;
+  if (ioctl(output_fd, SNDCTL_DSP_STEREO, &stereo) == -1) {
+    audio_cat->error() << "failed to set stereo on the dsp" << endl;
+    return;
+  }
+  // set the frequency
+  if (ioctl(output_fd, SNDCTL_DSP_SPEED, &audio_mix_freq) == -1) {
+    audio_cat->error() << "failed to set frequency on the dsp" << endl;
+    return;
+  }
+  while (1) {
+    if (have_buffers == 0) {
+      ipc_traits::sleep(0, 10000);
+    } else {
+      write(output_fd, current_buffer, audio_buffer_size);
+      {
+	mutex_lock m(buffer_mutex);
+	swap_buffers();
+	--have_buffers;
+	++want_buffers;
+      }
+    }
+  }
+}
+
+static void initialize(void) {
+  if (have_initialized)
+    return;
+
+  buffer1 = new byte[audio_buffer_size];
+  buffer2 = new byte[audio_buffer_size];
+  scratch_buffer = new byte[audio_buffer_size];
+  fetch_buffer = new byte[audio_buffer_size];
+  zero_buffer = new byte[audio_buffer_size];
+
+  for (int i=0; i<audio_buffer_size; ++i)
+    zero_buffer[i] = 0;
+
+  current_buffer = buffer1;
+  back_buffer = buffer2;
+
+  want_buffers = 2;
+  have_buffers = 0;
+  initializing = true;
+
+  audio_cat->info() << "spawning internal update thread" << endl;
+  update_thread = thread::create(internal_update, (void*)0L,
+				 thread_PRIORITY_NORMAL);
+
+  AudioManager::set_update_func(update_linux);
+  have_initialized = true;
+}
+
+LinuxSample::~LinuxSample(void) {
+}
+
+float LinuxSample::length(void) {
+  return (_data->get_size()) / (audio_mix_freq * sample_size);
+}
+
+AudioTraits::SampleClass::SampleStatus LinuxSample::status(void) {
+  BufferSet::iterator i = buffers.find(_data);
+  if (i != buffers.end())
+    return AudioTraits::SampleClass::PLAYING;
+  return AudioTraits::SampleClass::READY;
+}
+
+void LinuxSample::destroy(AudioTraits::SampleClass* sample) {
+  delete sample;
+}
+
+LinuxSample* LinuxSample::load_raw(byte* data, unsigned long size) {
+  return new LinuxSample(new Buffer(data, size));
+}
+
+LinuxMusic::~LinuxMusic(void) {
+}
+
+AudioTraits::MusicClass::MusicStatus LinuxMusic::status(void) {
+  return AudioTraits::MusicClass::READY;
+}
+
+LinuxPlayer* LinuxPlayer::_global_instance = (LinuxPlayer*)0L;
+
+LinuxPlayer::~LinuxPlayer(void) {
+}
+
+void LinuxPlayer::play_sample(AudioTraits::SampleClass* sample) {
+  initialize();
+  LinuxSample* lsample = (LinuxSample*)sample;
+  buffers.insert(lsample->get_data());
+}
+
+void LinuxPlayer::play_music(AudioTraits::MusicClass*) {
+}
+
+void LinuxPlayer::set_volume(AudioTraits::SampleClass*, int) {
+}
+
+void LinuxPlayer::set_volume(AudioTraits::MusicClass*, int) {
+}
+
+LinuxPlayer* LinuxPlayer::get_instance(void) {
+  if (_global_instance == (LinuxPlayer*)0L)
+    _global_instance = new LinuxPlayer();
+  return _global_instance;
+}