*** empty log message ***
[chuck-blob.git] / v2 / uana_extract.cpp
blob9df643cdf8ab13b936be61194b1d441b2d87c4e1
1 /*----------------------------------------------------------------------------
2 ChucK Concurrent, On-the-fly Audio Programming Language
3 Compiler and Virtual Machine
5 Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved.
6 http://chuck.cs.princeton.edu/
7 http://soundlab.cs.princeton.edu/
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 U.S.A.
23 -----------------------------------------------------------------------------*/
25 //-----------------------------------------------------------------------------
26 // file: uana_extract.cpp
27 // desc: ...
29 // author: Ge Wang (gewang@cs.princeton.edu)
30 // Rebecca Fiebrink (fiebrink@cs.princeton.edu)
31 // date: 20 November 2007
32 //-----------------------------------------------------------------------------
33 #include "uana_extract.h"
34 #include "chuck_type.h"
35 #include "chuck_vm.h"
36 #include "chuck_instr.h"
37 #include "chuck_lang.h"
38 #include "chuck_errmsg.h"
39 #include "util_math.h"
40 #include "util_xforms.h"
45 // Centroid
46 CK_DLL_TICK( Centroid_tick );
47 CK_DLL_TOCK( Centroid_tock );
48 CK_DLL_PMSG( Centroid_pmsg );
49 CK_DLL_SFUN( Centroid_compute );
51 // Flux
52 CK_DLL_CTOR( Flux_ctor );
53 CK_DLL_DTOR( Flux_dtor );
54 CK_DLL_TICK( Flux_tick );
55 CK_DLL_TOCK( Flux_tock );
56 CK_DLL_PMSG( Flux_pmsg );
57 CK_DLL_MFUN( Flux_ctrl_reset );
58 CK_DLL_SFUN( Flux_compute );
59 CK_DLL_SFUN( Flux_compute2 );
60 // offset
61 static t_CKUINT Flux_offset_data = 0;
63 // RMS
64 CK_DLL_TICK( RMS_tick );
65 CK_DLL_TOCK( RMS_tock );
66 CK_DLL_PMSG( RMS_pmsg );
67 CK_DLL_SFUN( RMS_compute );
69 // RollOff
70 CK_DLL_CTOR( RollOff_ctor );
71 CK_DLL_DTOR( RollOff_dtor );
72 CK_DLL_TICK( RollOff_tick );
73 CK_DLL_TOCK( RollOff_tock );
74 CK_DLL_PMSG( RollOff_pmsg );
75 CK_DLL_SFUN( RollOff_compute );
76 CK_DLL_MFUN( RollOff_ctrl_percent );
77 CK_DLL_MFUN( RollOff_cget_percent );
78 // offset
79 static t_CKUINT RollOff_offset_percent = 0;
81 // Feature Collector
82 CK_DLL_TICK( FeatureCollector_tick);
83 CK_DLL_TOCK( FeatureCollector_tock);
84 CK_DLL_PMSG( FeatureCollector_pmsg);
86 // AutoCorr
87 CK_DLL_CTOR( AutoCorr_ctor );
88 CK_DLL_DTOR( AutoCorr_dtor );
89 CK_DLL_TICK( AutoCorr_tick );
90 CK_DLL_TOCK( AutoCorr_tock );
91 CK_DLL_PMSG( AutoCorr_pmsg );
92 CK_DLL_SFUN( AutoCorr_compute );
93 CK_DLL_MFUN( AutoCorr_ctrl_normalize );
94 CK_DLL_MFUN( AutoCorr_cget_normalize );
95 // offset
96 static t_CKUINT AutoCorr_offset_data = 0;
98 // XCorr
99 CK_DLL_CTOR( XCorr_ctor );
100 CK_DLL_DTOR( XCorr_dtor );
101 CK_DLL_TICK( XCorr_tick );
102 CK_DLL_TOCK( XCorr_tock );
103 CK_DLL_PMSG( XCorr_pmsg );
104 CK_DLL_SFUN( XCorr_compute );
105 CK_DLL_MFUN( XCorr_ctrl_normalize );
106 CK_DLL_MFUN( XCorr_cget_normalize );
107 // offset
108 static t_CKUINT XCorr_offset_data = 0;
110 // ZeroX
111 CK_DLL_CTOR( ZeroX_ctor );
112 CK_DLL_DTOR( ZeroX_dtor );
113 CK_DLL_TICK( ZeroX_tick );
114 CK_DLL_TOCK( ZeroX_tock );
115 CK_DLL_PMSG( ZeroX_pmsg );
116 CK_DLL_SFUN( ZeroX_compute );
117 // offset
118 static t_CKUINT ZeroX_offset_data = 0;
120 // LPC
121 CK_DLL_CTOR( LPC_ctor );
122 CK_DLL_DTOR( LPC_dtor );
123 CK_DLL_TICK( LPC_tick );
124 CK_DLL_TOCK( LPC_tock );
125 CK_DLL_PMSG( LPC_pmsg );
126 CK_DLL_SFUN( LPC_compute );
127 CK_DLL_MFUN( LPC_ctrl_pitch );
128 CK_DLL_MFUN( LPC_cget_pitch );
129 CK_DLL_MFUN( LPC_ctrl_power );
130 CK_DLL_MFUN( LPC_cget_power );
131 CK_DLL_MFUN( LPC_ctrl_coefs );
132 CK_DLL_MFUN( LPC_cget_coefs );
133 // offset
134 static t_CKUINT LPC_offset_data = 0;
137 // utility functions
138 void xcorr_fft( SAMPLE * f, t_CKINT fs, SAMPLE * g, t_CKINT gs, SAMPLE * buffer, t_CKINT bs );
139 void xcorr_normalize( SAMPLE * buffy, t_CKINT bs, SAMPLE * f, t_CKINT fs, SAMPLE * g, t_CKINT gs );
144 //-----------------------------------------------------------------------------
145 // name: extract_query()
146 // desc: ...
147 //-----------------------------------------------------------------------------
148 DLL_QUERY extract_query( Chuck_DL_Query * QUERY )
150 Chuck_Env * env = Chuck_Env::instance();
152 Chuck_DL_Func * func = NULL;
154 //---------------------------------------------------------------------
155 // init as base class: FeatureCollector
156 //---------------------------------------------------------------------
157 if( !type_engine_import_uana_begin( env, "FeatureCollector", "UAna", env->global(),
158 NULL, NULL,
159 FeatureCollector_tick, FeatureCollector_tock, FeatureCollector_pmsg ) )
160 return FALSE;
162 // end the class import
163 type_engine_import_class_end( env );
165 //---------------------------------------------------------------------
166 // init as base class: Centroid
167 //---------------------------------------------------------------------
168 if( !type_engine_import_uana_begin( env, "Centroid", "UAna", env->global(),
169 NULL, NULL,
170 Centroid_tick, Centroid_tock, Centroid_pmsg ) )
171 return FALSE;
173 // compute
174 func = make_new_sfun( "float", "compute", Centroid_compute );
175 func->add_arg( "float[]", "input" );
176 if( !type_engine_import_sfun( env, func ) ) goto error;
178 // end the class import
179 type_engine_import_class_end( env );
181 //---------------------------------------------------------------------
182 // init as base class: Flux
183 //---------------------------------------------------------------------
184 if( !type_engine_import_uana_begin( env, "Flux", "UAna", env->global(),
185 Flux_ctor, Flux_dtor,
186 Flux_tick, Flux_tock, Flux_pmsg ) )
187 return FALSE;
189 // data offset
190 Flux_offset_data = type_engine_import_mvar( env, "int", "@Flux_data", FALSE );
191 if( Flux_offset_data == CK_INVALID_OFFSET ) goto error;
193 // compute
194 func = make_new_mfun( "void", "reset", Flux_ctrl_reset );
195 if( !type_engine_import_mfun( env, func ) ) goto error;
197 // compute
198 func = make_new_sfun( "float", "compute", Flux_compute );
199 func->add_arg( "float[]", "lhs" );
200 func->add_arg( "float[]", "rhs" );
201 if( !type_engine_import_sfun( env, func ) ) goto error;
203 // compute2
204 func = make_new_sfun( "float", "compute", Flux_compute2 );
205 func->add_arg( "float[]", "lhs" );
206 func->add_arg( "float[]", "rhs" );
207 func->add_arg( "float[]", "diff" );
208 if( !type_engine_import_sfun( env, func ) ) goto error;
210 // end the class import
211 type_engine_import_class_end( env );
213 //---------------------------------------------------------------------
214 // init as base class: RMS
215 //---------------------------------------------------------------------
216 if( !type_engine_import_uana_begin( env, "RMS", "UAna", env->global(),
217 NULL, NULL,
218 RMS_tick, RMS_tock, RMS_pmsg ) )
219 return FALSE;
221 // compute
222 func = make_new_sfun( "float", "compute", RMS_compute );
223 func->add_arg( "float[]", "input" );
224 if( !type_engine_import_sfun( env, func ) ) goto error;
226 // end the class import
227 type_engine_import_class_end( env );
229 //---------------------------------------------------------------------
230 // init as base class: RollOff
231 //---------------------------------------------------------------------
232 if( !type_engine_import_uana_begin( env, "RollOff", "UAna", env->global(),
233 RollOff_ctor, RollOff_dtor,
234 RollOff_tick, RollOff_tock, RollOff_pmsg ) )
235 return FALSE;
237 // data offset
238 RollOff_offset_percent = type_engine_import_mvar( env, "float", "@RollOff_data", FALSE );
239 if( RollOff_offset_percent == CK_INVALID_OFFSET ) goto error;
241 // compute
242 func = make_new_mfun( "float", "percent", RollOff_ctrl_percent );
243 func->add_arg( "float", "percent" );
244 if( !type_engine_import_mfun( env, func ) ) goto error;
246 // compute
247 func = make_new_mfun( "float", "percent", RollOff_cget_percent );
248 if( !type_engine_import_mfun( env, func ) ) goto error;
250 // compute
251 func = make_new_sfun( "float", "compute", RollOff_compute );
252 func->add_arg( "float[]", "input" );
253 func->add_arg( "float", "percent" );
254 if( !type_engine_import_sfun( env, func ) ) goto error;
256 // end the class import
257 type_engine_import_class_end( env );
259 //---------------------------------------------------------------------
260 // init as base class: AutoCorr
261 //---------------------------------------------------------------------
262 if( !type_engine_import_uana_begin( env, "AutoCorr", "UAna", env->global(),
263 AutoCorr_ctor, AutoCorr_dtor,
264 AutoCorr_tick, AutoCorr_tock, AutoCorr_pmsg ) )
265 return FALSE;
267 // data offset
268 AutoCorr_offset_data = type_engine_import_mvar( env, "float", "@AutoCorr_data", FALSE );
269 if( AutoCorr_offset_data == CK_INVALID_OFFSET ) goto error;
271 // normalize
272 func = make_new_mfun( "int", "normalize", AutoCorr_ctrl_normalize );
273 func->add_arg( "int", "flag" );
274 if( !type_engine_import_mfun( env, func ) ) goto error;
276 // normalize
277 func = make_new_mfun( "int", "normalize", AutoCorr_cget_normalize );
278 if( !type_engine_import_mfun( env, func ) ) goto error;
280 // compute
281 func = make_new_sfun( "float[]", "compute", AutoCorr_compute );
282 func->add_arg( "float[]", "input" );
283 func->add_arg( "int", "normalize" );
284 func->add_arg( "float[]", "output" );
285 if( !type_engine_import_sfun( env, func ) ) goto error;
287 // end the class import
288 type_engine_import_class_end( env );
290 //---------------------------------------------------------------------
291 // init as base class: XCorr
292 //---------------------------------------------------------------------
293 if( !type_engine_import_uana_begin( env, "XCorr", "UAna", env->global(),
294 XCorr_ctor, XCorr_dtor,
295 XCorr_tick, XCorr_tock, XCorr_pmsg ) )
296 return FALSE;
298 // data offset
299 XCorr_offset_data = type_engine_import_mvar( env, "float", "@XCorr_data", FALSE );
300 if( XCorr_offset_data == CK_INVALID_OFFSET ) goto error;
302 // normalize
303 func = make_new_mfun( "int", "normalize", XCorr_ctrl_normalize );
304 func->add_arg( "int", "flag" );
305 if( !type_engine_import_mfun( env, func ) ) goto error;
307 // normalize
308 func = make_new_mfun( "int", "normalize", XCorr_cget_normalize );
309 if( !type_engine_import_mfun( env, func ) ) goto error;
311 // compute
312 func = make_new_sfun( "float[]", "compute", XCorr_compute );
313 func->add_arg( "float[]", "f" );
314 func->add_arg( "float[]", "g" );
315 func->add_arg( "int", "normalize" );
316 func->add_arg( "float[]", "y" );
317 if( !type_engine_import_sfun( env, func ) ) goto error;
319 // end the class import
320 type_engine_import_class_end( env );
322 //---------------------------------------------------------------------
323 // init as base class: zerox
324 //---------------------------------------------------------------------
325 if( !type_engine_import_uana_begin( env, "ZeroX", "UAna", env->global(),
326 ZeroX_ctor, ZeroX_dtor,
327 ZeroX_tick, ZeroX_tock, NULL ) )
328 return FALSE;
330 // data offset
331 ZeroX_offset_data = type_engine_import_mvar( env, "int", "@ZeroX_data", FALSE );
332 if( ZeroX_offset_data == CK_INVALID_OFFSET ) goto error;
334 // compute
335 func = make_new_sfun( "float", "compute", ZeroX_compute );
336 func->add_arg( "float[]", "input" );
337 if( !type_engine_import_sfun( env, func ) ) goto error;
339 // end import
340 if( !type_engine_import_class_end( env ) )
341 return FALSE;
344 return TRUE;
346 error:
348 // end the class import
349 type_engine_import_class_end( env );
351 return FALSE;
354 CK_DLL_TICK( FeatureCollector_tick )
356 // do nothing
357 return TRUE;
360 // FeatureCollector_tock creates a flat vector from its upstream UAnae
361 // TODO: Stick complex features in Blob, too? But what about fft? (don't want duplication)
362 CK_DLL_TOCK( FeatureCollector_tock )
364 //t_CKFLOAT * features;
365 t_CKINT num_feats = 0;
366 t_CKINT num_incoming = UANA->numIncomingUAnae();
367 t_CKINT i, j;
370 // Get all incoming features and agglomerate into one vector
371 if( num_incoming > 0 )
373 //count the number of features in the array we're making
374 for( i = 0; i < num_incoming; i++ )
376 // get next blob
377 Chuck_UAnaBlobProxy * BLOB_IN = UANA->getIncomingBlob( i );
378 // sanity check
379 assert( BLOB_IN != NULL );
380 // count number of features from this UAna
381 Chuck_Array8 & these_fvals = BLOB_IN->fvals();
382 num_feats += these_fvals.size();
385 // get fvals of output BLOB
386 Chuck_Array8 & fvals = BLOB->fvals();
387 if( fvals.size() != num_feats )
388 fvals.set_size( num_feats );
390 t_CKINT next_index = 0;
391 for( i = 0; i < num_incoming; i++ )
393 // get next blob
394 Chuck_UAnaBlobProxy * BLOB_IN = UANA->getIncomingBlob( i );
395 Chuck_Array8 & these_fvals = BLOB_IN->fvals();
396 t_CKINT num_these = these_fvals.size();
397 for( j = 0; j < num_these; j++ )
399 t_CKFLOAT v;
400 these_fvals.get( j, &v );
401 fvals.set( next_index, v);
402 next_index++;
405 } else {
406 // no input to collect
407 BLOB->fvals().set_size(0);
410 return TRUE;
413 CK_DLL_PMSG( FeatureCollector_pmsg )
415 // do nothing
416 return TRUE;
419 static t_CKFLOAT compute_centroid( Chuck_Array8 & buffer, t_CKUINT size )
421 t_CKFLOAT m0 = 0.0;
422 t_CKFLOAT m1 = 0.0;
423 t_CKFLOAT centroid;
424 t_CKFLOAT v;
425 t_CKUINT i;
427 // Compute centroid using moments
428 for( i = 0; i < size; i++ )
430 buffer.get( i, &v );
431 m1 += (i * v);
432 m0 += v;
435 if( m0 != 0.0 )
436 centroid = m1 / m0;
437 else
438 centroid = size / 2.0; // Perfectly balanced
440 return centroid / size;
444 CK_DLL_TICK( Centroid_tick )
446 // do nothing
447 return TRUE;
450 CK_DLL_TOCK( Centroid_tock )
452 t_CKFLOAT result = 0.0;
454 // TODO: get buffer from stream, and set in ifft
455 if( UANA->numIncomingUAnae() > 0 )
457 // get first
458 Chuck_UAnaBlobProxy * BLOB_IN = UANA->getIncomingBlob( 0 );
459 // sanity check
460 assert( BLOB_IN != NULL );
461 // get the array
462 Chuck_Array8 & mag = BLOB_IN->fvals();
463 // compute centroid
464 result = compute_centroid( mag, mag.size() );
466 // otherwise zero out
467 else
469 // no input!
470 result = 0.0;
473 // get fvals of output BLOB
474 Chuck_Array8 & fvals = BLOB->fvals();
475 // ensure size == resulting size
476 if( fvals.size() != 1 )
477 fvals.set_size( 1 );
478 // copy the result in
479 fvals.set( 0, result );
481 return TRUE;
484 CK_DLL_PMSG( Centroid_pmsg )
486 // do nothing
487 return TRUE;
490 CK_DLL_SFUN( Centroid_compute )
492 // get array
493 Chuck_Array8 * array = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
494 // sanity check
495 if( !array )
497 // no centroid
498 RETURN->v_float = 0.0;
500 else
502 // do it
503 RETURN->v_float = compute_centroid( *array, array->size() );
508 // Flux state
509 struct StateOfFlux
511 Chuck_Array8 prev;
512 Chuck_Array8 norm;
513 t_CKBOOL initialized;
515 StateOfFlux()
517 initialized = FALSE;
522 // compute norm rms
523 static void compute_norm_rms( Chuck_Array8 & curr, Chuck_Array8 & norm )
525 t_CKUINT i;
526 t_CKFLOAT energy = 0.0;
527 t_CKFLOAT v;
529 // check size
530 if( norm.size() != curr.size() )
531 norm.set_size( curr.size() );
533 // get energy
534 for( i = 0; i < curr.size(); i++ )
536 curr.get( i, &v );
537 energy += v * v;
540 // check energy
541 if (energy == 0.0)
543 // all zeros
544 norm.zero( 0, norm.size() );
545 return;
547 else
548 energy = ::sqrt( energy );
550 for( i = 0; i < curr.size(); i++ )
552 curr.get( i, & v );
553 if( v > 0.0)
554 norm.set( i, v / energy );
555 else
556 norm.set( i, 0.0 );
560 // compute flux
561 static t_CKFLOAT compute_flux( Chuck_Array8 & curr, Chuck_Array8 & prev, Chuck_Array8 * write )
563 // sanity check
564 assert( curr.size() == prev.size() );
566 // ensure size
567 if( write != NULL && (write->size() != curr.size()) )
568 write->set_size( curr.size() );
570 // find difference
571 t_CKFLOAT v, w, result = 0.0;
572 for( t_CKUINT i = 0; i < curr.size(); i++ )
574 curr.get( i, &v );
575 prev.get( i, &w );
576 // accumulate into flux
577 result += (v - w)*(v - w);
578 // copy to write
579 if( write != NULL ) write->set( i, v );
582 // take sqrt of flux
583 return ::sqrt( result );
586 // compute flux
587 static t_CKFLOAT compute_flux( Chuck_Array8 & curr, StateOfFlux & sof )
589 // flux
590 t_CKFLOAT result = 0.0;
591 t_CKFLOAT v;
593 // verify size
594 if( curr.size() != sof.prev.size() )
596 sof.initialized = FALSE;
597 // resize prev
598 sof.prev.set_size( curr.size() );
601 // check initialized
602 if( sof.initialized )
604 // compute normalize rms
605 compute_norm_rms( curr, sof.norm );
606 // do it
607 result = compute_flux( sof.norm, sof.prev, &sof.prev );
610 // copy curr to prev
611 for( t_CKUINT i = 0; i < curr.size(); i++ )
613 // get the value
614 sof.norm.get( i, &v );
615 // set it
616 sof.prev.set( i, v );
619 // initialize
620 sof.initialized = TRUE;
622 // return result
623 return result;
626 CK_DLL_CTOR( Flux_ctor )
628 // allocate the flux state
629 StateOfFlux * state = new StateOfFlux();
630 OBJ_MEMBER_UINT( SELF, Flux_offset_data ) = (t_CKUINT)state;
634 CK_DLL_DTOR( Flux_dtor )
636 // clean up
637 StateOfFlux * state = (StateOfFlux *)OBJ_MEMBER_UINT( SELF, Flux_offset_data );
638 SAFE_DELETE( state );
639 OBJ_MEMBER_UINT( SELF, Flux_offset_data ) = 0;
643 CK_DLL_TICK( Flux_tick )
645 // do nothing
646 return TRUE;
650 CK_DLL_TOCK( Flux_tock )
652 t_CKFLOAT result = 0.0;
654 // get state
655 StateOfFlux * state = (StateOfFlux *)OBJ_MEMBER_UINT( SELF, Flux_offset_data );
657 // TODO: get buffer from stream, and set in ifft
658 if( UANA->numIncomingUAnae() > 0 )
660 // get first
661 Chuck_UAnaBlobProxy * BLOB_IN = UANA->getIncomingBlob( 0 );
662 // sanity check
663 assert( BLOB_IN != NULL );
664 // get the array
665 Chuck_Array8 & mag = BLOB_IN->fvals();
666 // compute flux
667 result = compute_flux( mag, *state );
669 // otherwise zero out
670 else
672 // no input!
673 result = 0.0;
676 // get fvals of output BLOB
677 Chuck_Array8 & fvals = BLOB->fvals();
678 // ensure size == resulting size
679 if( fvals.size() != 1 )
680 fvals.set_size( 1 );
681 // copy the result in
682 fvals.set( 0, result );
684 return TRUE;
687 CK_DLL_PMSG( Flux_pmsg )
689 // do nothing
690 return TRUE;
693 CK_DLL_MFUN( Flux_ctrl_reset )
695 // get state
696 StateOfFlux * state = (StateOfFlux *)OBJ_MEMBER_UINT( SELF, Flux_offset_data );
697 // set
698 state->initialized = FALSE;
701 CK_DLL_SFUN( Flux_compute )
703 // get inputs
704 Chuck_Array8 * lhs = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
705 Chuck_Array8 * rhs = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
707 // verify
708 if( !lhs || !rhs )
710 // 0
711 RETURN->v_float = 0.0;
713 else
715 // verify size
716 if( lhs->size() != rhs->size() )
718 // message
719 EM_error3( "(via Flux): compute() expects two arrays of equal size" );
720 // 0
721 RETURN->v_float = 0.0;
723 else
725 // flux
726 RETURN->v_float = compute_flux( *lhs, *rhs, NULL );
731 CK_DLL_SFUN( Flux_compute2 )
733 // get inputs
734 Chuck_Array8 * lhs = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
735 Chuck_Array8 * rhs = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
736 Chuck_Array8 * diff = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
738 // verify
739 if( !lhs || !rhs )
741 // 0
742 RETURN->v_float = 0.0;
744 else
746 // verify size
747 if( lhs->size() != rhs->size() )
749 // message
750 EM_error3( "(via Flux): compute() expects two arrays of equal size" );
751 // 0
752 RETURN->v_float = 0.0;
754 else
756 // flux
757 RETURN->v_float = compute_flux( *lhs, *rhs, diff );
763 static t_CKFLOAT compute_rms( Chuck_Array8 & buffer, t_CKUINT size )
765 t_CKFLOAT rms = 0.0;
766 t_CKFLOAT v;
767 t_CKUINT i;
769 // get sum of squares
770 for( i = 0; i < size; i++ )
772 buffer.get( i, &v );
773 rms += (v * v);
776 // go
777 rms /= size;
778 rms = ::sqrt(rms);
780 return rms;
784 CK_DLL_TICK( RMS_tick )
786 // do nothing
787 return TRUE;
790 CK_DLL_TOCK( RMS_tock )
792 t_CKFLOAT result = 0.0;
794 // TODO: get buffer from stream, and set in ifft
795 if( UANA->numIncomingUAnae() > 0 )
797 // get first
798 Chuck_UAnaBlobProxy * BLOB_IN = UANA->getIncomingBlob( 0 );
799 // sanity check
800 assert( BLOB_IN != NULL );
801 // get the array
802 Chuck_Array8 & mag = BLOB_IN->fvals();
803 // compute centroid
804 result = compute_rms( mag, mag.size() );
806 // otherwise zero out
807 else
809 // no input!
810 result = 0.0;
813 // get fvals of output BLOB
814 Chuck_Array8 & fvals = BLOB->fvals();
815 // ensure size == resulting size
816 if( fvals.size() != 1 )
817 fvals.set_size( 1 );
818 // copy the result in
819 fvals.set( 0, result );
821 return TRUE;
824 CK_DLL_PMSG( RMS_pmsg )
826 // do nothing
827 return TRUE;
830 CK_DLL_SFUN( RMS_compute )
832 // get array
833 Chuck_Array8 * array = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
834 // sanity check
835 if( !array )
837 // no RMS
838 RETURN->v_float = 0.0;
840 else
842 // do it
843 RETURN->v_float = compute_rms( *array, array->size() );
848 static t_CKFLOAT compute_rolloff( Chuck_Array8 & buffer, t_CKUINT size, t_CKFLOAT percent )
850 t_CKFLOAT sum = 0.0, v, target;
851 t_CKINT i;
853 // sanity check
854 assert( percent >= 0 && percent <= 1 );
856 // iterate
857 for( i = 0; i < size; i++ )
859 buffer.get( i, &v );
860 sum += v;
863 // the target
864 target = sum * percent;
865 sum = 0.0;
867 // iterate
868 for( i = 0; i < size; i++ )
870 buffer.get( i, &v );
871 sum += v;
872 if( sum >= target ) break;
875 return i/(t_CKFLOAT)size;
879 CK_DLL_CTOR( RollOff_ctor )
881 OBJ_MEMBER_FLOAT( SELF, RollOff_offset_percent ) = .85;
884 CK_DLL_DTOR( RollOff_dtor )
888 CK_DLL_TICK( RollOff_tick )
890 // do nothing
891 return TRUE;
894 CK_DLL_TOCK( RollOff_tock )
896 t_CKFLOAT result = 0.0;
898 // get percent
899 t_CKFLOAT percent = OBJ_MEMBER_FLOAT( SELF, RollOff_offset_percent );
901 // TODO: get buffer from stream, and set in ifft
902 if( UANA->numIncomingUAnae() > 0 )
904 // get first
905 Chuck_UAnaBlobProxy * BLOB_IN = UANA->getIncomingBlob( 0 );
906 // sanity check
907 assert( BLOB_IN != NULL );
908 // get the array
909 Chuck_Array8 & mag = BLOB_IN->fvals();
910 // compute rolloff
911 result = compute_rolloff( mag, mag.size(), percent );
913 // otherwise zero out
914 else
916 // no input!
917 result = 0.0;
920 // get fvals of output BLOB
921 Chuck_Array8 & fvals = BLOB->fvals();
922 // ensure size == resulting size
923 if( fvals.size() != 1 )
924 fvals.set_size( 1 );
925 // copy the result in
926 fvals.set( 0, result );
928 return TRUE;
931 CK_DLL_PMSG( RollOff_pmsg )
933 // do nothing
934 return TRUE;
937 CK_DLL_CTRL( RollOff_ctrl_percent )
939 // get percent
940 t_CKFLOAT percent = GET_NEXT_FLOAT(ARGS);
941 // check it
942 if( percent < 0.0 ) percent = 0.0;
943 else if( percent > 1.0 ) percent = 1.0;
944 // set it
945 OBJ_MEMBER_FLOAT(SELF, RollOff_offset_percent) = percent;
946 // return it
947 RETURN->v_float = percent;
950 CK_DLL_CGET( RollOff_cget_percent )
952 // return it
953 RETURN->v_float = OBJ_MEMBER_FLOAT(SELF, RollOff_offset_percent);
956 CK_DLL_SFUN( RollOff_compute )
958 // get array
959 Chuck_Array8 * array = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
960 // get percent
961 t_CKFLOAT percent = GET_NEXT_FLOAT(ARGS);
963 // sanity check
964 if( !array )
966 // no RollOff
967 RETURN->v_float = 0.0;
969 else
971 // do it
972 RETURN->v_float = compute_rolloff( *array, array->size(), percent );
979 // struct
980 struct Corr_Object
982 // whether to normalize
983 t_CKBOOL normalize;
984 // f
985 SAMPLE * fbuf;
986 t_CKINT fcap;
987 // g
988 SAMPLE * gbuf;
989 t_CKINT gcap;
990 // result
991 SAMPLE * buffy;
992 t_CKINT bufcap;
994 // static corr instance
995 static Corr_Object * ourCorr;
997 // constructor
998 Corr_Object()
1000 // default: do normalize
1001 normalize = FALSE;
1002 // zero out pointers
1003 fbuf = gbuf = buffy = NULL;
1004 fcap = gcap = bufcap = 0;
1005 // TODO: default
1006 resize( 512, 512 );
1009 // destructor
1010 ~Corr_Object()
1012 // done
1013 this->reset();
1016 // reset
1017 void reset()
1019 SAFE_DELETE_ARRAY( fbuf );
1020 SAFE_DELETE_ARRAY( gbuf );
1021 SAFE_DELETE_ARRAY( buffy );
1022 fcap = gcap = bufcap = 0;
1025 // clear
1026 void clear()
1028 if( fbuf ) memset( fbuf, 0, fcap * sizeof(SAMPLE) );
1029 if( gbuf ) memset( gbuf, 0, gcap * sizeof(SAMPLE) );
1030 if( buffy ) memset( buffy, 0, bufcap * sizeof(SAMPLE) );
1033 // resize inputs
1034 t_CKBOOL resize( t_CKINT fs, t_CKINT gs )
1036 // minimum size
1037 t_CKINT mincap = fs + gs - 1;
1038 // ensure power of two
1039 mincap = ensurepow2( mincap );
1040 // log
1041 EM_log( CK_LOG_FINE, "Corr resizing to %d-element buffers...", mincap );
1043 // verify
1044 if( fcap < mincap )
1046 SAFE_DELETE_ARRAY( fbuf );
1047 fbuf = new SAMPLE[mincap];
1048 fcap = mincap;
1050 if( gcap < mincap )
1052 SAFE_DELETE_ARRAY( gbuf );
1053 gbuf = new SAMPLE[mincap];
1054 gcap = mincap;
1056 if( bufcap < mincap )
1058 SAFE_DELETE_ARRAY( buffy );
1059 buffy = new SAMPLE[mincap];
1060 bufcap = mincap;
1063 // hopefully
1064 if( fbuf == NULL || gbuf == NULL || buffy == NULL )
1066 // error
1067 fprintf( stderr, "[chuck]: Corr failed to allocate %d-element buffer(s)...",
1068 mincap );
1069 // clean up
1070 this->reset();
1071 // done
1072 return FALSE;
1075 // clear it
1076 this->clear();
1078 return TRUE;
1081 // get our singleton
1082 static Corr_Object * getOurObject()
1084 // instantiate, if needed
1085 if( ourCorr == NULL )
1087 ourCorr = new Corr_Object();
1088 assert( ourCorr != NULL );
1091 // return new instance
1092 return ourCorr;
1096 // static initialization
1097 Corr_Object * Corr_Object::ourCorr = NULL;
1099 // compute correlation
1100 static void compute_corr( Corr_Object * corr, Chuck_Array8 & f, t_CKINT fs,
1101 Chuck_Array8 & g, t_CKINT gs, Chuck_Array8 & buffy )
1103 t_CKINT i;
1104 t_CKFLOAT v;
1105 t_CKINT size;
1107 // ensure size
1108 corr->resize( fs, gs );
1110 // copy into buffers
1111 for( i = 0; i < fs; i++ )
1113 f.get( i, &v );
1114 corr->fbuf[i] = v;
1116 for( i = 0; i < gs; i++ )
1118 g.get( i, &v );
1119 corr->gbuf[i] = v;
1122 // compute
1123 xcorr_fft( corr->fbuf, corr->fcap, corr->gbuf, corr->gcap,
1124 corr->buffy, corr->bufcap );
1126 // check flags
1127 if( corr->normalize )
1129 // normalize
1130 xcorr_normalize( corr->buffy, corr->bufcap,
1131 corr->fbuf, corr->fcap, corr->gbuf, corr->gcap );
1134 // copy into result
1135 size = fs + gs - 1;
1136 buffy.set_size( size );
1137 for( i = 0; i < size; i++ )
1139 buffy.set( i, corr->buffy[i] );
1143 // AutoCorr
1144 CK_DLL_CTOR( AutoCorr_ctor )
1146 Corr_Object * ac = new Corr_Object();
1147 OBJ_MEMBER_UINT( SELF, AutoCorr_offset_data ) = (t_CKUINT)ac;
1150 CK_DLL_DTOR( AutoCorr_dtor )
1152 Corr_Object * ac = (Corr_Object *)OBJ_MEMBER_UINT( SELF, AutoCorr_offset_data );
1153 SAFE_DELETE(ac);
1154 OBJ_MEMBER_UINT( SELF, AutoCorr_offset_data ) = 0;
1157 CK_DLL_TICK( AutoCorr_tick )
1159 // do nothing
1160 return TRUE;
1163 CK_DLL_TOCK( AutoCorr_tock )
1165 // get object
1166 Corr_Object * ac = (Corr_Object *)OBJ_MEMBER_UINT( SELF, AutoCorr_offset_data );
1168 // TODO: get buffer from stream, and set
1169 if( UANA->numIncomingUAnae() > 0 )
1171 // get first
1172 Chuck_UAnaBlobProxy * BLOB_IN = UANA->getIncomingBlob( 0 );
1173 // sanity check
1174 assert( BLOB_IN != NULL );
1175 // get the array
1176 Chuck_Array8 & mag = BLOB_IN->fvals();
1177 // get fvals of output BLOB
1178 Chuck_Array8 & fvals = BLOB->fvals();
1179 // compute autocorr
1180 compute_corr( ac, mag, mag.size(), mag, mag.size(), fvals );
1182 // otherwise zero out
1183 else
1185 // get fvals of output BLOB
1186 Chuck_Array8 & fvals = BLOB->fvals();
1187 // resize
1188 fvals.set_size( 0 );
1191 return TRUE;
1194 CK_DLL_PMSG( AutoCorr_pmsg )
1196 // do nothing
1197 return TRUE;
1200 CK_DLL_CTRL( AutoCorr_ctrl_normalize )
1202 // get object
1203 Corr_Object * ac = (Corr_Object *)OBJ_MEMBER_UINT( SELF, AutoCorr_offset_data );
1204 // get percent
1205 ac->normalize = GET_NEXT_INT(ARGS) != 0;
1206 // return it
1207 RETURN->v_int = ac->normalize;
1210 CK_DLL_CGET( AutoCorr_cget_normalize )
1212 // get object
1213 Corr_Object * ac = (Corr_Object *)OBJ_MEMBER_UINT( SELF, AutoCorr_offset_data );
1214 // return it
1215 RETURN->v_int = ac->normalize;
1218 CK_DLL_SFUN( AutoCorr_compute )
1220 // get input
1221 Chuck_Array8 * input = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
1222 // get normalize flag
1223 t_CKINT normalize = GET_NEXT_INT(ARGS) != 0;
1224 // get input
1225 Chuck_Array8 * output = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
1227 // set normalize
1228 Corr_Object::getOurObject()->normalize = normalize;
1229 // compute autocrr
1230 compute_corr( Corr_Object::getOurObject(), *input, input->size(),
1231 *input, input->size(), *output );
1235 // XCorr
1236 CK_DLL_CTOR( XCorr_ctor )
1238 Corr_Object * xc = new Corr_Object();
1239 OBJ_MEMBER_UINT( SELF, XCorr_offset_data ) = (t_CKUINT)xc;
1242 CK_DLL_DTOR( XCorr_dtor )
1244 Corr_Object * xc = (Corr_Object *)OBJ_MEMBER_UINT( SELF, XCorr_offset_data );
1245 SAFE_DELETE(xc);
1246 OBJ_MEMBER_UINT( SELF, XCorr_offset_data ) = 0;
1249 CK_DLL_TICK( XCorr_tick )
1251 // do nothing
1252 return TRUE;
1255 CK_DLL_TOCK( XCorr_tock )
1257 // get object
1258 Corr_Object * xc = (Corr_Object *)OBJ_MEMBER_UINT( SELF, XCorr_offset_data );
1260 // TODO: get buffer from stream, and set
1261 if( UANA->numIncomingUAnae() > 1 )
1263 // get first
1264 Chuck_UAnaBlobProxy * BLOB_F = UANA->getIncomingBlob( 0 );
1265 // get second
1266 Chuck_UAnaBlobProxy * BLOB_G = UANA->getIncomingBlob( 1 );
1267 // sanity check
1268 assert( BLOB_F != NULL && BLOB_G != NULL );
1269 // get the array
1270 Chuck_Array8 & mag_f = BLOB_F->fvals();
1271 Chuck_Array8 & mag_g = BLOB_G->fvals();
1272 // get fvals of output BLOB
1273 Chuck_Array8 & fvals = BLOB->fvals();
1274 // compute xcorr
1275 compute_corr( xc, mag_f, mag_f.size(), mag_g, mag_g.size(), fvals );
1277 // otherwise zero out
1278 else
1280 // get fvals of output BLOB
1281 Chuck_Array8 & fvals = BLOB->fvals();
1282 // resize
1283 fvals.set_size( 0 );
1286 return TRUE;
1289 CK_DLL_PMSG( XCorr_pmsg )
1291 // do nothing
1292 return TRUE;
1295 CK_DLL_CTRL( XCorr_ctrl_normalize )
1297 // get object
1298 Corr_Object * ac = (Corr_Object *)OBJ_MEMBER_UINT( SELF, XCorr_offset_data );
1299 // get percent
1300 ac->normalize = GET_NEXT_INT(ARGS) != 0;
1301 // return it
1302 RETURN->v_int = ac->normalize;
1305 CK_DLL_CGET( XCorr_cget_normalize )
1307 // get object
1308 Corr_Object * ac = (Corr_Object *)OBJ_MEMBER_UINT( SELF, XCorr_offset_data );
1309 // return it
1310 RETURN->v_int = ac->normalize;
1313 CK_DLL_SFUN( XCorr_compute )
1315 // get input
1316 Chuck_Array8 * f = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
1317 Chuck_Array8 * g = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
1318 // get normalize flag
1319 t_CKINT normalize = GET_NEXT_INT(ARGS) != 0;
1320 // get output
1321 Chuck_Array8 * output = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
1323 // set normalize
1324 Corr_Object::getOurObject()->normalize = normalize;
1325 // compute autocrr
1326 compute_corr( Corr_Object::getOurObject(), *f, f->size(),
1327 *g, g->size(), *output );
1333 //-----------------------------------------------------------------------------
1334 // name: xcorr_fft()
1335 // desc: FFT-based cross correlation
1336 //-----------------------------------------------------------------------------
1337 void xcorr_fft( SAMPLE * f, t_CKINT fsize, SAMPLE * g, t_CKINT gsize, SAMPLE * buffy, t_CKINT size )
1339 // sanity check
1340 assert( fsize == gsize == size );
1342 // take fft
1343 rfft( f, size/2, FFT_FORWARD );
1344 rfft( g, size/2, FFT_FORWARD );
1346 // complex
1347 t_CKCOMPLEX_SAMPLE * F = (t_CKCOMPLEX_SAMPLE *)f;
1348 t_CKCOMPLEX_SAMPLE * G = (t_CKCOMPLEX_SAMPLE *)g;
1349 t_CKCOMPLEX_SAMPLE * Y = (t_CKCOMPLEX_SAMPLE *)buffy;
1351 // loop
1352 for( t_CKINT i = 0; i < size/2; i++ )
1354 // conjugate F
1355 F[i].im = -F[i].im;
1356 // complex multiply
1357 Y[i].re = F[i].re*G[i].re - F[i].im*G[i].im;
1358 Y[i].im = F[i].im*G[i].re + F[i].re*G[i].im;
1361 // inverse fft
1362 rfft( buffy, size/2, FFT_INVERSE );
1368 //-----------------------------------------------------------------------------
1369 // name: xcorr_normalize()
1370 // desc: ...
1371 //-----------------------------------------------------------------------------
1372 void xcorr_normalize( SAMPLE * buffy, t_CKINT size, SAMPLE * f, t_CKINT fs, SAMPLE * g, t_CKINT gs )
1374 float sum = 0.000001f;
1376 // f^2(t)
1377 for( long i = 0; i < fs; i++ )
1378 sum += f[i]*f[i];
1379 // g^2(t)
1380 for( long j = 0; j < gs; j++ )
1381 sum += g[j]*g[j];
1382 // normalize: taking coherence into account
1383 for( long k = 0; k < size; k++ )
1384 buffy[k] /= sum;
1390 // ZeroX
1391 #define __SGN(x) (x >= 0.0f ? 1.0f : -1.0f )
1392 static t_CKINT compute_zerox( Chuck_Array8 & buffer, t_CKUINT size )
1394 t_CKUINT i, xings = 0;
1395 t_CKFLOAT v = 0, p = 0;
1396 buffer.get( 0, &p );
1398 // Compute centroid using moments
1399 for( i = 0; i < size; i++ )
1401 buffer.get( i, &v );
1402 xings += __SGN(v) != __SGN(p);
1403 p = v;
1406 return xings;
1409 CK_DLL_CTOR( ZeroX_ctor )
1411 OBJ_MEMBER_UINT(SELF, ZeroX_offset_data) = (t_CKUINT)new SAMPLE( 0.0f );
1414 CK_DLL_DTOR( ZeroX_dtor )
1416 delete (SAMPLE *)OBJ_MEMBER_UINT(SELF, ZeroX_offset_data);
1417 OBJ_MEMBER_UINT(SELF, ZeroX_offset_data) = 0;
1420 CK_DLL_TICK( ZeroX_tick )
1422 SAMPLE * d = (SAMPLE *)OBJ_MEMBER_UINT(SELF, ZeroX_offset_data);
1423 *out = __SGN(in) != __SGN(*d);
1424 *out *= __SGN(in);
1425 *d = in;
1427 return TRUE;
1430 CK_DLL_TOCK( ZeroX_tock )
1432 t_CKFLOAT result = 0.0;
1434 // TODO: get buffer from stream, and set in ifft
1435 if( UANA->numIncomingUAnae() > 0 )
1437 // get first
1438 Chuck_UAnaBlobProxy * BLOB_IN = UANA->getIncomingBlob( 0 );
1439 // sanity check
1440 assert( BLOB_IN != NULL );
1441 // get the array
1442 Chuck_Array8 & mag = BLOB_IN->fvals();
1443 // compute ZeroX
1444 result = (t_CKFLOAT)( compute_zerox( mag, mag.size() ) + .5 );
1446 // otherwise zero out
1447 else
1449 // no input!
1450 result = 0.0;
1453 // get fvals of output BLOB
1454 Chuck_Array8 & fvals = BLOB->fvals();
1455 // ensure size == resulting size
1456 if( fvals.size() != 1 )
1457 fvals.set_size( 1 );
1458 // copy the result in
1459 fvals.set( 0, result );
1461 return TRUE;
1464 CK_DLL_PMSG( ZeroX_pmsg )
1466 // do nothing
1467 return TRUE;
1470 CK_DLL_SFUN( ZeroX_compute )
1472 // get array
1473 Chuck_Array8 * array = (Chuck_Array8 *)GET_NEXT_OBJECT(ARGS);
1474 // sanity check
1475 if( !array )
1477 // no centroid
1478 RETURN->v_float = 0.0;
1480 else
1482 // do it
1483 RETURN->v_float = (t_CKFLOAT)( compute_centroid( *array, array->size() ) + .5 );