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