use the list context in credits which has to be defined for every target
[Rockbox.git] / apps / plugins / midi / synth.c
blobf0fa93d60e4016365f8b025c1454ebe6699c8c29
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 Stepan Moskovchenko
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "plugin.h"
20 #include "guspat.h"
21 #include "midiutil.h"
22 #include "synth.h"
24 extern struct plugin_api * rb;
26 void readTextBlock(int file, char * buf)
28 char c = 0;
31 c = readChar(file);
32 } while(c == '\n' || c == ' ' || c=='\t');
34 rb->lseek(file, -1, SEEK_CUR);
35 int cp = 0;
38 c = readChar(file);
39 buf[cp] = c;
40 cp++;
41 } while (c != '\n' && c != ' ' && c != '\t' && !eof(file));
42 buf[cp-1]=0;
43 rb->lseek(file, -1, SEEK_CUR);
46 /* Filename is the name of the config file */
47 /* The MIDI file should have been loaded at this point */
48 int initSynth(struct MIDIfile * mf, char * filename, char * drumConfig)
50 char patchUsed[128];
51 char drumUsed[128];
52 int a=0;
53 for(a=0; a<MAX_VOICES; a++)
55 voices[a].cp=0;
56 voices[a].vol=0;
57 voices[a].ch=0;
58 voices[a].isUsed=0;
59 voices[a].note=0;
62 for(a=0; a<16; a++)
64 chVol[a]=100; /* Default, not quite full blast.. */
65 chPan[a]=64; /* Center */
66 chPat[a]=0; /* Ac Gr Piano */
67 chPW[a]=256; /* .. not .. bent ? */
68 chPBDepth[a]=2; /* Default bend value is 2 */
70 for(a=0; a<128; a++)
72 patchSet[a]=NULL;
73 drumSet[a]=NULL;
74 patchUsed[a]=0;
75 drumUsed[a]=0;
79 * Always load the piano.
80 * Some files will assume its loaded without specifically
81 * issuing a Patch command... then we wonder why we can't hear anything
83 patchUsed[0]=1;
85 /* Scan the file to see what needs to be loaded */
86 if(mf != NULL)
88 for(a=0; a<mf->numTracks; a++)
90 unsigned int ts=0;
92 if(mf->tracks[a] == NULL)
94 printf("NULL TRACK !!!");
95 rb->splash(HZ*2, "Null Track in loader.");
96 return -1;
99 for(ts=0; ts<mf->tracks[a]->numEvents; ts++)
102 if((getEvent(mf->tracks[a], ts)->status) == (MIDI_NOTE_ON+9))
103 drumUsed[getEvent(mf->tracks[a], ts)->d1]=1;
105 if( (getEvent(mf->tracks[a], ts)->status & 0xF0) == MIDI_PRGM)
106 patchUsed[getEvent(mf->tracks[a], ts)->d1]=1;
109 } else
111 /* Initialize the whole drum set */
112 for(a=0; a<128; a++)
113 drumUsed[a]=1;
117 int file = rb->open(filename, O_RDONLY);
118 if(file < 0)
120 printf("");
121 printf("No MIDI patchset found.");
122 printf("Please install the instruments.");
123 printf("See Rockbox page for more info.");
125 rb->splash(HZ*2, "No Instruments");
126 return -1;
129 char name[40];
130 char fn[40];
132 /* Scan our config file and load the right patches as needed */
133 int c = 0;
134 name[0] = '\0';
135 printf("Loading instruments");
136 for(a=0; a<128; a++)
138 while(readChar(file)!=' ' && !eof(file));
139 readTextBlock(file, name);
141 rb->snprintf(fn, 40, ROCKBOX_DIR "/patchset/%s.pat", name);
142 /* printf("\nLOADING: <%s> ", fn); */
144 if(patchUsed[a]==1)
146 patchSet[a]=gusload(fn);
148 if(patchSet[a] == NULL) /* There was an error loading it */
149 return -1;
152 while((c != '\n'))
153 c = readChar(file);
155 rb->close(file);
157 file = rb->open(drumConfig, O_RDONLY);
158 if(file < 0)
160 rb->splash(HZ*2, "Bad drum config. Did you install the patchset?");
161 return -1;
164 /* Scan our config file and load the drum data */
165 int idx=0;
166 char number[30];
167 printf("Loading drums");
168 while(!eof(file))
170 readTextBlock(file, number);
171 readTextBlock(file, name);
172 rb->snprintf(fn, 40, ROCKBOX_DIR "/patchset/%s.pat", name);
174 idx = rb->atoi(number);
175 if(idx == 0)
176 break;
178 if(drumUsed[idx]==1)
180 drumSet[idx]=gusload(fn);
181 if(drumSet[idx] == NULL) /* Error loading patch */
182 return -1;
185 while((c != '\n') && (c != 255) && (!eof(file)))
186 c = readChar(file);
188 rb->close(file);
189 return 0;
192 #define getSample(s,wf) ((short *)(wf)->data)[s]
194 void setPoint(struct SynthObject * so, int pt) ICODE_ATTR;
195 void setPoint(struct SynthObject * so, int pt)
197 if(so->ch==9) /* Drums, no ADSR */
199 so->curOffset = 1<<27;
200 so->curRate = 1;
201 return;
204 if(so->wf==NULL)
206 printf("Crap... null waveform...");
207 exit(1);
209 if(so->wf->envRate==NULL)
211 printf("Waveform has no envelope set");
212 exit(1);
215 so->curPoint = pt;
217 int r;
218 int rate = so->wf->envRate[pt];
220 r=3-((rate>>6) & 0x3); /* Some blatant Timidity code for rate conversion... */
221 r*=3;
222 r = (rate & 0x3f) << r;
225 * Okay. This is the rate shift. Timidity defaults to 9, and sets
226 * it to 10 if you use the fast decay option. Slow decay sounds better
227 * on some files, except on some other files... you get chords that aren't
228 * done decaying yet.. and they dont harmonize with the next chord and it
229 * sounds like utter crap. Yes, even Timitidy does that. So I'm going to
230 * default this to 10, and maybe later have an option to set it to 9
231 * for longer decays.
233 so->curRate = r<<10;
236 * Do this here because the patches assume a 44100 sampling rate
237 * We've halved our sampling rate, ergo the ADSR code will be
238 * called half the time. Ergo, double the rate to keep stuff
239 * sounding right.
241 * Or just move the 1 up one line to optimize a tiny bit.
243 /* so->curRate = so->curRate << 1; */
246 so->targetOffset = so->wf->envOffset[pt]<<(20);
247 if(pt==0)
248 so->curOffset = 0;
251 inline void stopVoice(struct SynthObject * so)
253 if(so->state == STATE_RAMPDOWN)
254 return;
255 so->state = STATE_RAMPDOWN;
256 so->decay = 0;
259 static inline void synthVoice(struct SynthObject * so, int32_t * out, unsigned int samples)
261 struct GWaveform * wf;
262 register int s;
263 register int s1;
264 register int s2;
266 register unsigned int cp_temp = so->cp;
268 wf = so->wf;
270 const int mode_mask24 = wf->mode&24;
271 const int mode_mask28 = wf->mode&28;
272 const int mode_mask_looprev = wf->mode&LOOP_REVERSE;
274 const unsigned int num_samples = (wf->numSamples-1) << FRACTSIZE;
276 const unsigned int end_loop = wf->endLoop << FRACTSIZE;
277 const unsigned int start_loop = wf->startLoop << FRACTSIZE;
278 const int diff_loop = end_loop-start_loop;
280 while(samples > 0)
282 samples--;
283 /* Is voice being ramped? */
284 if(so->state == STATE_RAMPDOWN)
286 if(so->decay != 0) /* Ramp has been started */
288 so->decay = so->decay / 2;
290 if(so->decay < 10 && so->decay > -10)
291 so->isUsed = 0;
293 s1=so->decay;
294 s2 = s1*chPan[so->ch];
295 s1 = (s1<<7) -s2;
296 *(out++)+=(((s1&0x7FFF80) << 9) | ((s2&0x7FFF80) >> 7));
297 continue;
299 } else /* OK to advance voice */
301 cp_temp += so->delta;
304 s2 = getSample((cp_temp >> FRACTSIZE)+1, wf);
306 /* LOOP_REVERSE|LOOP_PINGPONG = 24 */
307 if(mode_mask24 && so->loopState == STATE_LOOPING && (cp_temp < start_loop))
309 if(mode_mask_looprev)
311 cp_temp += diff_loop;
312 s2=getSample((cp_temp >> FRACTSIZE), wf);
314 else
316 so->delta = -so->delta; /* At this point cp_temp is wrong. We need to take a step */
317 so->loopDir = LOOPDIR_FORWARD;
321 if(mode_mask28 && (cp_temp >= end_loop))
323 so->loopState = STATE_LOOPING;
324 if(!mode_mask24)
326 cp_temp -= diff_loop;
327 s2=getSample((cp_temp >> FRACTSIZE), wf);
329 else
331 so->delta = -so->delta;
332 so->loopDir = LOOPDIR_REVERSE;
336 /* Have we overrun? */
337 if(cp_temp >= num_samples)
339 cp_temp -= so->delta;
340 s2 = getSample((cp_temp >> FRACTSIZE)+1, wf);
341 stopVoice(so);
344 /* Better, working, linear interpolation */
345 s1=getSample((cp_temp >> FRACTSIZE), wf);
347 s = s1 + ((signed)((s2 - s1) * (cp_temp & ((1<<FRACTSIZE)-1)))>>FRACTSIZE);
349 if(so->curRate == 0)
351 stopVoice(so);
352 // so->isUsed = 0;
356 if(so->ch != 9 && so->state != STATE_RAMPDOWN) /* Stupid ADSR code... and don't do ADSR for drums */
358 if(so->curOffset < so->targetOffset)
360 so->curOffset += (so->curRate);
361 if(so -> curOffset > so->targetOffset && so->curPoint != 2)
363 if(so->curPoint != 5)
365 setPoint(so, so->curPoint+1);
367 else
369 stopVoice(so);
372 } else
374 so->curOffset -= (so->curRate);
375 if(so -> curOffset < so->targetOffset && so->curPoint != 2)
378 if(so->curPoint != 5)
380 setPoint(so, so->curPoint+1);
382 else
384 stopVoice(so);
391 if(so->curOffset < 0)
393 so->curOffset = so->targetOffset;
394 stopVoice(so);
397 s = (s * (so->curOffset >> 22) >> 8);
399 /* need to set ramp beginning */
400 if(so->state == STATE_RAMPDOWN && so->decay == 0)
402 so->decay = s*so->volscale>>14;
403 if(so->decay == 0)
404 so->decay = 1; /* stupid junk.. */
408 /* Scaling by channel volume and note volume is done in sequencer.c */
409 /* That saves us some multiplication and pointer operations */
410 s1=s*so->volscale>>14;
412 s2 = s1*chPan[so->ch];
413 s1 = (s1<<7) - s2;
414 *(out++)+=(((s1&0x7FFF80) << 9) | ((s2&0x7FFF80) >> 7));
418 so->cp=cp_temp; /* store this again */
419 return;
422 /* buffer to hold all the samples for the current tick, this is a hack
423 neccesary for coldfire targets as pcm_play_data uses the dma which cannot
424 access iram */
425 int32_t samp_buf[256] IBSS_ATTR;
427 /* synth num_samples samples and write them to the */
428 /* buffer pointed to by buf_ptr */
429 void synthSamples(int32_t *buf_ptr, unsigned int num_samples) ICODE_ATTR;
430 void synthSamples(int32_t *buf_ptr, unsigned int num_samples)
432 int i;
433 struct SynthObject *voicept;
435 rb->memset(samp_buf, 0, num_samples*4);
437 for(i=0; i < MAX_VOICES; i++)
439 voicept=&voices[i];
440 if(voicept->isUsed==1)
442 synthVoice(voicept, samp_buf, num_samples);
446 rb->memcpy(buf_ptr, samp_buf, num_samples*4);
448 /* TODO: Automatic Gain Control, anyone? */
449 /* Or, should this be implemented on the DSP's output volume instead? */
451 return; /* No more ghetto lowpass filter. Linear interpolation works well. */