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