Subversion Repositories DIN Is Noise

Rev

Rev 181 | Rev 459 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
* audio.cc
* DIN Is Noise is copyright (c) 2006-2017 Jagannathan Sampath
* For more information, please visit http://dinisnoise.org/
*/



#include "main.h"
#include "audio.h"
#include "chrono.h"
#include "command.h"
#include <math.h>
#include <fstream>
#include <limits.h>
#include <stdlib.h>
#include <string>
#include "RtError.h"
#include "log.h"
using namespace std;

extern string user_data_dir;
extern audio_out aout; // audio output
extern audio_clock clk; // din audio clock
extern int SAMPLE_RATE;
extern float SAMPLE_DURATION; // in secs
extern string APP_NAME;

#if defined __UNIX_JACK__
  #define SOUND_API RtAudio::UNIX_JACK
#elif defined __LINUX_ALSA__
  #define SOUND_API RtAudio::LINUX_ALSA
#elif defined __MACOSX_CORE__
  #define SOUND_API RtAudio::MACOSX_CORE
#elif defined __WINDOWS_DS__
  #define SOUND_API RtAudio::WINDOWS_DS
#endif

audio_out::audio_out () : dac (SOUND_API) {

  samples_buffers = 0;
  available = 0;
  result = ams = fms = vol = gatr = mix = mixa = bufL = bufR = fdrL = fdrR = 0;

  prefs_name = user_data_dir + "audio_prefs";
  load_prefs ();

  probe ();
  open ();

}

audio_out::~audio_out () {

  save_prefs ();

  int num_ptrs = 11;
  sample_t* ptr [] = {samples_buffers, result, ams, fms, vol, gatr, mix, bufL, bufR, fdrL, fdrR, mixa};
  for (int i = 0; i < num_ptrs; ++i) if (ptr[i]) delete[] ptr[i];

  if (available) delete[] available;

  dlog << "--- destroyed audio ---" << endl;

}

void audio_out::alloc () {

  // alloc memory for various buffers
  //

  samples_per_buffer = num_channels * samples_per_channel;
  samples_buffer_size = sizeof (sample_t) * samples_per_buffer;
  samples_channel_size = sizeof (sample_t) * samples_per_channel;

  if (samples_buffers) delete[] samples_buffers;
  samples_buffers = new sample_t [num_samples_buffers * samples_per_buffer];

  if (available) delete[] available;
  available = new int [num_samples_buffers];

  readi = writei = 0;
  readp = writep = samples_buffers;
  for (int i = 0; i < num_samples_buffers; ++i) available[i] = 0;

  int num_ptrs = 11;
  sample_t** ptr [] = {&result, &ams, &fms, &vol, &gatr, &mix, &bufL, &bufR, &fdrL, &fdrR, &mixa};
  for (int i = 0; i < num_ptrs; ++i) {
    if (ptr[i]) delete[] *ptr[i];
    *ptr[i] = new sample_t [samples_per_channel];
  }

}

void audio_out::set_sample_rate (int s) {
  SAMPLE_RATE = sample_rate = s;
  SAMPLE_DURATION = 1.0f / SAMPLE_RATE;
  clk.delta_ticks = samples_per_channel;
  clk.delta_secs = samples_per_channel * SAMPLE_DURATION;
}

void audio_out::open () {
  open (current_device, sample_rate, samples_per_channel);
}

int audio_out::open (int id, unsigned int sr, unsigned int spc) {

  RtAudio::StreamParameters parameters;
  if (id < 0 || id >= num_devices) {
    id = default_device;
    parameters.deviceId = id;
  } else
    parameters.deviceId = id;

  current_device = id;

  // current sample rate exists?
  find_sample_rate_id (sr);
  sr = infos[id].sampleRates[i_sample_rate];

  parameters.nChannels = num_channels;

  RtAudio::StreamOptions options;
  options.flags = RTAUDIO_NONINTERLEAVED;

  try {
    dac.openStream (&parameters, 0, RTAUDIO_FLOAT32, sr, &spc, audio_wanted, 0, &options);
    samples_per_channel = spc;
    alloc ();
    set_sample_rate (sr);
    dlog << "+++ opened audio stream +++" << endl;
  } catch (RtError& e) {
    dlog << "!!! couldnt open audio stream !!!" << endl;
  }
  return 0;

}

int audio_out::start () {
  try {
    dac.startStream ();
    dlog << "+++ " << APP_NAME << " is now streaming audio +++" << endl;
  } catch (RtError& e) {
    dlog << "!!! couldnt start the audio stream !!!" << endl;
  }
  return 0;
}

int audio_out::close () {
  if (dac.isStreamOpen ()) {
    dac.stopStream ();
    dac.closeStream ();
    dlog << "+++ stopped and closed audio stream +++" << endl;
    return 1;
  }
  return 0;
}

void audio_out::load_prefs () {
  //
  // load audio preferences from disk
  //

  ifstream file (prefs_name.c_str(), ios::in);
  string ignore;

  if (!file)
    defaults ();
  else {
    string type;
    file >> ignore >> type;
    if (type != "audio_prefs") {
      dlog << "!!! bad audio prefrences file: " << prefs_name << ", will use defaults. !!!" << endl;
      defaults ();
    } else {
      file >> ignore >> current_device;
      file >> ignore >> num_channels;
      file >> ignore >> samples_per_channel;
      file >> ignore >> sample_rate;
      file >> ignore >> num_samples_buffers;
    }
  }
}

void audio_out::save_prefs () {
  ofstream file (prefs_name.c_str(), ios::out);
  if (file) {
    file << "type: audio_prefs" << endl;
    if (current_device == default_device) current_device = -1;
    file << "current_device " << current_device << endl;
    file << "num_channels " << num_channels << endl;
    file << "samples_per_channel " << samples_per_channel << endl;
    file << "sample_rate " << sample_rate << endl;
    file << "num_samples_buffers " << num_samples_buffers << endl;
    dlog << "+++ saved audio prefrences in " << prefs_name << " +++" << endl;
  } else dlog << "!!! couldnt write audio preferences file: " << prefs_name << " !!!" << endl;
}

void audio_out::defaults () {
  dlog << "<<< using default for audio settings >>>" << endl;
  num_samples_buffers = 2;
  samples_per_channel = 1024;
  aout.set_sample_rate (44100);
}

int audio_out::audio_wanted (void *ob, void *ib, unsigned int spc, double t, RtAudioStreamStatus status, void *data) {

  // stream audio buffer to audio card
  //

  if (aout.available[aout.readi]) { // samples buffer written by main thread ready for streaming
    memcpy (ob, aout.readp, aout.samples_buffer_size); // copy written buffer into audio card

    // update samples buffer status to let main thread write
    aout.available [aout.readi] = 0;
    if (++aout.readi >= aout.num_samples_buffers) {
      aout.readi = 0;
      aout.readp = aout.samples_buffers;
    } else aout.readp += aout.samples_per_buffer;

    ++clk; // update audio clock
  }
  return 0;

}

void audio_out::probe () {

  names.clear ();
  num_devices = dac.getDeviceCount ();
  if (num_devices == 0) {
    dlog << "!!! no audio devices found !!!" << endl;
#ifdef __UNIX_JACK__
    cout << "!!! Please start JACK before running DIN Is Noise !!!" << endl;
    cout << "!!! DIN Is Noise aborted !!!" << endl;
    exit (1);
#endif
  } else {
    dlog << "+++ found " << num_devices << " audio devices +++" << endl;
    default_device = dac.getDefaultOutputDevice ();
    for (int i = 0; i < num_devices; ++i) {
      infos[i] = dac.getDeviceInfo(i);
      RtAudio::DeviceInfo& info = infos[i];
      names.push_back (info.name);
      dlog << " device name = " << info.name << endl;
    }
  }

}

void audio_out::goto_next_device (int i) {
  int prev_device = current_device;
  current_device += i;
  if (current_device < 0) current_device = num_devices - 1; else if (current_device >= num_devices) current_device = 0;
  if (infos[current_device].outputChannels == 0) current_device = prev_device;
  find_sample_rate_id (sample_rate);
}

void audio_out::find_sample_rate_id (unsigned int sr) {
  i_sample_rate = 0;
  std::vector<unsigned int> srates = infos[current_device].sampleRates;
  for (int i = 0, j = srates.size (); i < j; ++i) {
    if (sr == srates[i]) {
      i_sample_rate = i;
      break;
    }
  }
}

int audio_out::goto_next_sample_rate_id (int i) {
  i_sample_rate += i;
  vector<unsigned int> srates = infos[current_device].sampleRates;
  int nrates = srates.size ();
  if (i_sample_rate < 0) i_sample_rate = nrates - 1; else if (i_sample_rate >= nrates) i_sample_rate = 0;
  return srates[i_sample_rate];
}