summaryrefslogtreecommitdiff
path: root/apps/plugins/midi/synth.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/midi/synth.c')
-rw-r--r--apps/plugins/midi/synth.c430
1 files changed, 430 insertions, 0 deletions
diff --git a/apps/plugins/midi/synth.c b/apps/plugins/midi/synth.c
new file mode 100644
index 0000000000..0d07ed91d0
--- /dev/null
+++ b/apps/plugins/midi/synth.c
@@ -0,0 +1,430 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2005 Stepan Moskovchenko
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19
20
21extern struct plugin_api * rb;
22
23struct Event * getEvent(struct Track * tr, int evNum)
24{
25 return tr->dataBlock + (evNum*sizeof(struct Event));
26}
27
28void readTextBlock(int file, char * buf)
29{
30 char c = 0;
31 do
32 {
33 c = readChar(file);
34 } while(c == '\n' || c == ' ' || c=='\t');
35
36 rb->lseek(file, -1, SEEK_CUR);
37 int cp = 0;
38 do
39 {
40 c = readChar(file);
41 buf[cp] = c;
42 cp++;
43 } while (c != '\n' && c != ' ' && c != '\t' && !eof(file));
44 buf[cp-1]=0;
45 rb->lseek(file, -1, SEEK_CUR);
46}
47
48
49
50//Filename is the name of the config file
51//The MIDI file should have been loaded at this point
52void initSynth(struct MIDIfile * mf, char * filename, char * drumConfig)
53{
54 char patchUsed[128];
55 char drumUsed[128];
56 int a=0;
57 for(a=0; a<MAX_VOICES; a++)
58 {
59 voices[a].cp=0;
60 voices[a].vol=0;
61 voices[a].ch=0;
62 voices[a].isUsed=0;
63 voices[a].note=0;
64 }
65
66 for(a=0; a<16; a++)
67 {
68 chVol[a]=100; //Default, not quite full blast..
69 chPanLeft[a]=64; //Center
70 chPanRight[a]=64; //Center
71 chPat[a]=0; //Ac Gr Piano
72 chPW[a]=64; // .. not .. bent ?
73 }
74 for(a=0; a<128; a++)
75 {
76 patchSet[a]=NULL;
77 drumSet[a]=NULL;
78 patchUsed[a]=0;
79 drumUsed[a]=0;
80 }
81
82 //Always load the piano.
83 //Some files will assume its loaded without specifically
84 //issuing a Patch command... then we wonder why we can't hear anything
85 patchUsed[0]=1;
86
87 //Scan the file to see what needs to be loaded
88 for(a=0; a<mf->numTracks; a++)
89 {
90 int ts=0;
91
92 if(mf->tracks[a] == NULL)
93 {
94 printf("\nNULL TRACK !!!");
95 exit(1);
96 return;
97 }
98
99 for(ts=0; ts<mf->tracks[a]->numEvents; ts++)
100 {
101
102 if((getEvent(mf->tracks[a], ts)->status) == (MIDI_NOTE_ON+9))
103 drumUsed[getEvent(mf->tracks[a], ts)->d1]=1;
104
105 if( (getEvent(mf->tracks[a], ts)->status & 0xF0) == MIDI_PRGM)
106 {
107 if(patchUsed[getEvent(mf->tracks[a], ts)->d1]==0)
108 printf("\nI need to load patch %d.", getEvent(mf->tracks[a], ts)->d1);
109 patchUsed[getEvent(mf->tracks[a], ts)->d1]=1;
110 }
111 }
112 }
113
114 int file = rb->open(filename, O_RDONLY);
115
116 char name[30];
117 char fn[30];
118
119 //Scan our config file and load the right patches as needed
120 int c = 0;
121 rb->snprintf(name, 30, "");
122 for(a=0; a<128; a++)
123 {
124 while(readChar(file)!=' ' && !eof(file));
125 readTextBlock(file, name);
126
127 rb->snprintf(fn, 30, "/patchset/%s.pat", name);
128 printf("\nLOADING: <%s> ", fn);
129 if(patchUsed[a]==1)
130 patchSet[a]=gusload(fn);
131
132 while((c != '\n'))
133 c = readChar(file);
134 }
135 rb->close(file);
136
137 file = rb->open(drumConfig, O_RDONLY);
138
139 //Scan our config file and load the drum data
140 int idx=0;
141 char number[30];
142 while(!eof(file))
143 {
144 readTextBlock(file, number);
145 readTextBlock(file, name);
146 rb->snprintf(fn, 30, "/patchset/%s.pat", name);
147
148 idx = rb->atoi(number);
149 if(idx == 0)
150 break;
151
152 if(drumUsed[idx]==1)
153 drumSet[idx]=gusload(fn);
154 while((c != '\n') && (c != 255) && (!eof(file)))
155 {
156 printf("loop");
157 c = readChar(file);
158 }
159 }
160 rb->close(file);
161}
162
163
164
165inline signed short int getSample(struct GWaveform * wf, unsigned int s)
166{
167
168 //16 bit samples
169 if(wf->mode&1)
170 {
171
172 if(s<<1 >= wf->wavSize)
173 {
174 // printf("\nSAMPLE OUT OF RANGE: s=%d 2s=%d ws=%d", s, 2*s, wf->wavSize);
175 return 0;
176 }
177
178
179 /*
180 * Probably put the signed/unsigned and and 8-16 bit conversion
181 * into the patch loader and have it run there, once.
182 */
183
184
185 //If they are unsigned, convert them to signed
186 //or was it the other way around. Whatever, it works
187 unsigned char b1=wf->data[s<<1]+((wf->mode & 2) << 6);
188 unsigned char b2=wf->data[(s<<1)|1]+((wf->mode & 2) << 6);
189 return (b1 | (b2<<8));
190 }
191 else
192 { //8-bit samples
193 unsigned char b1=wf->data[s]+((wf->mode & 2) << 6);
194 return b1<<8;
195 }
196}
197
198
199
200
201inline void setPoint(struct SynthObject * so, int pt)
202{
203 if(so->ch==9) //Drums, no ADSR
204 {
205 so->curOffset = 1<<27;
206 so->curRate = 1;
207 return;
208 }
209
210 if(so->wf==NULL)
211 {
212 printf("\nCrap... null waveform...");
213 exit(1);
214 }
215 if(so->wf->envRate==NULL)
216 {
217 printf("\nWaveform has no envelope set");
218 exit(1);
219 }
220
221 so->curPoint = pt;
222
223 int r=0;
224
225
226
227
228 int rate = so->wf->envRate[pt];
229
230 r=3-((rate>>6) & 0x3); // Some blatant Timidity code for rate conversion...
231 r*=3;
232 r = (rate & 0x3f) << r;
233
234 /*
235 Okay. This is the rate shift. Timidity defaults to 9, and sets
236 it to 10 if you use the fast decay option. Slow decay sounds better
237 on some files, except on some other files... you get chords that aren't
238 done decaying yet.. and they dont harmonize with the next chord and it
239 sounds like utter crap. Yes, even Timitidy does that. So I'm going to
240 default this to 10, and maybe later have an option to set it to 9
241 for longer decays.
242 */
243 so->curRate = r<<9;
244
245
246 so->targetOffset = so->wf->envOffset[pt]<<(20);
247 if(pt==0)
248 so->curOffset = 0;
249}
250
251
252long msi=0;
253
254inline void stopVoice(struct SynthObject * so)
255{
256 if(so->state == STATE_RAMPDOWN)
257 return;
258 so->state = STATE_RAMPDOWN;
259 so->decay = 255;
260
261}
262
263int rampDown = 0;
264
265inline signed short int synthVoice(int v)
266{
267 //Probably can combine these 2 lines into one..
268 //But for now, this looks more readable
269// struct GPatch * pat = patchSet[chPat[voices[v].ch]];
270// struct GWaveform * wf = pat->waveforms[pat->noteTable[voices[v].note]];
271 struct SynthObject * so = &voices[v];
272 struct GWaveform * wf = so->wf;
273
274 signed int s;
275
276 if(so->state != STATE_RAMPDOWN)
277 {
278 if(so->loopDir==LOOPDIR_FORWARD)
279 {
280 so->cp += so->delta;
281 }
282 else
283 {
284 so->cp -= so->delta;
285 }
286 }
287
288 if( (so->cp>>9 >= (wf->wavSize)) && (so->state != STATE_RAMPDOWN))
289 stopVoice(so);
290
291 /*
292 //Original, working, no interpolation
293 s=getSample(wf, (so->cp>>10));
294 */
295
296
297
298 int s2=getSample(wf, (so->cp>>10)+1);
299
300 if((wf->mode & (LOOP_REVERSE|LOOP_PINGPONG)) && so->loopState == STATE_LOOPING && (so->cp>>10 <= (wf->startLoop>>1)))
301 {
302 if(wf->mode & LOOP_REVERSE)
303 {
304 so->cp = (wf->endLoop)<<9;
305 s2=getSample(wf, (so->cp>>10));
306 } else
307 so->loopDir = LOOPDIR_FORWARD;
308 }
309
310 if((wf->mode & 28) && (so->cp>>10 >= wf->endLoop>>1))
311 {
312 so->loopState = STATE_LOOPING;
313 if((wf->mode & (24)) == 0)
314 {
315 so->cp = (wf->startLoop)<<9;
316 s2=getSample(wf, (so->cp>>10));
317 } else
318 so->loopDir = LOOPDIR_REVERSE;
319 }
320
321 //Better, working, linear interpolation
322 int s1=getSample(wf, (so->cp>>10));
323 s = s1 + ((long)((s2 - s1) * (so->cp & 1023))>>10);
324
325 if(so->curRate == 0)
326 stopVoice(so);
327
328
329 if(so->ch != 9) //Stupid ADSR code... and don't do ADSR for drums
330 {
331 if(so->curOffset < so->targetOffset)
332 {
333 so->curOffset += (so->curRate);
334 if(so -> curOffset > so->targetOffset && so->curPoint != 2)
335 {
336 if(so->curPoint != 5)
337 setPoint(so, so->curPoint+1);
338 else
339 stopVoice(so);
340 }
341 } else
342 {
343 so->curOffset -= (so->curRate);
344 if(so -> curOffset < so->targetOffset && so->curPoint != 2)
345 {
346
347 if(so->curPoint != 5)
348 setPoint(so, so->curPoint+1);
349 else
350 stopVoice(so);
351
352 }
353 }
354 }
355
356 if(so->curOffset < 0)
357 so->isUsed=0; //This is OK
358
359 s = s * (so->curOffset >> 22);
360 s = s>>6;
361
362
363 if(so->state == STATE_RAMPDOWN)
364 {
365 so->decay--;
366 if(so->decay == 0)
367 so->isUsed=0;
368 }
369
370 s = s * so->decay; s = s >> 9;
371
372 return s*((signed short int)so->vol*(signed short int)chVol[so->ch])>>14;
373}
374
375
376
377int mhL[16];
378int mhR[16];
379int mp=0; //Mix position, for circular array
380// Was stuff for Ghetto Lowpass Filter, now deprecated.
381
382
383inline void synthSample(int * mixL, int * mixR)
384{
385 int a=0;
386 signed long int leftMix=0, rightMix=0, sample=0;
387 for(a=0; a<MAX_VOICES; a++)
388 {
389 if(voices[a].isUsed==1)
390 {
391 sample = synthVoice(a);
392
393 leftMix += (sample*chPanLeft[voices[a].ch])>>7;
394 rightMix += (sample*chPanRight[voices[a].ch])>>7;
395 }
396 }
397
398 //TODO: Automatic Gain Control, anyone?
399 //Or, should this be implemented on the DSP's output volume instead?
400 *mixL = leftMix;
401 *mixR = rightMix;
402
403 return; //No more ghetto lowpass filter.. linear intrpolation works well.
404
405
406 // HACK HACK HACK
407 // This is the infamous Ghetto Lowpass Filter
408 // Now that I have linear interpolation, it should not be needed anymore.
409 /*
410 mp++;
411 if(mp==4)
412 mp=0;
413
414 mhL[mp]=leftMix;
415 mhR[mp]=rightMix;
416
417 *mixL = 0;
418 *mixR = 0;
419
420
421 for(a=0; a<4; a++)
422 {
423 *mixL += mhL[a];
424 *mixR += mhR[a];
425 }
426 *mixL = *mixL>>4;
427 *mixR = *mixR>>4;
428 */
429 // END HACK END HACK END HACK
430}