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                                                mSpectrumElement(NULL),
 45                                                mSpectrum(NULL) {
 46     mMixBuffer.mNumberBuffers = 1;
 47     mMixBuffer.mBuffers[0].mData = NULL;
 48 
 49     pthread_mutex_init(&mBandLock, NULL);
 50 
 51     gst_init_check(NULL, NULL, NULL);
 52 }
 53 
 54 AVFAudioSpectrumUnit::~AVFAudioSpectrumUnit() {
 55     if (mMixBuffer.mBuffers[0].mData) {
 56         free(mMixBuffer.mBuffers[0].mData);
 57         mMixBuffer.mBuffers[0].mData = NULL;
 58     }
 59 
 60     ReleaseSpectralProcessor();
 61 }
 62 
 63 bool AVFAudioSpectrumUnit::ProcessBufferLists(const AudioBufferList& inBuffer,
 64                                                   UInt32 inFramesToProcess) {
 65     if (!mEnabled) {
 66         return true;
 67     }
 68 
 69     // (Re)allocate mix buffer if needed
 70     if (!mMixBuffer.mBuffers[0].mData || mMixBufferFrameCapacity < inFramesToProcess) {
 71         // allocate buffer list (only need to do this once)
 72         if (mMixBuffer.mBuffers[0].mData) {
 73             free(mMixBuffer.mBuffers[0].mData);
 74             mMixBuffer.mBuffers[0].mData = NULL;
 75         }
 76 
 77         mMixBufferFrameCapacity = mMaxFrames;
 78 
 79         mMixBuffer.mBuffers[0].mNumberChannels = 1;
 80         mMixBuffer.mBuffers[0].mData = calloc(mMixBufferFrameCapacity, sizeof (Float32));
 81         mMixBuffer.mBuffers[0].mDataByteSize = 0; // size of actual contained data, not size of buffer
 82     }
 83 
 84     if (mRebuildCrunch) {
 85         SetupSpectralProcessor();
 86     }
 87 
 88     if (mSpectrum != NULL) {
 89         // Mix the audio into one channel since JavaFX only supports single channel spectrum
 90         // Just use an arithmetic average, nothing fancy here
 91         float *buffer = (float*) mMixBuffer.mBuffers[0].mData;
 92         vDSP_vclr(buffer, 1, mMixBufferFrameCapacity);
 93         for (int ii = 0; ii < inBuffer.mNumberBuffers; ii++) {
 94             vDSP_vadd((float*) inBuffer.mBuffers[ii].mData, 1,
 95                     buffer, 1,
 96                     buffer, 1, inFramesToProcess);
 97         }
 98         float divisor = (float) inBuffer.mNumberBuffers;
 99         vDSP_vsdiv(buffer, 1,
100                 &divisor,
101                 buffer, 1, inFramesToProcess);
102         mMixBuffer.mBuffers[0].mDataByteSize = inFramesToProcess * sizeof (Float32);
103 
104         // Just reuse already allocated memory from mMixBuffer and do not free it
105         // in GStreamer
106         GstBuffer *gstBuffer =
107                 gst_buffer_new_wrapped_full(GST_MEMORY_FLAG_READONLY, // Allow only reading
108                 mMixBuffer.mBuffers[0].mData,
109                 mMixBuffer.mBuffers[0].mDataByteSize,
110                 0,
111                 mMixBuffer.mBuffers[0].mDataByteSize,
112                 NULL,
113                 NULL); // No need to free memory
114         if (gstBuffer == NULL) {
115             return false;
116         }
117 
118         GstFlowReturn result = gst_spectrum_transform_ip_api((GstBaseTransform *) mSpectrum, gstBuffer);
119         if (result != GST_FLOW_OK) {
120             return false;
121         }
122         gst_buffer_unref(gstBuffer);
123     }
124 
125     return true;
126 }
127 
128 bool AVFAudioSpectrumUnit::IsEnabled() {
129     return mEnabled;
130 }
131 
132 void AVFAudioSpectrumUnit::SetEnabled(bool isEnabled) {
133     mEnabled = isEnabled;
134     mRebuildCrunch = true;
135 }
136 
137 void AVFAudioSpectrumUnit::SetBands(int bands, CBandsHolder* holder) {
138     lockBands();
139     if (mBands) {
140         CBandsHolder::ReleaseRef(mBands);
141         mBands = NULL;
142     }
143     mBandCount = 0;
144     if (holder) {
145         mBands = CBandsHolder::AddRef(holder);
146         if (mBands) {
147             mBandCount = bands;
148         }
149     }
150     mRebuildCrunch = true;
151     unlockBands();
152 }
153 
154 size_t AVFAudioSpectrumUnit::GetBands() {
155     return mBandCount;
156 }
157 
158 double AVFAudioSpectrumUnit::GetInterval() {
159     return mUpdateInterval;
160 }
161 
162 void AVFAudioSpectrumUnit::SetInterval(double interval) {
163     if (mUpdateInterval != interval) {
164         mUpdateInterval = interval;
165         mRebuildCrunch = true;
166     }
167 }
168 
169 int AVFAudioSpectrumUnit::GetThreshold() {
170     return (int) mThreshold;
171 }
172 
173 void AVFAudioSpectrumUnit::SetThreshold(int threshold) {
174     if (mThreshold != (Float32) threshold) {
175         mThreshold = (Float32) threshold;
176         mRebuildCrunch = true;
177     }
178 }
179 
180 void AVFAudioSpectrumUnit::UpdateBands(int size, const float* magnitudes, const float* phases) {
181     // lock now otherwise the bands could change while we're processing
182     lockBands();
183     if (!mBands || size <= 0 || !mEnabled) {
184         unlockBands();
185         return;
186     }
187 
188     // Update band data
189     mBands->UpdateBands(size, magnitudes, magnitudes);
190 
191     // Call our listener to dispatch the spectrum event
192     if (mSpectrumCallbackProc) {
193         double duration = (double) mSamplesPerInterval / (double) 44100;
194         mSpectrumCallbackProc(mSpectrumCallbackContext, duration);
195     }
196 
197     unlockBands();
198 }
199 
200 void AVFAudioSpectrumUnit::SetSampleRate(UInt32 rate) {
201     mSampleRate = rate;
202 }
203 
204 void AVFAudioSpectrumUnit::SetChannels(UInt32 count) {
205     mChannels = count;
206 }
207 
208 void AVFAudioSpectrumUnit::SetMaxFrames(UInt32 maxFrames) {
209     mMaxFrames = maxFrames;
210 }
211 
212 void AVFAudioSpectrumUnit::SetSpectrumCallbackProc(AVFSpectrumUnitCallbackProc proc, void *context) {
213     mSpectrumCallbackProc = proc;
214     mSpectrumCallbackContext = context;
215 }
216 
217 static gboolean PostMessageCallback(GstElement * element, GstMessage * message) {
218     if (message == NULL) {
219         return FALSE;
220     }
221 
222     GstSpectrum *pSpectrum = GST_SPECTRUM(element);
223     if (pSpectrum == NULL || pSpectrum->user_data == NULL) {
224         return FALSE;
225     }
226 
227     AVFAudioSpectrumUnit *pSpectrumUnit = (AVFAudioSpectrumUnit*)pSpectrum->user_data;
228 
229     const GstStructure *pStr = gst_message_get_structure(message);
230     if (gst_structure_has_name(pStr, "spectrum")) {
231         GstClockTime timestamp, duration;
232 
233         if (!gst_structure_get_clock_time(pStr, "timestamp", &timestamp))
234             timestamp = GST_CLOCK_TIME_NONE;
235 
236         if (!gst_structure_get_clock_time(pStr, "duration", &duration))
237             duration = GST_CLOCK_TIME_NONE;
238 
239         size_t bandsNum = pSpectrumUnit->GetBands();
240 
241         if (bandsNum > 0) {
242             float *magnitudes = new float[bandsNum];
243             float *phases = new float[bandsNum];
244 
245             const GValue *magnitudes_value = gst_structure_get_value(pStr, "magnitude");
246             const GValue *phases_value = gst_structure_get_value(pStr, "phase");
247             for (int i = 0; i < bandsNum; i++) {
248                 magnitudes[i] = g_value_get_float(gst_value_list_get_value(magnitudes_value, i));
249                 phases[i] = g_value_get_float(gst_value_list_get_value(phases_value, i));
250             }
251             pSpectrumUnit->UpdateBands((int) bandsNum, magnitudes, phases);
252 
253             delete [] magnitudes;
254             delete [] phases;
255         }
256     }
257 
258     gst_message_unref(message);
259 
260     return TRUE;
261 }
262 
263 void AVFAudioSpectrumUnit::SetupSpectralProcessor() {
264     ReleaseSpectralProcessor();
265 
266     lockBands();
267 
268     mSpectrumElement = gst_element_factory_make("spectrum", NULL);
269     mSpectrum = GST_SPECTRUM(mSpectrumElement);
270     mSpectrum->user_data = (void*)this;
271 
272     // Set our own callback for post message
273     GstElementClass *klass;
274     klass = GST_ELEMENT_GET_CLASS(mSpectrumElement);
275     klass->post_message = PostMessageCallback;
276 
277     // Configure spectrum element
278     // Do send magnitude and phase information, off by default
279     g_object_set(mSpectrumElement, "post-messages", TRUE,
280                                    "message-magnitude", TRUE,
281                                    "message-phase", TRUE, NULL);
282 
283     g_object_set(mSpectrumElement, "bands", mBandCount, NULL);
284 
285     mSamplesPerInterval = (UInt32)(mSampleRate * mUpdateInterval);
286     guint64 value = (guint64) (mUpdateInterval * GST_SECOND);
287     g_object_set(mSpectrumElement, "interval", value, NULL);
288 
289     g_object_set(mSpectrumElement, "threshold", (int) mThreshold, NULL);
290 
291     // Since we do not run spectrum element in pipeline and it will not get configured
292     // correctly, we need to set required information directly.
293     GST_AUDIO_FILTER_RATE(mSpectrum) = mSampleRate;
294     GST_AUDIO_FILTER_CHANNELS(mSpectrum) = 1; // Always 1 channel
295 
296     // gst_spectrum_setup()
297     GstAudioInfo *info = gst_audio_info_new();
298     gst_audio_info_init(info);
299     gst_audio_info_set_format(info, GST_AUDIO_FORMAT_F32, mSampleRate, 1, NULL);
300     // bps = 4 bytes - 32-bit float, bpf = 4 bytes - 32-bit float mono
301     gst_spectrum_setup_api((GstAudioFilter*) mSpectrum, info, 4, 4);
302     gst_audio_info_free(info);
303 
304     // Set element to playing state
305     gst_element_set_state(mSpectrumElement, GST_STATE_PLAYING);
306 
307     mRebuildCrunch = false;
308     unlockBands();
309 }
310 
311 void AVFAudioSpectrumUnit::ReleaseSpectralProcessor() {
312     lockBands();
313 
314     if (mSpectrumElement) {
315         gst_element_set_state(mSpectrumElement, GST_STATE_NULL);
316         gst_object_unref(GST_OBJECT(mSpectrumElement));
317         mSpectrumElement = NULL;
318         mSpectrum = NULL;
319     }
320 
321     unlockBands();
322 }