Subversion Repositories DIN Is Noise

Rev

Rev 2184 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
964 jag 1
/*
2
* audio.cc
2302 jag 3
* DIN Is Noise is copyright (c) 2006-2025 Jagannathan Sampath
1713 jag 4
* DIN Is Noise is released under GNU Public License 2.0
1479 jag 5
* For more information, please visit https://dinisnoise.org/
964 jag 6
*/
7
 
8
 
9
#include "main.h"
10
#include "audio.h"
11
#include "chrono.h"
12
#include "command.h"
13
#include <math.h>
14
#include <fstream>
1365 jag 15
#include <cstring>
964 jag 16
#include <limits.h>
17
#include <stdlib.h>
18
#include <string>
1141 jag 19
#include "utils.h"
2182 jag 20
#include "RtAudio.h"
964 jag 21
#include "RtError.h"
22
#include "log.h"
1455 jag 23
#include "console.h"
1924 jag 24
#include "oscilloscope.h"
25
 
964 jag 26
using namespace std;
27
 
28
extern string user_data_dir;
29
extern string APP_NAME;
1924 jag 30
extern oscilloscope scope;
964 jag 31
 
32
#if defined __UNIX_JACK__
33
  #define SOUND_API RtAudio::UNIX_JACK
34
#elif defined __LINUX_ALSA__
35
  #define SOUND_API RtAudio::LINUX_ALSA
36
#elif defined __MACOSX_CORE__
37
  #define SOUND_API RtAudio::MACOSX_CORE
38
#elif defined __WINDOWS_DS__
39
  #define SOUND_API RtAudio::WINDOWS_DS
40
#endif
41
 
42
audio_out::audio_out () : dac (SOUND_API) {
43
 
44
  samples_buffers = 0;
45
  available = 0;
46
  result = ams = fms = vol = gatr = mix = mixa = bufL = bufR = fdr1 = fdr2 = 0;
47
 
48
  prefs_name = user_data_dir + "audio_prefs";
49
  load_prefs ();
50
 
51
  probe ();
1456 jag 52
  open ();
964 jag 53
 
54
}
55
 
56
audio_out::~audio_out () {
57
 
58
  save_prefs ();
59
 
60
  int num_ptrs = 11;
61
  sample_t* ptr [] = {samples_buffers, result, ams, fms, vol, gatr, mix, bufL, bufR, fdr1, fdr2, mixa};
62
  for (int i = 0; i < num_ptrs; ++i) if (ptr[i]) delete[] ptr[i];
63
 
64
  if (available) delete[] available;
65
 
66
  dlog << "--- destroyed audio ---" << endl;
67
 
68
}
69
 
70
void audio_out::alloc () {
71
 
72
  // alloc memory for various buffers
73
  //
74
 
75
  samples_per_buffer = num_channels * samples_per_channel;
76
  samples_buffer_size = sizeof (sample_t) * samples_per_buffer;
77
  samples_channel_size = sizeof (sample_t) * samples_per_channel;
78
 
79
  if (samples_buffers) delete[] samples_buffers;
80
  samples_buffers = new sample_t [num_samples_buffers * samples_per_buffer];
81
 
82
  if (available) delete[] available;
83
  available = new int [num_samples_buffers];
84
  memset (available, 0, num_samples_buffers * sizeof (int));
85
 
86
  readi = writei = 0;
87
  readp = writep = samples_buffers;
88
 
89
  int num_ptrs = 11;
90
  sample_t** ptr [] = {&result, &ams, &fms, &vol, &gatr, &mix, &bufL, &bufR, &fdr1, &fdr2, &mixa};
91
  for (int i = 0; i < num_ptrs; ++i) {
92
    if (ptr[i]) delete[] *ptr[i];
93
    *ptr[i] = new sample_t [samples_per_channel];
94
    memset (*ptr[i], 0, samples_channel_size);
95
  }
96
 
97
}
98
 
1456 jag 99
int audio_out::open () {
100
  return open (current_device, sample_rate, samples_per_channel);
964 jag 101
}
102
 
103
int audio_out::open (int id, unsigned int sr, unsigned int spc) {
104
 
105
  RtAudio::StreamParameters parameters;
1224 jag 106
  if (id > -1 && id < num_devices) {
107
    parameters.deviceId = id;
108
  } else {
964 jag 109
    id = default_device;
110
    parameters.deviceId = id;
1224 jag 111
  }
964 jag 112
 
113
  current_device = id;
114
 
115
  // current sample rate exists?
116
  find_sample_rate_id (sr);
117
  sr = infos[id].sampleRates[i_sample_rate];
118
 
119
  parameters.nChannels = num_channels;
120
 
121
  RtAudio::StreamOptions options;
122
  options.flags = RTAUDIO_NONINTERLEAVED;
123
 
124
  try {
125
    dac.openStream (&parameters, 0, RTAUDIO_FLOAT32, sr, &spc, audio_wanted, 0, &options);
126
    set_samples_per_channel (spc);
127
    alloc ();
128
    set_sample_rate (sr);
129
    dlog << "+++ opened audio stream +++" << endl;
1455 jag 130
    return 1;
964 jag 131
  } catch (RtError& e) {
132
    dlog << "!!! couldnt open audio stream !!!" << endl;
133
  }
134
  return 0;
135
 
136
}
137
 
138
int audio_out::start () {
139
  try {
140
    dac.startStream ();
141
    dlog << "+++ " << APP_NAME << " is now streaming audio +++" << endl;
142
  } catch (RtError& e) {
143
    dlog << "!!! couldnt start the audio stream !!!" << endl;
144
  }
145
  return 0;
146
}
147
 
148
int audio_out::close () {
149
  if (dac.isStreamOpen ()) {
150
    dac.stopStream ();
151
    dac.closeStream ();
152
    dlog << "+++ stopped and closed audio stream +++" << endl;
153
    return 1;
154
  }
155
  return 0;
156
}
157
 
158
void audio_out::load_prefs () {
159
  //
160
  // load audio preferences from disk
161
  //
162
 
163
  ifstream file (prefs_name.c_str(), ios::in);
164
  string ignore;
165
 
166
  if (!file) {
167
    dlog << "!!! bad audio_prefs file. sorry, have to abort !!!" << endl;
168
    exit (1);
169
  } else {
1425 jag 170
    file >> ignore >> current_device;
171
    file >> ignore >> num_channels;
172
    int spc; file >> ignore >> spc; set_samples_per_channel (spc);
173
    file >> ignore >> sample_rate;
174
    file >> ignore >> num_samples_buffers;
964 jag 175
  }
176
}
177
 
178
void audio_out::save_prefs () {
179
  ofstream file (prefs_name.c_str(), ios::out);
180
  if (file) {
181
    if (current_device == default_device) current_device = -1;
182
    file << "current_device " << current_device << endl;
183
    file << "num_channels " << num_channels << endl;
184
    file << "samples_per_channel " << samples_per_channel << endl;
185
    file << "sample_rate " << sample_rate << endl;
186
    file << "num_samples_buffers " << num_samples_buffers << endl;
187
    dlog << "+++ saved audio prefrences in " << prefs_name << " +++" << endl;
188
  } else dlog << "!!! couldnt write audio preferences file: " << prefs_name << " !!!" << endl;
189
}
190
 
191
void audio_out::set_samples_per_channel (int spc) {
192
  samples_per_channel = spc;
193
  last_sample = samples_per_channel - 1;
1924 jag 194
  scope.alloc (spc);
964 jag 195
}
196
 
197
void audio_out::set_sample_rate (int s) {
198
  SAMPLE_RATE = sample_rate = s;
199
  SAMPLE_DURATION = 1.0f / SAMPLE_RATE;
200
  clk.delta_ticks = samples_per_channel;
201
  clk.delta_secs = samples_per_channel * SAMPLE_DURATION;
202
}
203
 
204
int audio_out::audio_wanted (void *ob, void *ib, unsigned int spc, double t, RtAudioStreamStatus status, void *data) {
205
 
206
  // stream audio buffer to audio card
207
  //
208
 
209
  if (aout.available[aout.readi]) { // samples buffer written by main thread ready for streaming
210
    memcpy (ob, aout.readp, aout.samples_buffer_size); // copy written buffer into audio card
211
 
212
    // update samples buffer status to let main thread write
213
    aout.available [aout.readi] = 0;
214
    if (++aout.readi >= aout.num_samples_buffers) {
215
      aout.readi = 0;
216
      aout.readp = aout.samples_buffers;
217
    } else aout.readp += aout.samples_per_buffer;
218
 
219
    ++clk; // update audio clock
220
  }
221
  return 0;
222
 
223
}
224
 
225
void audio_out::probe () {
226
 
227
  names.clear ();
228
  num_devices = dac.getDeviceCount ();
229
  if (num_devices == 0) {
230
    dlog << "!!! no audio devices found !!!" << endl;
231
#ifdef __UNIX_JACK__
232
    cout << "!!! Please start JACK before running DIN Is Noise !!!" << endl;
233
    cout << "!!! DIN Is Noise aborted !!!" << endl;
234
    exit (1);
235
#endif
236
  } else {
1455 jag 237
    last_device = num_devices - 1;
964 jag 238
    dlog << "+++ found " << num_devices << " audio devices +++" << endl;
1456 jag 239
    infos.resize (num_devices);
964 jag 240
    default_device = dac.getDefaultOutputDevice ();
1141 jag 241
    dlog << "+++ default output device = " << default_device << " +++" << endl;
964 jag 242
    if (current_device == INVALID) current_device = default_device;
1455 jag 243
    next_device = current_device;
964 jag 244
    for (int i = 0; i < num_devices; ++i) {
1456 jag 245
      RtAudio::DeviceInfo& info = infos[i];
246
      info = dac.getDeviceInfo (i);
964 jag 247
      names.push_back (info.name);
248
      char br = '[';
2024 jag 249
      dlog << "name = " << info.name << " input channels [default?] = " << info.inputChannels << br << info.isDefaultInput << "], output channels [default?] = " << info.outputChannels << br << info.isDefaultOutput << "] duplex = " << info.duplexChannels << spc << endl;
2182 jag 250
      int k = info.sampleRates.size ();
2184 jag 251
      dlog << "sample rates [" << k << "]: ";
964 jag 252
      for (int i = 0, j = info.sampleRates.size (); i < j; ++i) {
1695 jag 253
        dlog << info.sampleRates[i] << spc;
964 jag 254
      }
255
      dlog << endl;
256
    }
257
  }
258
 
259
}
260
 
1427 jag 261
int audio_out::goto_next_device (int i) {
1455 jag 262
  next_device = current_device + i;
263
  wrap (0, next_device, last_device);
264
  if (infos[next_device].outputChannels == 0) {
1458 jag 265
    cons << RED << names[next_device] << " has no output channels" << eol;
1455 jag 266
    return 0;
267
  }
1427 jag 268
  current_device = next_device;
269
  find_sample_rate_id (sample_rate);
270
  return 1;
964 jag 271
}
272
 
273
void audio_out::find_sample_rate_id (unsigned int sr) {
274
  i_sample_rate = 0;
275
  std::vector<unsigned int> srates = infos[current_device].sampleRates;
276
  for (int i = 0, j = srates.size (); i < j; ++i) {
277
    if (sr == srates[i]) {
278
      i_sample_rate = i;
279
      break;
280
    }
281
  }
282
}
283
 
284
int audio_out::goto_next_sample_rate_id (int i) {
285
  i_sample_rate += i;
286
  vector<unsigned int> srates = infos[current_device].sampleRates;
287
  int nrates = srates.size ();
288
  if (i_sample_rate < 0) i_sample_rate = nrates - 1; else if (i_sample_rate >= nrates) i_sample_rate = 0;
289
  return srates[i_sample_rate];
290
}
1456 jag 291
 
292
void audio_out::list () {
293
  default_device = dac.getDefaultOutputDevice ();
294
  for (int i = 0; i < num_devices; ++i) {
295
    RtAudio::DeviceInfo& info = infos[i];
296
    cons << GREEN << "name = " << info.name << " input = " << info.inputChannels <<  " output = " << info.outputChannels \
297
         << " duplex = " << info.duplexChannels << eol;
298
    cons << CYAN << "  sample rates: ";
1695 jag 299
    for (int i = 0, j = info.sampleRates.size (); i < j; ++i) cons << int (info.sampleRates[i]) << spc;
1456 jag 300
    cons << eol;
301
  }
302
  cons << YELLOW << "number of devices = " << num_devices << eol;
303
  cons << "final device = " << last_device << ", next device = " << next_device << eol;
304
  cons << "default device = " << default_device << ", current device = " << current_device << eol;
305
}