1 /*
  2  * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 #include "AVFAudioSpectrumUnit.h"
 27 
 28 #include <iostream>
 29 #include <Accelerate/Accelerate.h>
 30 
 31 AVFAudioSpectrumUnit::AVFAudioSpectrumUnit() : mSpectrumCallbackProc(NULL),
 32                                                mSpectrumCallbackContext(NULL),
 33                                                mEnabled(true),
 34                                                mBandCount(128),
 35                                                mBands(NULL),
 36                                                mUpdateInterval(kDefaultAudioSpectrumUpdateInterval),
 37                                                mThreshold(kDefaultAudioSpectrumThreshold),
 38                                                mMixBufferFrameCapacity(0),
 39                                                mSampleRate(0),
 40                                                mChannels(0),
 41                                                mMaxFrames(0),
 42                                                mSamplesPerInterval(0),
 43                                                mRebuildCrunch(true),
 44                                                mFirstBufferDelivered(false),
 45                                                mSpectrumElement(NULL),
 46                                                mSpectrum(NULL) {
 47     mMixBuffer.mNumberBuffers = 1;
 48     mMixBuffer.mBuffers[0].mData = NULL;
 49 
 50     pthread_mutex_init(&mBandLock, NULL);
 51 
 52     gst_init_check(NULL, NULL, NULL);
 53 }
 54 
 55 AVFAudioSpectrumUnit::~AVFAudioSpectrumUnit() {
 56     if (mMixBuffer.mBuffers[0].mData) {
 57         free(mMixBuffer.mBuffers[0].mData);
 58         mMixBuffer.mBuffers[0].mData = NULL;
 59     }
 60 
 61     ReleaseSpectralProcessor();
 62 }
 63 
 64 bool AVFAudioSpectrumUnit::ProcessBufferLists(const AudioBufferList& inBuffer,
 65                                                   UInt32 inFramesToProcess) {
 66     if (!mEnabled) {
 67         return true;
 68     }
 69 
 70     // (Re)allocate mix buffer if needed
 71     if (!mMixBuffer.mBuffers[0].mData || mMixBufferFrameCapacity < inFramesToProcess) {
 72         // allocate buffer list (only need to do this once)
 73         if (mMixBuffer.mBuffers[0].mData) {
 74             free(mMixBuffer.mBuffers[0].mData);
 75             mMixBuffer.mBuffers[0].mData = NULL;
 76         }
 77 
 78         mMixBufferFrameCapacity = mMaxFrames;
 79 
 80         mMixBuffer.mBuffers[0].mNumberChannels = 1;
 81         mMixBuffer.mBuffers[0].mData = calloc(mMixBufferFrameCapacity, sizeof (Float32));
 82         mMixBuffer.mBuffers[0].mDataByteSize = 0; // size of actual contained data, not size of buffer
 83     }
 84 
 85     if (mRebuildCrunch) {
 86         SetupSpectralProcessor();
 87     }
 88 
 89     if (mSpectrum != NULL) {
 90         // Mix the audio into one channel since JavaFX only supports single channel spectrum
 91         // Just use an arithmetic average, nothing fancy here
 92         float *buffer = (float*) mMixBuffer.mBuffers[0].mData;
 93         vDSP_vclr(buffer, 1, mMixBufferFrameCapacity);
 94         for (int ii = 0; ii < inBuffer.mNumberBuffers; ii++) {
 95             vDSP_vadd((float*) inBuffer.mBuffers[ii].mData, 1,
 96                     buffer, 1,
 97                     buffer, 1, inFramesToProcess);
 98         }
 99         float divisor = (float) inBuffer.mNumberBuffers;
100         vDSP_vsdiv(buffer, 1,
101                 &divisor,
102                 buffer, 1, inFramesToProcess);
103         mMixBuffer.mBuffers[0].mDataByteSize = inFramesToProcess * sizeof (Float32);
104 
105         // Just reuse already allocated memory from mMixBuffer and do not free it
106         // in GStreamer
107         GstBuffer *gstBuffer =
108                 gst_buffer_new_wrapped_full(GST_MEMORY_FLAG_READONLY, // Allow only reading
109                 mMixBuffer.mBuffers[0].mData,
110                 mMixBuffer.mBuffers[0].mDataByteSize,
111                 0,
112                 mMixBuffer.mBuffers[0].mDataByteSize,
113                 NULL,
114                 NULL); // No need to free memory
115         if (gstBuffer == NULL) {
116             return false;
117         }
118 
119         GstFlowReturn result = gst_spectrum_transform_ip_api((GstBaseTransform *) mSpectrum, gstBuffer);
120         if (result != GST_FLOW_OK) {
121             return false;
122         }
123         gst_buffer_unref(gstBuffer);
124     }
125 
126     return true;
127 }
128 
129 bool AVFAudioSpectrumUnit::IsEnabled() {
130     return mEnabled;
131 }
132 
133 void AVFAudioSpectrumUnit::SetEnabled(bool isEnabled) {
134     mEnabled = isEnabled;
135     mRebuildCrunch = true;
136 }
137 
138 void AVFAudioSpectrumUnit::SetBands(int bands, CBandsHolder* holder) {
139     lockBands();
140     if (mBands) {
141         CBandsHolder::ReleaseRef(mBands);
142         mBands = NULL;
143     }
144     mBandCount = 0;
145     if (holder) {
146         mBands = CBandsHolder::AddRef(holder);
147         if (mBands) {
148             mBandCount = bands;
149         }
150     }
151     mRebuildCrunch = true;
152     unlockBands();
153 }
154 
155 size_t AVFAudioSpectrumUnit::GetBands() {
156     return mBandCount;
157 }
158 
159 double AVFAudioSpectrumUnit::GetInterval() {
160     return mUpdateInterval;
161 }
162 
163 void AVFAudioSpectrumUnit::SetInterval(double interval) {
164     if (mUpdateInterval != interval) {
165         mUpdateInterval = interval;
166         mRebuildCrunch = true;
167     }
168 }
169 
170 int AVFAudioSpectrumUnit::GetThreshold() {
171     return (int) mThreshold;
172 }
173 
174 void AVFAudioSpectrumUnit::SetThreshold(int threshold) {
175     if (mThreshold != (Float32) threshold) {
176         mThreshold = (Float32) threshold;
177         mRebuildCrunch = true;
178     }
179 }
180 
181 void AVFAudioSpectrumUnit::UpdateBands(int size, const float* magnitudes, const float* phases) {
182     // lock now otherwise the bands could change while we're processing
183     lockBands();
184     if (!mBands || size <= 0 || !mEnabled) {
185         unlockBands();
186         return;
187     }
188 
189     // Update band data
190     mBands->UpdateBands(size, magnitudes, magnitudes);
191 
192     // Call our listener to dispatch the spectrum event
193     if (mSpectrumCallbackProc) {
194         double duration = (double) mSamplesPerInterval / (double) 44100;
195         double timestamp = mFirstBufferDelivered ? -1.0 : 0.0;
196         mSpectrumCallbackProc(mSpectrumCallbackContext, duration, timestamp);
197     }
198 
199     unlockBands();
200 }
201 
202 void AVFAudioSpectrumUnit::SetSampleRate(UInt32 rate) {
203     mSampleRate = rate;
204 }
205 
206 void AVFAudioSpectrumUnit::SetChannels(UInt32 count) {
207     mChannels = count;
208 }
209 
210 void AVFAudioSpectrumUnit::SetMaxFrames(UInt32 maxFrames) {
211     mMaxFrames = maxFrames;
212 }
213 
214 void AVFAudioSpectrumUnit::SetSpectrumCallbackProc(AVFSpectrumUnitCallbackProc proc, void *context) {
215     mSpectrumCallbackProc = proc;
216     mSpectrumCallbackContext = context;
217 }
218 
219 void AVFAudioSpectrumUnit::SetFirstBufferDelivered(bool isFirstBufferDelivered) {
220     mFirstBufferDelivered = isFirstBufferDelivered;
221 }
222 
223 static gboolean PostMessageCallback(GstElement * element, GstMessage * message) {
224     if (message == NULL) {
225         return FALSE;
226     }
227 
228     GstSpectrum *pSpectrum = GST_SPECTRUM(element);
229     if (pSpectrum == NULL || pSpectrum->user_data == NULL) {
230         return FALSE;
231     }
232 
233     AVFAudioSpectrumUnit *pSpectrumUnit = (AVFAudioSpectrumUnit*)pSpectrum->user_data;
234 
235     const GstStructure *pStr = gst_message_get_structure(message);
236     if (gst_structure_has_name(pStr, "spectrum")) {
237         GstClockTime timestamp, duration;
238 
239         if (!gst_structure_get_clock_time(pStr, "timestamp", &timestamp))
240             timestamp = GST_CLOCK_TIME_NONE;
241 
242         if (!gst_structure_get_clock_time(pStr, "duration", &duration))
243             duration = GST_CLOCK_TIME_NONE;
244 
245         size_t bandsNum = pSpectrumUnit->GetBands();
246 
247         if (bandsNum > 0) {
248             float *magnitudes = new float[bandsNum];
249             float *phases = new float[bandsNum];
250 
251             const GValue *magnitudes_value = gst_structure_get_value(pStr, "magnitude");
252             const GValue *phases_value = gst_structure_get_value(pStr, "phase");
253             for (int i = 0; i < bandsNum; i++) {
254                 magnitudes[i] = g_value_get_float(gst_value_list_get_value(magnitudes_value, i));
255                 phases[i] = g_value_get_float(gst_value_list_get_value(phases_value, i));
256             }
257             pSpectrumUnit->UpdateBands((int) bandsNum, magnitudes, phases);
258 
259             delete [] magnitudes;
260             delete [] phases;
261         }
262     }
263 
264     gst_message_unref(message);
265 
266     return TRUE;
267 }
268 
269 void AVFAudioSpectrumUnit::SetupSpectralProcessor() {
270     ReleaseSpectralProcessor();
271 
272     lockBands();
273 
274     mSpectrumElement = gst_element_factory_make("spectrum", NULL);
275     mSpectrum = GST_SPECTRUM(mSpectrumElement);
276     mSpectrum->user_data = (void*)this;
277 
278     // Set our own callback for post message
279     GstElementClass *klass;
280     klass = GST_ELEMENT_GET_CLASS(mSpectrumElement);
281     klass->post_message = PostMessageCallback;
282 
283     // Configure spectrum element
284     // Do send magnitude and phase information, off by default
285     g_object_set(mSpectrumElement, "post-messages", TRUE,
286                                    "message-magnitude", TRUE,
287                                    "message-phase", TRUE, NULL);
288 
289     g_object_set(mSpectrumElement, "bands", mBandCount, NULL);
290 
291     mSamplesPerInterval = (UInt32)(mSampleRate * mUpdateInterval);
292     guint64 value = (guint64) (mUpdateInterval * GST_SECOND);
293     g_object_set(mSpectrumElement, "interval", value, NULL);
294 
295     g_object_set(mSpectrumElement, "threshold", (int) mThreshold, NULL);
296 
297     // Since we do not run spectrum element in pipeline and it will not get configured
298     // correctly, we need to set required information directly.
299     GST_AUDIO_FILTER_RATE(mSpectrum) = mSampleRate;
300     GST_AUDIO_FILTER_CHANNELS(mSpectrum) = 1; // Always 1 channel
301 
302     // gst_spectrum_setup()
303     GstAudioInfo *info = gst_audio_info_new();
304     gst_audio_info_init(info);
305     gst_audio_info_set_format(info, GST_AUDIO_FORMAT_F32, mSampleRate, 1, NULL);
306     // bps = 4 bytes - 32-bit float, bpf = 4 bytes - 32-bit float mono
307     gst_spectrum_setup_api((GstAudioFilter*) mSpectrum, info, 4, 4);
308     gst_audio_info_free(info);
309 
310     // Set element to playing state
311     gst_element_set_state(mSpectrumElement, GST_STATE_PLAYING);
312 
313     mRebuildCrunch = false;
314     unlockBands();
315 }
316 
317 void AVFAudioSpectrumUnit::ReleaseSpectralProcessor() {
318     lockBands();
319 
320     if (mSpectrumElement) {
321         gst_element_set_state(mSpectrumElement, GST_STATE_NULL);
322         gst_object_unref(GST_OBJECT(mSpectrumElement));
323         mSpectrumElement = NULL;
324         mSpectrum = NULL;
325     }
326 
327     unlockBands();
328 }