//------------------------------------------------------------------------------
// Pd Spectral Toolkit
//
// peaks~.c
//
// Detects an arbitrary number of spectral peaks
//
// Created by Cooper on 8/31/12.
// Updated for 64 Bit Support in September 2019.
// Copyright (C) 2019 Cooper Baker. All Rights Reserved.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// headers
//------------------------------------------------------------------------------
// main header for pd
#include "m_pd.h"
// utility header for Pd Spectral Toolkit project
#include "utility.h"
// disable compiler warnings on windows
#ifdef NT
#pragma warning( disable : 4244 )
#pragma warning( disable : 4305 )
#endif
//------------------------------------------------------------------------------
// peaks_class - pointer to this object's definition
//------------------------------------------------------------------------------
static t_class* peaks_class;
//------------------------------------------------------------------------------
// peaks - data structure holding this object's data
//------------------------------------------------------------------------------
typedef struct peaks
{
// this object - must always be first variable in struct
t_object object;
// needed for CLASS_MAINSIGNALIN macro call in in_tilde_setup
t_float inlet_1;
// needed for signalinlet_new call in peaks_new
t_float inlet_2;
// pointers to signal vector arrays
t_float* in1_peaks;
t_float* in2_peaks;
t_float* in1_temp;
t_float* in2_temp;
t_float* indices;
t_float* vector_index;
// memory size of a signal vector block
t_int memory_size;
// number of peaks to output
t_float num_peaks;
} t_peaks;
//------------------------------------------------------------------------------
// function prototypes
//------------------------------------------------------------------------------
static inline void peaks_quicksort_indices ( t_peaks* object, t_int beginning, t_int end );
static t_int* peaks_perform ( t_int* io );
static void peaks_dsp ( t_peaks* object, t_signal **sig );
static void* peaks_new ( t_symbol* selector, t_int items, t_atom* list );
static void peaks_free ( t_peaks* object );
void peaks_tilde_setup ( void );
//------------------------------------------------------------------------------
// peaks_quicksort_indices - sorts indices array based on the contents of in1_peaks
//------------------------------------------------------------------------------
static inline void peaks_quicksort_indices( t_peaks* object, t_int beginning, t_int end )
{
t_float* a = object->in1_peaks;
t_float* c = object->indices;
if( end > ( beginning + 1 ) )
{
t_float pivot = a[ ( t_int )c[ beginning ] ];
t_int left = beginning + 1;
t_int right = end;
t_float temp;
while( left < right )
{
if( a[ ( t_int )c[ left ] ] <= pivot )
{
++left;
}
else
{
--right;
temp = c[ left ];
c[ left ] = c[ right ];
c[ right ] = temp;
}
}
--left;
temp = c[ left ];
c[ left ] = c[ beginning ];
c[ beginning ] = temp;
peaks_quicksort_indices( object, beginning, left );
peaks_quicksort_indices( object, right, end );
}
}
//------------------------------------------------------------------------------
// peaks_select_peaks - chooses num_peaks largest peaks for output
//------------------------------------------------------------------------------
static inline void peaks_select_peaks( t_peaks* object, t_float num_peaks, t_int frames )
{
t_float* a = object->in1_peaks;
t_float* b = object->in2_peaks;
t_float* c = object->indices;
t_float* a_temp = object->in1_temp;
t_float* b_temp = object->in2_temp;
t_int i;
memset( a_temp, 0, object->memory_size );
memset( b_temp, 0, object->memory_size );
for( i = frames - 1 ; i >= frames - num_peaks ; --i )
{
a_temp[ i ] = a[ ( t_int )c[ i ] ];
b_temp[ i ] = b[ ( t_int )c[ i ] ];
}
memset( a, 0, object->memory_size );
memset( b, 0, object->memory_size );
for( i = frames - 1 ; i >= frames - num_peaks ; --i )
{
a[ ( t_int )c[ i ] ] = a_temp[ i ];
b[ ( t_int )c[ i ] ] = b_temp[ i ];
}
}
//------------------------------------------------------------------------------
// peaks_perform - the signal processing function of this object
//------------------------------------------------------------------------------
static t_int* peaks_perform( t_int* io )
{
// store variables from dsp input/output array
t_float* in1 = ( t_float* )( io[ 1 ] );
t_float* in2 = ( t_float* )( io[ 2 ] );
t_float* out1 = ( t_float* )( io[ 3 ] );
t_float* out2 = ( t_float* )( io[ 4 ] );
t_int frames = ( t_int )( io[ 5 ] );
t_peaks* object = ( t_peaks* )( io[ 6 ] );
// make local copies of memory pointers
t_float* in1_peaks = object->in1_peaks;
t_float* in2_peaks = object->in2_peaks;
// make local copies of object variables
t_int memory_size = object->memory_size;
t_float num_peaks = object->num_peaks;
// signal vector iterators
t_int nm1;
t_int n;
t_int np1;
// clear peaks memory
memset( in1_peaks, 0, memory_size );
memset( in2_peaks, 0, memory_size );
// iterate through spectral data looking for peaks
for( nm1 = 0, n = 1, np1 = 2 ; n < ( frames - 1 ) ; ++nm1, ++n, ++np1 )
{
// find peaks in in1 data ( current value > values on either side )
if( ( in1[ nm1 ] < in1[ n ] ) && ( in1[ n ] > in1[ np1 ] ) )
{
// store peak values in peaks arrays
in1_peaks[ n ] = in1[ n ];
in2_peaks[ n ] = in2[ n ];
}
}
// choose a specific number of peaks?
if( ( num_peaks > 0 ) && ( num_peaks <= frames ) )
{
// copy vector index array to indices array for sorting
memcpy( object->indices, object->vector_index, memory_size );
// sort the indices based on peaks in in1_peaks
peaks_quicksort_indices( object, 0, frames );
// select num_peaks largest peaks for output
peaks_select_peaks( object, num_peaks, frames );
}
// copy peaks arrays to outlet arrays
memcpy( out1, in1_peaks, memory_size );
memcpy( out2, in2_peaks, memory_size );
// return the dsp input/output array address plus one more than its size
// to provide a pointer to the next perform function in pd's call list
return &( io[ 7 ] );
}
//------------------------------------------------------------------------------
// peaks_dsp - installs this object's dsp function in pd's callback list
//------------------------------------------------------------------------------
static void peaks_dsp( t_peaks* object, t_signal **sig )
{
// temporary frame size variable
t_int frames = sig[ 0 ]->s_n;
// calculate memory size of signal vector for memset and realloc
t_int memory_size = sig[ 0 ]->s_n * sizeof( t_float );
// save memory_size for use in dsp loop
object->memory_size = memory_size;
// allocate enough memory to hold signal vector data ( in1 and indices need an extra location for sorting )
object->in1_peaks = realloc( object->in1_peaks, memory_size + sizeof( t_float ) );
object->in2_peaks = realloc( object->in2_peaks, memory_size );
object->in1_temp = realloc( object->in1_temp, memory_size );
object->in2_temp = realloc( object->in2_temp, memory_size );
object->indices = realloc( object->indices, memory_size + sizeof( t_float ) );
object->vector_index = realloc( object->vector_index, memory_size );
// init extra sorting values
object->in1_peaks[ frames ] = C_FLOAT_MIN;
object->indices [ frames ] = 0;
// increment values stored in vector_index
for( t_int index = 0 ; index < frames ; ++index )
{
object->vector_index[ index ] = ( t_float )index;
}
// dsp_add arguments
//--------------------------------------------------------------------------
// perform routine
// number of passed parameters
// inlet 1 sample vector
// inlet 2 sample vector
// outlet 1 sample vector
// outlet 2 sample vector
// sample frames to process (vector size)
// pointer to this object
dsp_add( peaks_perform, 6, sig[ 0 ]->s_vec, sig[ 1 ]->s_vec, sig[ 2 ]->s_vec, sig[ 3 ]->s_vec, frames, object );
}
//------------------------------------------------------------------------------
// peaks_new - instantiates a copy of this object in pd
//------------------------------------------------------------------------------
static void* peaks_new( t_symbol* selector, t_int items, t_atom* list )
{
// create a pointer to this object
t_peaks* object = ( t_peaks* )pd_new( peaks_class );
// create an additional signal inlet
signalinlet_new( &object->object, object->inlet_2 );
// create a float inlet
floatinlet_new( &object->object, &object->num_peaks );
// create two signal outlets
outlet_new( &object->object, gensym( "signal" ) );
outlet_new( &object->object, gensym( "signal" ) );
// initialize memory pointers
object->in1_peaks = NULL;
object->in2_peaks = NULL;
object->in1_temp = NULL;
object->in2_temp = NULL;
object->indices = NULL;
object->vector_index = NULL;
// init number of peaks: 0 outputs all peaks
object-> num_peaks = 0;
// parse initialization arguments
//--------------------------------------------------------------------------
if( items > 0 )
{
if( list[ 0 ].a_type == A_FLOAT )
{
object->num_peaks = atom_getfloatarg( 0, ( int )items, list );
}
else if( list[ 0 ].a_type == A_SYMBOL )
{
pd_error( object, "peaks~: invalid argument 1 type" );
}
}
if( items > 1 )
{
post( "peaks~: extra arguments ignored" );
}
return object;
}
//------------------------------------------------------------------------------
// peaks_free - cleans up memory allocated by this object
//------------------------------------------------------------------------------
static void peaks_free( t_peaks* object )
{
// if memory is allocated
if( object->in1_peaks )
{
// deallocate the memory
free( object->in1_peaks );
// set the memory pointer to null
object->in1_peaks = NULL;
}
if( object->in2_peaks )
{
free( object->in2_peaks );
object->in2_peaks = NULL;
}
if( object->in1_temp )
{
free( object->in1_temp );
object->in1_temp = NULL;
}
if( object->in2_temp )
{
free( object->in2_temp );
object->in2_temp = NULL;
}
if( object->indices )
{
free( object->indices );
object->indices = NULL;
}
if( object->vector_index )
{
free( object->vector_index );
object->vector_index = NULL;
}
}
//------------------------------------------------------------------------------
// peaks_tilde_setup - describes the attributes of this object to pd so it may be properly instantiated
// (must always be named with _tilde replacing ~ in the object name)
//------------------------------------------------------------------------------
void peaks_tilde_setup( void )
{
// peaks class
//--------------------------------------------------------------------------
// creates an instance of this object and describes it to pd
peaks_class = class_new( gensym( "peaks~" ), ( t_newmethod )peaks_new, ( t_method )peaks_free, sizeof( t_peaks ), 0, A_GIMME, 0 );
// declares leftmost inlet as a signal inlet
CLASS_MAINSIGNALIN( peaks_class, t_peaks, inlet_1 );
// installs peaks_dsp so that it will be called when dsp is turned on
class_addmethod( peaks_class, ( t_method )peaks_dsp, gensym( "dsp" ), 0 );
}
//------------------------------------------------------------------------------
// EOF
//------------------------------------------------------------------------------
// Pd Spectral Toolkit
//
// peaks~.c
//
// Detects an arbitrary number of spectral peaks
//
// Created by Cooper on 8/31/12.
// Updated for 64 Bit Support in September 2019.
// Copyright (C) 2019 Cooper Baker. All Rights Reserved.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// headers
//------------------------------------------------------------------------------
// main header for pd
#include "m_pd.h"
// utility header for Pd Spectral Toolkit project
#include "utility.h"
// disable compiler warnings on windows
#ifdef NT
#pragma warning( disable : 4244 )
#pragma warning( disable : 4305 )
#endif
//------------------------------------------------------------------------------
// peaks_class - pointer to this object's definition
//------------------------------------------------------------------------------
static t_class* peaks_class;
//------------------------------------------------------------------------------
// peaks - data structure holding this object's data
//------------------------------------------------------------------------------
typedef struct peaks
{
// this object - must always be first variable in struct
t_object object;
// needed for CLASS_MAINSIGNALIN macro call in in_tilde_setup
t_float inlet_1;
// needed for signalinlet_new call in peaks_new
t_float inlet_2;
// pointers to signal vector arrays
t_float* in1_peaks;
t_float* in2_peaks;
t_float* in1_temp;
t_float* in2_temp;
t_float* indices;
t_float* vector_index;
// memory size of a signal vector block
t_int memory_size;
// number of peaks to output
t_float num_peaks;
} t_peaks;
//------------------------------------------------------------------------------
// function prototypes
//------------------------------------------------------------------------------
static inline void peaks_quicksort_indices ( t_peaks* object, t_int beginning, t_int end );
static t_int* peaks_perform ( t_int* io );
static void peaks_dsp ( t_peaks* object, t_signal **sig );
static void* peaks_new ( t_symbol* selector, t_int items, t_atom* list );
static void peaks_free ( t_peaks* object );
void peaks_tilde_setup ( void );
//------------------------------------------------------------------------------
// peaks_quicksort_indices - sorts indices array based on the contents of in1_peaks
//------------------------------------------------------------------------------
static inline void peaks_quicksort_indices( t_peaks* object, t_int beginning, t_int end )
{
t_float* a = object->in1_peaks;
t_float* c = object->indices;
if( end > ( beginning + 1 ) )
{
t_float pivot = a[ ( t_int )c[ beginning ] ];
t_int left = beginning + 1;
t_int right = end;
t_float temp;
while( left < right )
{
if( a[ ( t_int )c[ left ] ] <= pivot )
{
++left;
}
else
{
--right;
temp = c[ left ];
c[ left ] = c[ right ];
c[ right ] = temp;
}
}
--left;
temp = c[ left ];
c[ left ] = c[ beginning ];
c[ beginning ] = temp;
peaks_quicksort_indices( object, beginning, left );
peaks_quicksort_indices( object, right, end );
}
}
//------------------------------------------------------------------------------
// peaks_select_peaks - chooses num_peaks largest peaks for output
//------------------------------------------------------------------------------
static inline void peaks_select_peaks( t_peaks* object, t_float num_peaks, t_int frames )
{
t_float* a = object->in1_peaks;
t_float* b = object->in2_peaks;
t_float* c = object->indices;
t_float* a_temp = object->in1_temp;
t_float* b_temp = object->in2_temp;
t_int i;
memset( a_temp, 0, object->memory_size );
memset( b_temp, 0, object->memory_size );
for( i = frames - 1 ; i >= frames - num_peaks ; --i )
{
a_temp[ i ] = a[ ( t_int )c[ i ] ];
b_temp[ i ] = b[ ( t_int )c[ i ] ];
}
memset( a, 0, object->memory_size );
memset( b, 0, object->memory_size );
for( i = frames - 1 ; i >= frames - num_peaks ; --i )
{
a[ ( t_int )c[ i ] ] = a_temp[ i ];
b[ ( t_int )c[ i ] ] = b_temp[ i ];
}
}
//------------------------------------------------------------------------------
// peaks_perform - the signal processing function of this object
//------------------------------------------------------------------------------
static t_int* peaks_perform( t_int* io )
{
// store variables from dsp input/output array
t_float* in1 = ( t_float* )( io[ 1 ] );
t_float* in2 = ( t_float* )( io[ 2 ] );
t_float* out1 = ( t_float* )( io[ 3 ] );
t_float* out2 = ( t_float* )( io[ 4 ] );
t_int frames = ( t_int )( io[ 5 ] );
t_peaks* object = ( t_peaks* )( io[ 6 ] );
// make local copies of memory pointers
t_float* in1_peaks = object->in1_peaks;
t_float* in2_peaks = object->in2_peaks;
// make local copies of object variables
t_int memory_size = object->memory_size;
t_float num_peaks = object->num_peaks;
// signal vector iterators
t_int nm1;
t_int n;
t_int np1;
// clear peaks memory
memset( in1_peaks, 0, memory_size );
memset( in2_peaks, 0, memory_size );
// iterate through spectral data looking for peaks
for( nm1 = 0, n = 1, np1 = 2 ; n < ( frames - 1 ) ; ++nm1, ++n, ++np1 )
{
// find peaks in in1 data ( current value > values on either side )
if( ( in1[ nm1 ] < in1[ n ] ) && ( in1[ n ] > in1[ np1 ] ) )
{
// store peak values in peaks arrays
in1_peaks[ n ] = in1[ n ];
in2_peaks[ n ] = in2[ n ];
}
}
// choose a specific number of peaks?
if( ( num_peaks > 0 ) && ( num_peaks <= frames ) )
{
// copy vector index array to indices array for sorting
memcpy( object->indices, object->vector_index, memory_size );
// sort the indices based on peaks in in1_peaks
peaks_quicksort_indices( object, 0, frames );
// select num_peaks largest peaks for output
peaks_select_peaks( object, num_peaks, frames );
}
// copy peaks arrays to outlet arrays
memcpy( out1, in1_peaks, memory_size );
memcpy( out2, in2_peaks, memory_size );
// return the dsp input/output array address plus one more than its size
// to provide a pointer to the next perform function in pd's call list
return &( io[ 7 ] );
}
//------------------------------------------------------------------------------
// peaks_dsp - installs this object's dsp function in pd's callback list
//------------------------------------------------------------------------------
static void peaks_dsp( t_peaks* object, t_signal **sig )
{
// temporary frame size variable
t_int frames = sig[ 0 ]->s_n;
// calculate memory size of signal vector for memset and realloc
t_int memory_size = sig[ 0 ]->s_n * sizeof( t_float );
// save memory_size for use in dsp loop
object->memory_size = memory_size;
// allocate enough memory to hold signal vector data ( in1 and indices need an extra location for sorting )
object->in1_peaks = realloc( object->in1_peaks, memory_size + sizeof( t_float ) );
object->in2_peaks = realloc( object->in2_peaks, memory_size );
object->in1_temp = realloc( object->in1_temp, memory_size );
object->in2_temp = realloc( object->in2_temp, memory_size );
object->indices = realloc( object->indices, memory_size + sizeof( t_float ) );
object->vector_index = realloc( object->vector_index, memory_size );
// init extra sorting values
object->in1_peaks[ frames ] = C_FLOAT_MIN;
object->indices [ frames ] = 0;
// increment values stored in vector_index
for( t_int index = 0 ; index < frames ; ++index )
{
object->vector_index[ index ] = ( t_float )index;
}
// dsp_add arguments
//--------------------------------------------------------------------------
// perform routine
// number of passed parameters
// inlet 1 sample vector
// inlet 2 sample vector
// outlet 1 sample vector
// outlet 2 sample vector
// sample frames to process (vector size)
// pointer to this object
dsp_add( peaks_perform, 6, sig[ 0 ]->s_vec, sig[ 1 ]->s_vec, sig[ 2 ]->s_vec, sig[ 3 ]->s_vec, frames, object );
}
//------------------------------------------------------------------------------
// peaks_new - instantiates a copy of this object in pd
//------------------------------------------------------------------------------
static void* peaks_new( t_symbol* selector, t_int items, t_atom* list )
{
// create a pointer to this object
t_peaks* object = ( t_peaks* )pd_new( peaks_class );
// create an additional signal inlet
signalinlet_new( &object->object, object->inlet_2 );
// create a float inlet
floatinlet_new( &object->object, &object->num_peaks );
// create two signal outlets
outlet_new( &object->object, gensym( "signal" ) );
outlet_new( &object->object, gensym( "signal" ) );
// initialize memory pointers
object->in1_peaks = NULL;
object->in2_peaks = NULL;
object->in1_temp = NULL;
object->in2_temp = NULL;
object->indices = NULL;
object->vector_index = NULL;
// init number of peaks: 0 outputs all peaks
object-> num_peaks = 0;
// parse initialization arguments
//--------------------------------------------------------------------------
if( items > 0 )
{
if( list[ 0 ].a_type == A_FLOAT )
{
object->num_peaks = atom_getfloatarg( 0, ( int )items, list );
}
else if( list[ 0 ].a_type == A_SYMBOL )
{
pd_error( object, "peaks~: invalid argument 1 type" );
}
}
if( items > 1 )
{
post( "peaks~: extra arguments ignored" );
}
return object;
}
//------------------------------------------------------------------------------
// peaks_free - cleans up memory allocated by this object
//------------------------------------------------------------------------------
static void peaks_free( t_peaks* object )
{
// if memory is allocated
if( object->in1_peaks )
{
// deallocate the memory
free( object->in1_peaks );
// set the memory pointer to null
object->in1_peaks = NULL;
}
if( object->in2_peaks )
{
free( object->in2_peaks );
object->in2_peaks = NULL;
}
if( object->in1_temp )
{
free( object->in1_temp );
object->in1_temp = NULL;
}
if( object->in2_temp )
{
free( object->in2_temp );
object->in2_temp = NULL;
}
if( object->indices )
{
free( object->indices );
object->indices = NULL;
}
if( object->vector_index )
{
free( object->vector_index );
object->vector_index = NULL;
}
}
//------------------------------------------------------------------------------
// peaks_tilde_setup - describes the attributes of this object to pd so it may be properly instantiated
// (must always be named with _tilde replacing ~ in the object name)
//------------------------------------------------------------------------------
void peaks_tilde_setup( void )
{
// peaks class
//--------------------------------------------------------------------------
// creates an instance of this object and describes it to pd
peaks_class = class_new( gensym( "peaks~" ), ( t_newmethod )peaks_new, ( t_method )peaks_free, sizeof( t_peaks ), 0, A_GIMME, 0 );
// declares leftmost inlet as a signal inlet
CLASS_MAINSIGNALIN( peaks_class, t_peaks, inlet_1 );
// installs peaks_dsp so that it will be called when dsp is turned on
class_addmethod( peaks_class, ( t_method )peaks_dsp, gensym( "dsp" ), 0 );
}
//------------------------------------------------------------------------------
// EOF
//------------------------------------------------------------------------------