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.c650
1 files changed, 319 insertions, 331 deletions
diff --git a/apps/plugins/midi/synth.c b/apps/plugins/midi/synth.c
index bafaf1c7ca..50becf32a4 100644
--- a/apps/plugins/midi/synth.c
+++ b/apps/plugins/midi/synth.c
@@ -20,158 +20,164 @@ extern struct plugin_api * rb;
20 20
21struct Event * getEvent(struct Track * tr, int evNum) 21struct Event * getEvent(struct Track * tr, int evNum)
22{ 22{
23 return tr->dataBlock + (evNum*sizeof(struct Event)); 23 return tr->dataBlock + (evNum*sizeof(struct Event));
24} 24}
25 25
26void readTextBlock(int file, char * buf) 26void readTextBlock(int file, char * buf)
27{ 27{
28 char c = 0; 28 char c = 0;
29 do 29 do
30 { 30 {
31 c = readChar(file); 31 c = readChar(file);
32 } while(c == '\n' || c == ' ' || c=='\t'); 32 } while(c == '\n' || c == ' ' || c=='\t');
33 33
34 rb->lseek(file, -1, SEEK_CUR); 34 rb->lseek(file, -1, SEEK_CUR);
35 int cp = 0; 35 int cp = 0;
36 do 36 do
37 { 37 {
38 c = readChar(file); 38 c = readChar(file);
39 buf[cp] = c; 39 buf[cp] = c;
40 cp++; 40 cp++;
41 } while (c != '\n' && c != ' ' && c != '\t' && !eof(file)); 41 } while (c != '\n' && c != ' ' && c != '\t' && !eof(file));
42 buf[cp-1]=0; 42 buf[cp-1]=0;
43 rb->lseek(file, -1, SEEK_CUR); 43 rb->lseek(file, -1, SEEK_CUR);
44} 44}
45 45
46 46
47 47
48//Filename is the name of the config file 48/* Filename is the name of the config file */
49//The MIDI file should have been loaded at this point 49/* The MIDI file should have been loaded at this point */
50int initSynth(struct MIDIfile * mf, char * filename, char * drumConfig) 50int initSynth(struct MIDIfile * mf, char * filename, char * drumConfig)
51{ 51{
52 char patchUsed[128]; 52 char patchUsed[128];
53 char drumUsed[128]; 53 char drumUsed[128];
54 int a=0; 54 int a=0;
55 for(a=0; a<MAX_VOICES; a++) 55 for(a=0; a<MAX_VOICES; a++)
56 { 56 {
57 voices[a].cp=0; 57 voices[a].cp=0;
58 voices[a].vol=0; 58 voices[a].vol=0;
59 voices[a].ch=0; 59 voices[a].ch=0;
60 voices[a].isUsed=0; 60 voices[a].isUsed=0;
61 voices[a].note=0; 61 voices[a].note=0;
62 } 62 }
63 63
64 for(a=0; a<16; a++) 64 for(a=0; a<16; a++)
65 { 65 {
66 chVol[a]=100; //Default, not quite full blast.. 66 chVol[a]=100; /* Default, not quite full blast.. */
67 chPanLeft[a]=64; //Center 67 chPanLeft[a]=64; /* Center */
68 chPanRight[a]=64; //Center 68 chPanRight[a]=64; /* Center */
69 chPat[a]=0; //Ac Gr Piano 69 chPat[a]=0; /* Ac Gr Piano */
70 chPW[a]=64; // .. not .. bent ? 70 chPW[a]=64; /* .. not .. bent ? */
71 } 71 }
72 for(a=0; a<128; a++) 72 for(a=0; a<128; a++)
73 { 73 {
74 patchSet[a]=NULL; 74 patchSet[a]=NULL;
75 drumSet[a]=NULL; 75 drumSet[a]=NULL;
76 patchUsed[a]=0; 76 patchUsed[a]=0;
77 drumUsed[a]=0; 77 drumUsed[a]=0;
78 } 78 }
79 79
80 //Always load the piano. 80 /*
81 //Some files will assume its loaded without specifically 81 * Always load the piano.
82 //issuing a Patch command... then we wonder why we can't hear anything 82 * Some files will assume its loaded without specifically
83 patchUsed[0]=1; 83 * issuing a Patch command... then we wonder why we can't hear anything
84 84 */
85 //Scan the file to see what needs to be loaded 85 patchUsed[0]=1;
86 for(a=0; a<mf->numTracks; a++) 86
87 { 87 /* Scan the file to see what needs to be loaded */
88 int ts=0; 88 for(a=0; a<mf->numTracks; a++)
89 89 {
90 if(mf->tracks[a] == NULL) 90 unsigned int ts=0;
91 { 91
92 printf("\nNULL TRACK !!!"); 92 if(mf->tracks[a] == NULL)
93 rb->splash(HZ*2, true, "Null Track in loader."); 93 {
94 return -1; 94 printf("\nNULL TRACK !!!");
95 } 95 rb->splash(HZ*2, true, "Null Track in loader.");
96 96 return -1;
97 for(ts=0; ts<mf->tracks[a]->numEvents; ts++) 97 }
98 { 98
99 99 for(ts=0; ts<mf->tracks[a]->numEvents; ts++)
100 if((getEvent(mf->tracks[a], ts)->status) == (MIDI_NOTE_ON+9)) 100 {
101 drumUsed[getEvent(mf->tracks[a], ts)->d1]=1; 101
102 102 if((getEvent(mf->tracks[a], ts)->status) == (MIDI_NOTE_ON+9))
103 if( (getEvent(mf->tracks[a], ts)->status & 0xF0) == MIDI_PRGM) 103 drumUsed[getEvent(mf->tracks[a], ts)->d1]=1;
104 { 104
105 if(patchUsed[getEvent(mf->tracks[a], ts)->d1]==0) 105 if( (getEvent(mf->tracks[a], ts)->status & 0xF0) == MIDI_PRGM)
106 printf("\nI need to load patch %d.", getEvent(mf->tracks[a], ts)->d1); 106 {
107 patchUsed[getEvent(mf->tracks[a], ts)->d1]=1; 107 if(patchUsed[getEvent(mf->tracks[a], ts)->d1]==0)
108 } 108 printf("\nI need to load patch %d.", getEvent(mf->tracks[a], ts)->d1);
109 } 109 patchUsed[getEvent(mf->tracks[a], ts)->d1]=1;
110 } 110 }
111 111 }
112 int file = rb->open(filename, O_RDONLY); 112 }
113 if(file == -1) 113
114 { 114 int file = rb->open(filename, O_RDONLY);
115 rb->splash(HZ*2, true, "Bad patch config.\nDid you install the patchset?"); 115 if(file == -1)
116 return -1; 116 {
117 } 117 rb->splash(HZ*2, true, "Bad patch config.\nDid you install the patchset?");
118 118 return -1;
119 char name[40]; 119 }
120 char fn[40]; 120
121 121 char name[40];
122 //Scan our config file and load the right patches as needed 122 char fn[40];
123 int c = 0; 123
124 rb->snprintf(name, 40, ""); 124 /* Scan our config file and load the right patches as needed */
125 for(a=0; a<128; a++) 125 int c = 0;
126 { 126 rb->snprintf(name, 40, "");
127 while(readChar(file)!=' ' && !eof(file)); 127 for(a=0; a<128; a++)
128 readTextBlock(file, name); 128 {
129 129 while(readChar(file)!=' ' && !eof(file));
130 rb->snprintf(fn, 40, "/.rockbox/patchset/%s.pat", name); 130 readTextBlock(file, name);
131 printf("\nLOADING: <%s> ", fn); 131
132 132 rb->snprintf(fn, 40, "/.rockbox/patchset/%s.pat", name);
133 if(patchUsed[a]==1) 133 printf("\nLOADING: <%s> ", fn);
134 patchSet[a]=gusload(fn); 134
135 135 if(patchUsed[a]==1)
136// if(patchSet[a] == NULL) 136 {
137// return -1; 137 patchSet[a]=gusload(fn);
138 138
139 while((c != '\n')) 139 if(patchSet[a] == NULL) /* There was an error loading it */
140 c = readChar(file); 140 return -1;
141 } 141 }
142 rb->close(file); 142
143 143 while((c != '\n'))
144 file = rb->open(drumConfig, O_RDONLY); 144 c = readChar(file);
145 if(file == -1) 145 }
146 { 146 rb->close(file);
147 rb->splash(HZ*2, true, "Bad drum config.\nDid you install the patchset?"); 147
148 return -1; 148 file = rb->open(drumConfig, O_RDONLY);
149 } 149 if(file == -1)
150 150 {
151 //Scan our config file and load the drum data 151 rb->splash(HZ*2, true, "Bad drum config.\nDid you install the patchset?");
152 int idx=0; 152 return -1;
153 char number[30]; 153 }
154 while(!eof(file)) 154
155 { 155 /* Scan our config file and load the drum data */
156 readTextBlock(file, number); 156 int idx=0;
157 readTextBlock(file, name); 157 char number[30];
158 rb->snprintf(fn, 40, "/.rockbox/patchset/%s.pat", name); 158 while(!eof(file))
159 159 {
160 idx = rb->atoi(number); 160 readTextBlock(file, number);
161 if(idx == 0) 161 readTextBlock(file, name);
162 break; 162 rb->snprintf(fn, 40, "/.rockbox/patchset/%s.pat", name);
163 163
164 if(drumUsed[idx]==1) 164 idx = rb->atoi(number);
165 drumSet[idx]=gusload(fn); 165 if(idx == 0)
166 166 break;
167// if(drumSet[idx] == NULL) 167
168// return -1; 168 if(drumUsed[idx]==1)
169 169 {
170 while((c != '\n') && (c != 255) && (!eof(file))) 170 drumSet[idx]=gusload(fn);
171 c = readChar(file); 171
172 } 172 if(drumSet[idx] == NULL) /* Error loading patch */
173 rb->close(file); 173 return -1;
174 return 0; 174 }
175
176 while((c != '\n') && (c != 255) && (!eof(file)))
177 c = readChar(file);
178 }
179 rb->close(file);
180 return 0;
175} 181}
176 182
177 183
@@ -182,7 +188,7 @@ struct GWaveform * wf IDATA_ATTR;
182int s IDATA_ATTR; 188int s IDATA_ATTR;
183short s1 IDATA_ATTR; 189short s1 IDATA_ATTR;
184short s2 IDATA_ATTR; 190short s2 IDATA_ATTR;
185short sample IDATA_ATTR; //For synthSample 191short sample IDATA_ATTR; /* For synthSample */
186unsigned int cpShifted IDATA_ATTR; 192unsigned int cpShifted IDATA_ATTR;
187 193
188unsigned char b1 IDATA_ATTR; 194unsigned char b1 IDATA_ATTR;
@@ -191,31 +197,9 @@ unsigned char b2 IDATA_ATTR;
191 197
192inline int getSample(int s) 198inline int getSample(int s)
193{ 199{
194 200 /* Sign conversion moved to guspat.c */
195 //16 bit samples 201 /* 8bit conversion NOT YET IMPLEMENTED in guspat.c */
196 if(wf->mode&1) 202 return ((short *) wf->data)[s];
197 {
198
199 if(s<<1 >= wf->wavSize)
200 {
201 printf("\n!!!!! %d \t %d", s<<1, wf->wavSize);
202 return 0;
203 }
204// signed short a = ((short *)wf->data)[s];
205
206 //Sign conversion moved into guspat.c
207 b1=wf->data[s<<1]+((wf->mode & 2) << 6);
208 b2=wf->data[(s<<1)|1]+((wf->mode & 2) << 6);
209 return (b1 | (b2<<8)) ;
210
211 //Get rid of this sometime
212 }
213 else
214 { //8-bit samples
215 //Do we even have anything 8-bit in our set?
216 int b1=wf->data[s]+((wf->mode & 2) << 6);
217 return b1<<8;
218 }
219} 203}
220 204
221 205
@@ -223,190 +207,194 @@ inline int getSample(int s)
223 207
224inline void setPoint(struct SynthObject * so, int pt) 208inline void setPoint(struct SynthObject * so, int pt)
225{ 209{
226 if(so->ch==9) //Drums, no ADSR 210 if(so->ch==9) /* Drums, no ADSR */
227 { 211 {
228 so->curOffset = 1<<27; 212 so->curOffset = 1<<27;
229 so->curRate = 1; 213 so->curRate = 1;
230 return; 214 return;
231 } 215 }
232 216
233 if(so->wf==NULL) 217 if(so->wf==NULL)
234 { 218 {
235 printf("\nCrap... null waveform..."); 219 printf("\nCrap... null waveform...");
236 exit(1); 220 exit(1);
237 } 221 }
238 if(so->wf->envRate==NULL) 222 if(so->wf->envRate==NULL)
239 { 223 {
240 printf("\nWaveform has no envelope set"); 224 printf("\nWaveform has no envelope set");
241 exit(1); 225 exit(1);
242 } 226 }
243 227
244 so->curPoint = pt; 228 so->curPoint = pt;
245 229
246 int r=0; 230 int r=0;
247 int rate = so->wf->envRate[pt]; 231 int rate = so->wf->envRate[pt];
248 232
249 r=3-((rate>>6) & 0x3); // Some blatant Timidity code for rate conversion... 233 r=3-((rate>>6) & 0x3); /* Some blatant Timidity code for rate conversion... */
250 r*=3; 234 r*=3;
251 r = (rate & 0x3f) << r; 235 r = (rate & 0x3f) << r;
252 236
253 /* 237 /*
254 Okay. This is the rate shift. Timidity defaults to 9, and sets 238 * Okay. This is the rate shift. Timidity defaults to 9, and sets
255 it to 10 if you use the fast decay option. Slow decay sounds better 239 * it to 10 if you use the fast decay option. Slow decay sounds better
256 on some files, except on some other files... you get chords that aren't 240 * on some files, except on some other files... you get chords that aren't
257 done decaying yet.. and they dont harmonize with the next chord and it 241 * done decaying yet.. and they dont harmonize with the next chord and it
258 sounds like utter crap. Yes, even Timitidy does that. So I'm going to 242 * sounds like utter crap. Yes, even Timitidy does that. So I'm going to
259 default this to 10, and maybe later have an option to set it to 9 243 * default this to 10, and maybe later have an option to set it to 9
260 for longer decays. 244 * for longer decays.
261 */ 245 */
262 so->curRate = r<<10; 246 so->curRate = r<<10;
263 247
264 //Do this here because the patches assume a 44100 sampling rate 248 /*
265 //We've halved our sampling rate, ergo the ADSR code will be 249 * Do this here because the patches assume a 44100 sampling rate
266 //called half the time. Ergo, double the rate to keep stuff 250 * We've halved our sampling rate, ergo the ADSR code will be
267 //sounding right. 251 * called half the time. Ergo, double the rate to keep stuff
268 so->curRate = so->curRate << 1; 252 * sounding right.
269 253 */
270 254 so->curRate = so->curRate << 1;
271 so->targetOffset = so->wf->envOffset[pt]<<(20); 255
272 if(pt==0) 256
273 so->curOffset = 0; 257 so->targetOffset = so->wf->envOffset[pt]<<(20);
258 if(pt==0)
259 so->curOffset = 0;
274} 260}
275 261
276 262
277inline void stopVoice(struct SynthObject * so) 263inline void stopVoice(struct SynthObject * so)
278{ 264{
279 if(so->state == STATE_RAMPDOWN) 265 if(so->state == STATE_RAMPDOWN)
280 return; 266 return;
281 so->state = STATE_RAMPDOWN; 267 so->state = STATE_RAMPDOWN;
282 so->decay = 255; 268 so->decay = 255;
283 269
284} 270}
285 271
286 272
287inline signed short int synthVoice() 273inline signed short int synthVoice()
288{ 274{
289 so = &voices[currentVoice]; 275 so = &voices[currentVoice];
290 wf = so->wf; 276 wf = so->wf;
291 277
292 278
293 if(so->state != STATE_RAMPDOWN) 279 if(so->state != STATE_RAMPDOWN)
294 { 280 {
295 so->cp += so->delta; 281 so->cp += so->delta;
296 } 282 }
297 283
298 cpShifted = so->cp >> 10; 284 cpShifted = so->cp >> 10;
299 285
300 if( (cpShifted >= (wf->wavSize>>1)) && (so->state != STATE_RAMPDOWN)) 286 if( (cpShifted > (wf->numSamples) && (so->state != STATE_RAMPDOWN)))
301 stopVoice(so); 287 {
288 stopVoice(so);
289 }
302 290
303 s2 = getSample((cpShifted)+1); 291 s2 = getSample((cpShifted)+1);
304 292
305 if((wf->mode & (LOOP_REVERSE|LOOP_PINGPONG)) && so->loopState == STATE_LOOPING && (cpShifted <= (wf->startLoop>>1))) 293 /* LOOP_REVERSE|LOOP_PINGPONG = 24 */
306 { 294 if((wf->mode & (24)) && so->loopState == STATE_LOOPING && (cpShifted <= (wf->startLoop)))
307 if(wf->mode & LOOP_REVERSE) 295 {
308 { 296 if(wf->mode & LOOP_REVERSE)
309 so->cp = (wf->endLoop)<<9; 297 {
310 cpShifted = so->cp >> 10; 298 so->cp = (wf->endLoop)<<10;
311 s2=getSample((cpShifted)); 299 cpShifted = wf->endLoop;
312 } else 300 s2=getSample((cpShifted));
313 { 301 } else
314 so->delta = -so->delta; 302 {
315 so->loopDir = LOOPDIR_FORWARD; 303 so->delta = -so->delta;
316 } 304 so->loopDir = LOOPDIR_FORWARD;
317 } 305 }
318 306 }
319 if((wf->mode & 28) && (so->cp>>10 >= wf->endLoop>>1)) 307
320 { 308 if((wf->mode & 28) && (cpShifted >= wf->endLoop))
321 so->loopState = STATE_LOOPING; 309 {
322 if((wf->mode & (24)) == 0) 310 so->loopState = STATE_LOOPING;
323 { 311 if((wf->mode & (24)) == 0)
324 so->cp = (wf->startLoop)<<9; 312 {
325 cpShifted = so->cp >> 10; 313 so->cp = (wf->startLoop)<<10;
326 s2=getSample((cpShifted)); 314 cpShifted = wf->startLoop;
327 } else 315 s2=getSample((cpShifted));
328 { 316 } else
329 so->delta = -so->delta; 317 {
330 so->loopDir = LOOPDIR_REVERSE; 318 so->delta = -so->delta;
331 } 319 so->loopDir = LOOPDIR_REVERSE;
332 } 320 }
333 321 }
334 //Better, working, linear interpolation 322
335 s1=getSample((cpShifted)); 323 /* Better, working, linear interpolation */
336 s = s1 + ((signed)((s2 - s1) * (so->cp & 1023))>>10); 324 s1=getSample((cpShifted));
337 325 s = s1 + ((signed)((s2 - s1) * (so->cp & 1023))>>10);
338 326
339//ADSR COMMENT WOULD GO FROM HERE......... 327
340 328/* ADSR COMMENT WOULD GO FROM HERE.........*/
341 if(so->curRate == 0) 329
342 stopVoice(so); 330 if(so->curRate == 0)
343 331 stopVoice(so);
344 332
345 if(so->ch != 9) //Stupid ADSR code... and don't do ADSR for drums 333
346 { 334 if(so->ch != 9) /* Stupid ADSR code... and don't do ADSR for drums */
347 if(so->curOffset < so->targetOffset) 335 {
348 { 336 if(so->curOffset < so->targetOffset)
349 so->curOffset += (so->curRate); 337 {
350 if(so -> curOffset > so->targetOffset && so->curPoint != 2) 338 so->curOffset += (so->curRate);
351 { 339 if(so -> curOffset > so->targetOffset && so->curPoint != 2)
352 if(so->curPoint != 5) 340 {
353 setPoint(so, so->curPoint+1); 341 if(so->curPoint != 5)
354 else 342 setPoint(so, so->curPoint+1);
355 stopVoice(so); 343 else
356 } 344 stopVoice(so);
357 } else 345 }
358 { 346 } else
359 so->curOffset -= (so->curRate); 347 {
360 if(so -> curOffset < so->targetOffset && so->curPoint != 2) 348 so->curOffset -= (so->curRate);
361 { 349 if(so -> curOffset < so->targetOffset && so->curPoint != 2)
362 350 {
363 if(so->curPoint != 5) 351
364 setPoint(so, so->curPoint+1); 352 if(so->curPoint != 5)
365 else 353 setPoint(so, so->curPoint+1);
366 stopVoice(so); 354 else
367 355 stopVoice(so);
368 } 356
369 } 357 }
370 } 358 }
371 359 }
372 if(so->curOffset < 0) 360
373 so->isUsed=0; //This is OK 361 if(so->curOffset < 0)
374 362 so->isUsed=0; /* This is OK because offset faded it out already */
375 363
376 s = (s * (so->curOffset >> 22) >> 6); 364
377 365 s = (s * (so->curOffset >> 22) >> 8);
378// ............. TO HERE 366
379 367/* ............. TO HERE */
380 368
381 if(so->state == STATE_RAMPDOWN) 369
382 { 370 if(so->state == STATE_RAMPDOWN)
383 so->decay--; 371 {
384 if(so->decay == 0) 372 so->decay--;
385 so->isUsed=0; 373 if(so->decay == 0)
386 } 374 so->isUsed=0;
387 375 s = (s * so->decay) >> 8;
388 s = s * so->decay; s = s >> 10; 376 }
389 377
390 return s*((signed short int)so->vol*(signed short int)chVol[so->ch])>>14; 378 return s*((signed short int)so->vol*(signed short int)chVol[so->ch])>>14;
391} 379}
392 380
393 381
394inline void synthSample(int * mixL, int * mixR) 382inline void synthSample(int * mixL, int * mixR)
395{ 383{
396// signed int leftMix=0, rightMix=0, 384 *mixL = 0;
397 *mixL = 0; 385 *mixR = 0;
398 *mixR = 0; 386 for(currentVoice=0; currentVoice<MAX_VOICES; currentVoice++)
399 for(currentVoice=0; currentVoice<MAX_VOICES; currentVoice++) 387 {
400 { 388 if(voices[currentVoice].isUsed==1)
401 if(voices[currentVoice].isUsed==1) 389 {
402 { 390 sample = synthVoice(currentVoice);
403 sample = synthVoice(currentVoice); 391 *mixL += (sample*chPanLeft[voices[currentVoice].ch])>>7;
404 *mixL += (sample*chPanLeft[voices[currentVoice].ch])>>7; 392 *mixR += (sample*chPanRight[voices[currentVoice].ch])>>7;
405 *mixR += (sample*chPanRight[voices[currentVoice].ch])>>7; 393 }
406 } 394 }
407 } 395
408 396 /* TODO: Automatic Gain Control, anyone? */
409 //TODO: Automatic Gain Control, anyone? 397 /* Or, should this be implemented on the DSP's output volume instead? */
410 //Or, should this be implemented on the DSP's output volume instead? 398
411 return; //No more ghetto lowpass filter.. linear intrpolation works well. 399 return; /* No more ghetto lowpass filter.. linear intrpolation works well. */
412} 400}