2019-04-18 15:03:01 +02:00
/* GStreamer
* Copyright ( C ) < 1999 > Erik Walthinsen < omega @ cse . ogi . edu >
* < 2006 , 2011 > Stefan Kost < ensonic @ users . sf . net >
* < 2007 - 2009 > Sebastian Dröge < sebastian . droege @ collabora . co . uk >
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
*
* 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
* Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public
* License along with this library ; if not , write to the
* Free Software Foundation , Inc . , 51 Franklin St , Fifth Floor ,
* Boston , MA 02110 - 1301 , USA .
*/
2020-02-08 03:40:30 +01:00
# include <QtGlobal>
2019-04-18 15:03:01 +02:00
# include <cstring>
2021-03-26 21:30:13 +01:00
# include <cmath>
2020-02-08 03:40:30 +01:00
# include <glib.h>
2019-04-18 15:03:01 +02:00
2019-06-12 06:34:59 +02:00
# include <gst/gst.h>
# include <gst/audio/gstaudiofilter.h>
2019-04-18 15:03:01 +02:00
# include <QMutex>
# include "gstfastspectrum.h"
2021-10-30 00:48:21 +02:00
GST_DEBUG_CATEGORY_STATIC ( gst_fastspectrum_debug ) ;
2019-04-18 15:03:01 +02:00
2021-11-09 19:16:28 +01:00
namespace {
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
// Spectrum properties
2021-11-09 19:16:28 +01:00
constexpr auto DEFAULT_INTERVAL = ( GST_SECOND / 10 ) ;
constexpr auto DEFAULT_BANDS = 128 ;
2019-04-18 15:03:01 +02:00
enum {
PROP_0 ,
PROP_INTERVAL ,
PROP_BANDS
} ;
2021-11-09 19:16:28 +01:00
} // namespace
2019-04-18 15:03:01 +02:00
# define gst_fastspectrum_parent_class parent_class
2023-06-15 20:10:25 +02:00
# ifdef __GNUC__
2022-06-10 00:02:19 +02:00
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wold-style-cast"
2023-06-15 20:10:25 +02:00
# endif
2021-10-30 00:48:21 +02:00
G_DEFINE_TYPE ( GstFastSpectrum , gst_fastspectrum , GST_TYPE_AUDIO_FILTER )
2023-06-15 20:10:25 +02:00
# ifdef __GNUC__
2022-06-10 00:02:19 +02:00
# pragma GCC diagnostic pop
2023-06-15 20:10:25 +02:00
# endif
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_finalize ( GObject * object ) ;
static void gst_fastspectrum_set_property ( GObject * object , guint prop_id , const GValue * value , GParamSpec * pspec ) ;
static void gst_fastspectrum_get_property ( GObject * object , guint prop_id , GValue * value , GParamSpec * pspec ) ;
static gboolean gst_fastspectrum_start ( GstBaseTransform * trans ) ;
static gboolean gst_fastspectrum_stop ( GstBaseTransform * trans ) ;
static GstFlowReturn gst_fastspectrum_transform_ip ( GstBaseTransform * trans , GstBuffer * buffer ) ;
static gboolean gst_fastspectrum_setup ( GstAudioFilter * base , const GstAudioInfo * info ) ;
static void gst_fastspectrum_class_init ( GstFastSpectrumClass * klass ) {
GObjectClass * gobject_class = G_OBJECT_CLASS ( klass ) ;
GstElementClass * element_class = GST_ELEMENT_CLASS ( klass ) ;
GstBaseTransformClass * trans_class = GST_BASE_TRANSFORM_CLASS ( klass ) ;
GstAudioFilterClass * filter_class = GST_AUDIO_FILTER_CLASS ( klass ) ;
2021-03-26 21:30:13 +01:00
GstCaps * caps = nullptr ;
2019-04-18 15:03:01 +02:00
gobject_class - > set_property = gst_fastspectrum_set_property ;
gobject_class - > get_property = gst_fastspectrum_get_property ;
gobject_class - > finalize = gst_fastspectrum_finalize ;
2021-10-30 00:48:21 +02:00
trans_class - > start = GST_DEBUG_FUNCPTR ( gst_fastspectrum_start ) ;
trans_class - > stop = GST_DEBUG_FUNCPTR ( gst_fastspectrum_stop ) ;
trans_class - > transform_ip = GST_DEBUG_FUNCPTR ( gst_fastspectrum_transform_ip ) ;
2019-04-18 15:03:01 +02:00
trans_class - > passthrough_on_same_caps = TRUE ;
2021-10-30 00:48:21 +02:00
filter_class - > setup = GST_DEBUG_FUNCPTR ( gst_fastspectrum_setup ) ;
2019-04-18 15:03:01 +02:00
2023-01-08 06:21:34 +01:00
g_object_class_install_property ( gobject_class , PROP_INTERVAL , g_param_spec_uint64 ( " interval " , " Interval " , " Interval of time between message posts (in nanoseconds) " , 1 , G_MAXUINT64 , DEFAULT_INTERVAL , static_cast < GParamFlags > ( G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS ) ) ) ;
2019-04-18 15:03:01 +02:00
2023-01-08 06:21:34 +01:00
g_object_class_install_property ( gobject_class , PROP_BANDS , g_param_spec_uint ( " bands " , " Bands " , " Number of frequency bands " , 0 , G_MAXUINT , DEFAULT_BANDS , static_cast < GParamFlags > ( G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS ) ) ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
GST_DEBUG_CATEGORY_INIT ( gst_fastspectrum_debug , " spectrum " , 0 , " audio spectrum analyser element " ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
gst_element_class_set_static_metadata ( element_class , " Spectrum analyzer " ,
" Filter/Analyzer/Audio " ,
" Run an FFT on the audio signal, output spectrum data " ,
" Erik Walthinsen <omega@cse.ogi.edu>, "
" Stefan Kost <ensonic@users.sf.net>, "
" Sebastian Dröge <sebastian.droege@collabora.co.uk> " ) ;
2019-04-18 15:03:01 +02:00
2021-11-09 19:16:28 +01:00
# if G_BYTE_ORDER == G_LITTLE_ENDIAN
caps = gst_caps_from_string ( GST_AUDIO_CAPS_MAKE ( " { S16LE, S24LE, S32LE, F32LE, F64LE } " ) " , layout = (string) interleaved, channels = 1 " ) ;
# else
caps = gst_caps_from_string ( GST_AUDIO_CAPS_MAKE ( " { S16BE, S24BE, S32BE, F32BE, F64BE } " ) " , layout = (string) interleaved, channels = 1 " ) ;
# endif
2021-10-30 00:48:21 +02:00
gst_audio_filter_class_add_pad_templates ( filter_class , caps ) ;
gst_caps_unref ( caps ) ;
2019-04-18 15:03:01 +02:00
klass - > fftw_lock = new QMutex ;
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_init ( GstFastSpectrum * spectrum ) {
2019-04-18 15:03:01 +02:00
spectrum - > interval = DEFAULT_INTERVAL ;
spectrum - > bands = DEFAULT_BANDS ;
2020-10-17 17:29:09 +02:00
spectrum - > channel_data_initialized = false ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
g_mutex_init ( & spectrum - > lock ) ;
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_alloc_channel_data ( GstFastSpectrum * spectrum ) {
2019-04-18 15:03:01 +02:00
guint bands = spectrum - > bands ;
guint nfft = 2 * bands - 2 ;
spectrum - > input_ring_buffer = new double [ nfft ] ;
2021-10-30 00:48:21 +02:00
spectrum - > fft_input = reinterpret_cast < double * > ( fftw_malloc ( sizeof ( double ) * nfft ) ) ;
spectrum - > fft_output = reinterpret_cast < fftw_complex * > ( fftw_malloc ( sizeof ( fftw_complex ) * ( nfft / 2 + 1 ) ) ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
spectrum - > spect_magnitude = new double [ bands ] { } ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
GstFastSpectrumClass * klass = reinterpret_cast < GstFastSpectrumClass * > ( G_OBJECT_GET_CLASS ( spectrum ) ) ;
2019-04-18 15:03:01 +02:00
{
QMutexLocker l ( klass - > fftw_lock ) ;
2021-10-30 00:48:21 +02:00
spectrum - > plan = fftw_plan_dft_r2c_1d ( static_cast < int > ( nfft ) , spectrum - > fft_input , spectrum - > fft_output , FFTW_ESTIMATE ) ;
2019-04-18 15:03:01 +02:00
}
2020-10-17 17:29:09 +02:00
spectrum - > channel_data_initialized = true ;
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_free_channel_data ( GstFastSpectrum * spectrum ) {
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
GstFastSpectrumClass * klass = reinterpret_cast < GstFastSpectrumClass * > ( G_OBJECT_GET_CLASS ( spectrum ) ) ;
2020-10-17 17:29:09 +02:00
if ( spectrum - > channel_data_initialized ) {
2019-04-18 15:03:01 +02:00
{
QMutexLocker l ( klass - > fftw_lock ) ;
fftw_destroy_plan ( spectrum - > plan ) ;
}
fftw_free ( spectrum - > fft_input ) ;
fftw_free ( spectrum - > fft_output ) ;
delete [ ] spectrum - > input_ring_buffer ;
delete [ ] spectrum - > spect_magnitude ;
2020-10-17 17:29:09 +02:00
spectrum - > channel_data_initialized = false ;
2019-04-18 15:03:01 +02:00
}
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_flush ( GstFastSpectrum * spectrum ) {
2019-04-18 15:03:01 +02:00
spectrum - > num_frames = 0 ;
spectrum - > num_fft = 0 ;
spectrum - > accumulated_error = 0 ;
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_reset_state ( GstFastSpectrum * spectrum ) {
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
GST_DEBUG_OBJECT ( spectrum , " resetting state " ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
gst_fastspectrum_free_channel_data ( spectrum ) ;
gst_fastspectrum_flush ( spectrum ) ;
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_finalize ( GObject * object ) {
2019-04-18 15:03:01 +02:00
2020-07-16 22:46:31 +02:00
GstFastSpectrum * spectrum = reinterpret_cast < GstFastSpectrum * > ( object ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
gst_fastspectrum_reset_state ( spectrum ) ;
g_mutex_clear ( & spectrum - > lock ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
G_OBJECT_CLASS ( parent_class ) - > finalize ( object ) ;
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_set_property ( GObject * object , guint prop_id , const GValue * value , GParamSpec * pspec ) {
2019-04-18 15:03:01 +02:00
2020-07-16 22:46:31 +02:00
GstFastSpectrum * filter = reinterpret_cast < GstFastSpectrum * > ( object ) ;
2019-04-18 15:03:01 +02:00
switch ( prop_id ) {
2021-10-30 00:48:21 +02:00
case PROP_INTERVAL : {
guint64 interval = g_value_get_uint64 ( value ) ;
g_mutex_lock ( & filter - > lock ) ;
2019-04-18 15:03:01 +02:00
if ( filter - > interval ! = interval ) {
filter - > interval = interval ;
2021-10-30 00:48:21 +02:00
gst_fastspectrum_reset_state ( filter ) ;
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
g_mutex_unlock ( & filter - > lock ) ;
2019-04-18 15:03:01 +02:00
break ;
}
2021-10-30 00:48:21 +02:00
case PROP_BANDS : {
guint bands = g_value_get_uint ( value ) ;
g_mutex_lock ( & filter - > lock ) ;
2019-04-18 15:03:01 +02:00
if ( filter - > bands ! = bands ) {
filter - > bands = bands ;
2021-10-30 00:48:21 +02:00
gst_fastspectrum_reset_state ( filter ) ;
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
g_mutex_unlock ( & filter - > lock ) ;
2019-04-18 15:03:01 +02:00
break ;
}
default :
2021-10-30 00:48:21 +02:00
G_OBJECT_WARN_INVALID_PROPERTY_ID ( object , prop_id , pspec ) ;
2019-04-18 15:03:01 +02:00
break ;
}
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_get_property ( GObject * object , guint prop_id , GValue * value , GParamSpec * pspec ) {
2019-04-18 15:03:01 +02:00
2020-07-16 22:46:31 +02:00
GstFastSpectrum * filter = reinterpret_cast < GstFastSpectrum * > ( object ) ;
2019-04-18 15:03:01 +02:00
switch ( prop_id ) {
case PROP_INTERVAL :
2021-10-30 00:48:21 +02:00
g_value_set_uint64 ( value , filter - > interval ) ;
2019-04-18 15:03:01 +02:00
break ;
case PROP_BANDS :
2021-10-30 00:48:21 +02:00
g_value_set_uint ( value , filter - > bands ) ;
2019-04-18 15:03:01 +02:00
break ;
default :
2021-10-30 00:48:21 +02:00
G_OBJECT_WARN_INVALID_PROPERTY_ID ( object , prop_id , pspec ) ;
2019-04-18 15:03:01 +02:00
break ;
}
}
2021-10-30 00:48:21 +02:00
static gboolean gst_fastspectrum_start ( GstBaseTransform * trans ) {
2019-04-18 15:03:01 +02:00
2020-07-16 22:46:31 +02:00
GstFastSpectrum * spectrum = reinterpret_cast < GstFastSpectrum * > ( trans ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
gst_fastspectrum_reset_state ( spectrum ) ;
2019-04-18 15:03:01 +02:00
return TRUE ;
}
2021-10-30 00:48:21 +02:00
static gboolean gst_fastspectrum_stop ( GstBaseTransform * trans ) {
2019-04-18 15:03:01 +02:00
2020-07-16 22:46:31 +02:00
GstFastSpectrum * spectrum = reinterpret_cast < GstFastSpectrum * > ( trans ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
gst_fastspectrum_reset_state ( spectrum ) ;
2019-04-18 15:03:01 +02:00
return TRUE ;
}
2021-10-30 00:48:21 +02:00
// Mixing data readers
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
static void input_data_mixed_float ( const guint8 * _in , double * out , guint len , double max_value , guint op , guint nfft ) {
2019-04-18 15:03:01 +02:00
2019-09-15 20:27:32 +02:00
Q_UNUSED ( max_value ) ;
2020-06-15 17:59:02 +02:00
const gfloat * in = reinterpret_cast < const gfloat * > ( _in ) ;
2021-10-30 00:48:21 +02:00
guint ip = 0 ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
for ( guint j = 0 ; j < len ; j + + ) {
2019-04-18 15:03:01 +02:00
out [ op ] = in [ ip + + ] ;
op = ( op + 1 ) % nfft ;
}
}
2021-10-30 00:48:21 +02:00
static void input_data_mixed_double ( const guint8 * _in , double * out , guint len , double max_value , guint op , guint nfft ) {
2019-04-18 15:03:01 +02:00
2019-09-15 20:27:32 +02:00
Q_UNUSED ( max_value ) ;
2020-06-15 17:59:02 +02:00
const gdouble * in = reinterpret_cast < const gdouble * > ( _in ) ;
2021-10-30 00:48:21 +02:00
guint ip = 0 ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
for ( guint j = 0 ; j < len ; j + + ) {
2019-04-18 15:03:01 +02:00
out [ op ] = in [ ip + + ] ;
op = ( op + 1 ) % nfft ;
}
}
2021-10-30 00:48:21 +02:00
static void input_data_mixed_int32_max ( const guint8 * _in , double * out , guint len , double max_value , guint op , guint nfft ) {
2019-04-18 15:03:01 +02:00
2020-06-15 17:59:02 +02:00
const gint32 * in = reinterpret_cast < const gint32 * > ( _in ) ;
2021-10-30 00:48:21 +02:00
guint ip = 0 ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
for ( guint j = 0 ; j < len ; j + + ) {
2019-04-18 15:03:01 +02:00
out [ op ] = in [ ip + + ] / max_value ;
op = ( op + 1 ) % nfft ;
}
}
2021-10-30 00:48:21 +02:00
static void input_data_mixed_int24_max ( const guint8 * _in , double * out , guint len , double max_value , guint op , guint nfft ) {
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
for ( guint j = 0 ; j < len ; j + + ) {
2019-04-18 15:03:01 +02:00
# if G_BYTE_ORDER == G_BIG_ENDIAN
2021-10-30 00:48:21 +02:00
guint32 value = GST_READ_UINT24_BE ( _in ) ;
2019-04-18 15:03:01 +02:00
# else
2021-10-30 00:48:21 +02:00
guint32 value = GST_READ_UINT24_LE ( _in ) ;
2019-04-18 15:03:01 +02:00
# endif
2021-08-23 21:21:08 +02:00
if ( value & 0x00800000 ) {
2019-04-18 15:03:01 +02:00
value | = 0xff000000 ;
2021-08-23 21:21:08 +02:00
}
2019-04-18 15:03:01 +02:00
out [ op ] = value / max_value ;
op = ( op + 1 ) % nfft ;
_in + = 3 ;
}
}
2021-10-30 00:48:21 +02:00
static void input_data_mixed_int16_max ( const guint8 * _in , double * out , guint len , double max_value , guint op , guint nfft ) {
2019-04-18 15:03:01 +02:00
2020-06-15 17:59:02 +02:00
const gint16 * in = reinterpret_cast < const gint16 * > ( _in ) ;
2021-10-30 00:48:21 +02:00
guint ip = 0 ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
for ( guint j = 0 ; j < len ; j + + ) {
2019-04-18 15:03:01 +02:00
out [ op ] = in [ ip + + ] / max_value ;
op = ( op + 1 ) % nfft ;
}
}
2021-10-30 00:48:21 +02:00
static gboolean gst_fastspectrum_setup ( GstAudioFilter * base , const GstAudioInfo * info ) {
2019-04-18 15:03:01 +02:00
2020-07-16 22:46:31 +02:00
GstFastSpectrum * spectrum = reinterpret_cast < GstFastSpectrum * > ( base ) ;
2020-06-14 17:02:47 +02:00
GstFastSpectrumInputData input_data = nullptr ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
g_mutex_lock ( & spectrum - > lock ) ;
switch ( GST_AUDIO_INFO_FORMAT ( info ) ) {
2019-04-18 15:03:01 +02:00
case GST_AUDIO_FORMAT_S16 :
input_data = input_data_mixed_int16_max ;
break ;
case GST_AUDIO_FORMAT_S24 :
input_data = input_data_mixed_int24_max ;
break ;
case GST_AUDIO_FORMAT_S32 :
input_data = input_data_mixed_int32_max ;
break ;
case GST_AUDIO_FORMAT_F32 :
input_data = input_data_mixed_float ;
break ;
case GST_AUDIO_FORMAT_F64 :
input_data = input_data_mixed_double ;
break ;
default :
2021-10-30 00:48:21 +02:00
g_assert_not_reached ( ) ;
2019-04-18 15:03:01 +02:00
break ;
}
spectrum - > input_data = input_data ;
2021-10-30 00:48:21 +02:00
gst_fastspectrum_reset_state ( spectrum ) ;
g_mutex_unlock ( & spectrum - > lock ) ;
2019-04-18 15:03:01 +02:00
return TRUE ;
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_run_fft ( GstFastSpectrum * spectrum , guint input_pos ) {
2019-04-18 15:03:01 +02:00
guint bands = spectrum - > bands ;
guint nfft = 2 * bands - 2 ;
2021-10-30 00:48:21 +02:00
for ( guint i = 0 ; i < nfft ; i + + ) {
2019-04-18 18:28:11 +02:00
spectrum - > fft_input [ i ] = spectrum - > input_ring_buffer [ ( input_pos + i ) % nfft ] ;
2021-08-23 21:21:08 +02:00
}
2019-04-18 15:03:01 +02:00
// Should be safe to execute the same plan multiple times in parallel.
fftw_execute ( spectrum - > plan ) ;
2021-10-30 00:48:21 +02:00
// Calculate magnitude in db
for ( guint i = 0 ; i < bands ; i + + ) {
gdouble val = spectrum - > fft_output [ i ] [ 0 ] * spectrum - > fft_output [ i ] [ 0 ] ;
2019-04-18 15:03:01 +02:00
val + = spectrum - > fft_output [ i ] [ 1 ] * spectrum - > fft_output [ i ] [ 1 ] ;
val / = nfft * nfft ;
spectrum - > spect_magnitude [ i ] + = val ;
}
}
2021-10-30 00:48:21 +02:00
static GstFlowReturn gst_fastspectrum_transform_ip ( GstBaseTransform * trans , GstBuffer * buffer ) {
2019-04-18 15:03:01 +02:00
2020-07-16 22:46:31 +02:00
GstFastSpectrum * spectrum = reinterpret_cast < GstFastSpectrum * > ( trans ) ;
2021-10-30 00:48:21 +02:00
guint rate = GST_AUDIO_FILTER_RATE ( spectrum ) ;
guint bps = GST_AUDIO_FILTER_BPS ( spectrum ) ;
guint bpf = GST_AUDIO_FILTER_BPF ( spectrum ) ;
double max_value = static_cast < double > ( ( 1UL < < ( ( bps < < 3 ) - 1 ) ) - 1 ) ;
2019-04-18 15:03:01 +02:00
guint bands = spectrum - > bands ;
guint nfft = 2 * bands - 2 ;
2021-03-26 21:30:13 +01:00
guint input_pos = 0 ;
2019-04-18 15:03:01 +02:00
GstMapInfo map ;
2021-03-26 21:30:13 +01:00
const guint8 * data = nullptr ;
gsize size = 0 ;
GstFastSpectrumInputData input_data = nullptr ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
g_mutex_lock ( & spectrum - > lock ) ;
gst_buffer_map ( buffer , & map , GST_MAP_READ ) ;
2019-04-18 15:03:01 +02:00
data = map . data ;
size = map . size ;
2021-10-30 00:48:21 +02:00
GST_LOG_OBJECT ( spectrum , " input size: % " G_GSIZE_FORMAT " bytes " , size ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
if ( GST_BUFFER_IS_DISCONT ( buffer ) ) {
GST_DEBUG_OBJECT ( spectrum , " Discontinuity detected -- flushing " ) ;
gst_fastspectrum_flush ( spectrum ) ;
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
// If we don't have a FFT context yet (or it was reset due to parameter changes) get one and allocate memory for everything
2020-10-17 17:29:09 +02:00
if ( ! spectrum - > channel_data_initialized ) {
2021-10-30 00:48:21 +02:00
GST_DEBUG_OBJECT ( spectrum , " allocating for bands %u " , bands ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
gst_fastspectrum_alloc_channel_data ( spectrum ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
// Number of sample frames we process before posting a message interval is in ns
spectrum - > frames_per_interval = gst_util_uint64_scale ( spectrum - > interval , rate , GST_SECOND ) ;
2019-04-18 15:03:01 +02:00
spectrum - > frames_todo = spectrum - > frames_per_interval ;
2021-10-30 00:48:21 +02:00
// Rounding error for frames_per_interval in ns, aggregated it in accumulated_error
2019-04-18 15:03:01 +02:00
spectrum - > error_per_interval = ( spectrum - > interval * rate ) % GST_SECOND ;
2021-08-23 21:21:08 +02:00
if ( spectrum - > frames_per_interval = = 0 ) {
2019-04-18 15:03:01 +02:00
spectrum - > frames_per_interval = 1 ;
2021-08-23 21:21:08 +02:00
}
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
GST_INFO_OBJECT ( spectrum , " interval % " GST_TIME_FORMAT " , fpi % " G_GUINT64_FORMAT " , error % " GST_TIME_FORMAT , GST_TIME_ARGS ( spectrum - > interval ) , spectrum - > frames_per_interval , GST_TIME_ARGS ( spectrum - > error_per_interval ) ) ;
2019-04-18 15:03:01 +02:00
spectrum - > input_pos = 0 ;
2021-10-30 00:48:21 +02:00
gst_fastspectrum_flush ( spectrum ) ;
2019-04-18 15:03:01 +02:00
}
2021-08-23 21:21:08 +02:00
if ( spectrum - > num_frames = = 0 ) {
2021-10-30 00:48:21 +02:00
spectrum - > message_ts = GST_BUFFER_TIMESTAMP ( buffer ) ;
2021-08-23 21:21:08 +02:00
}
2019-04-18 15:03:01 +02:00
input_pos = spectrum - > input_pos ;
input_data = spectrum - > input_data ;
while ( size > = bpf ) {
2021-10-30 00:48:21 +02:00
// Run input_data for a chunk of data
guint fft_todo = nfft - ( spectrum - > num_frames % nfft ) ;
guint msg_todo = spectrum - > frames_todo - spectrum - > num_frames ;
GST_LOG_OBJECT ( spectrum , " message frames todo: %u, fft frames todo: %u, input frames % " G_GSIZE_FORMAT , msg_todo , fft_todo , ( size / bpf ) ) ;
guint block_size = msg_todo ;
2021-08-23 21:21:08 +02:00
if ( block_size > ( size / bpf ) ) {
2019-04-18 15:03:01 +02:00
block_size = ( size / bpf ) ;
2021-08-23 21:21:08 +02:00
}
if ( block_size > fft_todo ) {
2019-04-18 15:03:01 +02:00
block_size = fft_todo ;
2021-08-23 21:21:08 +02:00
}
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
// Move the current frames into our ringbuffers
2019-04-18 15:03:01 +02:00
input_data ( data , spectrum - > input_ring_buffer , block_size , max_value , input_pos , nfft ) ;
data + = block_size * bpf ;
size - = block_size * bpf ;
input_pos = ( input_pos + block_size ) % nfft ;
spectrum - > num_frames + = block_size ;
2021-10-30 00:48:21 +02:00
gboolean have_full_interval = ( spectrum - > num_frames = = spectrum - > frames_todo ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
GST_LOG_OBJECT ( spectrum , " size: % " G_GSIZE_FORMAT " , do-fft = %d, do-message = %d " , size , ( spectrum - > num_frames % nfft = = 0 ) , have_full_interval ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
// If we have enough frames for an FFT or we have all frames required for the interval and we haven't run a FFT, then run an FFT
2019-04-18 15:03:01 +02:00
if ( ( spectrum - > num_frames % nfft = = 0 ) | | ( have_full_interval & & ! spectrum - > num_fft ) ) {
2021-10-30 00:48:21 +02:00
gst_fastspectrum_run_fft ( spectrum , input_pos ) ;
2019-04-18 15:03:01 +02:00
spectrum - > num_fft + + ;
}
2021-10-30 00:48:21 +02:00
// Do we have the FFTs for one interval?
2019-04-18 15:03:01 +02:00
if ( have_full_interval ) {
2021-10-30 00:48:21 +02:00
GST_DEBUG_OBJECT ( spectrum , " nfft: %u frames: % " G_GUINT64_FORMAT " fpi: % " G_GUINT64_FORMAT " error: % " GST_TIME_FORMAT , nfft , spectrum - > num_frames , spectrum - > frames_per_interval , GST_TIME_ARGS ( spectrum - > accumulated_error ) ) ;
2019-04-18 15:03:01 +02:00
spectrum - > frames_todo = spectrum - > frames_per_interval ;
if ( spectrum - > accumulated_error > = GST_SECOND ) {
spectrum - > accumulated_error - = GST_SECOND ;
spectrum - > frames_todo + + ;
}
spectrum - > accumulated_error + = spectrum - > error_per_interval ;
if ( spectrum - > output_callback ) {
// Calculate average
for ( guint i = 0 ; i < spectrum - > bands ; i + + ) {
2021-10-30 00:48:21 +02:00
spectrum - > spect_magnitude [ i ] / = static_cast < double > ( spectrum - > num_fft ) ;
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
spectrum - > output_callback ( spectrum - > spect_magnitude , static_cast < int > ( spectrum - > bands ) ) ;
2019-04-18 15:03:01 +02:00
// Reset spectrum accumulators
memset ( spectrum - > spect_magnitude , 0 , spectrum - > bands * sizeof ( double ) ) ;
}
2021-10-30 00:48:21 +02:00
if ( GST_CLOCK_TIME_IS_VALID ( spectrum - > message_ts ) ) {
spectrum - > message_ts + = gst_util_uint64_scale ( spectrum - > num_frames , GST_SECOND , rate ) ;
2021-08-23 21:21:08 +02:00
}
2019-04-18 15:03:01 +02:00
spectrum - > num_frames = 0 ;
spectrum - > num_fft = 0 ;
}
}
spectrum - > input_pos = input_pos ;
2021-10-30 00:48:21 +02:00
gst_buffer_unmap ( buffer , & map ) ;
g_mutex_unlock ( & spectrum - > lock ) ;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
g_assert ( size = = 0 ) ;
2019-04-18 15:03:01 +02:00
return GST_FLOW_OK ;
}