1 /*
2 * Copyright (C) 2019 Collabora Ltd.
3 * Author: Xavier Claessens <xavier.claessens@collabora.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation
8 * version 2.1 of the License.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "mlaudiowrapper.h"
26
27 #include <ml_audio.h>
28
29 #include <lumin/node/AudioNode.h>
30 #include <lumin/BaseApp.h>
31 #include <lumin/Prism.h>
32
33 GST_DEBUG_CATEGORY_EXTERN (mgl_debug);
34 #define GST_CAT_DEFAULT mgl_debug
35
36 using lumin::BaseApp;
37 using lumin::AudioNode;
38 using lumin::AudioBuffer;
39 using lumin::AudioBufferFormat;
40 using lumin::AudioSampleFormat;
41
42 struct _GstMLAudioWrapper
43 {
44 BaseApp *app;
45 AudioNode *node;
46 MLHandle handle;
47 };
48
49 AudioBufferFormat
convert_buffer_format(const MLAudioBufferFormat * format)50 convert_buffer_format(const MLAudioBufferFormat *format)
51 {
52 AudioBufferFormat ret;
53 ret.channel_count = format->channel_count;
54 ret.samples_per_second = format->samples_per_second;
55 ret.bits_per_sample = format->bits_per_sample;
56 ret.valid_bits_per_sample = format->valid_bits_per_sample;
57 switch (format->sample_format) {
58 case MLAudioSampleFormat_Int:
59 ret.sample_format = AudioSampleFormat::Integer;
60 break;
61 case MLAudioSampleFormat_Float:
62 ret.sample_format = AudioSampleFormat::Float;
63 break;
64 default:
65 g_warn_if_reached ();
66 ret.sample_format = (AudioSampleFormat)format->sample_format;
67 };
68 ret.reserved = format->reserved;
69
70 return ret;
71 }
72
73 GstMLAudioWrapper *
gst_ml_audio_wrapper_new(gpointer app)74 gst_ml_audio_wrapper_new (gpointer app)
75 {
76 GstMLAudioWrapper *self;
77
78 self = g_new0 (GstMLAudioWrapper, 1);
79 self->app = reinterpret_cast<BaseApp *>(app);
80 self->node = nullptr;
81 self->handle = ML_INVALID_HANDLE;
82
83 return self;
84 }
85
86 void
gst_ml_audio_wrapper_free(GstMLAudioWrapper * self)87 gst_ml_audio_wrapper_free (GstMLAudioWrapper *self)
88 {
89 if (self->node) {
90 self->app->RunOnMainThreadSync ([self] {
91 /* Stop playing sound, but user is responsible to destroy the node */
92 self->node->stopSound ();
93 });
94 } else {
95 MLAudioDestroySound (self->handle);
96 }
97
98 g_free (self);
99 }
100
101 MLResult
gst_ml_audio_wrapper_create_sound(GstMLAudioWrapper * self,const MLAudioBufferFormat * format,uint32_t buffer_size,MLAudioBufferCallback callback,gpointer user_data)102 gst_ml_audio_wrapper_create_sound (GstMLAudioWrapper *self,
103 const MLAudioBufferFormat *format,
104 uint32_t buffer_size,
105 MLAudioBufferCallback callback,
106 gpointer user_data)
107 {
108 if (self->node) {
109 auto format2 = convert_buffer_format (format);
110 bool success = FALSE;
111 success = self->node->createSoundWithOutputStream (&format2,
112 buffer_size, callback, user_data);
113 if (success)
114 self->node->startSound ();
115 return success ? MLResult_Ok : MLResult_UnspecifiedFailure;
116 }
117
118 MLResult result = MLAudioCreateSoundWithOutputStream (format, buffer_size,
119 callback, user_data, &self->handle);
120 if (result == MLResult_Ok)
121 result = MLAudioStartSound (self->handle);
122
123 return result;
124 }
125
126 MLResult
gst_ml_audio_wrapper_pause_sound(GstMLAudioWrapper * self)127 gst_ml_audio_wrapper_pause_sound (GstMLAudioWrapper *self)
128 {
129 g_return_val_if_fail (self->handle != ML_INVALID_HANDLE,
130 MLResult_UnspecifiedFailure);
131 return MLAudioPauseSound (self->handle);
132 }
133
134 MLResult
gst_ml_audio_wrapper_resume_sound(GstMLAudioWrapper * self)135 gst_ml_audio_wrapper_resume_sound (GstMLAudioWrapper *self)
136 {
137 g_return_val_if_fail (self->handle != ML_INVALID_HANDLE,
138 MLResult_UnspecifiedFailure);
139 return MLAudioResumeSound (self->handle);
140 }
141
142 MLResult
gst_ml_audio_wrapper_stop_sound(GstMLAudioWrapper * self)143 gst_ml_audio_wrapper_stop_sound (GstMLAudioWrapper *self)
144 {
145 g_return_val_if_fail (self->handle != ML_INVALID_HANDLE,
146 MLResult_UnspecifiedFailure);
147 return MLAudioStopSound (self->handle);
148 }
149
150 MLResult
gst_ml_audio_wrapper_get_latency(GstMLAudioWrapper * self,float * out_latency_in_msec)151 gst_ml_audio_wrapper_get_latency (GstMLAudioWrapper *self,
152 float *out_latency_in_msec)
153 {
154 if (self->handle == ML_INVALID_HANDLE) {
155 *out_latency_in_msec = 0;
156 return MLResult_Ok;
157 }
158
159 return MLAudioGetOutputStreamLatency (self->handle, out_latency_in_msec);
160 }
161
162 MLResult
gst_ml_audio_wrapper_get_buffer(GstMLAudioWrapper * self,MLAudioBuffer * out_buffer)163 gst_ml_audio_wrapper_get_buffer (GstMLAudioWrapper *self,
164 MLAudioBuffer *out_buffer)
165 {
166 return MLAudioGetOutputStreamBuffer (self->handle, out_buffer);
167 }
168
169 MLResult
gst_ml_audio_wrapper_release_buffer(GstMLAudioWrapper * self)170 gst_ml_audio_wrapper_release_buffer (GstMLAudioWrapper *self)
171 {
172 return MLAudioReleaseOutputStreamBuffer (self->handle);
173 }
174
175 void
gst_ml_audio_wrapper_set_handle(GstMLAudioWrapper * self,MLHandle handle)176 gst_ml_audio_wrapper_set_handle (GstMLAudioWrapper *self, MLHandle handle)
177 {
178 g_return_if_fail (self->handle == ML_INVALID_HANDLE || self->handle == handle);
179 self->handle = handle;
180 }
181
182 void
gst_ml_audio_wrapper_set_node(GstMLAudioWrapper * self,gpointer node)183 gst_ml_audio_wrapper_set_node (GstMLAudioWrapper *self,
184 gpointer node)
185 {
186 g_return_if_fail (self->node == nullptr);
187 self->node = reinterpret_cast<AudioNode *>(node);
188 }
189
190 gboolean
gst_ml_audio_wrapper_invoke_sync(GstMLAudioWrapper * self,GstMLAudioWrapperCallback callback,gpointer user_data)191 gst_ml_audio_wrapper_invoke_sync (GstMLAudioWrapper *self,
192 GstMLAudioWrapperCallback callback, gpointer user_data)
193 {
194 gboolean ret;
195
196 if (self->app) {
197 self->app->RunOnMainThreadSync ([self, callback, user_data, &ret] {
198 ret = callback (self, user_data);
199 });
200 } else {
201 ret = callback (self, user_data);
202 }
203
204 return ret;
205 }
206