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 (¶meters, 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];
}