1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
24 extern struct plugin_api
* rb
;
26 void readTextBlock(int file
, char * buf
)
32 } while(c
== '\n' || c
== ' ' || c
=='\t');
34 rb
->lseek(file
, -1, SEEK_CUR
);
41 } while (c
!= '\n' && c
!= ' ' && c
!= '\t' && !eof(file
));
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
)
53 for(a
=0; a
<MAX_VOICES
; 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 */
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
85 /* Scan the file to see what needs to be loaded */
88 for(a
=0; a
<mf
->numTracks
; a
++)
92 if(mf
->tracks
[a
] == NULL
)
94 printf("NULL TRACK !!!");
95 rb
->splash(HZ
*2, "Null Track in loader.");
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;
111 /* Initialize the whole drum set */
117 int file
= rb
->open(filename
, O_RDONLY
);
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");
132 /* Scan our config file and load the right patches as needed */
135 printf("Loading instruments");
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); */
146 patchSet
[a
]=gusload(fn
);
148 if(patchSet
[a
] == NULL
) /* There was an error loading it */
157 file
= rb
->open(drumConfig
, O_RDONLY
);
160 rb
->splash(HZ
*2, "Bad drum config. Did you install the patchset?");
164 /* Scan our config file and load the drum data */
167 printf("Loading drums");
170 readTextBlock(file
, number
);
171 readTextBlock(file
, name
);
172 rb
->snprintf(fn
, 40, ROCKBOX_DIR
"/patchset/%s.pat", name
);
174 idx
= rb
->atoi(number
);
180 drumSet
[idx
]=gusload(fn
);
181 if(drumSet
[idx
] == NULL
) /* Error loading patch */
185 while((c
!= '\n') && (c
!= 255) && (!eof(file
)))
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;
206 printf("Crap... null waveform...");
209 if(so
->wf
->envRate
==NULL
)
211 printf("Waveform has no envelope set");
218 int rate
= so
->wf
->envRate
[pt
];
220 r
=3-((rate
>>6) & 0x3); /* Some blatant Timidity code for rate conversion... */
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
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
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);
251 inline void stopVoice(struct SynthObject
* so
)
253 if(so
->state
== STATE_RAMPDOWN
)
255 so
->state
= STATE_RAMPDOWN
;
259 static inline void synthVoice(struct SynthObject
* so
, int32_t * out
, unsigned int samples
)
261 struct GWaveform
* wf
;
266 register unsigned int cp_temp
= so
->cp
;
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
;
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)
294 s2
= s1
*chPan
[so
->ch
];
296 *(out
++)+=(((s1
&0x7FFF80) << 9) | ((s2
&0x7FFF80) >> 7));
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
);
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
;
326 cp_temp
-= diff_loop
;
327 s2
=getSample((cp_temp
>> FRACTSIZE
), wf
);
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
);
344 /* Better, working, linear interpolation */
345 s1
=getSample((cp_temp
>> FRACTSIZE
), wf
);
347 s
= s1
+ ((signed)((s2
- s1
) * (cp_temp
& ((1<<FRACTSIZE
)-1)))>>FRACTSIZE
);
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);
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);
391 if(so
->curOffset
< 0)
393 so
->curOffset
= so
->targetOffset
;
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;
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
];
414 *(out
++)+=(((s1
&0x7FFF80) << 9) | ((s2
&0x7FFF80) >> 7));
418 so
->cp
=cp_temp
; /* store this again */
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
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
)
433 struct SynthObject
*voicept
;
435 rb
->memset(samp_buf
, 0, num_samples
*4);
437 for(i
=0; i
< MAX_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. */