Introduce Steinberg ASIO (Audio Streaming Input/Output) plugin
[platform/upstream/gstreamer.git] / sys / asio / gstasiodeviceprovider.cpp
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 #include "gstasiodeviceprovider.h"
25 #include "gstasioutils.h"
26 #include "gstasioobject.h"
27 #include <atlconv.h>
28
29 enum
30 {
31   PROP_0,
32   PROP_DEVICE_CLSID,
33 };
34
35 struct _GstAsioDevice
36 {
37   GstDevice parent;
38
39   gchar *device_clsid;
40   const gchar *factory_name;
41 };
42
43 G_DEFINE_TYPE (GstAsioDevice, gst_asio_device, GST_TYPE_DEVICE);
44
45 static void gst_asio_device_get_property (GObject * object,
46     guint prop_id, GValue * value, GParamSpec * pspec);
47 static void gst_asio_device_set_property (GObject * object,
48     guint prop_id, const GValue * value, GParamSpec * pspec);
49 static void gst_asio_device_finalize (GObject * object);
50 static GstElement *gst_asio_device_create_element (GstDevice * device,
51     const gchar * name);
52
53 static void
54 gst_asio_device_class_init (GstAsioDeviceClass * klass)
55 {
56   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
57   GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
58
59   dev_class->create_element = gst_asio_device_create_element;
60
61   gobject_class->get_property = gst_asio_device_get_property;
62   gobject_class->set_property = gst_asio_device_set_property;
63   gobject_class->finalize = gst_asio_device_finalize;
64
65   g_object_class_install_property (gobject_class, PROP_DEVICE_CLSID,
66       g_param_spec_string ("device-clsid", "Device CLSID",
67           "ASIO device CLSID as string including curly brackets", NULL,
68           (GParamFlags) (G_PARAM_READWRITE |
69               G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)));
70 }
71
72 static void
73 gst_asio_device_init (GstAsioDevice * self)
74 {
75 }
76
77 static void
78 gst_asio_device_finalize (GObject * object)
79 {
80   GstAsioDevice *self = GST_ASIO_DEVICE (object);
81
82   g_free (self->device_clsid);
83
84   G_OBJECT_CLASS (gst_asio_device_parent_class)->finalize (object);
85 }
86
87 static GstElement *
88 gst_asio_device_create_element (GstDevice * device, const gchar * name)
89 {
90   GstAsioDevice *self = GST_ASIO_DEVICE (device);
91   GstElement *elem;
92
93   elem = gst_element_factory_make (self->factory_name, name);
94
95   g_object_set (elem, "device-clsid", self->device_clsid, NULL);
96
97   return elem;
98 }
99
100 static void
101 gst_asio_device_get_property (GObject * object, guint prop_id,
102     GValue * value, GParamSpec * pspec)
103 {
104   GstAsioDevice *self = GST_ASIO_DEVICE (object);
105
106   switch (prop_id) {
107     case PROP_DEVICE_CLSID:
108       g_value_set_string (value, self->device_clsid);
109       break;
110     default:
111       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
112       break;
113   }
114 }
115
116 static void
117 gst_asio_device_set_property (GObject * object, guint prop_id,
118     const GValue * value, GParamSpec * pspec)
119 {
120   GstAsioDevice *self = GST_ASIO_DEVICE (object);
121
122   switch (prop_id) {
123     case PROP_DEVICE_CLSID:
124       g_free (self->device_clsid);
125       self->device_clsid = g_value_dup_string (value);
126       break;
127     default:
128       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
129       break;
130   }
131 }
132
133 struct _GstAsioDeviceProvider
134 {
135   GstDeviceProvider parent;
136 };
137
138 G_DEFINE_TYPE (GstAsioDeviceProvider, gst_asio_device_provider,
139     GST_TYPE_DEVICE_PROVIDER);
140
141 static GList *gst_asio_device_provider_probe (GstDeviceProvider * provider);
142
143 static void
144 gst_asio_device_provider_class_init (GstAsioDeviceProviderClass * klass)
145 {
146   GstDeviceProviderClass *provider_class = GST_DEVICE_PROVIDER_CLASS (klass);
147
148   provider_class->probe = GST_DEBUG_FUNCPTR (gst_asio_device_provider_probe);
149
150   gst_device_provider_class_set_static_metadata (provider_class,
151       "ASIO Device Provider",
152       "Source/Sink/Audio", "List ASIO source and sink devices",
153       "Seungha Yang <seungha@centricular.com>");
154 }
155
156 static void
157 gst_asio_device_provider_init (GstAsioDeviceProvider * provider)
158 {
159 }
160
161 static void
162 gst_asio_device_provider_probe_internal (GstAsioDeviceProvider * self,
163     gboolean is_src, GList * asio_device_list, GList ** devices)
164 {
165   const gchar *device_class, *factory_name;
166   GList *iter;
167
168   USES_CONVERSION;
169
170   if (is_src) {
171     device_class = "Audio/Source";
172     factory_name = "asiosrc";
173   } else {
174     device_class = "Audio/Sink";
175     factory_name = "asiosink";
176   }
177
178   for (iter = asio_device_list; iter; iter = g_list_next (iter)) {
179     GstDevice *device;
180     GstAsioDeviceInfo *info = (GstAsioDeviceInfo *) iter->data;
181     GstAsioObject *obj;
182     GstCaps *caps = nullptr;
183     GstStructure *props = nullptr;
184     long max_in_ch = 0;
185     long max_out_ch = 0;
186     HRESULT hr;
187     LPOLESTR clsid_str = nullptr;
188     glong min_buf_size = 0;
189     glong max_buf_size = 0;
190     glong preferred_buf_size = 0;
191     glong buf_size_granularity = 0;
192
193     obj = gst_asio_object_new (info, FALSE);
194     if (!obj)
195       continue;
196
197     if (!gst_asio_object_get_max_num_channels (obj, &max_in_ch, &max_out_ch))
198       goto done;
199
200     if (is_src && max_in_ch <= 0)
201       goto done;
202     else if (!is_src && max_out_ch <= 0)
203       goto done;
204
205     if (is_src) {
206       caps = gst_asio_object_get_caps (obj,
207           GST_ASIO_DEVICE_CLASS_CAPTURE, 1, max_in_ch);
208     } else {
209       caps = gst_asio_object_get_caps (obj,
210           GST_ASIO_DEVICE_CLASS_RENDER, 1, max_out_ch);
211     }
212     if (!caps)
213       goto done;
214
215     hr = StringFromIID (info->clsid, &clsid_str);
216     if (FAILED (hr))
217       goto done;
218
219     if (!gst_asio_object_get_buffer_size (obj, &min_buf_size, &max_buf_size,
220             &preferred_buf_size, &buf_size_granularity))
221       goto done;
222
223     props = gst_structure_new ("asio-proplist",
224         "device.api", G_TYPE_STRING, "asio",
225         "device.clsid", G_TYPE_STRING, OLE2A (clsid_str),
226         "asio.device.description", G_TYPE_STRING, info->driver_desc,
227         "asio.device.min-buf-size", G_TYPE_LONG, min_buf_size,
228         "asio.device.max-buf-size", G_TYPE_LONG, max_buf_size,
229         "asio.device.preferred-buf-size", G_TYPE_LONG, preferred_buf_size,
230         "asio.device.buf-size-granularity", G_TYPE_LONG, buf_size_granularity,
231         nullptr);
232
233     device = (GstDevice *) g_object_new (GST_TYPE_ASIO_DEVICE,
234         "device-clsid", OLE2A (clsid_str),
235         "display-name", info->driver_desc, "caps", caps,
236         "device-class", device_class, "properties", props, nullptr);
237     GST_ASIO_DEVICE (device)->factory_name = factory_name;
238
239     *devices = g_list_append (*devices, device);
240
241   done:
242     gst_clear_caps (&caps);
243     gst_clear_object (&obj);
244     if (props)
245       gst_structure_free (props);
246   }
247
248   return;
249 }
250
251 static GList *
252 gst_asio_device_provider_probe (GstDeviceProvider * provider)
253 {
254   GstAsioDeviceProvider *self = GST_ASIO_DEVICE_PROVIDER (provider);
255   GList *devices = nullptr;
256   guint num_device;
257   GList *asio_device_list = nullptr;
258
259   num_device = gst_asio_enum (&asio_device_list);
260
261   if (num_device == 0)
262     return nullptr;
263
264   gst_asio_device_provider_probe_internal (self,
265       TRUE, asio_device_list, &devices);
266   gst_asio_device_provider_probe_internal (self,
267       FALSE, asio_device_list, &devices);
268
269   g_list_free_full (asio_device_list,
270       (GDestroyNotify) gst_asio_device_info_free);
271
272   return devices;
273 }