/* * Copyright (C) 2019 Collabora Ltd. * Author: Xavier Claessens * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mlaudiowrapper.h" #include #include #include #include GST_DEBUG_CATEGORY_EXTERN (mgl_debug); #define GST_CAT_DEFAULT mgl_debug using lumin::BaseApp; using lumin::AudioNode; using lumin::AudioBuffer; using lumin::AudioBufferFormat; using lumin::AudioSampleFormat; struct _GstMLAudioWrapper { BaseApp *app; AudioNode *node; MLHandle handle; }; AudioBufferFormat convert_buffer_format(const MLAudioBufferFormat *format) { AudioBufferFormat ret; ret.channel_count = format->channel_count; ret.samples_per_second = format->samples_per_second; ret.bits_per_sample = format->bits_per_sample; ret.valid_bits_per_sample = format->valid_bits_per_sample; switch (format->sample_format) { case MLAudioSampleFormat_Int: ret.sample_format = AudioSampleFormat::Integer; break; case MLAudioSampleFormat_Float: ret.sample_format = AudioSampleFormat::Float; break; default: g_warn_if_reached (); ret.sample_format = (AudioSampleFormat)format->sample_format; }; ret.reserved = format->reserved; return ret; } GstMLAudioWrapper * gst_ml_audio_wrapper_new (gpointer app) { GstMLAudioWrapper *self; self = g_new0 (GstMLAudioWrapper, 1); self->app = reinterpret_cast(app); self->node = nullptr; self->handle = ML_INVALID_HANDLE; return self; } void gst_ml_audio_wrapper_free (GstMLAudioWrapper *self) { if (self->node) { self->app->RunOnMainThreadSync ([self] { /* Stop playing sound, but user is responsible to destroy the node */ self->node->stopSound (); }); } else { MLAudioDestroySound (self->handle); } g_free (self); } MLResult gst_ml_audio_wrapper_create_sound (GstMLAudioWrapper *self, const MLAudioBufferFormat *format, uint32_t buffer_size, MLAudioBufferCallback callback, gpointer user_data) { if (self->node) { auto format2 = convert_buffer_format (format); bool success = FALSE; success = self->node->createSoundWithOutputStream (&format2, buffer_size, callback, user_data); if (success) self->node->startSound (); return success ? MLResult_Ok : MLResult_UnspecifiedFailure; } MLResult result = MLAudioCreateSoundWithOutputStream (format, buffer_size, callback, user_data, &self->handle); if (result == MLResult_Ok) result = MLAudioStartSound (self->handle); return result; } MLResult gst_ml_audio_wrapper_pause_sound (GstMLAudioWrapper *self) { g_return_val_if_fail (self->handle != ML_INVALID_HANDLE, MLResult_UnspecifiedFailure); return MLAudioPauseSound (self->handle); } MLResult gst_ml_audio_wrapper_resume_sound (GstMLAudioWrapper *self) { g_return_val_if_fail (self->handle != ML_INVALID_HANDLE, MLResult_UnspecifiedFailure); return MLAudioResumeSound (self->handle); } MLResult gst_ml_audio_wrapper_stop_sound (GstMLAudioWrapper *self) { g_return_val_if_fail (self->handle != ML_INVALID_HANDLE, MLResult_UnspecifiedFailure); return MLAudioStopSound (self->handle); } MLResult gst_ml_audio_wrapper_get_latency (GstMLAudioWrapper *self, float *out_latency_in_msec) { if (self->handle == ML_INVALID_HANDLE) { *out_latency_in_msec = 0; return MLResult_Ok; } return MLAudioGetOutputStreamLatency (self->handle, out_latency_in_msec); } MLResult gst_ml_audio_wrapper_get_buffer (GstMLAudioWrapper *self, MLAudioBuffer *out_buffer) { return MLAudioGetOutputStreamBuffer (self->handle, out_buffer); } MLResult gst_ml_audio_wrapper_release_buffer (GstMLAudioWrapper *self) { return MLAudioReleaseOutputStreamBuffer (self->handle); } void gst_ml_audio_wrapper_set_handle (GstMLAudioWrapper *self, MLHandle handle) { g_return_if_fail (self->handle == ML_INVALID_HANDLE || self->handle == handle); self->handle = handle; } void gst_ml_audio_wrapper_set_node (GstMLAudioWrapper *self, gpointer node) { g_return_if_fail (self->node == nullptr); self->node = reinterpret_cast(node); } gboolean gst_ml_audio_wrapper_invoke_sync (GstMLAudioWrapper *self, GstMLAudioWrapperCallback callback, gpointer user_data) { gboolean ret; if (self->app) { self->app->RunOnMainThreadSync ([self, callback, user_data, &ret] { ret = callback (self, user_data); }); } else { ret = callback (self, user_data); } return ret; }