2012-10-30 03:52:39 +00:00
# include "cmPrefix.h"
# include "cmGlobal.h"
# include "cmRpt.h"
# include "cmErr.h"
# include "cmCtx.h"
# include "cmMem.h"
# include "cmMallocDebug.h"
# include "cmLinkedHeap.h"
# include "cmSymTbl.h"
# include "cmFloatTypes.h"
# include "cmComplexTypes.h"
# include "cmFileSys.h"
# include "cmJson.h"
# include "cmProcObj.h"
# include "cmProcTemplate.h"
# include "cmAudioFile.h"
# include "cmMath.h"
# include "cmProc.h"
# include "cmVectOps.h"
# include "cmProc3.h"
# include "cmMidi.h" // cmMidiToSciPitch();
cmPitchShift * cmPitchShiftAlloc ( cmCtx * c , cmPitchShift * p , unsigned procSmpCnt , cmReal_t srate )
{
cmPitchShift * op = cmObjAlloc ( cmPitchShift , c , p ) ;
if ( procSmpCnt ! = 0 )
if ( cmPitchShiftInit ( op , procSmpCnt , srate ) ! = cmOkRC )
cmPitchShiftFree ( & op ) ;
return op ;
}
cmRC_t cmPitchShiftFinal ( cmPitchShift * p )
{ return cmOkRC ; }
# define _cube_interp(x,y0,y1,y2,y3) ((y3) - (y2) - (y0) + (y1))*(x)*(x)*(x) + ((y0) - (y1) - ((y3) - (y2) - (y0) + (y1)))*(x)*(x) + ((y2) - (y0))*(x) + (y1)
# define _lin_interp(x,y0,y1) (y0) + (x) * ((y1)-(y0))
/*
cmRC_t cmPitchShiftFree ( cmPitchShift * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp ! = NULL & & * pp ! = NULL )
{
cmPitchShift * p = * pp ;
if ( ( rc = cmPitchShiftFinal ( p ) ) = = cmOkRC )
{
cmMemPtrFree ( & p - > x ) ;
cmMemPtrFree ( & p - > y ) ;
cmMemPtrFree ( & p - > cf ) ;
cmObjFree ( pp ) ;
}
}
return rc ;
}
cmRC_t cmPitchShiftInit ( cmPitchShift * p , unsigned procSmpCnt , double srate )
{
cmRC_t rc ;
if ( ( rc = cmPitchShiftFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > procSmpCnt = procSmpCnt ;
p - > srate = srate ;
p - > wn = 4096 ;
p - > cfn = 1024 ;
p - > outN = p - > procSmpCnt ;
p - > x = cmMemResizeZ ( cmSample_t , p - > x , procSmpCnt + 3 ) ;
p - > y = cmMemResizeZ ( cmSample_t , p - > y , p - > wn ) ;
p - > cf = cmMemResizeZ ( cmSample_t , p - > cf , p - > cfn ) ;
p - > pii = procSmpCnt + p - > cfn ;
p - > poi = 0 ;
p - > outV = p - > y ;
p - > prv_x0 = 0 ;
p - > prv_x1 = 0 ;
p - > genFl = true ;
p - > xfi = 3 ;
p - > cfi = 0 ;
p - > cubeFl = true ;
p - > linCfFl = true ;
assert ( p - > cfn + p - > procSmpCnt < p - > wn ) ;
unsigned i ;
for ( i = 0 ; i < p - > cfn ; + + i )
{
p - > cf [ i ] = ( sin ( ( ( double ) i / p - > cfn * M_PI ) - ( M_PI / 2.0 ) ) + 1.0 ) / 2.0 ;
p - > cf [ i ] = ( double ) i / p - > cfn ;
}
return rc ;
}
// Algorithm:
// 1. Generate srate converted values into p->y[] until it is full.
// 2. Playback from y[] ignoring any incoming samples until less than
// p->cfn + p->procSmpCnt samples remain in y[] (i.e. pii < procSmpCnt + cfn).
// 3. At this point begin generating samples from the input and crossfading them
// with the last cfn samples in y[].
// 4. After cfn new samples have been crossfaded proceed to fill the remaining
// space in y[]
// 5. Goto 2.
//
// Note that since we are pitch shifting down (ratio<1.0) the number of samples generated
// will always be greater than the number of input samples.
//
// Notes:
// For small downward shifts there will be large time aliasing because the length of time
// to fill the buffer (step 4) will because only a few extra samples
// are generated on each input cycle. Try decreasing the window length as the pitch ratio increases.
//
// Implement smoothly varying ratio changes.
//
// Change model so that at ratio 1.0 the input is effectively being constantly cross faded to produce
// identity output.
void _cmPitchShiftDown ( cmPitchShift * p , double ratio , const cmSample_t * x , unsigned xn )
{
// shift off the expired samples
memmove ( p - > y , p - > y + p - > procSmpCnt , ( p - > pii - p - > procSmpCnt ) * sizeof ( cmSample_t ) ) ;
p - > pii - = p - > procSmpCnt ;
// if not currently generating and there are less than cfn + procSmpCnt samples remaining ...
if ( p - > genFl = = false & & p - > pii < = p - > procSmpCnt + p - > cfn )
{
// ... then setup to begin generating
p - > cfi = 0 ;
p - > genFl = true ;
p - > xfi = 3 ;
}
if ( p - > genFl )
{
// setup the incoming samples
p - > x [ 0 ] = p - > prv_x0 ;
p - > x [ 1 ] = p - > prv_x1 ;
p - > x [ 2 ] = p - > prv_x2 ;
memcpy ( p - > x + 2 , x , p - > procSmpCnt * sizeof ( cmSample_t ) ) ;
int n0 = p - > cubeFl ? xn + 1 : xn + 2 ;
// as long as there are incoming samples available and space left in the output buffer
for ( ; p - > xfi < n0 & & p - > pii < p - > wn ; p - > xfi + = ratio )
{
unsigned xii = floor ( p - > xfi ) ;
double frac = p - > xfi - xii ;
// generate the value of the output sample
cmSample_t value ;
if ( p - > cubeFl )
value = _cube_interp ( frac , p - > x [ xii - 1 ] , p - > x [ xii ] , p - > x [ xii + 1 ] , p - > x [ xii + 2 ] ) ;
else
value = p - > x [ xii ] + frac * ( p - > x [ xii + 1 ] - p - > x [ xii ] ) ;
// if xfading
if ( p - > cfi < p - > cfn )
{
// calc index into y[] of the sample to crossfade with
int idx = p - > pii - p - > cfn + p - > cfi ;
p - > y [ idx ] = ( 1.0 - p - > cf [ p - > cfi ] ) * p - > y [ idx ] + p - > cf [ p - > cfi ] * value ;
+ + p - > cfi ;
}
else
{
// else fill the remaing space in y[]
p - > y [ p - > pii ] = value ;
+ + p - > pii ;
}
}
// if the output buffer is full then stop generating
if ( p - > pii = = p - > wn )
p - > genFl = false ;
}
// reset the input index to the beginning of the buffer
p - > xfi - = xn ;
p - > prv_x0 = x [ xn - 3 ] ;
p - > prv_x1 = x [ xn - 2 ] ;
p - > prv_x2 = x [ xn - 1 ] ;
}
cmRC_t cmPitchShiftExec ( cmPitchShift * p , const cmSample_t * x , cmSample_t * y , unsigned n , double shiftRatio )
{
assert ( n = = p - > procSmpCnt ) ;
if ( shiftRatio > = 1 )
memcpy ( y , x , n * sizeof ( cmSample_t ) ) ;
else
{
_cmPitchShiftDown ( p , shiftRatio , x , n ) ;
if ( y ! = NULL )
memcpy ( y , p - > y , n * sizeof ( cmSample_t ) ) ;
}
return cmOkRC ;
}
*/
cmRC_t cmPitchShiftFree ( cmPitchShift * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp ! = NULL & & * pp ! = NULL )
{
cmPitchShift * p = * pp ;
if ( ( rc = cmPitchShiftFinal ( p ) ) = = cmOkRC )
{
cmMemPtrFree ( & p - > b ) ;
cmMemPtrFree ( & p - > outV ) ;
cmMemPtrFree ( & p - > wnd ) ;
cmMemPtrFree ( & p - > osc [ 0 ] . y ) ;
cmMemPtrFree ( & p - > osc [ 1 ] . y ) ;
cmObjFree ( pp ) ;
}
}
return rc ;
}
/*
cmRC_t cmPitchShiftInit ( cmPitchShift * p , unsigned procSmpCnt , double srate )
{
cmRC_t rc ;
if ( ( rc = cmPitchShiftFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > procSmpCnt = procSmpCnt ;
p - > srate = srate ;
p - > wn = 4096 ;
p - > in = 2 * p - > wn + p - > procSmpCnt ;
p - > outN = p - > procSmpCnt ;
p - > m = cmMemResizeZ ( cmSample_t , p - > m , p - > in + 2 ) ;
p - > x = p - > m + 1 ;
p - > outV = cmMemResizeZ ( cmSample_t , p - > outV , procSmpCnt ) ;
p - > wnd = cmMemResizeZ ( cmSample_t , p - > wnd , p - > wn + 1 ) ;
p - > ii = 1 ;
p - > cubeFl = true ;
p - > ei0 = - 1 ;
p - > ei1 = - 1 ;
p - > osc [ 0 ] . ratio = 1.0 ;
p - > osc [ 0 ] . wi = 0 ;
p - > osc [ 0 ] . xi = 0 ;
p - > osc [ 0 ] . yN = 2 * p - > wn ;
p - > osc [ 0 ] . y = cmMemResizeZ ( cmSample_t , p - > osc [ 0 ] . y , p - > osc [ 0 ] . yN ) ;
p - > osc [ 0 ] . yii = 0 ;
p - > osc [ 0 ] . yoi = 0 ;
p - > osc [ 0 ] . ynn = 0 ;
p - > osc [ 1 ] . ratio = 1.0 ;
p - > osc [ 1 ] . wi = floor ( p - > wn / 2 ) ;
p - > osc [ 1 ] . xi = 0 ;
p - > osc [ 1 ] . yN = 2 * p - > wn ;
p - > osc [ 1 ] . y = cmMemResizeZ ( cmSample_t , p - > osc [ 1 ] . y , p - > osc [ 1 ] . yN ) ;
p - > osc [ 1 ] . yii = 0 ;
p - > osc [ 1 ] . yoi = 0 ;
p - > osc [ 1 ] . ynn = 0 ;
assert ( p - > in > = p - > procSmpCnt + 1 ) ;
cmVOS_Hann ( p - > wnd , p - > wn + 1 ) ;
return rc ;
}
# define _isInNoGoZone(p,i) ( ((0 <= (i)) && ((i) <= (p)->ei0)) || (((p)->ii <= (i)) && ((i) <= (p)->ei1)) )
# define _isNotInNoGoZone(p,i) (!_isInNoGoZone(p,i))
void _startNewWindow ( cmPitchShift * p , cmPitchShiftOsc_t * op , int li , double ratio )
{
op - > wi = 0 ;
if ( op - > ratio < 1.0 )
op - > xi = p - > ii - p - > procSmpCnt ;
else
op - > xi = p - > ii + ( li ) * ( p - > procSmpCnt / ratio ) ;
while ( op - > xi < 0 )
op - > xi + = p - > in - 1 ;
while ( op - > xi > ( p - > in - 1 ) )
op - > xi - = ( p - > in - 1 ) ;
op - > xi = floor ( op - > xi ) ;
}
void _cmPitchShiftOsc ( cmPitchShift * p , cmPitchShiftOsc_t * op , double ratio )
{
int li = 0 ;
bool contFl = 1 ;
// if we are waiting to start a new window until the output buffer drains
if ( op - > wi = = - 1 )
{
// if the output buffer has enough samples to complete this cycle - return
if ( op - > ynn > = p - > procSmpCnt )
return ;
// otherwise start a new window
_startNewWindow ( p , op , li , ratio ) ;
}
// iterate until all output samples have been generated
for ( li = 0 ; contFl ; + + li )
{
// iterate over one window or until output is complete
while ( op - > wi < p - > wn )
{
int wii = floor ( op - > wi ) ;
double frac = op - > wi - wii ;
int xii = ( op - > xi + wii ) % ( p - > in - 1 ) ;
cmSample_t wnd = p - > wnd [ wii ] + frac * ( p - > wnd [ wii + 1 ] - p - > wnd [ wii ] ) ;
cmSample_t val ;
// if enough samples have been generated and the next input will not be in the no-go zone
if ( ( op - > ynn > = p - > procSmpCnt ) & & _isNotInNoGoZone ( p , xii - 1 ) & & _isNotInNoGoZone ( p , xii + 2 ) )
{
contFl = false ;
break ;
}
if ( op - > ynn > = op - > yN )
{
printf ( " OVER \n " ) ;
contFl = false ;
break ;
}
// generate the value of the output sample
if ( p - > cubeFl )
val = _cube_interp ( frac , p - > x [ xii - 1 ] , p - > x [ xii ] , p - > x [ xii + 1 ] , p - > x [ xii + 2 ] ) ;
else
val = p - > x [ xii ] + frac * ( p - > x [ xii + 1 ] - p - > x [ xii ] ) ;
// apply the window function and assign to output
op - > y [ op - > yii ] = wnd * val ;
// incr the count of samples in y[]
+ + op - > ynn ;
// advance the output buffers input location
op - > yii = ( op - > yii + 1 ) % op - > yN ;
op - > wi + = ratio ; // increment the window location
}
// if a window completed
if ( op - > wi > = p - > wn )
{
if ( ( ratio < 1.0 ) & & ( op - > ynn > = p - > procSmpCnt ) )
{
op - > wi = - 1 ;
break ;
}
_startNewWindow ( p , op , li + 1 , ratio ) ;
}
}
}
// in=5
// m x
// 0 1 2 3 4 5
// 1 x x a b c d e
// 2 c d e f g h i
// 3 g h i j k l m
//
// ratio < 1 - the output oscillators must be able to generate 1 complete window prior to new samples
// overwriting the oscillators location in the input buffer.
cmRC_t cmPitchShiftExec ( cmPitchShift * p , const cmSample_t * x , cmSample_t * y , unsigned xn , double ratio )
{
cmRC_t rc = cmOkRC ;
//memcpy(y,x,xn*sizeof(cmSample_t));
//return rc;
int i ;
// copy in the incoming samples
for ( i = 0 ; i < xn ; + + i )
{
p - > x [ p - > ii ] = x [ i ] ;
+ + p - > ii ;
if ( p - > ii = = p - > in + 1 )
{
p - > x [ - 1 ] = p - > x [ p - > in - 2 ] ;
p - > x [ 0 ] = p - > x [ p - > in - 1 ] ;
p - > x [ 1 ] = p - > x [ p - > in ] ;
p - > ii = 2 ;
}
}
// locate the section of the input buffer which
// will be written over on the next cycle.
// The oscillators have to avoid ending in this area
if ( ( p - > in + 1 ) - p - > ii > = p - > procSmpCnt )
{
p - > ei1 = p - > ii + p - > procSmpCnt - 1 ;
p - > ei0 = - 1 ;
}
else
{
int n = ( p - > in + 1 ) - p - > ii ;
p - > ei1 = ( p - > ii + n ) - 1 ;
p - > ei0 = ( p - > procSmpCnt - n ) - 1 ;
}
//memset(p->outV,0,p->procSmpCnt*sizeof(cmSample_t));
_cmPitchShiftOsc ( p , p - > osc + 0 , ratio ) ;
_cmPitchShiftOsc ( p , p - > osc + 1 , ratio ) ;
// mix the indidual output of the two oscillators to form the final output
if ( p - > osc [ 0 ] . ynn < p - > procSmpCnt | | p - > osc [ 1 ] . ynn < p - > procSmpCnt )
printf ( " UNDER \n " ) ;
for ( i = 0 ; i < p - > procSmpCnt ; + + i )
{
p - > outV [ i ] = p - > osc [ 0 ] . y [ p - > osc [ 0 ] . yoi ] + p - > osc [ 1 ] . y [ p - > osc [ 1 ] . yoi ] ;
p - > osc [ 0 ] . yoi = ( p - > osc [ 0 ] . yoi + 1 ) % p - > osc [ 0 ] . yN ;
p - > osc [ 1 ] . yoi = ( p - > osc [ 1 ] . yoi + 1 ) % p - > osc [ 1 ] . yN ;
}
p - > osc [ 0 ] . ynn - = p - > procSmpCnt ;
p - > osc [ 1 ] . ynn - = p - > procSmpCnt ;
if ( y ! = NULL )
memcpy ( y , p - > outV , p - > outN * sizeof ( cmSample_t ) ) ;
return rc ;
}
*/
cmRC_t cmPitchShiftInit ( cmPitchShift * p , unsigned procSmpCnt , double srate )
{
cmRC_t rc ;
if ( ( rc = cmPitchShiftFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > procSmpCnt = procSmpCnt ;
p - > outN = procSmpCnt ;
p - > outV = cmMemAllocZ ( cmSample_t , procSmpCnt ) ;
p - > wn = 2048 ;
p - > xn = 2 * p - > wn + procSmpCnt ;
p - > bn = p - > xn + 3 ;
p - > b = cmMemAllocZ ( cmSample_t , p - > bn ) ;
p - > x = p - > b + 1 ;
p - > wnd = cmMemAllocZ ( cmSample_t , p - > wn + 1 ) ;
p - > xni = p - > xn - procSmpCnt + 2 ;
p - > cubeFl = true ;
int i ;
for ( i = 0 ; i < 2 ; + + i )
{
cmPitchShiftOsc_t * op = p - > osc + i ;
op - > xi = p - > procSmpCnt ;
op - > yn = p - > wn * 2 ;
op - > y = cmMemAllocZ ( cmSample_t , op - > yn ) ;
op - > yii = 0 ;
op - > yoi = 0 ;
op - > ynn = 0 ;
op - > wi = i = = 0 ? 0 : p - > wn / 2 ;
}
cmVOS_Hann ( p - > wnd , p - > wn + 1 ) ;
return rc ;
}
void _cmPitchShiftOscExec ( cmPitchShift * p , cmPitchShiftOsc_t * op , double ratio )
{
int k = 0 ;
// account for right buffer shift on input
op - > xi - = p - > procSmpCnt ;
// pass through code for testing
if ( 0 )
{
int i ;
for ( i = 0 ; i < p - > procSmpCnt ; + + i )
{
op - > y [ op - > yii ] = p - > x [ ( int ) floor ( op - > xi ) ] ;
op - > yii = ( op - > yii + 1 ) % op - > yn ;
op - > ynn + = 1 ;
op - > xi + = 1.0 ;
}
return ;
}
while ( 1 )
{
int wii = floor ( op - > wi ) ;
double wfrac = op - > wi - wii ;
int xii = floor ( op - > xi ) ;
double vfrac = op - > xi - xii ;
cmSample_t val ;
// if enough output samples have been generated and we are outside the no-land zone
if ( ( op - > ynn > = p - > procSmpCnt & & op - > xi > = p - > procSmpCnt ) )
break ;
if ( op - > xi > = p - > xn )
{
//printf("Wrap %f %f\n",op->xi,op->wi);
}
cmSample_t wnd = p - > wnd [ wii ] + wfrac * ( p - > wnd [ wii + 1 ] - p - > wnd [ wii ] ) ;
// generate the value of the output sample
if ( p - > cubeFl )
val = _cube_interp ( vfrac , p - > x [ xii - 1 ] , p - > x [ xii ] , p - > x [ xii + 1 ] , p - > x [ xii + 2 ] ) ;
else
val = p - > x [ xii ] + vfrac * ( p - > x [ xii + 1 ] - p - > x [ xii ] ) ;
// apply the window function and assign to output
op - > y [ op - > yii ] = wnd * val ;
// incr the count of samples in y[]
+ + op - > ynn ;
// advance the output buffers input location
op - > yii = ( op - > yii + 1 ) % op - > yn ;
op - > wi + = ratio < 1 ? 1 : ratio ; // increment the window location
op - > xi + = ratio ;
if ( op - > wi > = p - > wn )
{
+ + k ;
op - > wi = 0 ;
if ( ratio < 1 )
op - > xi = p - > xni ; // begin of most recent block of procSmpCnt input samples
else
op - > xi = k * p - > procSmpCnt / ratio ;
}
}
//if( op->ynn != p->procSmpCnt )
// printf("wi:%f xi:%f ynn:%i\n",op->wi,op->xi,op->ynn);
}
// b: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17
// x: -1 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
//
// On each cycle:
// 1) shift x[] left by procSmpCnt samples.
// 2) add new samples on right side of x[]
// 3) oscillator scans from right to left.
cmRC_t cmPitchShiftExec ( cmPitchShift * p , const cmSample_t * x , cmSample_t * y , unsigned xn , double ratio , bool bypassFl )
{
if ( 0 )
{
memcpy ( p - > outV , x , xn * sizeof ( cmSample_t ) ) ;
if ( y ! = NULL )
memcpy ( y , x , xn * sizeof ( cmSample_t ) ) ;
return cmOkRC ;
}
if ( y = = NULL )
return cmOkRC ;
if ( x = = NULL )
{
cmVOS_Zero ( y , xn ) ;
return cmOkRC ;
}
if ( bypassFl )
{
memcpy ( p - > outV , x , xn * sizeof ( cmSample_t ) ) ;
if ( y ! = NULL )
memcpy ( y , x , xn * sizeof ( cmSample_t ) ) ;
return cmOkRC ;
}
int i ;
memmove ( p - > x - 1 , p - > x + p - > procSmpCnt - 1 , ( p - > xn - p - > procSmpCnt + 3 ) * sizeof ( cmSample_t ) ) ;
memcpy ( p - > x + p - > xni , x , xn * sizeof ( cmSample_t ) ) ;
//memmove(p->x + p->procSmpCnt -1, p->x - 1, (p->xn - p->procSmpCnt + 3) * sizeof(cmSample_t));
//memcpy( p->x-1, x, xn * sizeof(cmSample_t));
_cmPitchShiftOscExec ( p , p - > osc + 0 , ratio ) ;
_cmPitchShiftOscExec ( p , p - > osc + 1 , ratio ) ;
// mix the indidual output of the two oscillators to form the final output
if ( p - > osc [ 0 ] . ynn < p - > procSmpCnt | | p - > osc [ 1 ] . ynn < p - > procSmpCnt )
printf ( " UNDER \n " ) ;
for ( i = 0 ; i < p - > procSmpCnt ; + + i )
{
p - > outV [ i ] = p - > osc [ 0 ] . y [ p - > osc [ 0 ] . yoi ] + p - > osc [ 1 ] . y [ p - > osc [ 1 ] . yoi ] ;
p - > osc [ 0 ] . yoi = ( p - > osc [ 0 ] . yoi + 1 ) % p - > osc [ 0 ] . yn ;
p - > osc [ 1 ] . yoi = ( p - > osc [ 1 ] . yoi + 1 ) % p - > osc [ 1 ] . yn ;
}
p - > osc [ 0 ] . ynn - = p - > procSmpCnt ;
p - > osc [ 1 ] . ynn - = p - > procSmpCnt ;
if ( y ! = NULL )
memcpy ( y , p - > outV , xn * sizeof ( cmSample_t ) ) ;
return cmOkRC ;
}
//=======================================================================================================================
cmLoopRecord * cmLoopRecordAlloc (
cmCtx * c , cmLoopRecord * p , unsigned procSmpCnt , unsigned maxRecdSmpCnt , unsigned xfadeSmpCnt )
{
cmLoopRecord * op = cmObjAlloc ( cmLoopRecord , c , p ) ;
if ( procSmpCnt ! = 0 )
if ( cmLoopRecordInit ( op , procSmpCnt , maxRecdSmpCnt , xfadeSmpCnt ) ! = cmOkRC )
cmLoopRecordFree ( & op ) ;
return op ;
}
/*
cmRC_t cmLoopRecordFree ( cmLoopRecord * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp ! = NULL & & * pp ! = NULL )
{
cmLoopRecord * p = * pp ;
if ( ( rc = cmLoopRecordFinal ( p ) ) = = cmOkRC )
{
cmMemPtrFree ( & p - > bufMem ) ;
cmMemPtrFree ( & p - > bufArray ) ;
cmMemPtrFree ( & p - > outV ) ;
cmMemPtrFree ( & p - > xfadeFunc ) ;
cmObjFree ( pp ) ;
}
}
return rc ;
}
cmRC_t cmLoopRecordInit ( cmLoopRecord * p , unsigned procSmpCnt , unsigned maxRecdSmpCnt , unsigned xfadeSmpCnt )
{
cmRC_t rc ;
unsigned i ;
if ( ( rc = cmLoopRecordFinal ( p ) ) ! = cmOkRC )
return rc ;
assert ( xfadeSmpCnt < maxRecdSmpCnt ) ;
p - > maxRecdSmpCnt = maxRecdSmpCnt ;
p - > bufArrayCnt = 2 ;
p - > bufMem = cmMemResizeZ ( cmSample_t , p - > bufMem , maxRecdSmpCnt * p - > bufArrayCnt ) ;
p - > bufArray = cmMemResizeZ ( cmLoopRecdBuf , p - > bufArray , p - > bufArrayCnt ) ;
p - > outV = cmMemResizeZ ( cmSample_t , p - > outV , procSmpCnt ) ;
p - > xfadeFunc = cmMemResizeZ ( cmSample_t , p - > xfadeFunc , xfadeSmpCnt + 1 ) ;
p - > outN = procSmpCnt ;
p - > procSmpCnt = procSmpCnt ;
p - > xfadeSmpCnt = xfadeSmpCnt ;
p - > recdBufIdx = 0 ;
p - > playBufIdx = 1 ;
p - > recdFl = false ;
p - > playFl = false ;
for ( i = 0 ; i < p - > bufArrayCnt ; + + i )
{
p - > bufArray [ i ] . bV = p - > bufMem + ( i * maxRecdSmpCnt ) ;
p - > bufArray [ i ] . bN = maxRecdSmpCnt ;
p - > bufArray [ i ] . xfi = xfadeSmpCnt ;
}
for ( i = 0 ; i < = p - > xfadeSmpCnt ; + + i )
p - > xfadeFunc [ i ] = ( cmReal_t ) i / p - > xfadeSmpCnt ;
return rc ;
}
cmRC_t cmLoopRecordFinal ( cmLoopRecord * p )
{ return cmOkRC ; }
cmRC_t cmLoopRecordExec ( cmLoopRecord * p , const cmSample_t * x , cmSample_t * y , unsigned xn , bool recdFl , bool playFl )
{
unsigned i ;
// if the recdFl was enabled
if ( recdFl = = true & & p - > recdFl = = false )
{
p - > recdFl = true ;
p - > bufArray [ p - > recdBufIdx ] . bii = 0 ;
p - > bufArray [ p - > recdBufIdx ] . boi = 0 ;
printf ( " Recd:on %i \n " , p - > recdBufIdx ) ;
}
else
// if the recdFl was disabled
if ( recdFl = = false & & p - > recdFl = = true )
{
p - > recdFl = false ;
playFl = true ;
p - > playBufIdx = p - > recdBufIdx ;
p - > recdBufIdx = ( p - > recdBufIdx + 1 ) % p - > bufArrayCnt ;
printf ( " Recd:off \n " ) ;
}
// if the playFl triggered on
if ( playFl = = true & & p - > playFl = = false )
{
p - > bufArray [ p - > playBufIdx ] . boi = 0 ;
p - > playFl = true ;
//printf("Play:on %i %i\n",p->playBufIdx,p->bufArray[p->playBufIdx].bii);
}
// if recording
if ( p - > recdFl )
{
cmLoopRecdBuf * rp = p - > bufArray + p - > recdBufIdx ;
for ( i = 0 ; i < xn & & rp - > bii < rp - > bN ; + + i , + + rp - > bii )
{
rp - > bV [ rp - > bii ] = x [ i ] ;
}
p - > recdFl = rp - > bii ! = rp - > bN ;
}
// if playing
if ( p - > playFl )
{
cmLoopRecdBuf * rp = p - > bufArray + p - > playBufIdx ;
unsigned xf0n = p - > xfadeSmpCnt / 2 ;
unsigned xf1n = rp - > bii - p - > xfadeSmpCnt / 2 ;
unsigned xfi0 = ( rp - > boi + p - > xfadeSmpCnt / 2 ) % rp - > bii ;
for ( i = 0 ; i < xn ; + + i )
{
bool fl = rp - > boi < xf0n | | rp - > boi > xf1n ;
cmSample_t env = fl ? p - > xfadeFunc [ rp - > xfi ] : 1 ;
p - > outV [ i ] = ( ( 1.0 - env ) * rp - > bV [ rp - > boi ] ) + ( env * rp - > bV [ xfi0 ] ) ;
rp - > boi = ( rp - > boi + 1 ) % rp - > bii ;
xfi0 = ( xfi0 + 1 ) % rp - > bii ;
}
}
if ( y ! = NULL )
memcpy ( y , p - > outV , xn * sizeof ( cmSample_t ) ) ;
return cmOkRC ;
}
*/
cmRC_t cmLoopRecordFree ( cmLoopRecord * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp ! = NULL & & * pp ! = NULL )
{
cmLoopRecord * p = * pp ;
if ( ( rc = cmLoopRecordFinal ( p ) ) = = cmOkRC )
{
cmMemPtrFree ( & p - > bufMem ) ;
cmMemPtrFree ( & p - > bufArray ) ;
cmMemPtrFree ( & p - > outV ) ;
cmObjFree ( pp ) ;
}
}
return rc ;
}
cmRC_t cmLoopRecordInit ( cmLoopRecord * p , unsigned procSmpCnt , unsigned maxRecdSmpCnt , unsigned xfadeSmpCnt )
{
cmRC_t rc ;
unsigned i ;
if ( ( rc = cmLoopRecordFinal ( p ) ) ! = cmOkRC )
return rc ;
assert ( xfadeSmpCnt < maxRecdSmpCnt ) ;
p - > maxRecdSmpCnt = maxRecdSmpCnt ;
p - > bufArrayCnt = 2 ;
p - > bufMem = cmMemResizeZ ( cmSample_t , p - > bufMem , ( maxRecdSmpCnt + 3 ) * p - > bufArrayCnt * 2 ) ;
p - > bufArray = cmMemResizeZ ( cmLoopRecdBuf , p - > bufArray , p - > bufArrayCnt ) ;
p - > outV = cmMemResizeZ ( cmSample_t , p - > outV , procSmpCnt ) ;
p - > outN = procSmpCnt ;
p - > procSmpCnt = procSmpCnt ;
p - > xfadeSmpCnt = xfadeSmpCnt ;
p - > recdBufIdx = 0 ;
p - > playBufIdx = 1 ;
p - > recdFl = false ;
p - > playFl = false ;
for ( i = 0 ; i < p - > bufArrayCnt ; + + i )
{
p - > bufArray [ i ] . xV = p - > bufMem + ( ( 2 * i + 0 ) * ( maxRecdSmpCnt + 3 ) ) + 1 ;
p - > bufArray [ i ] . xN = maxRecdSmpCnt ;
p - > bufArray [ i ] . xii = 0 ;
p - > bufArray [ i ] . wV = p - > bufMem + ( ( 2 * i + 1 ) * ( maxRecdSmpCnt + 3 ) ) ;
}
return rc ;
}
cmRC_t cmLoopRecordFinal ( cmLoopRecord * p )
{ return cmOkRC ; }
void _cmLoopRecordOscExec ( cmLoopRecord * p , cmLoopRecdBuf * rp , cmLoopRecdOsc * op , double ratio )
{
unsigned i ;
for ( i = 0 ; i < p - > outN ; + + i )
{
int xi = floor ( op - > xi ) ;
double vfrac = op - > xi - xi ;
cmSample_t val = _cube_interp ( vfrac , rp - > xV [ xi - 1 ] , rp - > xV [ xi ] , rp - > xV [ xi + 1 ] , rp - > xV [ xi + 2 ] ) ;
int wi = floor ( op - > wi ) ;
double wfrac = op - > wi - wi ;
cmSample_t wnd = rp - > wV [ wi ] + wfrac * ( rp - > wV [ wi + 1 ] - rp - > wV [ wi ] ) ;
wnd = ( wnd - 0.5 ) * op - > u + 0.5 ;
p - > outV [ i ] + = wnd * val ;
op - > wi + = ratio ;
if ( op - > wi > = rp - > xii )
{
op - > wi - = rp - > xii ;
op - > u * = - 1.0 ;
}
op - > xi + = ratio ;
if ( op - > xi > = rp - > xii )
op - > xi - = rp - > xii ;
}
}
//
// a b c d e f g h i - osc 0 sample value
// 0.0 .25 0.5 .75 1.0 1.0 .75 0.5 .25 - osc 0 window value
//
// e f g h i a b c d - osc 1 sample value
// 1.0 .75 0.5 .25 0.0 0.0 .25 0.5 .75 - osc 1 window value
//
// Notes:
// 1) The window values transition through zero
// at the loop point.
// 2)
cmRC_t cmLoopRecordExec ( cmLoopRecord * p , const cmSample_t * x , cmSample_t * y , unsigned xn , bool bypassFl , bool recdFl , bool playFl , double ratio , double pgain , double rgain )
{
int i , j ;
assert ( xn < = p - > outN ) ;
if ( bypassFl )
{
memcpy ( p - > outV , x , xn * sizeof ( cmSample_t ) ) ;
if ( y ! = NULL )
{
//memcpy(y,x,xn*sizeof(cmSample_t));
cmVOS_MultVVS ( y , xn , x , pgain ) ;
}
return cmOkRC ;
}
// if the recdFl was enabled
if ( recdFl = = true & & p - > recdFl = = false )
{
p - > recdFl = true ;
p - > bufArray [ p - > recdBufIdx ] . xii = 0 ;
//printf("Recd:on %i\n",p->recdBufIdx);
}
else
// if the recdFl was disabled
if ( recdFl = = false & & p - > recdFl = = true )
{
cmLoopRecdBuf * rp = p - > bufArray + p - > recdBufIdx ;
// arrange the 'wrap-around' samples
rp - > xV [ - 1 ] = rp - > xV [ rp - > xii - 1 ] ;
rp - > xV [ rp - > xii ] = rp - > xV [ 0 ] ;
rp - > xV [ rp - > xii + 1 ] = rp - > xV [ 1 ] ;
// calc the length of the cross-fade
if ( rp - > xii < p - > xfadeSmpCnt * 2 )
rp - > xfN = rp - > xii / 2 ;
else
rp - > xfN = p - > xfadeSmpCnt ;
assert ( rp - > xfN > 0 ) ;
// fill the crossfade window with zeros
cmVOS_Zero ( rp - > wV , rp - > xii ) ;
// fill the last xfN samples in the xfade window vector with a fade-in slope
int xfi = rp - > xii - rp - > xfN ;
for ( j = 0 ; j < rp - > xfN ; + + j )
rp - > wV [ xfi + j ] = ( cmSample_t ) j / rp - > xfN ;
// initialize the oscillators
for ( j = 0 ; j < 2 ; + + j )
{
//
rp - > osc [ j ] . xi = j = = 0 ? 0.0 : rp - > xfN ; // oscillators are p->xfN samples out of phase
rp - > osc [ j ] . u = j = = 0 ? 1.0 : - 1.0 ; // set windows to have opposite polarity
rp - > osc [ j ] . wi = xfi ; // begin window at cross-fade
}
p - > recdFl = false ;
p - > playFl = true ;
p - > playBufIdx = p - > recdBufIdx ;
p - > recdBufIdx = ( p - > recdBufIdx + 1 ) % p - > bufArrayCnt ;
//printf("Recd:off\n");
}
// if recording
if ( p - > recdFl )
{
cmLoopRecdBuf * rp = p - > bufArray + p - > recdBufIdx ;
for ( i = 0 ; i < xn & & rp - > xii < rp - > xN ; + + i , + + rp - > xii )
{
rp - > xV [ rp - > xii ] = x [ i ] ;
}
}
if ( playFl & & p - > bufArray [ p - > playBufIdx ] . xii > 0 )
{
p - > playFl = ! p - > playFl ;
if ( p - > playFl )
{
// reset oscillators to start at the begin of loop
}
}
if ( p - > playFl )
{
cmLoopRecdBuf * rp = p - > bufArray + p - > playBufIdx ;
cmVOS_Zero ( p - > outV , p - > outN ) ;
_cmLoopRecordOscExec ( p , rp , rp - > osc + 0 , ratio ) ;
_cmLoopRecordOscExec ( p , rp , rp - > osc + 1 , ratio ) ;
}
if ( y ! = NULL )
{
//memcpy(y,p->outV,xn*sizeof(cmSample_t));
for ( i = 0 ; i < p - > outN ; + + i )
y [ i ] = ( pgain * x [ i ] ) + ( rgain * p - > outV [ i ] ) ;
}
return cmOkRC ;
}
//=======================================================================================================================
cmGateDetect * cmGateDetectAlloc ( cmCtx * c , cmGateDetect * p , unsigned procSmpCnt , cmReal_t onThreshPct , cmReal_t onThreshDb , cmReal_t offThreshDb )
{
cmGateDetect * op = cmObjAlloc ( cmGateDetect , c , p ) ;
if ( procSmpCnt ! = 0 )
if ( cmGateDetectInit ( op , procSmpCnt , onThreshPct , onThreshDb , offThreshDb ) ! = cmOkRC )
cmGateDetectFree ( & op ) ;
return op ;
}
cmRC_t cmGateDetectFree ( cmGateDetect * * pp )
{
cmRC_t rc ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmGateDetect * p = * pp ;
if ( ( rc = cmGateDetectFinal ( p ) ) ! = cmOkRC )
return rc ;
cmMemPtrFree ( & p - > rmsV ) ;
cmMemPtrFree ( & p - > wndV ) ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmGateDetectInit ( cmGateDetect * p , unsigned procSmpCnt , cmReal_t onThreshPct , cmReal_t onThreshDb , cmReal_t offThreshDb )
{
cmRC_t rc ;
if ( ( rc = cmGateDetectFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > rmsN = 3 ;
p - > rmsV = cmMemResizeZ ( cmSample_t , p - > rmsV , p - > rmsN ) ;
p - > wndN = 9 ;
p - > wndV = cmMemResizeZ ( cmSample_t , p - > wndV , p - > wndN ) ;
p - > rms = 0 ;
p - > durSmpCnt = 0 ;
p - > gateFl = false ;
p - > deltaFl = false ;
p - > onThreshPct = onThreshPct ;
p - > onThreshDb = onThreshDb ;
p - > offThreshDb = offThreshDb ;
return rc ;
}
cmRC_t cmGateDetectFinal ( cmGateDetect * p )
{
return cmOkRC ;
}
cmRC_t cmGateDetectExec22 ( cmGateDetect * p , const cmSample_t * x , unsigned xn )
{
p - > deltaFl = false ;
p - > rms = cmVOS_RMS ( x , xn , xn ) ;
cmReal_t db = p - > rms = = 0 ? - 100 : 20 * log10 ( p - > rms ) ;
//cmSample_t b0 = 0;
cmSample_t b1 = 0 ;
cmVOS_Shift ( p - > rmsV , p - > rmsN , - 1 , p - > rms ) ;
// if gate is on
if ( p - > gateFl )
{
p - > deltaFl = db < p - > offThreshDb ;
}
else
{
//cmVOS_Lsq1(NULL,p->rmsV,p->rmsN,&b0,&b1);
// p->deltaFl = b1 > p->onThreshPct && db > p->onThreshDb;
unsigned i ;
for ( i = 0 ; i < p - > rmsN - 1 ; + + i )
b1 + = p - > rmsV [ i + 1 ] - p - > rmsV [ i ] ;
b1 / = p - > rmsN ;
p - > deltaFl = b1 > p - > onThreshPct & & db > p - > onThreshDb ;
}
if ( p - > deltaFl )
{
p - > gateFl = ! p - > gateFl ;
p - > durSmpCnt = xn ;
}
else
{
p - > durSmpCnt + = xn ;
}
//p->rms = b1;
//printf("%f %f %i %f;\n",db,p->rms,p->deltaFl,b1);
return cmOkRC ;
}
cmRC_t cmGateDetectExec ( cmGateDetect * p , const cmSample_t * x , unsigned xn )
{
p - > deltaFl = false ;
p - > rms = cmVOS_RMS ( x , xn , xn ) ;
cmReal_t db = p - > rms = = 0 ? - 100 : 20 * log10 ( p - > rms ) ;
unsigned i ;
cmSample_t d = 0 ;
// update the rms window
cmVOS_Shift ( p - > rmsV , p - > rmsN , - 1 , p - > rms ) ;
// calc. derr of RMS
for ( i = 0 ; i < p - > rmsN - 1 ; + + i )
d + = p - > rmsV [ i + 1 ] - p - > rmsV [ i ] ;
d / = p - > rmsN ;
// zero negative derr's
d = d < 0 ? 0 : d ;
// update the peak window
cmVOS_Shift ( p - > wndV , p - > wndN , - 1 , d ) ;
p - > mean = cmVOS_Mean ( p - > wndV , p - > wndN ) ;
// if this is an offset
if ( p - > gateFl & & db < p - > offThreshDb )
{
p - > gateFl = false ;
p - > deltaFl = true ;
}
else
// if this may be an onset.
if ( db > p - > onThreshDb )
{
i = p - > wndN - 2 ;
// if previous derr was a peak
if ( p - > wndV [ i ] > 0 & & p - > wndV [ i - 1 ] < p - > wndV [ i ] & & p - > wndV [ i ] > p - > wndV [ i + 1 ] )
{
// and the peak is the largest value in the exteneded window
if ( p - > wndV [ i ] > p - > mean )
{
p - > deltaFl = true ;
p - > gateFl = true ;
p - > durSmpCnt = 0 ;
}
}
}
if ( p - > gateFl )
p - > durSmpCnt + = xn ;
p - > rms = p - > d0 ;
p - > d0 = d ;
return cmOkRC ;
}
//=======================================================================================================================
cmGateDetect2 * cmGateDetectAlloc2 ( cmCtx * c , cmGateDetect2 * p , unsigned procSmpCnt , const cmGateDetectParams * args )
{
cmGateDetect2 * op = cmObjAlloc ( cmGateDetect2 , c , p ) ;
if ( procSmpCnt ! = 0 )
if ( cmGateDetectInit2 ( op , procSmpCnt , args ) ! = cmOkRC )
cmGateDetectFree2 ( & op ) ;
return op ;
}
cmRC_t cmGateDetectFree2 ( cmGateDetect2 * * pp )
{
cmRC_t rc ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmGateDetect2 * p = * pp ;
if ( ( rc = cmGateDetectFinal2 ( p ) ) ! = cmOkRC )
return rc ;
cmMemPtrFree ( & p - > medV ) ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmGateDetectInit2 ( cmGateDetect2 * p , unsigned procSmpCnt , const cmGateDetectParams * args )
{
cmRC_t rc ;
if ( ( rc = cmGateDetectFinal2 ( p ) ) ! = cmOkRC )
return rc ;
unsigned pkCnt = 3 ;
unsigned eleCnt = 3 * args - > medCnt + args - > avgCnt + args - > suprCnt + args - > offCnt + pkCnt ;
unsigned i ;
p - > args = * args ;
p - > medV = cmMemResizeZ ( cmSample_t , p - > medV , eleCnt ) ;
p - > fcofV = p - > medV + args - > medCnt ;
p - > fdlyV = p - > fcofV + args - > medCnt ;
p - > avgV = p - > fdlyV + args - > medCnt ;
p - > suprV = p - > avgV + args - > avgCnt ;
p - > offV = p - > suprV + args - > suprCnt ;
p - > pkV = p - > offV + args - > offCnt ;
assert ( p - > medV + eleCnt = = p - > pkV + pkCnt ) ;
p - > medIdx = 0 ;
p - > avgIdx = 0 ;
p - > suprIdx = args - > suprCnt ;
p - > offIdx = 0 ;
p - > pkFl = false ;
p - > gateFl = false ;
p - > onFl = false ;
p - > offFl = false ;
cmGateDetectSetOnThreshDb2 ( p , args - > onThreshDb ) ;
cmGateDetectSetOnThreshDb2 ( p , args - > offThreshDb ) ;
p - > fcofV [ 0 ] = 1.0 ;
for ( i = 1 ; i < args - > medCnt ; + + i )
{
p - > fcofV [ i ] = ( 1.0 - 1.0 / args - > medCnt ) / ( pow ( 2.0 , i - 1 ) ) ;
//printf("%i %f ",i,p->fcofV[i]);
}
for ( i = 0 ; i < args - > suprCnt ; + + i )
{
p - > suprV [ i ] = 1.0 / pow ( args - > suprCoeff , i + 1.0 ) ;
//printf("%i %f ",i,p->suprV[i]);
}
return rc ;
}
cmRC_t cmGateDetectFinal2 ( cmGateDetect2 * p )
{ return cmOkRC ; }
void _cmGateDetectPickPeak ( cmGateDetect2 * p , cmSample_t input )
{
p - > pkV [ 0 ] = p - > pkV [ 1 ] ;
p - > pkV [ 1 ] = p - > pkV [ 2 ] ;
p - > pkV [ 2 ] = input ;
// if supression is active - apply it to the center pkV[]
if ( p - > suprIdx < p - > args . suprCnt )
{
p - > pkV [ 1 ] - = p - > pkV [ 1 ] * p - > suprV [ p - > suprIdx ] ;
p - > suprIdx = ( p - > suprIdx + 1 ) % p - > args . suprCnt ;
}
p - > sup = p - > pkV [ 1 ] ;
// if the center value in pkV[] is a local peak and above the onset threshold
if ( p - > pkV [ 1 ] > p - > onThresh & & p - > pkV [ 0 ] < p - > pkV [ 1 ] & & p - > pkV [ 1 ] > p - > pkV [ 2 ] )
{
p - > suprIdx = 0 ;
p - > gateFl = true ;
p - > onFl = true ;
}
// update the offset buffer
p - > offV [ p - > offIdx ] = fabs ( p - > pkV [ 2 ] ) ;
p - > offIdx = ( p - > offIdx + 1 ) % p - > args . offCnt ;
// if this is an offset
if ( p - > gateFl = = true & & cmVOS_Mean ( p - > offV , p - > args . offCnt ) < p - > offThresh )
{
p - > gateFl = false ;
p - > offFl = true ;
}
}
void _cmGateDetectPickPeak2 ( cmGateDetect2 * p , cmSample_t input )
{
// if supression is active - apply it to the input
if ( p - > suprIdx < p - > args . suprCnt )
{
input - = input * p - > suprV [ p - > suprIdx ] ;
+ + p - > suprIdx ; // = (p->suprIdx + 1) % p->args.suprCnt;
}
// update the peak buffer
p - > pkV [ 0 ] = p - > pkV [ 1 ] ;
p - > pkV [ 1 ] = p - > pkV [ 2 ] ;
p - > pkV [ 2 ] = input ;
p - > sup = input ;
// if the signal increased above the threshold and is not already waiting for a peak
if ( p - > pkV [ 1 ] < input & & input > p - > onThresh & & p - > pkFl = = false )
{
p - > pkFl = true ;
p - > gateFl = true ;
p - > onFl = true ;
}
// if the center value in pkV[] is a local peak and above the onset threshold ...
if ( p - > gateFl & & p - > pkFl & & p - > pkV [ 0 ] < p - > pkV [ 1 ] & & p - > pkV [ 1 ] > p - > pkV [ 2 ] )
{
p - > suprIdx = 0 ; // ... turn on supression
p - > pkFl = false ; // ... not longer waiting for the peak
}
// update the offset buffer
p - > offV [ p - > offIdx ] = fabs ( input ) ;
p - > offIdx = ( p - > offIdx + 1 ) % p - > args . offCnt ;
// if this is an offset
if ( p - > gateFl = = true & & cmVOS_Mean ( p - > offV , p - > args . offCnt ) < p - > offThresh )
{
p - > gateFl = false ;
p - > offFl = true ;
p - > pkFl = false ;
}
}
cmRC_t cmGateDetectExec2 ( cmGateDetect2 * p , const cmSample_t * x , unsigned xn )
{
p - > rms = cmVOS_RMS ( x , xn , xn ) ; // take RMS of incoming window
p - > medV [ p - > medIdx ] = p - > rms ; // input RMS to median filter
p - > medIdx = ( p - > medIdx + 1 ) % p - > args . medCnt ;
p - > med = cmVOS_Median ( p - > medV , p - > args . medCnt ) ; // calc the median
p - > dif = cmMax ( 0 , p - > rms - p - > med ) ; // dif = half_rect( rms - med )
p - > avgV [ p - > avgIdx ] = p - > dif ; // input dif to avg filter
p - > avgIdx = ( p - > avgIdx + 1 ) % p - > args . avgCnt ;
p - > avg = cmVOS_Mean ( p - > avgV , p - > args . avgCnt ) ; // calc the avg
p - > ons = p - > dif - p - > avg ; // ons = dif - avg
cmVOS_Shift ( p - > fdlyV , p - > args . medCnt , 1 , p - > ons ) ;
p - > flt = cmVOS_MultSumVV ( p - > fdlyV , p - > fcofV , p - > args . medCnt ) ;
p - > offFl = false ;
p - > onFl = false ;
_cmGateDetectPickPeak2 ( p , p - > flt ) ;
return cmOkRC ;
}
void _cmGateDetectSetDb2 ( cmGateDetect2 * p , cmReal_t db , cmReal_t * dbPtr )
{
* dbPtr = pow ( 10.0 , db / 20.0 ) ;
}
void cmGateDetectSetOnThreshDb2 ( cmGateDetect2 * p , cmReal_t db )
{ _cmGateDetectSetDb2 ( p , db , & p - > onThresh ) ; }
void cmGateDetectSetOffThreshDb2 ( cmGateDetect2 * p , cmReal_t db )
{ _cmGateDetectSetDb2 ( p , db , & p - > offThresh ) ; }
//=======================================================================================================================
cmAutoGain * cmAutoGainAlloc ( cmCtx * c , cmAutoGain * p , unsigned procSmpCnt , cmReal_t srate , cmReal_t hopMs , unsigned chCnt , const cmGateDetectParams * gd_args )
{
cmAutoGain * op = cmObjAlloc ( cmAutoGain , c , p ) ;
op - > sbp = cmShiftBufAlloc ( c , NULL , 0 , 0 , 0 ) ;
op - > gdp = cmGateDetectAlloc2 ( c , NULL , 0 , NULL ) ;
if ( procSmpCnt ! = 0 )
if ( cmAutoGainInit ( op , procSmpCnt , srate , hopMs , chCnt , gd_args ) ! = cmOkRC )
cmAutoGainFree ( & op ) ;
return op ;
}
cmRC_t cmAutoGainFree ( cmAutoGain * * pp )
{
cmRC_t rc ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmAutoGain * p = * pp ;
if ( ( rc = cmAutoGainFinal ( p ) ) ! = cmOkRC )
return rc ;
cmMemPtrFree ( & p - > chArray ) ;
p - > chCnt = 0 ;
cmShiftBufFree ( & p - > sbp ) ;
cmGateDetectFree2 ( & p - > gdp ) ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmAutoGainInit ( cmAutoGain * p , unsigned procSmpCnt , cmReal_t srate , cmReal_t hopMs , unsigned chCnt , const cmGateDetectParams * gd_args )
{
unsigned i ;
cmRC_t rc ;
if ( ( rc = cmAutoGainFinal ( p ) ) ! = cmOkRC )
return rc ;
unsigned hopSmpCnt = ( unsigned ) floor ( srate * hopMs / 1000.0 ) ;
unsigned wndSmpCnt = hopSmpCnt * gd_args - > medCnt ;
cmShiftBufInit ( p - > sbp , procSmpCnt , wndSmpCnt , hopSmpCnt ) ;
cmGateDetectInit2 ( p - > gdp , procSmpCnt , gd_args ) ;
p - > chCnt = chCnt ;
p - > chArray = cmMemResizeZ ( cmAutoGainCh , p - > chArray , p - > chCnt ) ;
p - > chp = NULL ;
for ( i = 0 ; i < p - > chCnt ; + + i )
p - > chArray [ i ] . id = cmInvalidIdx ;
return rc ;
}
cmRC_t cmAutoGainFinal ( cmAutoGain * p )
{ return cmOkRC ; }
void _cmAutoGainChannelFinish ( cmAutoGain * p )
{
if ( p - > chp ! = NULL )
{
if ( p - > gateMax ! = 0 )
{
p - > gateSum + = p - > gateMax ;
p - > gateCnt + = 1 ;
}
p - > chp - > gateMaxAvg = p - > gateCnt = = 0 ? 0.0 : p - > gateSum / p - > gateCnt ;
}
}
cmRC_t cmAutoGainStartCh ( cmAutoGain * p , unsigned id )
{
cmReal_t rmsMax = 1.0 ;
unsigned i ;
if ( id = = cmInvalidIdx )
return cmOkRC ;
// do intermediate channel calculations on the last selected channel
if ( p - > chp ! = NULL )
_cmAutoGainChannelFinish ( p ) ;
// if 'id' has already been used
// then select the associated channel as the current channel
// otherwise select the next avail channel as the current channel
for ( i = 0 ; i < p - > chCnt ; + + i )
if ( p - > chArray [ i ] . id = = id | | p - > chArray [ i ] . id = = cmInvalidId )
{
p - > chp = p - > chArray + i ;
break ;
}
if ( p - > chp = = NULL )
return cmCtxRtCondition ( & p - > obj , cmArgAssertRC , " All channels are in use. " ) ;
p - > chp - > id = id ;
p - > gateCnt = 0 ;
p - > gateSum = 0 ;
p - > gateMax = 0 ;
p - > minRms = rmsMax ;
return cmOkRC ;
}
// The goal of this process is to locate the max RMS value during
// the duration of each note. When all the notes have been processed for
// a given channel the average maximum value for all of the notes is
// then calculated.
cmRC_t cmAutoGainProcCh ( cmAutoGain * p , const cmSample_t * x , unsigned xn )
{
// shift the new samples into the shift buffer
while ( cmShiftBufExec ( p - > sbp , x , xn ) )
{
// update the gate detector
cmGateDetectExec2 ( p - > gdp , p - > sbp - > outV , p - > sbp - > outN ) ;
// write the output matrix file
//if( _cmPuWriteMtxFile(p,segFl) != kOkPuRC )
// goto errLabel;
// if this frame is an RMS minimum or onset or offset
// then select it as a possible segment end.
// Note that for onsets this will effectively force the end to
// come after the onset because the onset will not be an energy minimum
// relative to subsequent frames.
if ( p - > gdp - > rms < p - > minRms | | p - > gdp - > onFl | | p - > gdp - > offFl )
{
p - > minRms = p - > gdp - > rms ;
// count onsets
if ( p - > gdp - > onFl )
+ + p - > chp - > onCnt ;
// count offsets
if ( p - > gdp - > offFl )
{
+ + p - > chp - > offCnt ;
// update the gate sum and count
p - > gateSum + = p - > gateMax ;
p - > gateCnt + = 1 ;
p - > gateMax = 0 ;
}
}
// track the max RMS value during this gate
if ( p - > gdp - > gateFl & & p - > gdp - > rms > p - > gateMax )
p - > gateMax = p - > gdp - > rms ;
}
return cmOkRC ;
}
cmRC_t cmAutoGainCalcGains ( cmAutoGain * p )
{
unsigned i ;
cmReal_t avg = 0 ;
if ( p - > chCnt = = 0 )
return cmOkRC ;
// verify that all channels were input
for ( i = 0 ; i < p - > chCnt ; + + i )
if ( p - > chArray [ i ] . id = = cmInvalidId )
break ;
if ( i ! = p - > chCnt )
return cmCtxRtCondition ( & p - > obj , cmArgAssertRC , " All channels must be set prior to calculating gains. " ) ;
// process the last channel
_cmAutoGainChannelFinish ( p ) ;
// p->chp isn't used again unless we restart the indidual channel processing
p - > chp = NULL ;
for ( i = 0 ; i < p - > chCnt ; + + i )
avg + = p - > chArray [ i ] . gateMaxAvg ;
avg / = p - > chCnt ;
for ( i = 0 ; i < p - > chCnt ; + + i )
{
cmReal_t d = p - > chArray [ i ] . gateMaxAvg = = 0 ? 1.0 : p - > chArray [ i ] . gateMaxAvg ;
p - > chArray [ i ] . gain = avg / d ;
}
return cmOkRC ;
}
void cmAutoGainPrint ( cmAutoGain * p , cmRpt_t * rpt )
{
unsigned i = 0 ;
for ( i = 0 ; i < p - > chCnt ; + + i )
{
cmAutoGainCh * chp = p - > chArray + i ;
cmRptPrintf ( rpt , " midi:%3i %4s on:%i off:%i avg:%5.5f gain:%5.5f \n " ,
chp - > id ,
cmStringNullGuard ( cmMidiToSciPitch ( chp - > id , NULL , 0 ) ) ,
chp - > onCnt , chp - > offCnt , chp - > gateMaxAvg , chp - > gain ) ;
}
}
//==========================================================================================================================================
cmChCfg * cmChCfgAlloc ( cmCtx * c , cmChCfg * p , cmCtx_t * ctx , const cmChar_t * fn )
{
cmChCfg * op = cmObjAlloc ( cmChCfg , c , p ) ;
if ( fn ! = NULL )
if ( cmChCfgInit ( op , ctx , fn ) ! = cmOkRC )
cmChCfgFree ( & op ) ;
return op ;
}
cmRC_t cmChCfgFree ( cmChCfg * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmChCfg * p = * pp ;
if ( ( rc = cmChCfgFinal ( p ) ) ! = cmOkRC )
return rc ;
cmMemPtrFree ( & p - > chArray ) ;
cmFsFreeFn ( p - > fn ) ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
enum { kChColIdx , kSsiColIdx , kPitchColIdx , kMidiColIdx , kGainColIdx , kNsFlColIdx , kCdFlColIdx , kColCnt } ;
cmRC_t cmChCfgInit ( cmChCfg * p , cmCtx_t * ctx , const cmChar_t * fn )
{
cmRC_t rc = cmOkRC ;
unsigned i , j ;
const cmChar_t * ch_array_label = " ch_array " ;
typedef struct
{
unsigned id ;
const char * label ;
} map_t ;
map_t map [ kColCnt ] =
{
{ kChColIdx , " ch " } ,
{ kSsiColIdx , " ssi " } ,
{ kPitchColIdx , " pitch " } ,
{ kMidiColIdx , " midi " } ,
{ kGainColIdx , " gain " } ,
{ kNsFlColIdx , " ns_fl " } ,
{ kCdFlColIdx , " cd_fl " }
} ;
if ( ( rc = cmChCfgFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > fn = cmFsMakeFn ( cmFsPrefsDir ( ) , fn , NULL , NULL ) ;
// read the channel cfg JSON file
if ( cmJsonInitializeFromFile ( & p - > jsH , p - > fn , ctx ) ! = kOkJsRC )
{
rc = cmCtxRtCondition ( & p - > obj , cmSubSysFailRC , " JSON initialize failed on '%s'. " , cmStringNullGuard ( p - > fn ) ) ;
goto errLabel ;
}
// locate the 'ch_array' node
if ( ( p - > cap = cmJsonFindValue ( p - > jsH , ch_array_label , cmJsonRoot ( p - > jsH ) , kArrayTId ) ) = = NULL )
{
rc = cmCtxRtCondition ( & p - > obj , cmSubSysFailRC , " Unable to locate the JSON element '%s'. " , ch_array_label ) ;
goto errLabel ;
}
unsigned rowCnt = cmJsonChildCount ( p - > cap ) ;
// there must be at least 1 label row and 1 ch row
if ( rowCnt < 2 )
{
rc = cmCtxRtCondition ( & p - > obj , cmSubSysFailRC , " The 'ch_array' appears to be empty. " ) ;
goto errLabel ;
}
// allocate the ch array
p - > chCnt = rowCnt - 1 ;
p - > chArray = cmMemResizeZ ( cmChCfgCh , p - > chArray , p - > chCnt ) ;
p - > nsChCnt = 0 ;
// read each row
for ( i = 0 ; i < rowCnt ; + + i )
{
// get the array on row i
const cmJsonNode_t * np = cmJsonArrayElementC ( p - > cap , i ) ;
// all row arrays contain 'kColCnt' elements
if ( cmJsonChildCount ( np ) ! = kColCnt )
{
rc = cmCtxRtCondition ( & p - > obj , cmSubSysFailRC , " All 'ch_array' element at index %i does not contain %i values. " , i , kColCnt ) ;
goto errLabel ;
}
// for each columns
for ( j = 0 ; j < kColCnt ; + + j )
{
const cmJsonNode_t * cp = cmJsonArrayElementC ( np , j ) ;
// the first row contains the labels ....
if ( i = = 0 )
{
// verify that the column format is as expected
if ( cmJsonIsString ( cp ) = = false | | strcmp ( cp - > u . stringVal , map [ j ] . label ) ! = 0 )
{
rc = cmCtxRtCondition ( & p - > obj , cmSubSysFailRC , " The first row column at index %i of 'ch_array' should be the label '%s'. " , map [ j ] . label ) ;
goto errLabel ;
}
}
else // ... other rows contain Ch Cfg's.
{
cmChCfgCh * chp = p - > chArray + ( i - 1 ) ;
switch ( j )
{
case kChColIdx : rc = cmJsonUIntValue ( cp , & chp - > ch ) ; break ;
case kSsiColIdx : rc = cmJsonUIntValue ( cp , & chp - > ssi ) ; break ;
case kPitchColIdx : rc = cmJsonStringValue ( cp , & chp - > pitchStr ) ; break ;
case kMidiColIdx : rc = cmJsonUIntValue ( cp , & chp - > midi ) ; break ;
case kGainColIdx : rc = cmJsonRealValue ( cp , & chp - > gain ) ; break ;
case kNsFlColIdx :
rc = cmJsonBoolValue ( cp , & chp - > nsFl ) ;
if ( chp - > nsFl )
+ + p - > nsChCnt ;
break ;
case kCdFlColIdx : rc = cmJsonBoolValue ( cp , & chp - > cdFl ) ; break ;
default :
{ assert ( 0 ) ; }
}
if ( rc ! = kOkJsRC )
{
rc = cmCtxRtCondition ( & p - > obj , cmSubSysFailRC , " An error occurred while reading the '%s' column on row index:%i . " , map [ j ] . label , i ) ;
goto errLabel ;
}
}
}
}
errLabel :
return rc ;
}
cmRC_t cmChCfgFinal ( cmChCfg * p )
{
cmRC_t rc = cmOkRC ;
if ( cmJsonIsValid ( p - > jsH ) )
if ( cmJsonFinalize ( & p - > jsH ) ! = kOkJsRC )
rc = cmCtxRtCondition ( & p - > obj , cmSubSysFailRC , " JSON finalize failed. " ) ;
return rc ;
}
cmRC_t cmChCfgWrite ( cmChCfg * p )
{
cmRC_t rc = cmOkRC ;
unsigned i ;
for ( i = 0 ; i < p - > chCnt ; + + i )
{
// get the array on row i+1
cmJsonNode_t * np = cmJsonArrayElement ( p - > cap , i + 1 ) ;
// get the ele in the 'gainColIdx' column
np = cmJsonArrayElement ( np , kGainColIdx ) ;
assert ( cmJsonIsReal ( np ) ) ;
np - > u . realVal = p - > chArray [ i ] . gain ;
}
if ( cmJsonWrite ( p - > jsH , cmJsonRoot ( p - > jsH ) , p - > fn ) ! = kOkJsRC )
rc = cmCtxRtCondition ( & p - > obj , cmSubSysFailRC , " The JSON Channel Cfg file write failed on '%s'. " , cmStringNullGuard ( p - > fn ) ) ;
return rc ;
}
void cmChCfgPrint ( cmChCfg * p , cmRpt_t * rpt )
{
unsigned i ;
for ( i = 0 ; i < p - > chCnt ; + + i )
{
const cmChCfgCh * chp = p - > chArray + i ;
cmRptPrintf ( rpt , " %3i %3i %4s %3i %5.5f \n " , chp - > ch , chp - > ssi , chp - > pitchStr , chp - > midi , chp - > gain ) ;
}
}
unsigned cmChCfgChannelCount ( cmCtx_t * ctx , const cmChar_t * fn , unsigned * nsChCntPtr )
{
cmChCfg * ccp ;
unsigned chCnt = 0 ;
cmCtx * c = cmCtxAlloc ( NULL , & ctx - > rpt , cmLHeapNullHandle , cmSymTblNullHandle ) ;
if ( ( ccp = cmChCfgAlloc ( c , NULL , ctx , fn ) ) = = NULL )
goto errLabel ;
chCnt = ccp - > chCnt ;
if ( nsChCntPtr ! = NULL )
* nsChCntPtr = ccp - > nsChCnt ;
cmChCfgFree ( & ccp ) ;
errLabel :
cmCtxFree ( & c ) ;
return chCnt ;
}
unsigned cmChCfgChannelIndex ( cmCtx_t * ctx , const cmChar_t * fn , unsigned chIdx )
{
cmChCfg * ccp ;
unsigned retChIdx = - 1 ;
cmCtx * c = cmCtxAlloc ( NULL , & ctx - > rpt , cmLHeapNullHandle , cmSymTblNullHandle ) ;
if ( ( ccp = cmChCfgAlloc ( c , NULL , ctx , fn ) ) = = NULL )
goto errLabel ;
if ( chIdx > = ccp - > chCnt )
cmCtxRtCondition ( & ccp - > obj , cmArgAssertRC , " The channel index %i is not less than the channel count %i when querying '%s'. " , chIdx , ccp - > chCnt , cmStringNullGuard ( ccp - > fn ) ) ;
else
retChIdx = ccp - > chArray [ chIdx ] . ch ;
cmChCfgFree ( & ccp ) ;
errLabel :
cmCtxFree ( & c ) ;
return retChIdx ;
}
//==========================================================================================================================================
cmChordDetect * cmChordDetectAlloc ( cmCtx * c , cmChordDetect * p , cmReal_t srate , unsigned chCnt , cmReal_t maxTimeSpanMs , unsigned minNotesPerChord )
{
cmChordDetect * op = cmObjAlloc ( cmChordDetect , c , p ) ;
if ( srate ! = 0 )
if ( cmChordDetectInit ( op , srate , chCnt , maxTimeSpanMs , minNotesPerChord ) ! = cmOkRC )
cmChordDetectFree ( & op ) ;
return op ;
}
cmRC_t cmChordDetectFree ( cmChordDetect * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmChordDetect * p = * pp ;
if ( ( rc = cmChordDetectFinal ( p ) ) ! = cmOkRC )
return rc ;
cmMemPtrFree ( & p - > chArray ) ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmChordDetectInit ( cmChordDetect * p , cmReal_t srate , unsigned chCnt , cmReal_t maxTimeSpanMs , unsigned minNotesPerChord )
{
cmRC_t rc ;
if ( ( rc = cmChordDetectFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > chArray = cmMemResizeZ ( cmChordDetectCh , p - > chArray , chCnt ) ;
p - > chCnt = chCnt ;
p - > minNotesPerChord = minNotesPerChord ;
p - > detectFl = false ;
p - > timeSmp = 0 ;
p - > srate = srate ;
cmChordDetectSetSpanMs ( p , maxTimeSpanMs ) ;
unsigned i ;
for ( i = 0 ; i < p - > chCnt ; + + i )
p - > chArray [ i ] . readyFl = true ;
return rc ;
}
cmRC_t cmChordDetectFinal ( cmChordDetect * P )
{ return cmOkRC ; }
cmRC_t cmChordDetectExec ( cmChordDetect * p , unsigned procSmpCnt , const bool * gateV , const cmReal_t * rmsV , unsigned chCnt )
{
assert ( chCnt = = p - > chCnt ) ;
chCnt = cmMin ( chCnt , p - > chCnt ) ;
cmRC_t rc = cmOkRC ;
unsigned candCnt = 0 ;
cmChordDetectCh * oldCand = NULL ;
unsigned i ;
p - > detectFl = false ;
p - > timeSmp + = procSmpCnt ;
// update the readyFl's and candFl's based on onset and offsets
for ( i = 0 ; i < chCnt ; + + i )
{
cmChordDetectCh * cp = p - > chArray + i ;
// if onset and the channel is ready
if ( gateV [ i ] & & cp - > gateFl = = false & & cp - > readyFl )
{
cp - > candSmpTime = p - > timeSmp ;
cp - > candRMS = rmsV [ i ] ;
cp - > candFl = true ;
}
// if offset then this channel is again ready to be a candidate
if ( gateV [ i ] = = false & & cp - > gateFl )
{
cp - > readyFl = true ;
}
cp - > gateFl = gateV [ i ] ;
cp - > chordFl = false ;
}
// check if we have enough channels to form a chord and
// expire channels that have passed the max time out value
for ( i = 0 ; i < chCnt ; + + i )
{
cmChordDetectCh * cp = p - > chArray + i ;
unsigned ageSmpCnt = p - > timeSmp - cp - > candSmpTime ;
// if this is a candiate ...
if ( cp - > candFl )
{
// ... that has not expired
if ( ageSmpCnt < = p - > maxTimeSpanSmpCnt )
{
// track the oldest candidate
if ( oldCand = = NULL | | cp - > candSmpTime < oldCand - > candSmpTime )
oldCand = cp ;
+ + candCnt ;
}
else // ... that has expired
{
cp - > candFl = false ;
}
}
}
// If we have enough notes and the oldest candiate will expire on the next cycle.
// (By waiting for the oldest note to almost expire we gather the maximum number of
// possible notes given the time duration. If we remove this condition then
// we will tend to trigger chord detection with fewer notes.)
if ( oldCand ! = NULL )
{
unsigned nextAge = p - > timeSmp + procSmpCnt - oldCand - > candSmpTime ;
if ( candCnt > = p - > minNotesPerChord & & nextAge > = p - > maxTimeSpanSmpCnt )
{
for ( i = 0 ; i < chCnt ; + + i )
{
cmChordDetectCh * cp = p - > chArray + i ;
if ( cp - > candFl )
{
cp - > chordFl = true ;
cp - > candFl = false ;
}
}
p - > detectFl = true ;
}
}
return rc ;
}
cmRC_t cmChordDetectSetSpanMs ( cmChordDetect * p , cmReal_t maxTimeSpanMs )
{
p - > maxTimeSpanSmpCnt = floor ( maxTimeSpanMs * p - > srate / 1000.0 ) ;
return cmOkRC ;
}
//==========================================================================================================================================
cmXfader * cmXfaderAlloc ( cmCtx * c , cmXfader * p , cmReal_t srate , unsigned chCnt , cmReal_t fadeTimeMs )
{
cmXfader * op = cmObjAlloc ( cmXfader , c , p ) ;
if ( srate ! = 0 )
if ( cmXfaderInit ( op , srate , chCnt , fadeTimeMs ) ! = cmOkRC )
cmXfaderFree ( & op ) ;
return op ;
}
cmRC_t cmXfaderFree ( cmXfader * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmXfader * p = * pp ;
if ( ( rc = cmXfaderFinal ( p ) ) ! = cmOkRC )
return rc ;
cmMemPtrFree ( & p - > chArray ) ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmXfaderInit ( cmXfader * p , cmReal_t srate , unsigned chCnt , cmReal_t fadeTimeMs )
{
cmRC_t rc ;
if ( ( rc = cmXfaderFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > chCnt = chCnt ;
p - > chArray = cmMemResizeZ ( cmXfaderCh , p - > chArray , p - > chCnt ) ;
p - > srate = srate ;
p - > gateFl = false ;
p - > onFl = false ;
p - > offFl = false ;
cmXfaderSetXfadeTime ( p , fadeTimeMs ) ;
return rc ;
}
cmRC_t cmXfaderFinal ( cmXfader * p )
{ return cmOkRC ; }
cmRC_t cmXfaderExec ( cmXfader * p , unsigned procSmpCnt , const bool * chGateV , unsigned chCnt )
{
assert ( chCnt = = p - > chCnt ) ;
chCnt = cmMin ( chCnt , p - > chCnt ) ;
bool gateFl = false ;
// gain change associated with procSmpCnt
2013-08-13 22:21:42 +00:00
//cmReal_t dgain = (cmReal_t)procSmpCnt / p->fadeSmpCnt;
cmReal_t i_dgain = ( cmReal_t ) procSmpCnt / p - > fadeInSmpCnt ;
cmReal_t o_dgain = ( cmReal_t ) procSmpCnt / p - > fadeOutSmpCnt ;
2012-10-30 03:52:39 +00:00
unsigned i ;
// for each channel
for ( i = 0 ; i < chCnt ; + + i )
{
cmXfaderCh * cp = p - > chArray + i ;
2012-12-18 22:46:07 +00:00
cp - > onFl = false ;
cp - > offFl = false ;
2012-10-30 03:52:39 +00:00
if ( chGateV ! = NULL )
2012-12-18 22:46:07 +00:00
{
cp - > onFl = chGateV [ i ] & & ! cp - > gateFl ; // notice fade-in transition begin
2012-10-30 03:52:39 +00:00
cp - > gateFl = chGateV [ i ] ;
2012-12-18 22:46:07 +00:00
}
cmReal_t g = cp - > gain ;
2012-10-30 03:52:39 +00:00
if ( cp - > gateFl )
2013-11-02 01:30:40 +00:00
{
2013-08-13 22:21:42 +00:00
cp - > gain = cmMin ( cp - > gain + i_dgain , 1.0 ) ;
2013-11-03 19:50:03 +00:00
cp - > ep_gain = sqrt ( 0.5 - 0.5 * cos ( 3.14159 * cp - > gain ) ) ;
2013-11-02 01:30:40 +00:00
}
2012-10-30 03:52:39 +00:00
else
2012-12-18 22:46:07 +00:00
{
2013-08-13 22:21:42 +00:00
cp - > gain = cmMax ( cp - > gain - o_dgain , 0.0 ) ;
2013-11-02 01:30:40 +00:00
cp - > ep_gain = sqrt ( 0.5 - 0.5 * cos ( 3.14159 * cp - > gain ) ) ;
2012-12-18 22:46:07 +00:00
cp - > offFl = g > 0.0 & & cp - > gain = = 0.0 ; // notice fade-out transition end
}
2012-10-30 03:52:39 +00:00
if ( cp - > gain ! = 0.0 )
gateFl = true ;
2012-12-18 22:46:07 +00:00
2012-10-30 03:52:39 +00:00
}
p - > onFl = false ;
p - > offFl = false ;
if ( p - > gateFl = = false & & gateFl = = true )
p - > onFl = true ;
else
if ( p - > gateFl = = true & & gateFl = = false )
p - > offFl = true ;
p - > gateFl = gateFl ;
return cmOkRC ;
}
cmRC_t cmXfaderExecAudio ( cmXfader * p , unsigned procSmpCnt , const bool * gateV , unsigned chCnt , const cmSample_t * x [ ] , cmSample_t * y )
{
cmRC_t rc ;
assert ( chCnt = = p - > chCnt ) ;
chCnt = cmMin ( chCnt , p - > chCnt ) ;
if ( ( rc = cmXfaderExec ( p , procSmpCnt , gateV , chCnt ) ) ! = cmOkRC )
return rc ;
unsigned i ;
for ( i = 0 ; i < chCnt ; + + i )
if ( x [ i ] ! = NULL )
2013-11-02 01:30:40 +00:00
cmVOS_MultVaVS ( y , procSmpCnt , x [ i ] , p - > chArray [ i ] . ep_gain ) ;
2012-10-30 03:52:39 +00:00
return rc ;
}
void cmXfaderSetXfadeTime ( cmXfader * p , cmReal_t fadeTimeMs )
{
p - > fadeSmpCnt = floor ( fadeTimeMs * p - > srate / 1000.0 ) ;
2013-08-13 22:21:42 +00:00
p - > fadeInSmpCnt = p - > fadeSmpCnt ;
p - > fadeOutSmpCnt = p - > fadeSmpCnt ;
}
void cmXfaderSetXfadeInTime ( cmXfader * p , cmReal_t fadeTimeMs )
{
p - > fadeInSmpCnt = floor ( fadeTimeMs * p - > srate / 1000.0 ) ;
}
void cmXfaderSetXfadeOutTime ( cmXfader * p , cmReal_t fadeTimeMs )
{
p - > fadeOutSmpCnt = floor ( fadeTimeMs * p - > srate / 1000.0 ) ;
2012-10-30 03:52:39 +00:00
}
void cmXfaderSelectOne ( cmXfader * p , unsigned chIdx )
{
unsigned i = 0 ;
for ( i = 0 ; i < p - > chCnt ; + + i )
p - > chArray [ i ] . gateFl = i = = chIdx ;
}
void cmXfaderAllOff ( cmXfader * p )
{
unsigned i = 0 ;
for ( i = 0 ; i < p - > chCnt ; + + i )
p - > chArray [ i ] . gateFl = false ;
}
2013-11-03 19:50:03 +00:00
void cmXfaderJumpToDestinationGain ( cmXfader * p )
{
unsigned i = 0 ;
for ( i = 0 ; i < p - > chCnt ; + + i )
{
if ( p - > chArray [ i ] . gateFl )
p - > chArray [ i ] . gain = 1.0 ;
else
p - > chArray [ i ] . gain = 0.0 ;
p - > chArray [ i ] . onFl = false ;
p - > chArray [ i ] . offFl = false ;
p - > chArray [ i ] . ep_gain = p - > chArray [ i ] . gain ;
}
}
2012-10-30 03:52:39 +00:00
//==========================================================================================================================================
cmFader * cmFaderAlloc ( cmCtx * c , cmFader * p , cmReal_t srate , cmReal_t fadeTimeMs )
{
cmFader * op = cmObjAlloc ( cmFader , c , p ) ;
if ( srate ! = 0 )
if ( cmFaderInit ( op , srate , fadeTimeMs ) ! = cmOkRC )
cmFaderFree ( & op ) ;
return op ;
}
cmRC_t cmFaderFree ( cmFader * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmFader * p = * pp ;
if ( ( rc = cmFaderFinal ( p ) ) ! = cmOkRC )
return rc ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmFaderInit ( cmFader * p , cmReal_t srate , cmReal_t fadeTimeMs )
{
cmRC_t rc ;
if ( ( rc = cmFaderFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > srate = srate ;
p - > gain = 0.0 ;
cmFaderSetFadeTime ( p , fadeTimeMs ) ;
return rc ;
}
cmRC_t cmFaderFinal ( cmFader * p )
{ return cmOkRC ; }
cmRC_t cmFaderExec ( cmFader * p , unsigned procSmpCnt , bool gateFl , bool mixFl , const cmSample_t * x , cmSample_t * y )
{
cmReal_t d = ( gateFl ? 1.0 : - 1.0 ) * procSmpCnt / p - > fadeSmpCnt ;
// TODO: add fade curves
p - > gain = cmMin ( 1.0 , cmMax ( 0.0 , p - > gain + d ) ) ;
if ( x ! = NULL & & y ! = NULL )
{
if ( mixFl )
cmVOS_MultVaVS ( y , procSmpCnt , x , 1.0 /* p->gain*/ ) ;
else
cmVOS_MultVVS ( y , procSmpCnt , x , p - > gain ) ;
}
return cmOkRC ;
}
void cmFaderSetFadeTime ( cmFader * p , cmReal_t fadeTimeMs )
{
p - > fadeSmpCnt = ( unsigned ) floor ( fadeTimeMs * p - > srate / 1000.0 ) ;
}
//=======================================================================================================================
cmCombFilt * cmCombFiltAlloc ( cmCtx * c , cmCombFilt * p , cmReal_t srate , bool feedbackFl , cmReal_t minHz , cmReal_t alpha , cmReal_t hz , bool bypassFl )
{
cmCombFilt * op = cmObjAlloc ( cmCombFilt , c , p ) ;
op - > idp = cmIDelayAlloc ( c , NULL , 0 , 0 , NULL , NULL , NULL , 0 ) ;
if ( srate ! = 0 )
if ( cmCombFiltInit ( op , srate , feedbackFl , minHz , alpha , hz , bypassFl ) ! = cmOkRC )
cmCombFiltFree ( & op ) ;
return op ;
}
cmRC_t cmCombFiltFree ( cmCombFilt * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmCombFilt * p = * pp ;
if ( ( rc = cmCombFiltFinal ( p ) ) ! = cmOkRC )
return rc ;
cmMemFree ( p - > a ) ;
cmMemFree ( p - > b ) ;
cmMemFree ( p - > d ) ;
cmIDelayFree ( & p - > idp ) ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmCombFiltInit ( cmCombFilt * p , cmReal_t srate , bool feedbackFl , cmReal_t minHz , cmReal_t alpha , cmReal_t hz , bool bypassFl )
{
cmRC_t rc ;
if ( ( rc = cmCombFiltFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > feedbackFl = feedbackFl ;
p - > dN = cmMax ( 1 , floor ( srate / minHz ) ) ;
p - > d = cmMemResizeZ ( cmReal_t , p - > d , p - > dN + 1 ) ;
p - > a = cmMemResizeZ ( cmReal_t , p - > a , p - > dN ) ;
p - > b = cmMemResizeZ ( cmReal_t , p - > b , p - > dN ) ;
p - > dn = 1 ;
p - > alpha = alpha ;
p - > srate = srate ;
p - > bypassFl = bypassFl ;
cmReal_t tapMs = p - > dn * 1000.0 / srate ;
cmReal_t tapFf = 1.0 ;
cmReal_t tapFb = 1.0 ;
cmIDelayInit ( p - > idp , srate , p - > dN * 1000.0 / srate , & tapMs , & tapFf , & tapFb , 1 ) ;
cmCombFiltSetHz ( p , hz ) ;
return rc ;
}
cmRC_t cmCombFiltFinal ( cmCombFilt * p )
{ return cmOkRC ; }
cmRC_t cmCombFiltExec ( cmCombFilt * p , const cmSample_t * x , cmSample_t * y , unsigned n )
{
if ( y = = NULL )
return cmOkRC ;
if ( x = = NULL )
{
cmVOS_Zero ( y , n ) ;
return cmOkRC ;
}
if ( p - > bypassFl )
cmVOS_Copy ( y , n , x ) ;
else
cmIDelayExec ( p - > idp , x , y , n ) ;
return cmOkRC ;
}
void cmCombFiltSetAlpha ( cmCombFilt * p , cmReal_t alpha )
{
p - > b0 = 1.0 - fabs ( alpha ) ; // normalization coeff (could be applied directly to the input or output)
p - > a [ p - > dn - 1 ] = - alpha ;
p - > alpha = alpha ;
p - > idp - > tff [ 0 ] = p - > b0 ;
p - > idp - > tfb [ 0 ] = - alpha ;
}
cmRC_t cmCombFiltSetHz ( cmCombFilt * p , cmReal_t hz )
{
if ( hz < p - > minHz )
return cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " The comb filter frequency %f Hz is invalid given the minimum frequency setting of %f Hz. " , hz , p - > minHz ) ;
// clear the current alpha
p - > a [ p - > dn - 1 ] = 0 ;
// change location of alpha in p->a[]
p - > dn = cmMin ( p - > dN - 1 , cmMax ( 1 , floor ( p - > srate / hz ) ) ) ;
p - > hz = hz ;
cmCombFiltSetAlpha ( p , p - > alpha ) ; // the location of filter coeff changed - reset it here
cmIDelaySetTapMs ( p - > idp , 0 , p - > dn * 1000.0 / p - > srate ) ;
return cmOkRC ;
}
//=======================================================================================================================
cmDcFilt * cmDcFiltAlloc ( cmCtx * c , cmDcFilt * p , bool bypassFl )
{
cmDcFilt * op = cmObjAlloc ( cmDcFilt , c , p ) ;
if ( cmDcFiltInit ( op , bypassFl ) ! = cmOkRC )
cmDcFiltFree ( & op ) ;
return op ;
}
cmRC_t cmDcFiltFree ( cmDcFilt * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmDcFilt * p = * pp ;
if ( ( rc = cmDcFiltFinal ( p ) ) ! = cmOkRC )
return rc ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmDcFiltInit ( cmDcFilt * p , bool bypassFl )
{
cmRC_t rc ;
if ( ( rc = cmDcFiltFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > b0 = 1 ;
p - > b [ 0 ] = - 1 ;
p - > a [ 0 ] = - 0.999 ;
p - > d [ 0 ] = 0 ;
p - > d [ 1 ] = 0 ;
p - > bypassFl = bypassFl ;
return rc ;
}
cmRC_t cmDcFiltFinal ( cmDcFilt * p )
{ return cmOkRC ; }
cmRC_t cmDcFiltExec ( cmDcFilt * p , const cmSample_t * x , cmSample_t * y , unsigned n )
{
if ( p - > bypassFl )
cmVOS_Copy ( y , n , x ) ;
else
cmVOS_Filter ( y , n , x , n , p - > b0 , p - > b , p - > a , p - > d , 1 ) ;
return cmOkRC ;
}
//=======================================================================================================================
cmIDelay * cmIDelayAlloc ( cmCtx * c , cmIDelay * p , cmReal_t srate , cmReal_t maxDelayMs , const cmReal_t * tapMs , const cmReal_t * tapFfGain , const cmReal_t * tapFbGain , unsigned tapCnt )
{
cmIDelay * op = cmObjAlloc ( cmIDelay , c , p ) ;
if ( srate ! = 0 )
if ( cmIDelayInit ( op , srate , maxDelayMs , tapMs , tapFfGain , tapFbGain , tapCnt ) ! = cmOkRC )
cmIDelayFree ( & op ) ;
return op ;
}
cmRC_t cmIDelayFree ( cmIDelay * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmIDelay * p = * pp ;
if ( ( rc = cmIDelayFinal ( p ) ) ! = cmOkRC )
return rc ;
cmMemFree ( p - > m ) ;
cmMemFree ( p - > ti ) ;
cmMemFree ( p - > tff ) ;
cmMemFree ( p - > tfb ) ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmIDelayInit ( cmIDelay * p , cmReal_t srate , cmReal_t maxDelayMs , const cmReal_t * tapMs , const cmReal_t * tapFfGain , const cmReal_t * tapFbGain , unsigned tapCnt )
{
cmRC_t rc ;
if ( ( rc = cmIDelayFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > dn = ( unsigned ) floor ( maxDelayMs * srate / 1000.0 ) ;
p - > mn = p - > dn + 3 ;
p - > m = cmMemResizeZ ( cmSample_t , p - > m , p - > mn ) ;
p - > d = p - > m + 1 ;
p - > tn = tapCnt ;
p - > ti = cmMemResize ( cmReal_t , p - > ti , tapCnt ) ;
p - > tff = cmMemResize ( cmReal_t , p - > tff , tapCnt ) ;
p - > tfb = cmMemResize ( cmReal_t , p - > tfb , tapCnt ) ;
p - > srate = srate ;
unsigned i ;
for ( i = 0 ; i < tapCnt ; + + i )
{
cmIDelaySetTapMs ( p , i , tapMs [ i ] ) ;
p - > tff [ i ] = tapFfGain [ i ] ;
p - > tfb [ i ] = tapFbGain [ i ] ;
}
return rc ;
}
cmRC_t cmIDelayFinal ( cmIDelay * p )
{ return cmOkRC ; }
cmRC_t cmIDelayExec ( cmIDelay * p , const cmSample_t * x , cmSample_t * y , unsigned n )
{
unsigned i , j ;
for ( i = 0 ; i < n ; + + i )
{
cmSample_t fb = 0 ;
y [ i ] = 0 ;
// calculate the output sample
for ( j = 0 ; j < p - > tn ; + + j )
{
// get tap_j
cmReal_t tfi = p - > ii - p - > ti [ j ] ;
// mod it into 0:dn-1
while ( tfi < 0 )
tfi + = p - > dn ;
int ti = floor ( tfi ) ;
cmReal_t tf = tfi - ti ;
cmSample_t v = _lin_interp ( tf , p - > d [ ti ] , p - > d [ ti + 1 ] ) ; // _cube_interp(tf,p->d[ti-1],p->d[ti],p->d[ti+1],p->d[ti+2]);
y [ i ] + = p - > tff [ j ] * v ;
fb + = p - > tfb [ j ] * v ;
}
// insert incoming sample
p - > d [ p - > ii ] = x [ i ] + fb ;
// advance the delay line
+ + p - > ii ;
// handle beg/end delay sample duplication
// -1 0 1 2 .... dn-1 dn dn+1
// a b c a b c
// The samples at position -1 0 1 must be duplicated
// at positions dn-1,dn,dn+1 so that output sample calculation
// does not need to deal with buffer wrap-around issues.
if ( p - > ii = = 1 )
p - > d [ p - > dn ] = x [ i ] ;
else
if ( p - > ii = = 2 )
p - > d [ p - > dn + 1 ] = x [ i ] ;
else
if ( p - > ii = = p - > dn )
{
p - > d [ - 1 ] = x [ i ] ;
p - > ii = 0 ;
}
}
return cmOkRC ;
}
cmRC_t cmIDelaySetTapMs ( cmIDelay * p , unsigned tapIdx , cmReal_t tapMs )
{
cmRC_t rc = cmOkRC ;
if ( tapIdx < p - > tn )
p - > ti [ tapIdx ] = tapMs * p - > srate / 1000.0 ;
else
rc = cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " Tap index %i is out of range 0 - %i. " , tapIdx , p - > tn - 1 ) ;
return rc ;
}
//=======================================================================================================================
cmGroupSel * cmGroupSelAlloc ( cmCtx * c , cmGroupSel * p , unsigned chCnt , unsigned groupCnt , unsigned chsPerGroup )
{
cmGroupSel * op = cmObjAlloc ( cmGroupSel , c , p ) ;
if ( chCnt ! = 0 )
if ( cmGroupSelInit ( op , chCnt , groupCnt , chsPerGroup ) ! = cmOkRC )
cmGroupSelFree ( & op ) ;
return op ;
}
cmRC_t cmGroupSelFree ( cmGroupSel * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmGroupSel * p = * pp ;
if ( ( rc = cmGroupSelFinal ( p ) ) ! = cmOkRC )
return rc ;
cmMemFree ( p - > groupArray ) ;
cmMemFree ( p - > chArray ) ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmGroupSelInit ( cmGroupSel * p , unsigned chCnt , unsigned groupCnt , unsigned chsPerGroup )
{
cmRC_t rc ;
if ( ( rc = cmGroupSelFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > chArray = cmMemResizeZ ( cmGroupSelCh , p - > chArray , chCnt ) ;
p - > groupArray = cmMemResizeZ ( cmGroupSelGrp , p - > groupArray , chCnt ) ;
p - > chCnt = chCnt ;
p - > groupCnt = groupCnt ;
p - > chsPerGroup = chsPerGroup ;
unsigned i ;
for ( i = 0 ; i < p - > chCnt ; + + i )
p - > chArray [ i ] . groupIdx = cmInvalidIdx ;
for ( i = 0 ; i < p - > groupCnt ; + + i )
{
p - > groupArray [ i ] . chIdxArray = cmMemAllocZ ( unsigned , p - > chCnt ) ;
p - > groupArray [ i ] . chIdxCnt = 0 ;
}
return rc ;
}
cmRC_t cmGroupSelFinal ( cmGroupSel * p )
{
unsigned i ;
for ( i = 0 ; i < p - > groupCnt ; + + i )
cmMemFree ( p - > groupArray [ i ] . chIdxArray ) ;
return cmOkRC ;
}
cmRC_t cmGroupSetChannelGate ( cmGroupSel * p , unsigned chIdx , bool gateFl )
{
assert ( chIdx < p - > chCnt ) ;
cmGroupSelCh * cp = p - > chArray + chIdx ;
if ( gateFl = = true & & cp - > gateFl = = false )
{
cp - > readyFl = true ;
}
if ( gateFl = = false & & cp - > gateFl = = true )
{
cp - > offsetFl = true ;
cp - > readyFl = false ;
}
cp - > gateFl = gateFl ;
//printf("ch:%i gate:%i ready:%i offset:%i grp:%i\n",chIdx,cp->gateFl,cp->readyFl,cp->offsetFl,cp->groupIdx);
return cmOkRC ;
}
cmRC_t cmGroupSetChannelRMS ( cmGroupSel * p , unsigned chIdx , cmReal_t rms )
{
assert ( chIdx < p - > chCnt ) ;
cmGroupSelCh * cp = p - > chArray + chIdx ;
cp - > rms = rms ;
return cmOkRC ;
}
cmRC_t cmGroupSelExec ( cmGroupSel * p )
{
unsigned i , j ;
unsigned availGroupCnt = 0 ;
p - > chsPerGroup = cmMin ( p - > chsPerGroup , p - > chCnt ) ;
p - > updateFl = false ;
// clear the create and release flags on each group
// and get the count of available groups
for ( i = 0 ; i < p - > groupCnt ; + + i )
{
cmGroupSelGrp * gp = p - > groupArray + i ;
gp - > createFl = false ;
if ( gp - > releaseFl )
{
// clear the groupIdx from the channels assigned to this released group
for ( j = 0 ; j < gp - > chIdxCnt ; + + j )
p - > chArray [ gp - > chIdxArray [ j ] ] . groupIdx = cmInvalidIdx ;
gp - > releaseFl = false ;
gp - > chIdxCnt = 0 ;
}
if ( gp - > chIdxCnt = = 0 )
+ + availGroupCnt ;
}
// count the number of ready but unassigned ch's and
// release any groups which contain a channel with a gate offset
unsigned readyChCnt = 0 ;
for ( i = 0 ; i < p - > chCnt ; + + i )
{
cmGroupSelCh * cp = p - > chArray + i ;
// count the number of channels that are ready to be assigned
if ( cp - > readyFl )
+ + readyChCnt ;
// if this channel offset and it had been assigned to a group
// then mark its group for release
if ( cp - > offsetFl & & cp - > groupIdx ! = cmInvalidIdx )
{
p - > groupArray [ cp - > groupIdx ] . releaseFl = true ;
p - > updateFl = true ;
}
}
// assign ready ch's to available groups
while ( readyChCnt > p - > chsPerGroup & & availGroupCnt > 0 )
{
unsigned groupIdx ;
// locate an available group
for ( groupIdx = 0 ; groupIdx < p - > groupCnt ; + + groupIdx )
if ( p - > groupArray [ groupIdx ] . chIdxCnt = = 0 )
break ;
if ( groupIdx = = p - > groupCnt )
break ;
- - availGroupCnt ;
cmGroupSelGrp * gp = p - > groupArray + groupIdx ;
printf ( " gs - group:%i chs: " , groupIdx ) ;
// assign chsPerGroup ch's to this group
for ( i = 0 ; i < p - > chCnt & & gp - > chIdxCnt < p - > chsPerGroup ; + + i )
if ( p - > chArray [ i ] . readyFl )
{
p - > chArray [ i ] . readyFl = false ; // this ch is no longer ready because it is assigned
p - > chArray [ i ] . groupIdx = groupIdx ; // set this ch's group idx
gp - > chIdxArray [ gp - > chIdxCnt ] = i ; // assign channel to group
gp - > chIdxCnt + = 1 ; // update the group ch count
gp - > createFl = true ;
p - > updateFl = true ;
- - readyChCnt ;
printf ( " %i " , i ) ;
}
printf ( " \n " ) ;
}
return cmOkRC ;
}
//=======================================================================================================================
cmAudioNofM * cmAudioNofMAlloc ( cmCtx * c , cmAudioNofM * p , cmReal_t srate , unsigned inChCnt , unsigned outChCnt , cmReal_t fadeTimeMs )
{
cmAudioNofM * op = cmObjAlloc ( cmAudioNofM , c , p ) ;
if ( srate ! = 0 )
if ( cmAudioNofMInit ( op , srate , inChCnt , outChCnt , fadeTimeMs ) ! = cmOkRC )
cmAudioNofMFree ( & op ) ;
return op ;
}
cmRC_t cmAudioNofMFree ( cmAudioNofM * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmAudioNofM * p = * pp ;
if ( ( rc = cmAudioNofMFinal ( p ) ) ! = cmOkRC )
return rc ;
cmMemFree ( p - > outArray ) ;
cmMemFree ( p - > inArray ) ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmAudioNofMInit ( cmAudioNofM * p , cmReal_t srate , unsigned iChCnt , unsigned oChCnt , cmReal_t fadeTimeMs )
{
cmRC_t rc ;
if ( ( rc = cmAudioNofMFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > iChCnt = iChCnt ;
p - > inArray = cmMemResizeZ ( cmAudioNofM_In , p - > inArray , iChCnt ) ;
p - > oChCnt = oChCnt ;
p - > outArray = cmMemResizeZ ( cmAudioNofM_Out , p - > outArray , oChCnt ) ;
p - > nxtOutChIdx = 0 ;
unsigned i ;
for ( i = 0 ; i < p - > iChCnt ; + + i )
{
p - > inArray [ i ] . fader = cmFaderAlloc ( p - > obj . ctx , NULL , srate , fadeTimeMs ) ;
p - > inArray [ i ] . outChIdx = cmInvalidIdx ;
}
cmAudioNofMSetFadeMs ( p , fadeTimeMs ) ;
return rc ;
}
cmRC_t cmAudioNofMFinal ( cmAudioNofM * p )
{
unsigned i ;
for ( i = 0 ; i < p - > iChCnt ; + + i )
cmFaderFree ( & p - > inArray [ i ] . fader ) ;
return cmOkRC ;
}
cmRC_t cmAudioNofMSetChannelGate ( cmAudioNofM * p , unsigned inChIdx , bool gateFl )
{
assert ( inChIdx < p - > iChCnt ) ;
cmAudioNofM_In * ip = p - > inArray + inChIdx ;
if ( ip - > gateFl & & gateFl = = false )
ip - > offsetFl = true ;
if ( ip - > gateFl = = false & & gateFl )
ip - > onsetFl = true ;
ip - > gateFl = gateFl ;
printf ( " nom: %p ch:%i gate:%i on:%i off:%i \n " , p , inChIdx , ip - > gateFl , ip - > offsetFl , ip - > onsetFl ) ;
return cmOkRC ;
}
cmRC_t cmAudioNofMExec ( cmAudioNofM * p , const cmSample_t * x [ ] , unsigned inChCnt , cmSample_t * y [ ] , unsigned outChCnt , unsigned n )
{
assert ( inChCnt = = p - > iChCnt & & outChCnt = = p - > oChCnt ) ;
unsigned i ;
// for each output channel
for ( i = 0 ; i < p - > oChCnt ; + + i )
{
cmAudioNofM_Out * op = p - > outArray + i ;
cmAudioNofM_In * ip = op - > list ;
cmAudioNofM_In * pp = NULL ;
// for each input assigned to this output chanenl
while ( ip ! = NULL )
{
// if the channel is no longer active
if ( ip - > offsetFl & & ip - > fader - > gain = = 0.0 )
{
printf ( " nom: off - out:%i \n " , ip - > outChIdx ) ;
// remove it from the output channels list
ip - > offsetFl = false ;
ip - > outChIdx = cmInvalidIdx ;
if ( pp = = NULL )
op - > list = ip - > link ;
else
pp - > link = ip - > link ;
}
pp = ip ;
ip = ip - > link ;
}
}
// for each input channel
for ( i = 0 ; i < inChCnt ; + + i )
{
cmAudioNofM_In * ip = p - > inArray + i ;
// if this channel is starting
if ( ip - > onsetFl = = true & & ip - > offsetFl = = false )
{
// assign it to the next output channel
ip - > onsetFl = false ;
ip - > outChIdx = p - > nxtOutChIdx ;
ip - > link = p - > outArray [ ip - > outChIdx ] . list ;
p - > outArray [ ip - > outChIdx ] . list = ip ;
p - > nxtOutChIdx = ( p - > nxtOutChIdx + 1 ) % p - > oChCnt ;
printf ( " nom: on - in:%i out:%i \n " , i , ip - > outChIdx ) ;
}
// if this channel is active - then mix its input
if ( ip - > outChIdx ! = cmInvalidIdx )
{
cmFaderExec ( ip - > fader , n , ip - > gateFl , true , x [ i ] , y [ ip - > outChIdx ] ) ;
}
}
return cmOkRC ;
}
cmRC_t cmAudioNofMSetFadeMs ( cmAudioNofM * p , cmReal_t fadeTimeMs )
{
unsigned i ;
for ( i = 0 ; i < p - > iChCnt ; + + i )
cmFaderSetFadeTime ( p - > inArray [ i ] . fader , fadeTimeMs ) ;
return cmOkRC ;
}
//=======================================================================================================================
cmAdsr * cmAdsrAlloc ( cmCtx * c , cmAdsr * p , cmReal_t srate , bool trigFl , cmReal_t minL , cmReal_t dlyMs , cmReal_t atkMs , cmReal_t atkL , cmReal_t dcyMs , cmReal_t susMs , cmReal_t susL , cmReal_t rlsMs )
{
cmAdsr * op = cmObjAlloc ( cmAdsr , c , p ) ;
if ( srate ! = 0 )
if ( cmAdsrInit ( op , srate , trigFl , minL , dlyMs , atkMs , atkL , dcyMs , susMs , susL , rlsMs ) ! = cmOkRC )
cmAdsrFree ( & op ) ;
return op ;
}
cmRC_t cmAdsrFree ( cmAdsr * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmAdsr * p = * pp ;
if ( ( rc = cmAdsrFinal ( p ) ) ! = cmOkRC )
return rc ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmAdsrInit ( cmAdsr * p , cmReal_t srate , bool trigFl , cmReal_t minL , cmReal_t dlyMs , cmReal_t atkMs , cmReal_t atkL , cmReal_t dcyMs , cmReal_t susMs , cmReal_t susL , cmReal_t rlsMs )
{
cmRC_t rc ;
if ( ( rc = cmAdsrFinal ( p ) ) ! = cmOkRC )
return rc ;
// this is a limitation of the design - the design should be replaced with one
// which increments/decrements the level until it reaches a limit instead of calculating
// durations
//assert(atkL>=0 && minL>=0);
p - > srate = srate ;
p - > trigModeFl = trigFl ;
p - > levelMin = minL ;
p - > scaleDur = 1.0 ;
cmAdsrSetTime ( p , dlyMs , kDlyAdsrId ) ;
cmAdsrSetTime ( p , atkMs , kAtkAdsrId ) ;
cmAdsrSetTime ( p , dcyMs , kDcyAdsrId ) ;
cmAdsrSetTime ( p , susMs , kSusAdsrId ) ;
cmAdsrSetTime ( p , rlsMs , kRlsAdsrId ) ;
cmAdsrSetLevel ( p , atkL , kAtkAdsrId ) ;
cmAdsrSetLevel ( p , susL , kSusAdsrId ) ;
p - > state = kDoneAdsrId ;
p - > durSmp = 0 ;
p - > level = p - > levelMin ;
p - > gateFl = false ;
p - > atkDurSmp = 0 ;
p - > rlsDurSmp = 0 ;
return cmOkRC ;
}
cmRC_t cmAdsrFinal ( cmAdsr * p )
{ return cmOkRC ; }
cmReal_t cmAdsrExec ( cmAdsr * p , unsigned procSmpCnt , bool gateFl , cmReal_t tscale , cmReal_t ascale )
{
double scaleAmp = ascale ;
double scaleDur = tscale ;
// if onset
if ( p - > gateFl = = false & & gateFl = = true )
{
p - > scaleDur = scaleDur = = 0 ? 1.0 : fabs ( scaleDur ) ;
//printf("sd:%f %f\n",scaleDur,scaleAmp);
switch ( p - > state )
{
case kDlyAdsrId :
// if in delay mode when the re-attack occurs don't do anything
break ;
case kAtkAdsrId :
case kDcyAdsrId :
case kSusAdsrId :
case kRlsAdsrId :
// if the atk level == 0 then fall through to kDoneAdsrId
if ( p - > actAtkLevel ! = 0 )
{
// re-attak mode:
// Scale the attack time to the current level relative to the attack level.
// In general this will result in a decrease in the attack duration.
p - > atkDurSmp = cmMax ( 1 , floor ( p - > atkSmp * ( p - > actAtkLevel - p - > level ) / p - > actAtkLevel ) ) ;
p - > atkBegLevel = p - > level ;
p - > durSmp = 0 ;
p - > state = kAtkAdsrId ;
p - > actAtkLevel = p - > atkLevel * scaleAmp ;
p - > actSusLevel = p - > susLevel * scaleAmp ;
break ;
}
case kDoneAdsrId :
p - > atkBegLevel = p - > levelMin ;
p - > atkDurSmp = p - > atkSmp ;
p - > state = p - > dlySmp = = 0 ? kAtkAdsrId : kDlyAdsrId ;
p - > durSmp = 0 ;
p - > actAtkLevel = p - > atkLevel * scaleAmp ;
p - > actSusLevel = p - > susLevel * scaleAmp ;
break ;
default :
{ assert ( 0 ) ; }
}
}
// if an offset occurred and we are not in trigger mode - then go into release mode
if ( p - > trigModeFl = = false & & p - > gateFl = = true & & gateFl = = false )
{
switch ( p - > state )
{
case kDlyAdsrId :
case kAtkAdsrId :
case kDcyAdsrId :
case kSusAdsrId :
if ( p - > actSusLevel = = 0 )
p - > state = kDoneAdsrId ;
else
{
// scale the release time to the current level relative to the sustain level
p - > rlsDurSmp = cmMax ( 1 , floor ( p - > rlsSmp * p - > level / p - > actSusLevel ) ) ;
p - > rlsLevel = p - > level ;
p - > durSmp = 0 ;
}
break ;
case kRlsAdsrId :
case kDoneAdsrId :
// nothing to do
break ;
default :
{ assert ( 0 ) ; }
}
p - > state = kRlsAdsrId ;
}
p - > gateFl = gateFl ;
switch ( p - > state )
{
case kDlyAdsrId :
p - > level = p - > levelMin ;
if ( p - > durSmp > = p - > dlySmp )
{
p - > state = kAtkAdsrId ;
p - > durSmp = 0 ;
if ( p - > trigModeFl )
{
p - > atkBegLevel = p - > level ;
p - > atkDurSmp = p - > atkSmp ;
}
}
break ;
case kAtkAdsrId :
if ( p - > atkDurSmp ! = 0 )
p - > level = p - > atkBegLevel + ( p - > actAtkLevel - p - > atkBegLevel ) * cmMin ( p - > durSmp , p - > atkDurSmp ) / p - > atkDurSmp ;
if ( p - > durSmp > = p - > atkDurSmp | | p - > atkDurSmp = = 0 )
{
p - > state = kDcyAdsrId ;
p - > durSmp = 0 ;
}
break ;
case kDcyAdsrId :
if ( p - > dcySmp ! = 0 )
p - > level = p - > actAtkLevel - ( ( p - > actAtkLevel - p - > actSusLevel ) * cmMin ( p - > durSmp , p - > dcySmp ) / p - > dcySmp ) ;
if ( p - > durSmp > = p - > dcySmp | | p - > dcySmp = = 0 )
{
p - > state = kSusAdsrId ;
p - > durSmp = 0 ;
}
break ;
case kSusAdsrId :
p - > level = p - > actSusLevel ;
if ( p - > trigModeFl = = true & & p - > durSmp > = p - > susSmp )
{
p - > state = kRlsAdsrId ;
p - > durSmp = 0 ;
p - > rlsLevel = p - > level ;
p - > rlsDurSmp = p - > rlsSmp ;
}
break ;
case kRlsAdsrId :
if ( p - > rlsDurSmp ! = 0 )
p - > level = p - > rlsLevel - ( ( p - > rlsLevel - p - > levelMin ) * cmMin ( p - > durSmp , p - > rlsDurSmp ) / p - > rlsDurSmp ) ;
if ( p - > durSmp > = p - > rlsDurSmp | | p - > rlsDurSmp = = 0 )
{
p - > state = kDoneAdsrId ;
p - > durSmp = 0 ;
}
break ;
case kDoneAdsrId :
p - > level = p - > levelMin ;
break ;
default :
{ assert ( 0 ) ; }
}
p - > durSmp + = floor ( procSmpCnt / p - > scaleDur ) ;
return p - > level ;
}
void _cmAdsrSetTime ( cmAdsr * p , cmReal_t ms , int * smpPtr )
{
* smpPtr = cmMax ( 1 , floor ( p - > srate * ms / 1000.0 ) ) ;
}
void cmAdsrSetTime ( cmAdsr * p , cmReal_t ms , unsigned id )
{
switch ( id )
{
case kDlyAdsrId :
_cmAdsrSetTime ( p , ms , & p - > dlySmp ) ;
break ;
case kAtkAdsrId :
_cmAdsrSetTime ( p , ms , & p - > atkSmp ) ;
break ;
case kDcyAdsrId :
_cmAdsrSetTime ( p , ms , & p - > dcySmp ) ;
break ;
case kSusAdsrId :
_cmAdsrSetTime ( p , ms , & p - > susSmp ) ;
break ;
case kRlsAdsrId :
_cmAdsrSetTime ( p , ms , & p - > rlsSmp ) ;
break ;
default :
{ assert ( 0 ) ; }
}
}
void cmAdsrSetLevel ( cmAdsr * p , cmReal_t level , unsigned id )
{
switch ( id )
{
case kDlyAdsrId :
p - > levelMin = level ;
break ;
case kAtkAdsrId :
p - > atkLevel = level ;
p - > actAtkLevel = p - > atkLevel ;
break ;
case kSusAdsrId :
p - > susLevel = level ;
p - > actSusLevel = p - > susLevel ;
break ;
default :
{ assert ( 0 ) ; }
}
}
void cmAdsrReport ( cmAdsr * p , cmRpt_t * rpt )
{
cmRptPrintf ( rpt , " state:%i gate:%i phs:%i d:%i a:%i (%i) d:%i s:%i r:%i (%i) - min:%f atk:%f \n " , p - > state , p - > gateFl , p - > durSmp , p - > dlySmp , p - > atkSmp , p - > atkDurSmp , p - > dcySmp , p - > susSmp , p - > rlsSmp , p - > rlsDurSmp , p - > levelMin , p - > atkLevel ) ;
}
//=======================================================================================================================
cmCompressor * cmCompressorAlloc ( cmCtx * c , cmCompressor * p , cmReal_t srate , unsigned procSmpCnt , cmReal_t inGain , cmReal_t rmsWndMaxMs , cmReal_t rmsWndMs , cmReal_t threshDb , cmReal_t ratio_num , cmReal_t atkMs , cmReal_t rlsMs , cmReal_t outGain , bool bypassFl )
{
cmCompressor * op = cmObjAlloc ( cmCompressor , c , p ) ;
if ( srate ! = 0 )
if ( cmCompressorInit ( op , srate , procSmpCnt , inGain , rmsWndMaxMs , rmsWndMs , threshDb , ratio_num , atkMs , rlsMs , outGain , bypassFl ) ! = cmOkRC )
cmCompressorFree ( & op ) ;
return op ;
}
cmRC_t cmCompressorFree ( cmCompressor * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmCompressor * p = * pp ;
if ( ( rc = cmCompressorFinal ( p ) ) ! = cmOkRC )
return rc ;
cmMemFree ( p - > rmsWnd ) ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmCompressorInit ( cmCompressor * p , cmReal_t srate , unsigned procSmpCnt , cmReal_t inGain , cmReal_t rmsWndMaxMs , cmReal_t rmsWndMs , cmReal_t threshDb , cmReal_t ratio_num , cmReal_t atkMs , cmReal_t rlsMs , cmReal_t outGain , bool bypassFl )
{
cmRC_t rc ;
if ( ( rc = cmCompressorFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > srate = srate ;
p - > procSmpCnt = procSmpCnt ;
p - > threshDb = threshDb ;
p - > ratio_num = ratio_num ;
cmCompressorSetAttackMs ( p , atkMs ) ;
cmCompressorSetReleaseMs ( p , rlsMs ) ;
p - > inGain = inGain ;
p - > outGain = outGain ;
p - > bypassFl = bypassFl ;
p - > rmsWndAllocCnt = cmMax ( 1 , ( unsigned ) floor ( rmsWndMaxMs * srate / ( 1000.0 * procSmpCnt ) ) ) ;
p - > rmsWnd = cmMemResizeZ ( cmSample_t , p - > rmsWnd , p - > rmsWndAllocCnt ) ;
cmCompressorSetRmsWndMs ( p , rmsWndMs ) ;
p - > rmsWndIdx = 0 ;
p - > state = kRlsCompId ;
p - > timeConstDb = 10.0 ;
p - > accumDb = p - > threshDb ;
return rc ;
}
cmRC_t cmCompressorFinal ( cmCompressor * p )
{ return cmOkRC ; }
/*
The ratio determines to what degree a signal above the threshold is reduced .
Given a 2 : 1 ratio , a signal 2 dB above the threshold will be reduced to 1 db above the threshold .
Given a 4 : 1 ratio , a signal 2 dB above the threshold will be reduced to 0.25 db above the threshold .
Gain_reduction_db = ( thresh - signal ) / ratio_numerator ( difference between the threshold and signal level after reduction )
Gain Coeff = 10 ^ ( gain_reduction_db / 20 ) ;
Total_reduction_db = signal - threshold + Gain_reduc_db
( total change in signal level )
The attack can be viewed as beginning at the threshold and moving to the peak
over some period of time . In linear terms this will go from 1.0 to the max gain
reductions . In this case we step from thresh to peak at a fixed rate in dB
based on the attack time .
Db : thresh - [ thesh : peak ] / ratio_num
Linear : pow ( 10 , ( thresh - [ thesh : peak ] / ratio_num ) / 20 ) ;
During attacks p - > accumDb increments toward the p - > pkDb .
During release p - > accumDb decrements toward the threshold .
( thresh - accumDb ) / ratio_num gives the signal level which will be achieved
if this value is converted to linear form and applied as a gain coeff .
See compressor . m
*/
cmRC_t cmCompressorExec ( cmCompressor * p , const cmSample_t * x , cmSample_t * y , unsigned n )
{
cmSample_t xx [ n ] ;
cmVOS_MultVVS ( xx , n , x , p - > inGain ) ; // apply input gain
p - > rmsWnd [ p - > rmsWndIdx ] = cmVOS_RMS ( xx , n , n ) ; // calc and store signal RMS
p - > rmsWndIdx = ( p - > rmsWndIdx + 1 ) % p - > rmsWndCnt ; // advance the RMS storage buffer
cmReal_t rmsLin = cmVOS_Sum ( p - > rmsWnd , p - > rmsWndCnt ) / p - > rmsWndCnt ; // calc avg RMS
cmReal_t rmsDb = cmMax ( - 100.0 , 20 * log10 ( cmMax ( 0.00001 , rmsLin ) ) ) ; // convert avg RMS to dB
rmsDb + = 100.0 ;
// if the compressor is bypassed
if ( p - > bypassFl )
{
cmVOS_Copy ( y , n , x ) ; // copy through - with no input gain
return cmOkRC ;
}
// if the signal is above the threshold
if ( rmsDb < = p - > threshDb )
p - > state = kRlsCompId ;
else
{
if ( rmsDb > p - > pkDb )
p - > pkDb = rmsDb ;
p - > state = kAtkCompId ;
}
switch ( p - > state )
{
case kAtkCompId :
p - > accumDb = cmMin ( p - > pkDb , p - > accumDb + p - > timeConstDb * n / p - > atkSmp ) ;
break ;
case kRlsCompId :
p - > accumDb = cmMax ( p - > threshDb , p - > accumDb - p - > timeConstDb * n / p - > rlsSmp ) ;
break ;
}
p - > gain = pow ( 10.0 , ( p - > threshDb - p - > accumDb ) / ( p - > ratio_num * 20.0 ) ) ;
cmVOS_MultVVS ( y , n , xx , p - > gain * p - > outGain ) ;
return cmOkRC ;
}
void _cmCompressorSetMs ( cmCompressor * p , cmReal_t ms , unsigned * smpPtr )
{ * smpPtr = cmMax ( 1 , ( unsigned ) floor ( ms * p - > srate / 1000.0 ) ) ; }
void cmCompressorSetAttackMs ( cmCompressor * p , cmReal_t ms )
{ _cmCompressorSetMs ( p , ms , & p - > atkSmp ) ; }
void cmCompressorSetReleaseMs ( cmCompressor * p , cmReal_t ms )
{ _cmCompressorSetMs ( p , ms , & p - > rlsSmp ) ; }
void cmCompressorSetThreshDb ( cmCompressor * p , cmReal_t threshDb )
{ p - > threshDb = cmMax ( 0.0 , 100 + threshDb ) ; }
void cmCompressorSetRmsWndMs ( cmCompressor * p , cmReal_t ms )
{
p - > rmsWndCnt = cmMax ( 1 , ( unsigned ) floor ( ms * p - > srate / ( 1000.0 * p - > procSmpCnt ) ) ) ;
// do not allow rmsWndCnt to exceed rmsWndAllocCnt
if ( p - > rmsWndCnt > p - > rmsWndAllocCnt )
p - > rmsWndCnt = p - > rmsWndAllocCnt ;
}
//=======================================================================================================================
cmBiQuadEq * cmBiQuadEqAlloc ( cmCtx * c , cmBiQuadEq * p , cmReal_t srate , unsigned mode , cmReal_t f0Hz , cmReal_t Q , cmReal_t gainDb , bool bypassFl )
{
cmBiQuadEq * op = cmObjAlloc ( cmBiQuadEq , c , p ) ;
if ( srate ! = 0 )
if ( cmBiQuadEqInit ( op , srate , mode , f0Hz , Q , gainDb , bypassFl ) ! = cmOkRC )
cmBiQuadEqFree ( & op ) ;
return op ;
}
cmRC_t cmBiQuadEqFree ( cmBiQuadEq * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmBiQuadEq * p = * pp ;
if ( ( rc = cmBiQuadEqFinal ( p ) ) ! = cmOkRC )
return rc ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
void _cmBiQuadEqInit ( cmBiQuadEq * p )
{
cmReal_t w0 = 2 * M_PI * p - > f0Hz / p - > srate ;
cmReal_t cos_w0 = cos ( w0 ) ;
cmReal_t alpha = sin ( w0 ) / ( 2 * p - > Q ) ;
if ( p - > mode = = kLowShelfBqId | | p - > mode = = kHighShelfBqId )
{
cmReal_t c = p - > mode = = kLowShelfBqId ? 1.0 : - 1.0 ;
cmReal_t A = pow ( 10.0 , p - > gainDb / 40.0 ) ;
cmReal_t B = 2 * sqrt ( A ) * alpha ;
p - > b [ 0 ] = A * ( ( A + 1 ) - c * ( A - 1 ) * cos_w0 + B ) ;
p - > b [ 1 ] = c * 2 * A * ( ( A - 1 ) - c * ( A + 1 ) * cos_w0 ) ;
p - > b [ 2 ] = A * ( ( A + 1 ) - c * ( A - 1 ) * cos_w0 - B ) ;
p - > a [ 0 ] = ( A + 1 ) + c * ( A - 1 ) * cos_w0 + B ;
p - > a [ 1 ] = - c * 2 * ( ( A - 1 ) + c * ( A + 1 ) * cos_w0 ) ;
p - > a [ 2 ] = ( A + 1 ) + c * ( A - 1 ) * cos_w0 - B ;
}
else
{
if ( p - > mode ! = kPeakBqId )
{
p - > a [ 0 ] = 1 + alpha ;
p - > a [ 1 ] = - 2 * cos_w0 ;
p - > a [ 2 ] = 1 - alpha ;
}
switch ( p - > mode )
{
case kLpfBqId :
case kHpFBqId :
{
cmReal_t c = p - > mode = = kLpfBqId ? 1.0 : - 1.0 ;
p - > b [ 0 ] = ( 1 - c * cos_w0 ) / 2 ;
p - > b [ 1 ] = c * ( 1 - c * cos_w0 ) ;
p - > b [ 2 ] = ( 1 - c * cos_w0 ) / 2 ;
}
break ;
case kBpfBqId :
p - > b [ 0 ] = p - > Q * alpha ;
p - > b [ 1 ] = 0 ;
p - > b [ 2 ] = - p - > Q * alpha ;
break ;
case kNotchBqId :
p - > b [ 0 ] = 1 ;
p - > b [ 1 ] = - 2 * cos_w0 ;
p - > b [ 2 ] = 1 ;
break ;
case kAllpassBqId :
p - > b [ 0 ] = 1 - alpha ;
p - > b [ 1 ] = - 2 * cos_w0 ;
p - > b [ 2 ] = 1 + alpha ;
break ;
case kPeakBqId :
{
cmReal_t A = pow ( 10.0 , p - > gainDb / 40.0 ) ;
p - > b [ 0 ] = 1 + alpha * A ;
p - > b [ 1 ] = - 2 * cos_w0 ;
p - > b [ 2 ] = 1 - alpha * A ;
p - > a [ 0 ] = 1 + alpha / A ;
p - > a [ 1 ] = - 2 * cos_w0 ;
p - > a [ 2 ] = 1 - alpha / A ;
}
break ;
}
}
cmReal_t a0 = p - > a [ 0 ] ;
unsigned i = 0 ;
for ( ; i < 3 ; + + i )
{
p - > b [ i ] / = a0 ;
p - > a [ i ] / = a0 ;
}
if ( 0 )
{
printf ( " sr:%f mode:%i f0:%f q:%f gain:%f \n " , p - > srate , p - > mode , p - > f0Hz , p - > Q , p - > gainDb ) ;
for ( i = 0 ; i < 3 ; + + i )
printf ( " a[%i]=%f b[%i]=%f " , i , p - > a [ i ] , i , p - > b [ i ] ) ;
printf ( " \n " ) ;
}
}
cmRC_t cmBiQuadEqInit ( cmBiQuadEq * p , cmReal_t srate , unsigned mode , cmReal_t f0Hz , cmReal_t Q , cmReal_t gainDb , bool bypassFl )
{
cmRC_t rc ;
if ( ( rc = cmBiQuadEqFinal ( p ) ) ! = cmOkRC )
return rc ;
memset ( p - > d , 0 , sizeof ( p - > d ) ) ;
p - > srate = srate ;
cmBiQuadEqSet ( p , mode , f0Hz , Q , gainDb ) ;
_cmBiQuadEqInit ( p ) ;
return rc ;
}
cmRC_t cmBiQuadEqFinal ( cmBiQuadEq * p )
{ return cmOkRC ; }
cmRC_t cmBiQuadEqExec ( cmBiQuadEq * p , const cmSample_t * x , cmSample_t * y , unsigned n )
{
if ( y = = NULL )
return cmOkRC ;
if ( x = = NULL )
{
cmVOS_Zero ( y , n ) ;
return cmOkRC ;
}
if ( p - > bypassFl )
{
cmVOS_Copy ( y , n , x ) ;
return cmOkRC ;
}
// Direct Form I implementation
unsigned i = 0 ;
for ( ; i < n ; + + i )
{
y [ i ] = p - > b [ 0 ] * x [ i ] + p - > b [ 1 ] * p - > d [ 0 ] + p - > b [ 2 ] * p - > d [ 1 ] - p - > a [ 1 ] * p - > d [ 2 ] - p - > a [ 2 ] * p - > d [ 3 ] ;
p - > d [ 1 ] = p - > d [ 0 ] ;
p - > d [ 0 ] = x [ i ] ;
p - > d [ 3 ] = p - > d [ 2 ] ;
p - > d [ 2 ] = y [ i ] ;
}
return cmOkRC ;
}
void cmBiQuadEqSet ( cmBiQuadEq * p , unsigned mode , cmReal_t f0Hz , cmReal_t Q , cmReal_t gainDb )
{
p - > mode = mode ;
p - > f0Hz = f0Hz ;
p - > Q = Q ;
p - > gainDb = gainDb ;
_cmBiQuadEqInit ( p ) ;
}
//=======================================================================================================================
cmDistDs * cmDistDsAlloc ( cmCtx * c , cmDistDs * p , cmReal_t srate , cmReal_t inGain , cmReal_t downSrate , cmReal_t bits , bool rectFl , bool fullFl , cmReal_t clipDb , cmReal_t outGain , bool bypassFl )
{
cmDistDs * op = cmObjAlloc ( cmDistDs , c , p ) ;
if ( srate ! = 0 )
if ( cmDistDsInit ( op , srate , inGain , downSrate , bits , rectFl , fullFl , clipDb , outGain , bypassFl ) ! = cmOkRC )
cmDistDsFree ( & op ) ;
return op ;
}
cmRC_t cmDistDsFree ( cmDistDs * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return cmOkRC ;
cmDistDs * p = * pp ;
if ( ( rc = cmDistDsFinal ( p ) ) ! = cmOkRC )
return rc ;
cmObjFree ( pp ) ;
return cmOkRC ;
}
cmRC_t cmDistDsInit ( cmDistDs * p , cmReal_t srate , cmReal_t inGain , cmReal_t downSrate , cmReal_t bits , bool rectFl , bool fullFl , cmReal_t clipDb , cmReal_t outGain , bool bypassFl )
{
cmRC_t rc ;
if ( ( rc = cmDistDsFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > srate = srate ;
p - > downSrate = downSrate ;
p - > bits = bits ;
p - > rectFl = rectFl ;
p - > fullFl = fullFl ;
p - > clipDb = clipDb ;
p - > inGain = inGain ;
p - > outGain = outGain ;
p - > bypassFl = bypassFl ;
p - > fracIdx = 0 ;
p - > lastVal = 0 ;
p - > lastY = 0 ;
p - > lastX = 0 ;
return rc ;
}
cmRC_t cmDistDsFinal ( cmDistDs * p )
{ return cmOkRC ; }
void _cmDistDsExpr0 ( cmDistDs * p , const cmSample_t * x , cmSample_t * y , unsigned n )
{
unsigned i = 0 ;
for ( i = 0 ; i < n ; + + i )
{
p - > lastY = fmod ( p - > lastY + fabs ( x [ i ] - p - > lastX ) , 2.0 ) ;
y [ i ] = cmVOS_RMS ( x , n , n ) * x [ i ] * ( p - > lastY - 1.0 ) ;
p - > lastX = x [ i ] ;
}
}
void _cmDistDsExpr1 ( cmDistDs * p , cmSample_t * x , cmSample_t * y , unsigned n )
{
unsigned i = 0 ;
for ( i = 0 ; i < n ; + + i )
{
p - > lastY = fmod ( p - > lastY + fabs ( x [ i ] - p - > lastX ) , 2.0 * M_PI ) ;
y [ i ] = x [ i ] * sin ( p - > lastY ) ;
p - > lastX = x [ i ] ;
}
}
cmRC_t cmDistDsExec ( cmDistDs * p , const cmSample_t * x , cmSample_t * y , unsigned n )
{
if ( x = = NULL )
{
if ( y ! = NULL )
cmVOS_Zero ( y , n ) ;
return cmOkRC ;
}
if ( p - > bypassFl )
{
cmVOS_Copy ( y , n , x ) ;
return cmOkRC ;
}
unsigned maxVal = cmMax ( 2 , ( unsigned ) round ( pow ( 2.0 , p - > bits ) ) ) ;
double incr = 1.0 ; // p->downSrate / p->srate;
unsigned i ;
enum { kNoRectId , kFullRectId , kHalfRectId } ;
unsigned rectCode = kNoRectId ;
//cmSample_t t[n];
//_cmDistDsExpr0(p,x,t,n);
//x = t;
if ( p - > rectFl )
{
if ( p - > fullFl )
rectCode = kFullRectId ;
else
rectCode = kHalfRectId ;
}
double clipLevel = p - > clipDb < - 100.0 ? 0.0 : pow ( 10.0 , p - > clipDb / 20.0 ) ;
for ( i = 0 ; i < n ; + + i )
{
double ii = floor ( p - > fracIdx ) ;
p - > fracIdx + = incr ;
// if it is time to sample again
if ( floor ( p - > fracIdx ) ! = ii )
{
cmSample_t v = p - > inGain * floor ( x [ i ] * maxVal ) / maxVal ;
switch ( rectCode )
{
case kFullRectId :
v = ( cmSample_t ) fabs ( v ) ;
break ;
case kHalfRectId :
if ( v < 0.0 )
v = 0.0 ;
break ;
}
if ( fabs ( v ) > clipLevel )
v = v < 0.0 ? - clipLevel : clipLevel ;
p - > lastVal = v * p - > outGain ;
}
y [ i ] = p - > lastVal ;
}
return cmOkRC ;
}