File: MCLDCepstrumUGens.cpp

package info (click to toggle)
supercollider-sc3-plugins 3.7.1~repack-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 14,332 kB
  • ctags: 11,704
  • sloc: cpp: 140,180; lisp: 14,746; ansic: 2,133; xml: 86; makefile: 82; haskell: 21; sh: 8
file content (218 lines) | stat: -rw-r--r-- 6,736 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/*

Cesprum UGens for SuperCollider, by Dan Stowell.
(c) Dan Stowell 2009, 2010, 2011.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA

*/

#include "SC_PlugIn.h"
#include "FFT_UGens.h"
#include "SC_fftlib.h" // for Cepstrum and reverse

// Used by Cepstrum
#define SMALLEST_NUM_FOR_LOG 2e-42

//////////////////////////////////////////////////////////////////////////////////////////////////

struct Cepstrum : Unit
{
	SndBuf *buf_ceps, *buf_spec;
	scfft *m_scfft;
};
struct ICepstrum : Cepstrum {
};

//////////////////////////////////////////////////////////////////////////////////////////////////

extern "C"
{
	void load(InterfaceTable *inTable);

	void Cepstrum_Ctor(Cepstrum* unit);
	void Cepstrum_next(Cepstrum* unit, int inNumSamples);
	void Cepstrum_Dtor(Cepstrum* unit);

	void ICepstrum_Ctor(ICepstrum* unit);
	void ICepstrum_next(ICepstrum* unit, int inNumSamples);
	void ICepstrum_Dtor(ICepstrum* unit);
}

InterfaceTable *ft;

void init_SCComplex(InterfaceTable *inTable);

//////////////////////////////////////////////////////////////////////////////////////////////////

void Cepstrum_Ctor(Cepstrum* unit){
	SETCALC(Cepstrum_next);
	unit->m_scfft = NULL; // ensure scfft will be inialised, later.
	Cepstrum_next(unit, 1);
}

void ICepstrum_Ctor(ICepstrum* unit){
	SETCALC(ICepstrum_next);
	unit->m_scfft = NULL; // ensure scfft will be inialised, later.
	ICepstrum_next(unit, 1);
}

bool Cepstrum_next_common(Cepstrum* unit, float fbufnum_spec, float fbufnum_ceps, SCFFT_Direction dirn) {
	// Grab the buffer references, check they're sane and match the required sizes etc
	uint32 ibufnum_ceps = (int)fbufnum_ceps;
	uint32 ibufnum_spec = (int)fbufnum_spec;
	World *world = unit->mWorld;
	SndBuf *buf_ceps;
	if (ibufnum_ceps >= world->mNumSndBufs) {
		int localBufNum = ibufnum_ceps - world->mNumSndBufs;
		Graph *parent = unit->mParent;
		if(localBufNum <= parent->localBufNum) {
			buf_ceps = parent->mLocalSndBufs + localBufNum;
		} else {
			buf_ceps = world->mSndBufs;
		}
	} else {
		buf_ceps = world->mSndBufs + ibufnum_ceps;
	}
	SndBuf *buf_spec;
	if (ibufnum_spec >= world->mNumSndBufs) {
		int localBufNum = ibufnum_spec - world->mNumSndBufs;
		Graph *parent = unit->mParent;
		if(localBufNum <= parent->localBufNum) {
			buf_spec = parent->mLocalSndBufs + localBufNum;
		} else {
			buf_spec = world->mSndBufs;
		}
	} else {
		buf_spec = world->mSndBufs + ibufnum_spec;
	}
	if (buf_ceps->samples != buf_spec->samples >> 1){
		Print("Cepstrum_next_common error: cepstrum buffer (%i) must be half the size of fft buffer (%i)\n", buf_ceps->samples, buf_spec->samples);
		return false;
	}
	unit->buf_spec = buf_spec;
	unit->buf_ceps = buf_ceps;

	if(unit->m_scfft == NULL){
		//Print("unit->m_scfft == NULL so setting up.\n");
		unsigned int cepsize = unit->buf_ceps->samples;

        SCWorld_Allocator alloc(ft, unit->mWorld);
		unit->m_scfft = scfft_create(cepsize, cepsize, kRectWindow, unit->buf_ceps->data, unit->buf_ceps->data, dirn, alloc);
	}

	// We could check to make sure that the buffer references match what the scfft struct knows...

	return true;
}

void Cepstrum_next(Cepstrum* unit, int inNumSamples){
	// Second buffer is the ordinary chain buf
	float fbufnum_spec = ZIN0(1);
	if (fbufnum_spec < 0.f) { ZOUT0(0) = -1.f; return; }
	// First buffer holds the cepstrum (2x smaller than FFT chain buf)
	float fbufnum_ceps = ZIN0(0);
	if(!Cepstrum_next_common(unit, fbufnum_spec, fbufnum_ceps, kForward))
		return;

	SndBuf *buf_ceps = unit->buf_ceps;
	SndBuf *buf_spec = unit->buf_spec;
	int numbins = buf_ceps->samples; // "numbins" slightly different usage than in many PV ugens

	// OK so the chain has fired, we've got our source and target SndBufs

	// Ensure the source buf is in polar representation
	ToPolarApx(buf_spec);

	// Copy the __logarithms__ of the alternate slots (the DC and the other magnitudes, except nyq) into the ceps buf
	float *specdata = buf_spec->data;
	float *cepsdata = buf_ceps->data;
	for(int i=0; i<numbins; ++i){
		*cepsdata = std::abs(*specdata);
		if(*cepsdata < SMALLEST_NUM_FOR_LOG){
			//Print("tiny number: %g\n", *cepsdata);
			*cepsdata = SMALLEST_NUM_FOR_LOG;
		}
		*cepsdata = std::log(*cepsdata);
		++specdata;
		++specdata;
		++cepsdata;
	}

	// Now apply in-place FFT
	scfft_dofft(unit->m_scfft);

	// flag the buffer as being a PV cartesian buf
	buf_ceps->coord = coord_Complex;

	// tell the world the chain is ready
	ZOUT0(0) = fbufnum_ceps;
}

void ICepstrum_next(ICepstrum* unit, int inNumSamples){
	// First buffer holds the cepstrum (2x smaller than FFT chain buf)
	float fbufnum_ceps = ZIN0(0);
	if (fbufnum_ceps < 0.f) { ZOUT0(0) = -1.f; return; }
	// Second buffer is the ordinary chain buf
	float fbufnum_spec = ZIN0(1);
	if(!Cepstrum_next_common(unit, fbufnum_spec, fbufnum_ceps, kBackward))
		return;

	SndBuf *buf_ceps = unit->buf_ceps;
	SndBuf *buf_spec = unit->buf_spec;
	int numbins = buf_ceps->samples; // "numbins" slightly different usage than in many PV ugens

	// OK so the chain has fired, we've got our source and target SndBufs

	// Ensure the ceps buf is in cartesian representation
	ToComplexApx(buf_ceps);

	// inverse FFT in-place
	scfft_doifft(unit->m_scfft);

	// undo the logarithm at the same time as copying the magnitudes back into the spectral buffer
	float *specdata = buf_spec->data;
	float *cepsdata = buf_ceps->data;
	for(int i=0; i<numbins; ++i){
		*specdata = std::exp(*cepsdata);
		++specdata;
		++specdata;
		++cepsdata;
	}

	// the chain buffer should already be flagged as polar (from the forward cepstrum), but just in case...
	buf_spec->coord = coord_Polar;

	// tell the world the chain is ready
	ZOUT0(0) = fbufnum_spec;
}

void Cepstrum_Dtor(Cepstrum* unit){
	if(unit->m_scfft) RTFree(unit->mWorld, unit->m_scfft);
}

void ICepstrum_Dtor(ICepstrum* unit){
	if(unit->m_scfft) RTFree(unit->mWorld, unit->m_scfft);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

PluginLoad(MCLDCepstrum)
{
	ft= inTable;

	DefineDtorUnit(Cepstrum);
	DefineDtorUnit(ICepstrum);
}