2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //Partitioned Convolution, Nick Collins mid October 2008
22 //PartConv(in, fftsize, irbufnum, accumbufnum)
25 #include "FFT_UGens.h"
29 struct PartConv
: public Unit
{
31 uint32 m_specbufnumcheck
;
32 float* m_fd_accumulate
; //will be exactly fftsize*frames in size
35 int m_partitions
; //number of frames impulse response is partitioned into
39 int m_fftsize
; //must be power of two
40 //int m_windowsize; //also half fftsize, was partition size, just use nover2 for this
43 //int m_hopsize; //hopsize will be half fftsize
51 scfft
* m_scifft
; //inverse
56 int m_blocksize
, m_sr
;
58 int m_numamort
; //will relate number of partitions to number of spare blocks
66 void PartConv_next(PartConv
*unit
, int inNumSamples
);
67 void PartConv_Ctor(PartConv
* unit
);
68 void PartConv_Dtor(PartConv
* unit
);
72 void PartConv_Ctor( PartConv
* unit
)
75 unit
->m_fftsize
= (int) ZIN0(1);
76 unit
->m_nover2
= unit
->m_fftsize
>>1;
77 //unit->m_windowsize= unit->m_fftsize>>1;
79 //unit->m_hopsize= unit->m_fftsize>>1;
80 //unit->m_shuntsize== unit->hopsize;
86 unit
->m_inputbuf
= (float*)RTAlloc(unit
->mWorld
, unit
->m_fftsize
* sizeof(float));
87 unit
->m_spectrum
= (float*)RTAlloc(unit
->mWorld
, unit
->m_fftsize
* sizeof(float));
89 SCWorld_Allocator
alloc(ft
, unit
->mWorld
);
90 unit
->m_scfft
= scfft_create(unit
->m_fftsize
, unit
->m_fftsize
, kRectWindow
, unit
->m_inputbuf
, unit
->m_spectrum
, kForward
, alloc
);
93 unit
->m_inputbuf2
= (float*)RTAlloc(unit
->mWorld
, unit
->m_fftsize
* sizeof(float));
94 unit
->m_spectrum2
= (float*)RTAlloc(unit
->mWorld
, unit
->m_fftsize
* sizeof(float));
96 unit
->m_scifft
= scfft_create(unit
->m_fftsize
, unit
->m_fftsize
, kRectWindow
, unit
->m_inputbuf2
, unit
->m_spectrum2
, kBackward
, alloc
);
98 //debug test: changing scale factors in case amplitude summation is a problem
99 //unit->m_scfft->scalefac=1.0/45.254833995939;
100 //unit->m_scifft->scalefac=1.0/45.254833995939;
104 unit
->m_output
= (float*)RTAlloc(unit
->mWorld
, unit
->m_fftsize
* sizeof(float));
107 memset(unit
->m_output
, 0, unit
->m_fftsize
* sizeof(float));
109 memset(unit
->m_inputbuf
, 0, unit
->m_fftsize
* sizeof(float));
112 //get passed in buffer
113 unit
->m_fd_accumulate
=NULL
;
115 uint32 bufnum
= (uint32
)ZIN0(2);
118 if (bufnum
>= unit
->mWorld
->mNumSndBufs
) {
120 int localBufNum
= bufnum
- unit
->mWorld
->mNumSndBufs
;
121 Graph
*parent
= unit
->mParent
;
122 if(localBufNum
<= parent
->localMaxBufNum
) {
123 buf
= parent
->mLocalSndBufs
+ localBufNum
;
127 printf("PartConv Error: Invalid Spectral data bufnum %d \n", bufnum
);
128 SETCALC(*ClearUnitOutputs
);
135 buf
= unit
->mWorld
->mSndBufs
+ bufnum
;
137 unit
->m_specbufnumcheck
= bufnum
;
139 //buf = unit->mWorld->mSndBufs + bufnum;
143 //unit->mDone = true;
144 printf("PartConv Error: Spectral data buffer not allocated \n");
145 SETCALC(*ClearUnitOutputs
);
150 unit
->m_irspectra
= buf
->data
;
152 unit
->m_fullsize
= buf
->samples
;
154 unit
->m_partitions
=buf
->samples
/(unit
->m_fftsize
); //should be exact
155 //printf("another check partitions %d irspecsize %d fftsize %d \n", unit->m_partitions,unit->m_fullsize, unit->m_fftsize);
157 if((buf
->samples
% unit
->m_fftsize
!=0) || (buf
->samples
==0)) {
158 printf("PartConv Error: fftsize doesn't divide spectral data buffer size or spectral data buffer size is zero\n");
159 SETCALC(*ClearUnitOutputs
);
164 //CHECK SAMPLING RATE AND BUFFER SIZE
165 unit
->m_blocksize
= unit
->mWorld
->mFullRate
.mBufLength
;
166 //if(unit->m_blocksize!=64) printf("TPV complains: block size not 64, you have %d\n", unit->m_blocksize);
167 unit
->m_sr
= unit
->mWorld
->mSampleRate
;
168 //if(unit->m_sr!=44100) printf("TPV complains: sample rate not 44100, you have %d\n", unit->m_sr);
170 if(unit
->m_nover2
% unit
->m_blocksize
!=0) {
171 printf("PartConv Error: block size doesn't divide partition size\n");
172 SETCALC(*ClearUnitOutputs
);
177 //must be exact divisor
178 int blocksperpartition
= unit
->m_nover2
/unit
->m_blocksize
;
180 unit
->m_spareblocks
= blocksperpartition
-1;
182 if(unit
->m_spareblocks
<1) {
183 printf("PartConv Error: no spareblocks, amortisation not possible! \n");
184 SETCALC(*ClearUnitOutputs
);
190 unit
->m_numamort
= (unit
->m_partitions
-1)/unit
->m_spareblocks
; //will relate number of partitions to number of spare blocks
191 unit
->m_lastamort
= (unit
->m_partitions
-1)- ((unit
->m_spareblocks
-1)*(unit
->m_numamort
)); //allow for error on last one
192 unit
->m_amortcount
= -1; //starts as flag to avoid any amortisation before have first fft done
193 unit
->m_partitionsdone
=1;
195 //printf("Amortisation stats partitions %d nover2 %d blocksize %d spareblocks %d numamort %d lastamort %d \n", unit->m_partitions,unit->m_nover2, unit->m_blocksize, unit->m_spareblocks, unit->m_numamort, unit->m_lastamort);
197 unit
->m_fd_accumulate
= (float*)RTAlloc(unit
->mWorld
, unit
->m_fullsize
* sizeof(float));
198 memset(unit
->m_fd_accumulate
, 0, unit
->m_fullsize
* sizeof(float));
199 unit
->m_fd_accum_pos
=0;
201 // used to be passed in as buffer, simplified interface
202 // bufnum = (uint32)ZIN0(3);
203 // if (bufnum >= unit->mWorld->mNumSndBufs) {
206 // buf = unit->mWorld->mSndBufs + bufnum;
209 // printf("PartConv Error: Accumulation buffer not allocated \n");
210 // SETCALC(*ClearUnitOutputs);
211 // unit->mDone = true;
215 // unit->m_fd_accumulate = buf->data;
218 SETCALC(PartConv_next
);
222 void PartConv_Dtor(PartConv
*unit
)
224 RTFree(unit
->mWorld
, unit
->m_inputbuf
);
225 RTFree(unit
->mWorld
, unit
->m_inputbuf2
);
226 RTFree(unit
->mWorld
, unit
->m_spectrum
);
227 RTFree(unit
->mWorld
, unit
->m_spectrum2
);
228 RTFree(unit
->mWorld
, unit
->m_output
);
229 if (unit
->m_fd_accumulate
) RTFree(unit
->mWorld
, unit
->m_fd_accumulate
);
231 SCWorld_Allocator
alloc(ft
, unit
->mWorld
);
233 scfft_destroy(unit
->m_scfft
, alloc
);
236 scfft_destroy(unit
->m_scifft
, alloc
);
239 void PartConv_next( PartConv
*unit
, int inNumSamples
)
243 int pos
= unit
->m_pos
;
246 if (!(unit
->mWorld
->mSndBufs
+ unit
->m_specbufnumcheck
)->data
) {
247 //unit->mDone = true;
248 printf("PartConv Error: Spectral data buffer not allocated \n");
249 ClearUnitOutputs(unit
, inNumSamples
);
250 SETCALC(*ClearUnitOutputs
);
255 float * input
= unit
->m_inputbuf
;
256 float * output
= unit
->m_output
;
257 int outputpos
= unit
->m_outputpos
;
260 memcpy(input
+pos
, in
, inNumSamples
* sizeof(float));
264 //if ready for new FFT
265 int nover2
= unit
->m_nover2
;
267 //assumes that blocksize perfectly divides windowsize
269 //FFT this input, second half of input always zero
271 //memset(input+unit->m_nover2, 0, sizeof(float)*unit->m_nover2);
273 scfft_dofft(unit
->m_scfft
);
275 //reset pos into input buffer
280 float * spectrum
= unit
->m_spectrum
;
281 float * spectrum2
= unit
->m_spectrum2
;
283 //multiply spectra and accumulate for all ir spectra across storage buffer
285 int fftsize
= unit
->m_fftsize
;
286 //int frames= unit->m_partitions;
287 int accumpos
= unit
->m_fd_accum_pos
;
288 float * accumbuffer
= unit
->m_fd_accumulate
;
290 float * irspectrum
= unit
->m_irspectra
;
292 int fullsize
= unit
->m_fullsize
;
294 //JUST DO FIRST ONE FOR NOW, AMORTISED FOR OTHERS
296 for (int i
=0; i
<1; ++i
) {
298 int irpos
= (i
*fftsize
);
299 int posnow
= (accumpos
+irpos
)%fullsize
;
300 float * target
= accumbuffer
+posnow
;
301 float * ir
= irspectrum
+irpos
;
303 //real multiply for dc and nyquist
304 target
[0] += ir
[0]*spectrum
[0];
305 target
[1] += ir
[1]*spectrum
[1];
307 //complex multiply for frequency bins
308 for (int j
=1; j
<nover2
; ++j
) {
311 int binposi
= binposr
+1;
314 target
[binposr
] += (ir
[binposr
]*spectrum
[binposr
]) - (ir
[binposi
]*spectrum
[binposi
]);
315 target
[binposi
] += (ir
[binposi
]*spectrum
[binposr
]) + (ir
[binposr
]*spectrum
[binposi
]);
320 // float * irreal = ir;
321 // float * irimag = ir+1;
322 // float * specreal = spectrum;
323 // float * specimag = spectrum+1;
324 // float * targetreal= target;
325 // float * targetimag= target+1;
327 // for (j=1; j<nover2; ++j) {
329 // irreal+=2; irimag+=2;
330 // specreal+=2; specimag+=2;
331 // targetreal+=2; targetimag+=2;
333 // //complex multiply
334 // (*targetreal)+= (*irreal)*(*specreal) - (*irimag)*(*specimag);
335 // (*targetimag)+= (*irimag)*(*specreal) - (*irreal)*(*specimag);
342 //need separate scfft object with forward= false
343 //IFFT this partition
344 float * input2
= unit
->m_inputbuf2
;
345 memcpy(input2
, accumbuffer
+accumpos
, fftsize
* sizeof(float));
346 scfft_doifft(unit
->m_scifft
);
348 //shunt output data down and zero top bit
349 memcpy(output
, output
+nover2
, nover2
* sizeof(float));
350 memset(output
+nover2
, 0, nover2
* sizeof(float));
352 //surely last entry should be zero?
353 //printf("sanity check %f \n",spectrum2[0]); //spectrum2[fftsize-1]
357 for (int j
=0; j
<fftsize
; ++j
)
358 output
[j
] += spectrum2
[j
];
360 //int testindex2= rgen.irand(fftsize-1);
361 //printf("outputtest j %d value %f index %d value2 %f \n",testindex2 , output[testindex2], testindex2, *(accumbuffer+accumpos+testindex2));
363 //zero this partition
364 memset(accumbuffer
+accumpos
, 0, fftsize
* sizeof(float));
366 //ONLY DO AT END OF AMORTISATION???? no, amort code has extra -1 in indexing to cope
367 //update partition counter
368 accumpos
= (accumpos
+fftsize
)%fullsize
;
369 unit
->m_fd_accum_pos
= accumpos
;
371 //set up for amortisation (calculate output for other partitions of impulse response)
372 unit
->m_amortcount
=0;
373 unit
->m_partitionsdone
=1;
376 //amortisation steps:
377 //complex multiply of this new fft spectrum against existing irspectrums and sum to accumbuffer
378 if (unit
->m_amortcount
>=0) {
380 float * spectrum
= unit
->m_spectrum
;
382 //multiply spectra and accumulate for all ir spectra across storage buffer
384 int fftsize
= unit
->m_fftsize
;
385 int nover2
= unit
->m_nover2
;
386 //int frames= unit->m_partitions;
387 int accumpos
= unit
->m_fd_accum_pos
;
388 float * accumbuffer
= unit
->m_fd_accumulate
;
390 float * irspectrum
= unit
->m_irspectra
;
392 int fullsize
= unit
->m_fullsize
;
397 if(unit
->m_amortcount
==(unit
->m_spareblocks
-1)) {
398 number
= unit
->m_lastamort
;
402 number
= unit
->m_numamort
;
405 starti
= unit
->m_partitionsdone
-1;
406 stopi
= starti
+number
-1;
408 //printf("amort check count %d starti %d stopi %d number %d framesdone %d \n",unit->m_amortcount, starti, stopi, number, unit->m_partitionsdone);
410 unit
->m_partitionsdone
+= number
;
411 ++unit
->m_amortcount
;
414 //JUST DO FIRST ONE FOR NOW
416 for (int i
=starti
; i
<=stopi
; ++i
) {
418 int posnow
= (accumpos
+(i
*fftsize
))%fullsize
;
419 float * target
= accumbuffer
+posnow
;
420 int irpos
= (i
*fftsize
);
421 float * ir
= irspectrum
+irpos
;
423 //real multiply for dc and nyquist
424 target
[0]+= ir
[0]*spectrum
[0];
425 target
[1]+= ir
[1]*spectrum
[1];
427 //complex multiply for frequency bins
428 for (int j
=1; j
<nover2
; ++j
) {
431 int binposi
= binposr
+1;
434 target
[binposr
]+= (ir
[binposr
]*spectrum
[binposr
]) - (ir
[binposi
]*spectrum
[binposi
]);
435 target
[binposi
]+= (ir
[binposi
]*spectrum
[binposr
]) + (ir
[binposr
]*spectrum
[binposi
]);
439 //printf("posnow %d i %d dc %f nyquist %f\n", posnow, i, target[400], target[405]);
443 //if(unit->m_amortcount==(unit->m_spareblocks-1)) {
444 // number= unit->m_numamort;
452 //control rate, just one out value
454 //for(j=0; j<inNumSamples; ++j) {
455 //out[j]= outputbuf[outputpos+j];
460 memcpy(out
, output
+outputpos
, inNumSamples
* sizeof(float));
462 //debugging tests: output values tend to be fftsize too big, probably due to complex multiply and also summation over all partitions
463 // RGen& rgen = *unit->mParent->mRGen;
464 // int testindex= rgen.irand(inNumSamples-1);
465 // printf("inNumSamples %d testindex %d out %f output %f \n",inNumSamples, testindex, out[testindex], *(output+outputpos+testindex));
467 outputpos
+=inNumSamples
;
469 unit
->m_outputpos
= outputpos
;
478 //channels not used- should just be mono, num frames= num samples
480 void PreparePartConv(World
*world
, struct SndBuf
*buf
, struct sc_msg_iter
*msg
)
482 //int channels1 = buf->channels;
483 float *data1
= buf
->data
;
485 uint32 frombufnum
= msg
->geti();
486 int fftsize
= msg
->geti();
488 //output size must be frombuf->frames*2
490 if (frombufnum
>= world
->mNumSndBufs
) frombufnum
= 0;
491 SndBuf
* frombuf
= world
->mSndBufs
+ frombufnum
;
493 int frames2
= frombuf
->frames
;
494 //int channels2 = frombuf->channels;
496 float *data2
= frombuf
->data
;
497 //int size = buf->samples;
500 int nover2
= fftsize
>>1;
502 //int numpartitions = msg->geti();
505 if(frames2
%nover2
==0)
506 numpartitions
= frames2
/nover2
;
508 numpartitions
= (frames2
/nover2
)+1;
510 //check numpartitions*fftsize= frames2
512 //printf("reality check numpartitions %d fftsize %d product %d numirframes %d numinputframes %d \n", numpartitions, fftsize, numpartitions*fftsize, frames1, frames2);
516 float * inputbuf
= (float*)RTAlloc(world
, fftsize
* sizeof(float));
517 float * spectrum
= (float*)RTAlloc(world
, fftsize
* sizeof(float));
519 SCWorld_Allocator
alloc(ft
, world
);
520 scfft
* m_scfft
= scfft_create(fftsize
, fftsize
, kRectWindow
, inputbuf
, spectrum
, kForward
, alloc
);
523 memset(inputbuf
, 0, sizeof(float)*fftsize
);
525 //run through input data buffer, taking nover2 chunks, zero padding each
526 for (int i
=0; i
<numpartitions
; ++i
) {
527 int indexnow
= nover2
*i
;
528 int indexout
= fftsize
*i
;
530 if(i
<(numpartitions
-1))
531 //memset(inputbuf, 0, sizeof(float)*fftsize);
532 memcpy(inputbuf
, data2
+indexnow
, nover2
* sizeof(float));
535 int takenow
= frames2
%nover2
;
540 memcpy(inputbuf
, data2
+indexnow
, takenow
* sizeof(float));
543 memset(inputbuf
+takenow
, 0, (nover2
-takenow
)*sizeof(float));
546 scfft_dofft(m_scfft
);
548 memcpy(data1
+indexout
, spectrum
, fftsize
* sizeof(float));
553 RTFree(world
, inputbuf
);
554 RTFree(world
, spectrum
);
557 scfft_destroy(m_scfft
, alloc
);
562 void initPartConv(InterfaceTable
*inTable
)
566 DefineDtorCantAliasUnit(PartConv
);
568 DefineBufGen("PreparePartConv", PreparePartConv
);