1 /*
2 * Copyright (C) 2020-2021 Alexandros Theodotou <alex at zrythm dot org>
3 *
4 * This file is part of Zrythm
5 *
6 * Zrythm is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Zrythm is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "zrythm-config.h"
21
22 #ifdef HAVE_RTAUDIO
23
24 #include "audio/engine.h"
25 #include "audio/engine_rtaudio.h"
26 #include "audio/port.h"
27 #include "audio/rtaudio_device.h"
28 #include "project.h"
29 #include "utils/objects.h"
30 #include "utils/string.h"
31
32 #include <gtk/gtk.h>
33
34 static void
error_cb(rtaudio_error_t err,const char * msg)35 error_cb (
36 rtaudio_error_t err,
37 const char * msg)
38 {
39 g_critical ("RtAudio error: %s", msg);
40 }
41
42 static int
myaudio_cb(float * out_buf,float * in_buf,unsigned int nframes,double stream_time,rtaudio_stream_status_t status,RtAudioDevice * self)43 myaudio_cb (
44 float * out_buf,
45 float * in_buf,
46 unsigned int nframes,
47 double stream_time,
48 rtaudio_stream_status_t status,
49 RtAudioDevice * self)
50 {
51 /*g_debug ("calling callback");*/
52
53 /* if input device, receive data */
54 if (in_buf)
55 {
56 zix_sem_wait (&self->audio_ring_sem);
57
58 if (status != 0)
59 {
60 /* xrun */
61 g_message ("XRUN in RtAudio");
62 }
63
64 zix_ring_write (
65 self->audio_ring, in_buf,
66 nframes * sizeof (float));
67
68 #if 0
69 for (unsigned int i = 0; i < nframes; i++)
70 {
71 if (in_buf[i] > 0.08f)
72 {
73 g_message (
74 "have input %f", (double) in_buf[i]);
75 }
76 }
77 #endif
78
79 zix_sem_post (&self->audio_ring_sem);
80 }
81
82 return 0;
83 }
84
85 RtAudioDevice *
rtaudio_device_new(int is_input,const char * device_name,unsigned int device_id,unsigned int channel_idx,Port * port)86 rtaudio_device_new (
87 int is_input,
88 const char * device_name,
89 unsigned int device_id,
90 unsigned int channel_idx,
91 Port * port)
92 {
93 RtAudioDevice * self = object_new (RtAudioDevice);
94
95 self->is_input = is_input;
96 self->id = device_id;
97 self->port = port;
98 self->channel_idx = channel_idx;
99 self->handle =
100 engine_rtaudio_create_rtaudio (AUDIO_ENGINE);
101 if (!self->handle)
102 {
103 g_warning ("Failed to create RtAudio handle");
104 free (self);
105 return NULL;
106 }
107
108 rtaudio_device_info_t dev_nfo;
109 if (device_name)
110 {
111 int dev_count =
112 rtaudio_device_count (self->handle);
113 for (int i = 0; i < dev_count; i++)
114 {
115 rtaudio_device_info_t cur_dev_nfo =
116 rtaudio_get_device_info (
117 self->handle, i);
118 if (string_is_equal (
119 cur_dev_nfo.name, device_name))
120 {
121 dev_nfo = cur_dev_nfo;
122 break;
123 }
124 }
125 }
126 else
127 {
128 dev_nfo =
129 rtaudio_get_device_info (
130 self->handle, (int) device_id);
131 }
132 self->name = g_strdup (dev_nfo.name);
133
134 self->audio_ring =
135 zix_ring_new (
136 sizeof (float) *
137 (size_t) RTAUDIO_DEVICE_BUFFER_SIZE);
138
139 zix_sem_init (&self->audio_ring_sem, 1);
140
141 return self;
142 }
143
144 /**
145 * Opens a device allocated with
146 * rtaudio_device_new().
147 *
148 * @param start Also start the device.
149 *
150 * @return Non-zero if error.
151 */
152 int
rtaudio_device_open(RtAudioDevice * self,int start)153 rtaudio_device_open (
154 RtAudioDevice * self,
155 int start)
156 {
157 g_message ("opening rtaudio device");
158
159 rtaudio_device_info_t dev_nfo =
160 rtaudio_get_device_info (
161 self->handle, (int) self->id);
162 g_message (
163 "RtAudio device %d: %s",
164 self->id, dev_nfo.name);
165
166 /* prepare params */
167 struct rtaudio_stream_parameters stream_params = {
168 .device_id = (unsigned int) self->id,
169 .num_channels = 1,
170 .first_channel = self->channel_idx,
171 };
172 struct rtaudio_stream_options stream_opts = {
173 .flags = RTAUDIO_FLAGS_SCHEDULE_REALTIME,
174 .num_buffers = 2, .priority = 99,
175 .name = "Zrythm",
176 };
177
178 unsigned int samplerate =
179 AUDIO_ENGINE->sample_rate;
180 unsigned int buffer_size =
181 AUDIO_ENGINE->block_length;
182 /* input stream */
183 int ret =
184 rtaudio_open_stream (
185 self->handle, NULL, &stream_params,
186 RTAUDIO_FORMAT_FLOAT32, samplerate,
187 &buffer_size, (rtaudio_cb_t) myaudio_cb, self,
188 &stream_opts, (rtaudio_error_cb_t) error_cb);
189 if (ret)
190 {
191 g_warning (
192 "An error occurred opening the RtAudio "
193 "stream: %s", rtaudio_error (self->handle));
194 return -1;
195 }
196 g_message ("Opened %s with samplerate %u and "
197 "buffer size %u",
198 dev_nfo.name, samplerate,
199 buffer_size);
200 self->opened = 1;
201
202 if (start)
203 {
204 return rtaudio_device_start (self);
205 }
206
207 return 0;
208 }
209
210 int
rtaudio_device_start(RtAudioDevice * self)211 rtaudio_device_start (
212 RtAudioDevice * self)
213 {
214 int ret =
215 rtaudio_start_stream (self->handle);
216 if (ret)
217 {
218 g_critical (
219 "An error occurred starting the RtAudio "
220 "stream: %s", rtaudio_error (self->handle));
221 return ret;
222 }
223 rtaudio_device_info_t dev_nfo =
224 rtaudio_get_device_info (
225 self->handle, (int) self->id);
226 g_message ("RtAudio device %s started",
227 dev_nfo.name);
228 self->started = 1;
229
230 return 0;
231 }
232
233 int
rtaudio_device_stop(RtAudioDevice * self)234 rtaudio_device_stop (
235 RtAudioDevice * self)
236 {
237 int ret = rtaudio_stop_stream (self->handle);
238 if (ret)
239 {
240 g_critical (
241 "An error occurred stopping the RtAudio "
242 "stream: %s", rtaudio_error (self->handle));
243 return ret;
244 }
245 rtaudio_device_info_t dev_nfo =
246 rtaudio_get_device_info (
247 self->handle, (int) self->id);
248 g_message ("RtAudio device %s stopped",
249 dev_nfo.name);
250 self->started = 0;
251
252 return 0;
253 }
254
255 /**
256 * Close the RtAudioDevice.
257 *
258 * @param free Also free the memory.
259 */
260 int
rtaudio_device_close(RtAudioDevice * self,int free_device)261 rtaudio_device_close (
262 RtAudioDevice * self,
263 int free_device)
264 {
265 g_message ("closing rtaudio device");
266 rtaudio_close_stream (self->handle);
267 self->opened = 0;
268
269 if (free_device)
270 {
271 rtaudio_device_free (self);
272 }
273
274 return 0;
275 }
276
277 void
rtaudio_device_free(RtAudioDevice * self)278 rtaudio_device_free (
279 RtAudioDevice * self)
280 {
281 if (self->audio_ring)
282 zix_ring_free (self->audio_ring);
283
284 zix_sem_destroy (&self->audio_ring_sem);
285
286 if (self->name)
287 g_free (self->name);
288
289 free (self);
290 }
291
292 #endif // HAVE_RTAUDIO
293