• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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