1 /* GStreamer
2 * Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24
25 #include "gstasioringbuffer.h"
26 #include <string.h>
27 #include "gstasioutils.h"
28 #include "gstasioobject.h"
29
30 GST_DEBUG_CATEGORY_STATIC (gst_asio_ring_buffer_debug);
31 #define GST_CAT_DEFAULT gst_asio_ring_buffer_debug
32
33 struct _GstAsioRingBuffer
34 {
35 GstAudioRingBuffer parent;
36
37 GstAsioDeviceClassType type;
38
39 GstAsioObject *asio_object;
40 guint *channel_indices;
41 guint num_channels;
42 ASIOBufferInfo **infos;
43
44 guint64 callback_id;
45 gboolean callback_installed;
46
47 gboolean running;
48 guint buffer_size;
49
50 /* Used to detect sample gap */
51 gboolean is_first;
52 guint64 expected_sample_position;
53 gboolean trace_sample_position;
54 };
55
56 enum
57 {
58 PROP_0,
59 PROP_DEVICE_INFO,
60 };
61
62 static void gst_asio_ring_buffer_dispose (GObject * object);
63
64 static gboolean gst_asio_ring_buffer_open_device (GstAudioRingBuffer * buf);
65 static gboolean gst_asio_ring_buffer_close_device (GstAudioRingBuffer * buf);
66 static gboolean gst_asio_ring_buffer_acquire (GstAudioRingBuffer * buf,
67 GstAudioRingBufferSpec * spec);
68 static gboolean gst_asio_ring_buffer_release (GstAudioRingBuffer * buf);
69 static gboolean gst_asio_ring_buffer_start (GstAudioRingBuffer * buf);
70 static gboolean gst_asio_ring_buffer_stop (GstAudioRingBuffer * buf);
71 static guint gst_asio_ring_buffer_delay (GstAudioRingBuffer * buf);
72
73 static gboolean gst_asio_buffer_switch_cb (GstAsioObject * obj,
74 glong index, ASIOBufferInfo * infos, guint num_infos,
75 ASIOChannelInfo * input_channel_infos,
76 ASIOChannelInfo * output_channel_infos,
77 ASIOSampleRate sample_rate, glong buffer_size, gpointer user_data);
78
79 #define gst_asio_ring_buffer_parent_class parent_class
80 G_DEFINE_TYPE (GstAsioRingBuffer, gst_asio_ring_buffer,
81 GST_TYPE_AUDIO_RING_BUFFER);
82
83 static void
gst_asio_ring_buffer_class_init(GstAsioRingBufferClass * klass)84 gst_asio_ring_buffer_class_init (GstAsioRingBufferClass * klass)
85 {
86 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
87 GstAudioRingBufferClass *ring_buffer_class =
88 GST_AUDIO_RING_BUFFER_CLASS (klass);
89
90 gobject_class->dispose = gst_asio_ring_buffer_dispose;
91
92 ring_buffer_class->open_device =
93 GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_open_device);
94 ring_buffer_class->close_device =
95 GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_close_device);
96 ring_buffer_class->acquire = GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_acquire);
97 ring_buffer_class->release = GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_release);
98 ring_buffer_class->start = GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_start);
99 ring_buffer_class->resume = GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_start);
100 ring_buffer_class->stop = GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_stop);
101 ring_buffer_class->delay = GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_delay);
102
103 GST_DEBUG_CATEGORY_INIT (gst_asio_ring_buffer_debug,
104 "asioringbuffer", 0, "asioringbuffer");
105 }
106
107 static void
gst_asio_ring_buffer_init(GstAsioRingBuffer * self)108 gst_asio_ring_buffer_init (GstAsioRingBuffer * self)
109 {
110 }
111
112 static void
gst_asio_ring_buffer_dispose(GObject * object)113 gst_asio_ring_buffer_dispose (GObject * object)
114 {
115 GstAsioRingBuffer *self = GST_ASIO_RING_BUFFER (object);
116
117 gst_clear_object (&self->asio_object);
118 g_clear_pointer (&self->channel_indices, g_free);
119
120 G_OBJECT_CLASS (parent_class)->dispose (object);
121 }
122
123 static gboolean
gst_asio_ring_buffer_open_device(GstAudioRingBuffer * buf)124 gst_asio_ring_buffer_open_device (GstAudioRingBuffer * buf)
125 {
126 GstAsioRingBuffer *self = GST_ASIO_RING_BUFFER (buf);
127
128 GST_DEBUG_OBJECT (self, "Open");
129
130 return TRUE;
131 }
132
133 static gboolean
gst_asio_ring_buffer_close_device(GstAudioRingBuffer * buf)134 gst_asio_ring_buffer_close_device (GstAudioRingBuffer * buf)
135 {
136 GstAsioRingBuffer *self = GST_ASIO_RING_BUFFER (buf);
137
138 GST_DEBUG_OBJECT (self, "Close");
139
140 return TRUE;
141 }
142
143 #define PACK_ASIO_64(v) ((v).lo | ((guint64)((v).hi) << 32))
144
145 static gboolean
gst_asio_buffer_switch_cb(GstAsioObject * obj,glong index,ASIOBufferInfo * infos,guint num_infos,ASIOChannelInfo * input_channel_infos,ASIOChannelInfo * output_channel_infos,ASIOSampleRate sample_rate,glong buffer_size,ASIOTime * time_info,gpointer user_data)146 gst_asio_buffer_switch_cb (GstAsioObject * obj, glong index,
147 ASIOBufferInfo * infos, guint num_infos,
148 ASIOChannelInfo * input_channel_infos,
149 ASIOChannelInfo * output_channel_infos,
150 ASIOSampleRate sample_rate, glong buffer_size,
151 ASIOTime * time_info, gpointer user_data)
152 {
153 GstAsioRingBuffer *self = (GstAsioRingBuffer *) user_data;
154 GstAudioRingBuffer *ringbuffer = GST_AUDIO_RING_BUFFER_CAST (self);
155 gint segment;
156 guint8 *readptr;
157 gint len;
158 guint i, j;
159 guint num_channels = 0;
160 guint bps = GST_AUDIO_INFO_WIDTH (&ringbuffer->spec.info) >> 3;
161
162 g_assert (index == 0 || index == 1);
163 g_assert (num_infos >= self->num_channels);
164
165 GST_TRACE_OBJECT (self, "Buffer Switch callback, index %ld", index);
166
167 if (!gst_audio_ring_buffer_prepare_read (ringbuffer,
168 &segment, &readptr, &len)) {
169 GST_WARNING_OBJECT (self, "No segment available");
170 return TRUE;
171 }
172
173 GST_TRACE_OBJECT (self, "segment %d, length %d", segment, len);
174
175 /* Check missing frames */
176 if (self->type == GST_ASIO_DEVICE_CLASS_CAPTURE) {
177 if (self->is_first) {
178 if (time_info) {
179 self->expected_sample_position =
180 PACK_ASIO_64 (time_info->timeInfo.samplePosition) + buffer_size;
181 self->trace_sample_position = TRUE;
182 } else {
183 GST_WARNING_OBJECT (self, "ASIOTime is not available");
184 self->trace_sample_position = FALSE;
185 }
186
187 self->is_first = FALSE;
188 } else if (self->trace_sample_position) {
189 if (!time_info) {
190 GST_WARNING_OBJECT (self, "ASIOTime is not available");
191 self->trace_sample_position = FALSE;
192 } else {
193 guint64 sample_position =
194 PACK_ASIO_64 (time_info->timeInfo.samplePosition);
195 if (self->expected_sample_position < sample_position) {
196 guint64 gap_frames = sample_position - self->expected_sample_position;
197 gint gap_size = gap_frames * bps;
198
199 GST_WARNING_OBJECT (self, "%" G_GUINT64_FORMAT " frames are missing");
200
201 while (gap_size >= len) {
202 gst_audio_format_info_fill_silence (ringbuffer->spec.info.finfo,
203 readptr, len);
204 gst_audio_ring_buffer_advance (ringbuffer, 1);
205
206 gst_audio_ring_buffer_prepare_read (ringbuffer,
207 &segment, &readptr, &len);
208
209 gap_size -= len;
210 }
211 }
212
213 self->expected_sample_position = sample_position + buffer_size;
214 GST_TRACE_OBJECT (self, "Sample Position %" G_GUINT64_FORMAT
215 ", next: %" G_GUINT64_FORMAT, sample_position,
216 self->expected_sample_position);
217 }
218 }
219 }
220
221 /* Given @infos might contain more channel data, pick channels what we want to
222 * read */
223 for (i = 0; i < num_infos; i++) {
224 ASIOBufferInfo *info = &infos[i];
225
226 if (self->type == GST_ASIO_DEVICE_CLASS_CAPTURE) {
227 if (!info->isInput)
228 continue;
229 } else {
230 if (info->isInput)
231 continue;
232 }
233
234 for (j = 0; j < self->num_channels; j++) {
235 if (self->channel_indices[j] != info->channelNum)
236 continue;
237
238 g_assert (num_channels < self->num_channels);
239 self->infos[num_channels++] = info;
240 break;
241 }
242 }
243
244 if (num_channels < self->num_channels) {
245 GST_ERROR_OBJECT (self, "Too small number of channel %d (expected %d)",
246 num_channels, self->num_channels);
247 } else {
248 if (self->type == GST_ASIO_DEVICE_CLASS_CAPTURE ||
249 self->type == GST_ASIO_DEVICE_CLASS_LOOPBACK_CAPTURE) {
250 if (num_channels == 1) {
251 memcpy (readptr, self->infos[0]->buffers[index], len);
252 } else {
253 guint gst_offset = 0, asio_offset = 0;
254
255 /* Interleaves audio */
256 while (gst_offset < len) {
257 for (i = 0; i < num_channels; i++) {
258 ASIOBufferInfo *info = self->infos[i];
259
260 memcpy (readptr + gst_offset,
261 ((guint8 *) info->buffers[index]) + asio_offset, bps);
262
263 gst_offset += bps;
264 }
265 asio_offset += bps;
266 }
267 }
268 } else {
269 if (num_channels == 1) {
270 memcpy (self->infos[0]->buffers[index], readptr, len);
271 } else {
272 guint gst_offset = 0, asio_offset = 0;
273
274 /* Interleaves audio */
275 while (gst_offset < len) {
276 for (i = 0; i < num_channels; i++) {
277 ASIOBufferInfo *info = self->infos[i];
278
279 memcpy (((guint8 *) info->buffers[index]) + asio_offset,
280 readptr + gst_offset, bps);
281
282 gst_offset += bps;
283 }
284 asio_offset += bps;
285 }
286 }
287 }
288 }
289
290 if (self->type == GST_ASIO_DEVICE_CLASS_RENDER)
291 gst_audio_ring_buffer_clear (ringbuffer, segment);
292 gst_audio_ring_buffer_advance (ringbuffer, 1);
293
294 return TRUE;
295 }
296
297 static gboolean
gst_asio_ring_buffer_acquire(GstAudioRingBuffer * buf,GstAudioRingBufferSpec * spec)298 gst_asio_ring_buffer_acquire (GstAudioRingBuffer * buf,
299 GstAudioRingBufferSpec * spec)
300 {
301 GstAsioRingBuffer *self = GST_ASIO_RING_BUFFER (buf);
302
303 if (!self->asio_object) {
304 GST_ERROR_OBJECT (self, "No configured ASIO object");
305 return FALSE;
306 }
307
308 if (!self->channel_indices || self->num_channels == 0) {
309 GST_ERROR_OBJECT (self, "No configured channels");
310 return FALSE;
311 }
312
313 if (!gst_asio_object_set_sample_rate (self->asio_object,
314 GST_AUDIO_INFO_RATE (&spec->info))) {
315 GST_ERROR_OBJECT (self, "Failed to set sample rate");
316 return FALSE;
317 }
318
319 spec->segsize = self->buffer_size *
320 (GST_AUDIO_INFO_WIDTH (&spec->info) >> 3) *
321 GST_AUDIO_INFO_CHANNELS (&spec->info);
322 spec->segtotal = 2;
323
324 buf->size = spec->segtotal * spec->segsize;
325 buf->memory = (guint8 *) g_malloc (buf->size);
326 gst_audio_format_info_fill_silence (buf->spec.info.finfo,
327 buf->memory, buf->size);
328
329 return TRUE;
330 }
331
332 static gboolean
gst_asio_ring_buffer_release(GstAudioRingBuffer * buf)333 gst_asio_ring_buffer_release (GstAudioRingBuffer * buf)
334 {
335 GST_DEBUG_OBJECT (buf, "Release");
336
337 g_clear_pointer (&buf->memory, g_free);
338
339 return TRUE;
340 }
341
342 static gboolean
gst_asio_ring_buffer_start(GstAudioRingBuffer * buf)343 gst_asio_ring_buffer_start (GstAudioRingBuffer * buf)
344 {
345 GstAsioRingBuffer *self = GST_ASIO_RING_BUFFER (buf);
346 GstAsioObjectCallbacks callbacks;
347
348 GST_DEBUG_OBJECT (buf, "Start");
349
350 callbacks.buffer_switch = gst_asio_buffer_switch_cb;
351 callbacks.user_data = self;
352
353 self->is_first = TRUE;
354 self->expected_sample_position = 0;
355
356 if (!gst_asio_object_install_callback (self->asio_object, self->type,
357 &callbacks, &self->callback_id)) {
358 GST_ERROR_OBJECT (self, "Failed to install callback");
359 return FALSE;
360 }
361
362 self->callback_installed = TRUE;
363
364 if (!gst_asio_object_start (self->asio_object)) {
365 GST_ERROR_OBJECT (self, "Failed to start");
366
367 gst_asio_ring_buffer_stop (buf);
368
369 return FALSE;
370 }
371
372 self->running = TRUE;
373
374 return TRUE;
375 }
376
377 static gboolean
gst_asio_ring_buffer_stop(GstAudioRingBuffer * buf)378 gst_asio_ring_buffer_stop (GstAudioRingBuffer * buf)
379 {
380 GstAsioRingBuffer *self = GST_ASIO_RING_BUFFER (buf);
381
382 GST_DEBUG_OBJECT (buf, "Stop");
383
384 self->running = FALSE;
385
386 if (!self->asio_object)
387 return TRUE;
388
389 if (self->callback_installed)
390 gst_asio_object_uninstall_callback (self->asio_object, self->callback_id);
391
392 self->callback_installed = FALSE;
393 self->callback_id = 0;
394 self->is_first = TRUE;
395 self->expected_sample_position = 0;
396
397 return TRUE;
398 }
399
400 static guint
gst_asio_ring_buffer_delay(GstAudioRingBuffer * buf)401 gst_asio_ring_buffer_delay (GstAudioRingBuffer * buf)
402 {
403 /* FIXME: impl. */
404
405 return 0;
406 }
407
408 GstAsioRingBuffer *
gst_asio_ring_buffer_new(GstAsioObject * object,GstAsioDeviceClassType type,const gchar * name)409 gst_asio_ring_buffer_new (GstAsioObject * object, GstAsioDeviceClassType type,
410 const gchar * name)
411 {
412 GstAsioRingBuffer *self;
413
414 g_return_val_if_fail (GST_IS_ASIO_OBJECT (object), nullptr);
415
416 self =
417 (GstAsioRingBuffer *) g_object_new (GST_TYPE_ASIO_RING_BUFFER,
418 "name", name, nullptr);
419 g_assert (self);
420
421 self->type = type;
422 self->asio_object = (GstAsioObject *) gst_object_ref (object);
423
424 return self;
425 }
426
427 gboolean
gst_asio_ring_buffer_configure(GstAsioRingBuffer * buf,guint * channel_indices,guint num_channles,guint preferred_buffer_size)428 gst_asio_ring_buffer_configure (GstAsioRingBuffer * buf,
429 guint * channel_indices, guint num_channles, guint preferred_buffer_size)
430 {
431 g_return_val_if_fail (GST_IS_ASIO_RING_BUFFER (buf), FALSE);
432 g_return_val_if_fail (buf->asio_object != nullptr, FALSE);
433 g_return_val_if_fail (num_channles > 0, FALSE);
434
435 GST_DEBUG_OBJECT (buf, "Configure");
436
437 buf->buffer_size = preferred_buffer_size;
438
439 if (!gst_asio_object_create_buffers (buf->asio_object, buf->type,
440 channel_indices, num_channles, &buf->buffer_size)) {
441 GST_ERROR_OBJECT (buf, "Failed to configure");
442
443 g_clear_pointer (&buf->channel_indices, g_free);
444 buf->num_channels = 0;
445
446 return FALSE;
447 }
448
449 GST_DEBUG_OBJECT (buf, "configured buffer size: %d", buf->buffer_size);
450
451 g_free (buf->channel_indices);
452 buf->channel_indices = g_new0 (guint, num_channles);
453
454 for (guint i = 0; i < num_channles; i++)
455 buf->channel_indices[i] = channel_indices[i];
456
457 buf->num_channels = num_channles;
458
459 g_clear_pointer (&buf->infos, g_free);
460 buf->infos = g_new0 (ASIOBufferInfo *, num_channles);
461
462 return TRUE;
463 }
464
465 GstCaps *
gst_asio_ring_buffer_get_caps(GstAsioRingBuffer * buf)466 gst_asio_ring_buffer_get_caps (GstAsioRingBuffer * buf)
467 {
468 g_return_val_if_fail (GST_IS_ASIO_RING_BUFFER (buf), nullptr);
469 g_assert (buf->asio_object != nullptr);
470
471 return gst_asio_object_get_caps (buf->asio_object,
472 buf->type, buf->num_channels, buf->num_channels);
473 }
474