/[svn]/linuxsampler/trunk/src/engines/gig/Synthesizer.h
ViewVC logotype

Contents of /linuxsampler/trunk/src/engines/gig/Synthesizer.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 411 - (show annotations) (download) (as text)
Sat Feb 26 02:01:14 2005 UTC (19 years, 2 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 20580 byte(s)
* design change: using now one sampler engine instance and one disk thread
  instance for all sampler channels that are connected to the same audio
  output device (and are using the same engine type of course)
* added EngineFactory / EngineChannelFactory to remove the annoying build
  dependencies e.g. of the lscpserver to the actual sampler engine
  implementations
* bumped version to 0.3.0 (current CVS state is still quite broken,
  previous, stable CVS version was tagged as "v0_2_0" and is also available
  as source tarball)

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 Christian Schoenebeck *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #ifndef __LS_GIG_SYNTHESIZER_H__
25 #define __LS_GIG_SYNTHESIZER_H__
26
27 #include "../../common/global.h"
28 #include "../../common/RTMath.h"
29 #include "../common/Resampler.h"
30 #include "../common/BiquadFilter.h"
31 #include "Filter.h"
32 #include "Voice.h"
33
34 #define SYNTHESIS_MODE_SET_CONSTPITCH(iMode,bVal) if (bVal) iMode |= 0x01; else iMode &= ~0x01 /* (un)set mode bit 0 */
35 #define SYNTHESIS_MODE_SET_LOOP(iMode,bVal) if (bVal) iMode |= 0x02; else iMode &= ~0x02 /* (un)set mode bit 1 */
36 #define SYNTHESIS_MODE_SET_INTERPOLATE(iMode,bVal) if (bVal) iMode |= 0x04; else iMode &= ~0x04 /* (un)set mode bit 2 */
37 #define SYNTHESIS_MODE_SET_FILTER(iMode,bVal) if (bVal) iMode |= 0x08; else iMode &= ~0x08 /* (un)set mode bit 3 */
38 #define SYNTHESIS_MODE_SET_CHANNELS(iMode,bVal) if (bVal) iMode |= 0x10; else iMode &= ~0x10 /* (un)set mode bit 4 */
39 #define SYNTHESIS_MODE_SET_IMPLEMENTATION(iMode,bVal) if (bVal) iMode |= 0x20; else iMode &= ~0x20 /* (un)set mode bit 5 */
40 #define SYNTHESIS_MODE_SET_PROFILING(iMode,bVal) if (bVal) iMode |= 0x40; else iMode &= ~0x40 /* (un)set mode bit 6 */
41
42 #define SYNTHESIS_MODE_GET_CONSTPITCH(iMode) iMode & 0x01
43 #define SYNTHESIS_MODE_GET_LOOP(iMode) iMode & 0x02
44 #define SYNTHESIS_MODE_GET_INTERPOLATE(iMode) iMode & 0x04
45 #define SYNTHESIS_MODE_GET_FILTER(iMode) iMode & 0x08
46 #define SYNTHESIS_MODE_GET_CHANNELS(iMode) iMode & 0x10
47 #define SYNTHESIS_MODE_GET_IMPLEMENTATION(iMode) iMode & 0x20
48
49 // that's usually gig::Voice of course, but we make it a macro so we can
50 // include this code for our synthesis benchmark which uses fake data
51 // structures
52 #ifndef VOICE
53 # define VOICE Voice
54 #endif // VOICE
55
56 namespace LinuxSampler { namespace gig {
57
58 typedef void SynthesizeFragment_Fn(VOICE&, uint, sample_t*, uint);
59
60 void* GetSynthesisFunction(const int SynthesisMode);
61 void RunSynthesisFunction(const int SynthesisMode, VOICE& voice, uint Samples, sample_t* pSrc, uint Skip);
62
63 enum channels_t {
64 MONO,
65 STEREO
66 };
67
68 template<implementation_t IMPLEMENTATION, channels_t CHANNELS, bool USEFILTER, bool INTERPOLATE, bool DOLOOP, bool CONSTPITCH>
69 class Synthesizer : public __RTMath<IMPLEMENTATION>, public LinuxSampler::Resampler<INTERPOLATE> {
70 public:
71 template<typename VOICE_T>
72 inline static void SynthesizeFragment(VOICE_T& Voice, uint Samples, sample_t* pSrc, uint i) {
73 if (IMPLEMENTATION == ASM_X86_MMX_SSE) {
74 float fPos = (float) Voice.Pos;
75 SynthesizeFragment(Voice, Samples, pSrc, i, Voice.pSample->LoopPlayCount,
76 Voice.pSample->LoopStart,
77 Voice.pSample->LoopEnd,
78 Voice.pSample->LoopSize,
79 Voice.LoopCyclesLeft,
80 (void *)&fPos,
81 Voice.PitchBase,
82 Voice.PitchBend);
83 #if ARCH_X86
84 if (INTERPOLATE) EMMS;
85 #endif
86 Voice.Pos = (double) fPos;
87 } else {
88 SynthesizeFragment(Voice, Samples, pSrc, i, Voice.pSample->LoopPlayCount,
89 Voice.pSample->LoopStart,
90 Voice.pSample->LoopEnd,
91 Voice.pSample->LoopSize,
92 Voice.LoopCyclesLeft,
93 (void *)&Voice.Pos,
94 Voice.PitchBase,
95 Voice.PitchBend);
96 }
97 }
98
99 //protected:
100
101 template<typename VOICE_T>
102 inline static void SynthesizeFragment(VOICE_T& Voice, uint Samples, sample_t* pSrc, uint& i, uint& LoopPlayCount, uint LoopStart, uint LoopEnd, uint LoopSize, uint& LoopCyclesLeft, void* Pos, float& PitchBase, float& PitchBend) {
103 const float loopEnd = Float(LoopEnd);
104 const float PBbyPB = Mul(PitchBase, PitchBend);
105 const float f_LoopStart = Float(LoopStart);
106 const float f_LoopSize = Float(LoopSize);
107 if (DOLOOP) {
108 if (LoopPlayCount) {
109 // render loop (loop count limited)
110 while (i < Samples && LoopCyclesLeft) {
111 if (CONSTPITCH) {
112 const uint processEnd = Min(Samples, i + DiffToLoopEnd(loopEnd,Pos, PBbyPB) + 1); //TODO: instead of +1 we could also round up
113 while (i < processEnd) Synthesize(Voice, Pos, pSrc, i);
114 }
115 else Synthesize(Voice, Pos, pSrc, i);
116 if (WrapLoop(f_LoopStart, f_LoopSize, loopEnd, Pos)) LoopCyclesLeft--;
117 }
118 // render on without loop
119 while (i < Samples) Synthesize(Voice, Pos, pSrc, i);
120 }
121 else { // render loop (endless loop)
122 while (i < Samples) {
123 if (CONSTPITCH) {
124 const uint processEnd = Min(Samples, i + DiffToLoopEnd(loopEnd, Pos, PBbyPB) + 1); //TODO: instead of +1 we could also round up
125 while (i < processEnd) Synthesize(Voice, Pos, pSrc, i);
126 }
127 else Synthesize(Voice, Pos, pSrc, i);
128 WrapLoop(f_LoopStart, f_LoopSize, loopEnd, Pos);
129 }
130 }
131 }
132 else { // no looping
133 while (i < Samples) { Synthesize(Voice, Pos, pSrc, i);}
134 }
135 }
136
137 template<typename VOICE_T>
138 inline static void Synthesize(VOICE_T& Voice, void* Pos, sample_t* pSrc, uint& i) {
139 Synthesize(pSrc, Pos,
140 Voice.pEngine->pSynthesisParameters[Event::destination_vco][i],
141 Voice.pEngineChannel->pOutputLeft,
142 Voice.pEngineChannel->pOutputRight,
143 i,
144 Voice.pEngine->pSynthesisParameters[Event::destination_vca],
145 &Voice.PanLeft,
146 &Voice.PanRight,
147 Voice.FilterLeft,
148 Voice.FilterRight,
149 Voice.pEngine->pBasicFilterParameters[i],
150 Voice.pEngine->pMainFilterParameters[i]);
151 }
152
153 inline static int DiffToLoopEnd(const float& LoopEnd, const void* Pos, const float& Pitch) {
154 switch (IMPLEMENTATION) {
155 // pure C++ implementation (thus platform independent)
156 case CPP: {
157 return uint((LoopEnd - *((double *)Pos)) / Pitch);
158 }
159 #if ARCH_X86
160 case ASM_X86_MMX_SSE: {
161 int result;
162 __asm__ __volatile__ (
163 "movss (%1), %%xmm0 #read loopend\n\t"
164 "subss (%2), %%xmm0 #sub pos\n\t"
165 "divss (%3), %%xmm0 #div by pitch\n\t"
166 "cvtss2si %%xmm0, %0 #convert to int\n\t"
167 : "=r" (result) /* %0 */
168 : "r" (&LoopEnd), /* %1 */
169 "r" (Pos), /* %2 */
170 "r" (&Pitch) /* %3 */
171 );
172 return result;
173 }
174 #endif // ARCH_X86
175 }
176 }
177
178 inline static int WrapLoop(const float& LoopStart, const float& LoopSize, const float& LoopEnd, void* vPos) {
179 switch (IMPLEMENTATION) {
180 // pure C++ implementation (thus platform independent)
181 case CPP: {
182 double * Pos = (double *)vPos;
183 if (*Pos < LoopEnd) return 0;
184 *Pos = fmod(*Pos - LoopEnd, LoopSize) + LoopStart;
185 return 1;
186 }
187 #if ARCH_X86
188 case ASM_X86_MMX_SSE: {
189 int result = 0;
190 __asm__ __volatile__ (
191 "movss (%2), %%xmm0 # load LoopEnd\n\t"
192 "movss (%1), %%xmm1 # load Pos\n\t"
193 "comiss %%xmm0, %%xmm1 # LoopEnd <> Pos\n\t"
194 "jb 1f # jump if no work needs to be done\n\t"
195 "movss (%3), %%xmm2 # load LoopSize\n\t"
196 "subss %%xmm0, %%xmm1 # Pos - LoopEnd\n\t"
197 //now the fmodf
198 "movss %%xmm1, %%xmm3 # xmm3 = (Pos - LoopEnd)\n\t"
199 "divss %%xmm2, %%xmm1 # (Pos - LoopEnd) / LoopSize\n\t"
200 "cvttss2si %%xmm1, %%eax # convert to int\n\t"
201 "cvtsi2ss %%eax, %%xmm1 # convert back to float\n\t"
202 "movss (%4), %%xmm0 # load LoopStart\n\t"
203 "mulss %%xmm2, %%xmm1 # LoopSize * int((Pos-LoopEnd)/LoopSize)\n\t"
204 "subss %%xmm1, %%xmm3 # xmm2 = fmodf(Pos - LoopEnd, LoopSize)\n\t"
205 //done with fmodf
206 "addss %%xmm0, %%xmm3 # add LoopStart\n\t"
207 "movss %%xmm3, (%1) # update Pos\n\t"
208 "movl $1, (%0) # result = 1\n\t"
209 ".balign 16 \n\t"
210 "1:\n\t"
211 :: "r" (&result), /* %0 */
212 "r" (vPos), /* %1 */
213 "r" (&LoopEnd), /* %2 */
214 "r" (&LoopSize), /* %3 */
215 "r" (&LoopStart) /* %4 */
216 );
217 return result;
218 }
219 #endif // ARCH_X86
220 }
221 }
222
223 inline static void Synthesize(sample_t* pSrc, void* Pos, float& Pitch, float* pOutL, float* pOutR, uint& i, float* Volume, float* PanL, float* PanR, Filter& FilterL, Filter& FilterR, biquad_param_t& bqBase, biquad_param_t& bqMain) {
224 switch (IMPLEMENTATION) {
225 // pure C++ implementation (thus platform independent)
226 case CPP: {
227 switch (CHANNELS) {
228 case MONO: {
229 float samplePoint = GetNextSampleMonoCPP(pSrc, (double *)Pos, Pitch);
230 if (USEFILTER) samplePoint = FilterL.Apply(&bqBase, &bqMain, samplePoint);
231 pOutL[i] += samplePoint * Volume[i] * *PanL;
232 pOutR[i] += samplePoint * Volume[i] * *PanR;
233 i++;
234 break;
235 }
236 case STEREO: {
237 stereo_sample_t samplePoint = GetNextSampleStereoCPP(pSrc, (double *)Pos, Pitch);
238 if (USEFILTER) {
239 samplePoint.left = FilterL.Apply(&bqBase, &bqMain, samplePoint.left);
240 samplePoint.right = FilterR.Apply(&bqBase, &bqMain, samplePoint.right);
241 }
242 pOutL[i] += samplePoint.left * Volume[i] * *PanL;
243 pOutR[i] += samplePoint.right * Volume[i] * *PanR;
244 i++;
245 break;
246 }
247 }
248 break;
249 }
250 #if ARCH_X86
251 // Assembly optimization using the MMX & SSE(1) instruction set (thus only for x86)
252 case ASM_X86_MMX_SSE: {
253 const int ii = i & 0xfffffffc;
254 i += 4;
255 switch (CHANNELS) {
256 case MONO: {
257 GetNext4SamplesMonoMMXSSE(pSrc, (float *)Pos, Pitch); // outputs samples in xmm2
258 if (USEFILTER) {
259 /* prepare filter input */
260 __asm__ __volatile__ (
261 "movaps %xmm2,%xmm0"
262 );
263 FilterL.Apply4StepsSSE(&bqBase, &bqMain); // xmm0 input, xmm7 output
264 __asm__ __volatile__ (
265 "movaps %xmm7,%xmm2 # mono filter result -> xmm2"
266 );
267 }
268 /* apply panorama and volume factors */
269 __asm__ __volatile__ (
270 "movss (%1),%%xmm0 # load pan left\n\t"
271 "movss (%2),%%xmm1 # load pan right\n\t"
272 "movaps (%0),%%xmm4 # load vca\n\t"
273 "shufps $0x00,%%xmm0,%%xmm0 # copy pan left to the other 3 cells\n\t"
274 "shufps $0x00,%%xmm1,%%xmm1 # copy pan right to the other 3 cells\n\t"
275 "mulps %%xmm2,%%xmm0 # left = sample * pan_left\n\t"
276 "mulps %%xmm2,%%xmm1 # right = sample * pan_right\n\t"
277 "mulps %%xmm4,%%xmm0 # left = vca * (sample * pan_left)\n\t"
278 "mulps %%xmm4,%%xmm1 # right = vca * (sample * pan_right)\n\t"
279 : /* no output */
280 : "r" (&Volume[ii]), /* %0 */
281 "r" (PanL), /* %1 */
282 "r" (PanR) /* %2 */
283 : "xmm0", /* holds final left sample (for the 4 samples) at the end */
284 "xmm1" /* holds final right sample (for the 4 samples) at the end */
285 );
286 break;
287 }
288 case STEREO: {
289 GetNext4SamplesStereoMMXSSE(pSrc, (float *)Pos, Pitch); // outputs samples in xmm2 (left channel) and xmm3 (right channel)
290 if (USEFILTER) {
291 __asm__ __volatile__ (
292 "movaps %xmm2,%xmm0 # prepare left channel for filter\n\t"
293 "movaps %xmm3,%xmm1 # save right channel not to get overwritten by filter algorithms\n\t"
294 );
295 FilterL.Apply4StepsSSE(&bqBase, &bqMain); // xmm0 input, xmm7 output
296 __asm__ __volatile__ (
297 "movaps %xmm1,%xmm0 # prepare right channel for filter\n\t"
298 "movaps %xmm7,%xmm1 # save filter output for left channel\n\t"
299 );
300 FilterR.Apply4StepsSSE(&bqBase, &bqMain); // xmm0 input, xmm7 output
301 __asm__ __volatile__ (
302 "movaps %xmm1,%xmm2 # result left channel -> xmm2\n\t"
303 "movaps %xmm7,%xmm3 # result right channel -> xmm3\n\t"
304 );
305 }
306 /* apply panorama and volume factors */
307 __asm__ __volatile__ (
308 "movss (%1),%%xmm0 # load pan left\n\t"
309 "movss (%2),%%xmm1 # load pan right\n\t"
310 "movaps (%0),%%xmm4 # load vca\n\t"
311 "shufps $0x00,%%xmm0,%%xmm0 # copy pan left to the other 3 cells\n\t"
312 "shufps $0x00,%%xmm1,%%xmm1 # copy pan right to the other 3 cells\n\t"
313 "mulps %%xmm2,%%xmm0 # left = sample_left * pan_left\n\t"
314 "mulps %%xmm3,%%xmm1 # right = sample_right * pan_right\n\t"
315 "mulps %%xmm4,%%xmm0 # left = vca * (sample_left * pan_left)\n\t"
316 "mulps %%xmm4,%%xmm1 # right = vca * (sample_right * pan_right)\n\t"
317 : /* no output */
318 : "r" (&Volume[ii]), /* %0 */
319 "r" (PanL), /* %1 */
320 "r" (PanR) /* %2 */
321 );
322 break;
323 }
324 }
325 /* mix the 4 samples to the output channels */
326 __asm__ __volatile__ (
327 "addps (%0),%%xmm0 # mix calculated sample(s) to output left\n\t"
328 "movaps %%xmm0,(%0) # output to left channel\n\t"
329 "addps (%1),%%xmm1 # mix calculated sample(s) to output right\n\t"
330 "movaps %%xmm1,(%1) # output to right channel\n\t"
331 : /* no output */
332 : "r" (&pOutL[ii]), /* %0 - must be 16 byte aligned ! */
333 "r" (&pOutR[ii]) /* %1 - must be 16 byte aligned ! */
334 );
335 }
336 #endif // ARCH_X86
337 }
338 }
339 };
340
341 }} // namespace LinuxSampler::gig
342
343 #endif // __LS_GIG_SYNTHESIZER_H__

  ViewVC Help
Powered by ViewVC