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"
28 struct PartConv
: public Unit
{
30 uint32 m_specbufnumcheck
;
31 float* m_fd_accumulate
; //will be exactly fftsize*frames in size
34 int m_partitions
; //number of frames impulse response is partitioned into
38 int m_fftsize
; //must be power of two
39 //int m_windowsize; //also half fftsize, was partition size, just use nover2 for this
42 //int m_hopsize; //hopsize will be half fftsize
50 scfft
* m_scifft
; //inverse
55 int m_blocksize
, m_sr
;
57 int m_numamort
; //will relate number of partitions to number of spare blocks
65 void PartConv_next(PartConv
*unit
, int inNumSamples
);
66 void PartConv_Ctor(PartConv
* unit
);
67 void PartConv_Dtor(PartConv
* unit
);
71 void PartConv_Ctor( PartConv
* unit
)
74 unit
->m_fftsize
= (int) ZIN0(1);
75 unit
->m_nover2
= unit
->m_fftsize
>>1;
76 //unit->m_windowsize= unit->m_fftsize>>1;
78 //unit->m_hopsize= unit->m_fftsize>>1;
79 //unit->m_shuntsize== unit->hopsize;
85 unit
->m_inputbuf
= (float*)RTAlloc(unit
->mWorld
, unit
->m_fftsize
* sizeof(float));
86 unit
->m_spectrum
= (float*)RTAlloc(unit
->mWorld
, unit
->m_fftsize
* sizeof(float));
88 SCWorld_Allocator
alloc(ft
, unit
->mWorld
);
89 unit
->m_scfft
= scfft_create(unit
->m_fftsize
, unit
->m_fftsize
, kRectWindow
, unit
->m_inputbuf
, unit
->m_spectrum
, kForward
, alloc
);
92 unit
->m_inputbuf2
= (float*)RTAlloc(unit
->mWorld
, unit
->m_fftsize
* sizeof(float));
93 unit
->m_spectrum2
= (float*)RTAlloc(unit
->mWorld
, unit
->m_fftsize
* sizeof(float));
95 unit
->m_scifft
= scfft_create(unit
->m_fftsize
, unit
->m_fftsize
, kRectWindow
, unit
->m_inputbuf2
, unit
->m_spectrum2
, kBackward
, alloc
);
97 //debug test: changing scale factors in case amplitude summation is a problem
98 //unit->m_scfft->scalefac=1.0/45.254833995939;
99 //unit->m_scifft->scalefac=1.0/45.254833995939;
103 unit
->m_output
= (float*)RTAlloc(unit
->mWorld
, unit
->m_fftsize
* sizeof(float));
106 memset(unit
->m_output
, 0, unit
->m_fftsize
* sizeof(float));
108 memset(unit
->m_inputbuf
, 0, unit
->m_fftsize
* sizeof(float));
111 //get passed in buffer
112 unit
->m_fd_accumulate
=NULL
;
114 uint32 bufnum
= (uint32
)ZIN0(2);
117 if (bufnum
>= unit
->mWorld
->mNumSndBufs
) {
119 int localBufNum
= bufnum
- unit
->mWorld
->mNumSndBufs
;
120 Graph
*parent
= unit
->mParent
;
121 if(localBufNum
<= parent
->localMaxBufNum
) {
122 buf
= parent
->mLocalSndBufs
+ localBufNum
;
126 printf("PartConv Error: Invalid Spectral data bufnum %d \n", bufnum
);
127 SETCALC(*ClearUnitOutputs
);
134 buf
= unit
->mWorld
->mSndBufs
+ bufnum
;
136 unit
->m_specbufnumcheck
= bufnum
;
138 //buf = unit->mWorld->mSndBufs + bufnum;
142 //unit->mDone = true;
143 printf("PartConv Error: Spectral data buffer not allocated \n");
144 SETCALC(*ClearUnitOutputs
);
149 unit
->m_irspectra
= buf
->data
;
151 unit
->m_fullsize
= buf
->samples
;
153 unit
->m_partitions
=buf
->samples
/(unit
->m_fftsize
); //should be exact
154 //printf("another check partitions %d irspecsize %d fftsize %d \n", unit->m_partitions,unit->m_fullsize, unit->m_fftsize);
156 if((buf
->samples
% unit
->m_fftsize
!=0) || (buf
->samples
==0)) {
157 printf("PartConv Error: fftsize doesn't divide spectral data buffer size or spectral data buffer size is zero\n");
158 SETCALC(*ClearUnitOutputs
);
163 //CHECK SAMPLING RATE AND BUFFER SIZE
164 unit
->m_blocksize
= unit
->mWorld
->mFullRate
.mBufLength
;
165 //if(unit->m_blocksize!=64) printf("TPV complains: block size not 64, you have %d\n", unit->m_blocksize);
166 unit
->m_sr
= unit
->mWorld
->mSampleRate
;
167 //if(unit->m_sr!=44100) printf("TPV complains: sample rate not 44100, you have %d\n", unit->m_sr);
169 if(unit
->m_nover2
% unit
->m_blocksize
!=0) {
170 printf("PartConv Error: block size doesn't divide partition size\n");
171 SETCALC(*ClearUnitOutputs
);
176 //must be exact divisor
177 int blocksperpartition
= unit
->m_nover2
/unit
->m_blocksize
;
179 unit
->m_spareblocks
= blocksperpartition
-1;
181 if(unit
->m_spareblocks
<1) {
182 printf("PartConv Error: no spareblocks, amortisation not possible! \n");
183 SETCALC(*ClearUnitOutputs
);
189 unit
->m_numamort
= (unit
->m_partitions
-1)/unit
->m_spareblocks
; //will relate number of partitions to number of spare blocks
190 unit
->m_lastamort
= (unit
->m_partitions
-1)- ((unit
->m_spareblocks
-1)*(unit
->m_numamort
)); //allow for error on last one
191 unit
->m_amortcount
= -1; //starts as flag to avoid any amortisation before have first fft done
192 unit
->m_partitionsdone
=1;
194 //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);
196 unit
->m_fd_accumulate
= (float*)RTAlloc(unit
->mWorld
, unit
->m_fullsize
* sizeof(float));
197 memset(unit
->m_fd_accumulate
, 0, unit
->m_fullsize
* sizeof(float));
198 unit
->m_fd_accum_pos
=0;
200 // used to be passed in as buffer, simplified interface
201 // bufnum = (uint32)ZIN0(3);
202 // if (bufnum >= unit->mWorld->mNumSndBufs) {
205 // buf = unit->mWorld->mSndBufs + bufnum;
208 // printf("PartConv Error: Accumulation buffer not allocated \n");
209 // SETCALC(*ClearUnitOutputs);
210 // unit->mDone = true;
214 // unit->m_fd_accumulate = buf->data;
217 SETCALC(PartConv_next
);
221 void PartConv_Dtor(PartConv
*unit
)
223 RTFree(unit
->mWorld
, unit
->m_inputbuf
);
224 RTFree(unit
->mWorld
, unit
->m_inputbuf2
);
225 RTFree(unit
->mWorld
, unit
->m_spectrum
);
226 RTFree(unit
->mWorld
, unit
->m_spectrum2
);
227 RTFree(unit
->mWorld
, unit
->m_output
);
228 if (unit
->m_fd_accumulate
) RTFree(unit
->mWorld
, unit
->m_fd_accumulate
);
230 SCWorld_Allocator
alloc(ft
, unit
->mWorld
);
232 scfft_destroy(unit
->m_scfft
, alloc
);
235 scfft_destroy(unit
->m_scifft
, alloc
);
238 void PartConv_next( PartConv
*unit
, int inNumSamples
)
242 int pos
= unit
->m_pos
;
245 if (!(unit
->mWorld
->mSndBufs
+ unit
->m_specbufnumcheck
)->data
) {
246 //unit->mDone = true;
247 printf("PartConv Error: Spectral data buffer not allocated \n");
248 ClearUnitOutputs(unit
, inNumSamples
);
249 SETCALC(*ClearUnitOutputs
);
254 float * input
= unit
->m_inputbuf
;
255 float * output
= unit
->m_output
;
256 int outputpos
= unit
->m_outputpos
;
259 memcpy(input
+pos
, in
, inNumSamples
* sizeof(float));
263 //if ready for new FFT
264 int nover2
= unit
->m_nover2
;
266 //assumes that blocksize perfectly divides windowsize
268 //FFT this input, second half of input always zero
270 //memset(input+unit->m_nover2, 0, sizeof(float)*unit->m_nover2);
272 scfft_dofft(unit
->m_scfft
);
274 //reset pos into input buffer
279 float * spectrum
= unit
->m_spectrum
;
280 float * spectrum2
= unit
->m_spectrum2
;
282 //multiply spectra and accumulate for all ir spectra across storage buffer
284 int fftsize
= unit
->m_fftsize
;
285 //int frames= unit->m_partitions;
286 int accumpos
= unit
->m_fd_accum_pos
;
287 float * accumbuffer
= unit
->m_fd_accumulate
;
289 float * irspectrum
= unit
->m_irspectra
;
291 int fullsize
= unit
->m_fullsize
;
293 //JUST DO FIRST ONE FOR NOW, AMORTISED FOR OTHERS
295 for (int i
=0; i
<1; ++i
) {
297 int irpos
= (i
*fftsize
);
298 int posnow
= (accumpos
+irpos
)%fullsize
;
299 float * target
= accumbuffer
+posnow
;
300 float * ir
= irspectrum
+irpos
;
302 //real multiply for dc and nyquist
303 target
[0] += ir
[0]*spectrum
[0];
304 target
[1] += ir
[1]*spectrum
[1];
306 //complex multiply for frequency bins
307 for (int j
=1; j
<nover2
; ++j
) {
310 int binposi
= binposr
+1;
313 target
[binposr
] += (ir
[binposr
]*spectrum
[binposr
]) - (ir
[binposi
]*spectrum
[binposi
]);
314 target
[binposi
] += (ir
[binposi
]*spectrum
[binposr
]) + (ir
[binposr
]*spectrum
[binposi
]);
319 // float * irreal = ir;
320 // float * irimag = ir+1;
321 // float * specreal = spectrum;
322 // float * specimag = spectrum+1;
323 // float * targetreal= target;
324 // float * targetimag= target+1;
326 // for (j=1; j<nover2; ++j) {
328 // irreal+=2; irimag+=2;
329 // specreal+=2; specimag+=2;
330 // targetreal+=2; targetimag+=2;
332 // //complex multiply
333 // (*targetreal)+= (*irreal)*(*specreal) - (*irimag)*(*specimag);
334 // (*targetimag)+= (*irimag)*(*specreal) - (*irreal)*(*specimag);
341 //need separate scfft object with forward= false
342 //IFFT this partition
343 float * input2
= unit
->m_inputbuf2
;
344 memcpy(input2
, accumbuffer
+accumpos
, fftsize
* sizeof(float));
345 scfft_doifft(unit
->m_scifft
);
347 //shunt output data down and zero top bit
348 memcpy(output
, output
+nover2
, nover2
* sizeof(float));
349 memset(output
+nover2
, 0, nover2
* sizeof(float));
351 //surely last entry should be zero?
352 //printf("sanity check %f \n",spectrum2[0]); //spectrum2[fftsize-1]
356 for (int j
=0; j
<fftsize
; ++j
)
357 output
[j
] += spectrum2
[j
];
359 //int testindex2= rgen.irand(fftsize-1);
360 //printf("outputtest j %d value %f index %d value2 %f \n",testindex2 , output[testindex2], testindex2, *(accumbuffer+accumpos+testindex2));
362 //zero this partition
363 memset(accumbuffer
+accumpos
, 0, fftsize
* sizeof(float));
365 //ONLY DO AT END OF AMORTISATION???? no, amort code has extra -1 in indexing to cope
366 //update partition counter
367 accumpos
= (accumpos
+fftsize
)%fullsize
;
368 unit
->m_fd_accum_pos
= accumpos
;
370 //set up for amortisation (calculate output for other partitions of impulse response)
371 unit
->m_amortcount
=0;
372 unit
->m_partitionsdone
=1;
375 //amortisation steps:
376 //complex multiply of this new fft spectrum against existing irspectrums and sum to accumbuffer
377 if (unit
->m_amortcount
>=0) {
379 float * spectrum
= unit
->m_spectrum
;
381 //multiply spectra and accumulate for all ir spectra across storage buffer
383 int fftsize
= unit
->m_fftsize
;
384 int nover2
= unit
->m_nover2
;
385 //int frames= unit->m_partitions;
386 int accumpos
= unit
->m_fd_accum_pos
;
387 float * accumbuffer
= unit
->m_fd_accumulate
;
389 float * irspectrum
= unit
->m_irspectra
;
391 int fullsize
= unit
->m_fullsize
;
396 if(unit
->m_amortcount
==(unit
->m_spareblocks
-1)) {
397 number
= unit
->m_lastamort
;
401 number
= unit
->m_numamort
;
404 starti
= unit
->m_partitionsdone
-1;
405 stopi
= starti
+number
-1;
407 //printf("amort check count %d starti %d stopi %d number %d framesdone %d \n",unit->m_amortcount, starti, stopi, number, unit->m_partitionsdone);
409 unit
->m_partitionsdone
+= number
;
410 ++unit
->m_amortcount
;
413 //JUST DO FIRST ONE FOR NOW
415 for (int i
=starti
; i
<=stopi
; ++i
) {
417 int posnow
= (accumpos
+(i
*fftsize
))%fullsize
;
418 float * target
= accumbuffer
+posnow
;
419 int irpos
= (i
*fftsize
);
420 float * ir
= irspectrum
+irpos
;
422 //real multiply for dc and nyquist
423 target
[0]+= ir
[0]*spectrum
[0];
424 target
[1]+= ir
[1]*spectrum
[1];
426 //complex multiply for frequency bins
427 for (int j
=1; j
<nover2
; ++j
) {
430 int binposi
= binposr
+1;
433 target
[binposr
]+= (ir
[binposr
]*spectrum
[binposr
]) - (ir
[binposi
]*spectrum
[binposi
]);
434 target
[binposi
]+= (ir
[binposi
]*spectrum
[binposr
]) + (ir
[binposr
]*spectrum
[binposi
]);
438 //printf("posnow %d i %d dc %f nyquist %f\n", posnow, i, target[400], target[405]);
442 //if(unit->m_amortcount==(unit->m_spareblocks-1)) {
443 // number= unit->m_numamort;
451 //control rate, just one out value
453 //for(j=0; j<inNumSamples; ++j) {
454 //out[j]= outputbuf[outputpos+j];
459 memcpy(out
, output
+outputpos
, inNumSamples
* sizeof(float));
461 //debugging tests: output values tend to be fftsize too big, probably due to complex multiply and also summation over all partitions
462 // RGen& rgen = *unit->mParent->mRGen;
463 // int testindex= rgen.irand(inNumSamples-1);
464 // printf("inNumSamples %d testindex %d out %f output %f \n",inNumSamples, testindex, out[testindex], *(output+outputpos+testindex));
466 outputpos
+=inNumSamples
;
468 unit
->m_outputpos
= outputpos
;
477 //channels not used- should just be mono, num frames= num samples
479 void PreparePartConv(World
*world
, struct SndBuf
*buf
, struct sc_msg_iter
*msg
)
481 int frames1
= buf
->frames
;
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
);