diff options
author | Stepan Moskovchenko <stevenm@rockbox.org> | 2007-10-15 05:11:37 +0000 |
---|---|---|
committer | Stepan Moskovchenko <stevenm@rockbox.org> | 2007-10-15 05:11:37 +0000 |
commit | 1515ff852224c822a6d3db8c458eab2c9037704f (patch) | |
tree | e427fbec1b397d18abffc12b7fe74e67c2cad807 /apps/plugins/midi/synth.c | |
parent | 99f955088149d5938ce4c9ca5624377f464b1380 (diff) | |
download | rockbox-1515ff852224c822a6d3db8c458eab2c9037704f.tar.gz rockbox-1515ff852224c822a6d3db8c458eab2c9037704f.zip |
MIDI: At long last, though quick and dirty, pitch bend depth! Or, I think it works. Tested on two
files. Let me know if anyone discovers any problems with this. This commit also includes Nils's synth
loop optimization patch. I hope committing it does not cause problems.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15112 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/midi/synth.c')
-rw-r--r-- | apps/plugins/midi/synth.c | 261 |
1 files changed, 133 insertions, 128 deletions
diff --git a/apps/plugins/midi/synth.c b/apps/plugins/midi/synth.c index 568c7bb1ce..f0fa93d60e 100644 --- a/apps/plugins/midi/synth.c +++ b/apps/plugins/midi/synth.c | |||
@@ -65,6 +65,7 @@ int initSynth(struct MIDIfile * mf, char * filename, char * drumConfig) | |||
65 | chPan[a]=64; /* Center */ | 65 | chPan[a]=64; /* Center */ |
66 | chPat[a]=0; /* Ac Gr Piano */ | 66 | chPat[a]=0; /* Ac Gr Piano */ |
67 | chPW[a]=256; /* .. not .. bent ? */ | 67 | chPW[a]=256; /* .. not .. bent ? */ |
68 | chPBDepth[a]=2; /* Default bend value is 2 */ | ||
68 | } | 69 | } |
69 | for(a=0; a<128; a++) | 70 | for(a=0; a<128; a++) |
70 | { | 71 | { |
@@ -255,191 +256,195 @@ inline void stopVoice(struct SynthObject * so) | |||
255 | so->decay = 0; | 256 | so->decay = 0; |
256 | } | 257 | } |
257 | 258 | ||
258 | static inline int synthVoice(struct SynthObject * so) | 259 | static inline void synthVoice(struct SynthObject * so, int32_t * out, unsigned int samples) |
259 | { | 260 | { |
260 | struct GWaveform * wf; | 261 | struct GWaveform * wf; |
261 | register int s; | 262 | register int s; |
262 | register unsigned int cpShifted; | 263 | register int s1; |
263 | register short s1; | 264 | register int s2; |
264 | register short s2; | 265 | |
266 | register unsigned int cp_temp = so->cp; | ||
265 | 267 | ||
266 | wf = so->wf; | 268 | wf = so->wf; |
267 | 269 | ||
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; | ||
268 | 273 | ||
269 | /* Is voice being ramped? */ | 274 | const unsigned int num_samples = (wf->numSamples-1) << FRACTSIZE; |
270 | if(so->state == STATE_RAMPDOWN) | ||
271 | { | ||
272 | if(so->decay != 0) /* Ramp has been started */ | ||
273 | { | ||
274 | so->decay = so->decay / 2; | ||
275 | 275 | ||
276 | if(so->decay < 10 && so->decay > -10) | 276 | const unsigned int end_loop = wf->endLoop << FRACTSIZE; |
277 | so->isUsed = 0; | 277 | const unsigned int start_loop = wf->startLoop << FRACTSIZE; |
278 | const int diff_loop = end_loop-start_loop; | ||
278 | 279 | ||
279 | return so->decay; | 280 | while(samples > 0) |
280 | } | ||
281 | } else /* OK to advance voice */ | ||
282 | { | 281 | { |
283 | so->cp += so->delta; | 282 | samples--; |
284 | } | 283 | /* Is voice being ramped? */ |
285 | 284 | if(so->state == STATE_RAMPDOWN) | |
286 | 285 | { | |
287 | cpShifted = so->cp >> FRACTSIZE; | 286 | if(so->decay != 0) /* Ramp has been started */ |
287 | { | ||
288 | so->decay = so->decay / 2; | ||
288 | 289 | ||
290 | if(so->decay < 10 && so->decay > -10) | ||
291 | so->isUsed = 0; | ||
289 | 292 | ||
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; | ||
298 | } | ||
299 | } else /* OK to advance voice */ | ||
300 | { | ||
301 | cp_temp += so->delta; | ||
302 | } | ||
290 | 303 | ||
291 | s2 = getSample((cpShifted)+1, wf); | 304 | s2 = getSample((cp_temp >> FRACTSIZE)+1, wf); |
292 | 305 | ||
293 | /* LOOP_REVERSE|LOOP_PINGPONG = 24 */ | 306 | /* LOOP_REVERSE|LOOP_PINGPONG = 24 */ |
294 | if((wf->mode & (24)) && so->loopState == STATE_LOOPING && (cpShifted < (wf->startLoop))) | 307 | if(mode_mask24 && so->loopState == STATE_LOOPING && (cp_temp < start_loop)) |
295 | { | ||
296 | if(wf->mode & LOOP_REVERSE) | ||
297 | { | ||
298 | cpShifted = wf->endLoop-(wf->startLoop-cpShifted); | ||
299 | so->cp = (cpShifted)<<FRACTSIZE; | ||
300 | s2=getSample((cpShifted), wf); | ||
301 | } | ||
302 | else | ||
303 | { | 308 | { |
304 | so->delta = -so->delta; /* At this point cpShifted is wrong. We need to take a step */ | 309 | if(mode_mask_looprev) |
305 | so->loopDir = LOOPDIR_FORWARD; | 310 | { |
311 | cp_temp += diff_loop; | ||
312 | s2=getSample((cp_temp >> FRACTSIZE), wf); | ||
313 | } | ||
314 | else | ||
315 | { | ||
316 | so->delta = -so->delta; /* At this point cp_temp is wrong. We need to take a step */ | ||
317 | so->loopDir = LOOPDIR_FORWARD; | ||
318 | } | ||
306 | } | 319 | } |
307 | } | ||
308 | 320 | ||
309 | if((wf->mode & 28) && (cpShifted >= wf->endLoop)) | 321 | if(mode_mask28 && (cp_temp >= end_loop)) |
310 | { | ||
311 | so->loopState = STATE_LOOPING; | ||
312 | if((wf->mode & (24)) == 0) | ||
313 | { | 322 | { |
314 | cpShifted = wf->startLoop + (cpShifted-wf->endLoop); | 323 | so->loopState = STATE_LOOPING; |
315 | so->cp = (cpShifted)<<FRACTSIZE; | 324 | if(!mode_mask24) |
316 | s2=getSample((cpShifted), wf); | 325 | { |
326 | cp_temp -= diff_loop; | ||
327 | s2=getSample((cp_temp >> FRACTSIZE), wf); | ||
328 | } | ||
329 | else | ||
330 | { | ||
331 | so->delta = -so->delta; | ||
332 | so->loopDir = LOOPDIR_REVERSE; | ||
333 | } | ||
317 | } | 334 | } |
318 | else | 335 | |
336 | /* Have we overrun? */ | ||
337 | if(cp_temp >= num_samples) | ||
319 | { | 338 | { |
320 | so->delta = -so->delta; | 339 | cp_temp -= so->delta; |
321 | so->loopDir = LOOPDIR_REVERSE; | 340 | s2 = getSample((cp_temp >> FRACTSIZE)+1, wf); |
341 | stopVoice(so); | ||
322 | } | 342 | } |
323 | } | ||
324 | |||
325 | /* Have we overrun? */ | ||
326 | if( (cpShifted >= (wf->numSamples-1))) | ||
327 | { | ||
328 | so->cp -= so->delta; | ||
329 | cpShifted = so->cp >> FRACTSIZE; | ||
330 | s2 = getSample((cpShifted)+1, wf); | ||
331 | stopVoice(so); | ||
332 | } | ||
333 | |||
334 | |||
335 | /* Better, working, linear interpolation */ | ||
336 | s1=getSample((cpShifted), wf); | ||
337 | 343 | ||
338 | s = s1 + ((signed)((s2 - s1) * (so->cp & ((1<<FRACTSIZE)-1)))>>FRACTSIZE); | 344 | /* Better, working, linear interpolation */ |
345 | s1=getSample((cp_temp >> FRACTSIZE), wf); | ||
339 | 346 | ||
347 | s = s1 + ((signed)((s2 - s1) * (cp_temp & ((1<<FRACTSIZE)-1)))>>FRACTSIZE); | ||
340 | 348 | ||
341 | if(so->curRate == 0) | 349 | if(so->curRate == 0) |
342 | { | 350 | { |
343 | stopVoice(so); | 351 | stopVoice(so); |
344 | // so->isUsed = 0; | 352 | // so->isUsed = 0; |
345 | 353 | ||
346 | } | 354 | } |
347 | 355 | ||
348 | if(so->ch != 9 && so->state != STATE_RAMPDOWN) /* Stupid ADSR code... and don't do ADSR for drums */ | 356 | if(so->ch != 9 && so->state != STATE_RAMPDOWN) /* Stupid ADSR code... and don't do ADSR for drums */ |
349 | { | ||
350 | if(so->curOffset < so->targetOffset) | ||
351 | { | 357 | { |
352 | so->curOffset += (so->curRate); | 358 | if(so->curOffset < so->targetOffset) |
353 | if(so -> curOffset > so->targetOffset && so->curPoint != 2) | ||
354 | { | 359 | { |
355 | if(so->curPoint != 5) | 360 | so->curOffset += (so->curRate); |
361 | if(so -> curOffset > so->targetOffset && so->curPoint != 2) | ||
356 | { | 362 | { |
357 | setPoint(so, so->curPoint+1); | 363 | if(so->curPoint != 5) |
364 | { | ||
365 | setPoint(so, so->curPoint+1); | ||
366 | } | ||
367 | else | ||
368 | { | ||
369 | stopVoice(so); | ||
370 | } | ||
358 | } | 371 | } |
359 | else | 372 | } else |
373 | { | ||
374 | so->curOffset -= (so->curRate); | ||
375 | if(so -> curOffset < so->targetOffset && so->curPoint != 2) | ||
360 | { | 376 | { |
361 | stopVoice(so); | 377 | |
378 | if(so->curPoint != 5) | ||
379 | { | ||
380 | setPoint(so, so->curPoint+1); | ||
381 | } | ||
382 | else | ||
383 | { | ||
384 | stopVoice(so); | ||
385 | } | ||
386 | |||
362 | } | 387 | } |
363 | } | 388 | } |
364 | } else | 389 | } |
390 | |||
391 | if(so->curOffset < 0) | ||
365 | { | 392 | { |
366 | so->curOffset -= (so->curRate); | 393 | so->curOffset = so->targetOffset; |
367 | if(so -> curOffset < so->targetOffset && so->curPoint != 2) | 394 | stopVoice(so); |
368 | { | 395 | } |
369 | 396 | ||
370 | if(so->curPoint != 5) | 397 | s = (s * (so->curOffset >> 22) >> 8); |
371 | { | ||
372 | setPoint(so, so->curPoint+1); | ||
373 | } | ||
374 | else | ||
375 | { | ||
376 | stopVoice(so); | ||
377 | } | ||
378 | 398 | ||
379 | } | 399 | /* need to set ramp beginning */ |
400 | if(so->state == STATE_RAMPDOWN && so->decay == 0) | ||
401 | { | ||
402 | so->decay = s*so->volscale>>14; | ||
403 | if(so->decay == 0) | ||
404 | so->decay = 1; /* stupid junk.. */ | ||
380 | } | 405 | } |
381 | } | ||
382 | 406 | ||
383 | if(so->curOffset < 0) | ||
384 | { | ||
385 | so->curOffset = so->targetOffset; | ||
386 | stopVoice(so); | ||
387 | } | ||
388 | 407 | ||
389 | s = (s * (so->curOffset >> 22) >> 8); | 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; | ||
390 | 411 | ||
412 | s2 = s1*chPan[so->ch]; | ||
413 | s1 = (s1<<7) - s2; | ||
414 | *(out++)+=(((s1&0x7FFF80) << 9) | ((s2&0x7FFF80) >> 7)); | ||
391 | 415 | ||
392 | /* need to set ramp beginning */ | ||
393 | if(so->state == STATE_RAMPDOWN && so->decay == 0) | ||
394 | { | ||
395 | so->decay = s*so->volscale>>14; | ||
396 | if(so->decay == 0) | ||
397 | so->decay = 1; /* stupid junk.. */ | ||
398 | } | 416 | } |
399 | 417 | ||
400 | 418 | so->cp=cp_temp; /* store this again */ | |
401 | /* Scaling by channel volume and note volume is done in sequencer.c */ | 419 | return; |
402 | /* That saves us some multiplication and pointer operations */ | ||
403 | return s*so->volscale>>14; | ||
404 | } | 420 | } |
405 | 421 | ||
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; | ||
426 | |||
406 | /* synth num_samples samples and write them to the */ | 427 | /* synth num_samples samples and write them to the */ |
407 | /* buffer pointed to by buf_ptr */ | 428 | /* buffer pointed to by buf_ptr */ |
408 | void synthSamples(int32_t *buf_ptr, unsigned int num_samples) ICODE_ATTR; | 429 | void synthSamples(int32_t *buf_ptr, unsigned int num_samples) ICODE_ATTR; |
409 | void synthSamples(int32_t *buf_ptr, unsigned int num_samples) | 430 | void synthSamples(int32_t *buf_ptr, unsigned int num_samples) |
410 | { | 431 | { |
411 | int i; | 432 | int i; |
412 | register int dL; | 433 | struct SynthObject *voicept; |
413 | register int dR; | 434 | |
414 | register int sample; | 435 | rb->memset(samp_buf, 0, num_samples*4); |
415 | register struct SynthObject *voicept; | ||
416 | while(num_samples>0) | ||
417 | { | ||
418 | dL=0; | ||
419 | dR=0; | ||
420 | voicept=&voices[0]; | ||
421 | 436 | ||
422 | for(i=MAX_VOICES; i > 0; i--) | 437 | for(i=0; i < MAX_VOICES; i++) |
438 | { | ||
439 | voicept=&voices[i]; | ||
440 | if(voicept->isUsed==1) | ||
423 | { | 441 | { |
424 | if(voicept->isUsed==1) | 442 | synthVoice(voicept, samp_buf, num_samples); |
425 | { | ||
426 | sample = synthVoice(voicept); | ||
427 | dL += sample; | ||
428 | sample *= chPan[voicept->ch]; | ||
429 | dR += sample; | ||
430 | } | ||
431 | voicept++; | ||
432 | } | 443 | } |
444 | } | ||
433 | 445 | ||
434 | dL = (dL << 7) - dR; | 446 | rb->memcpy(buf_ptr, samp_buf, num_samples*4); |
435 | |||
436 | /* combine the left and right 16 bit samples into 32 bits and write */ | ||
437 | /* to the buffer, left sample in the high word and right in the low word */ | ||
438 | *buf_ptr=(((dL&0x7FFF80) << 9) | ((dR&0x7FFF80) >> 7)); | ||
439 | 447 | ||
440 | buf_ptr++; | ||
441 | num_samples--; | ||
442 | } | ||
443 | /* TODO: Automatic Gain Control, anyone? */ | 448 | /* TODO: Automatic Gain Control, anyone? */ |
444 | /* Or, should this be implemented on the DSP's output volume instead? */ | 449 | /* Or, should this be implemented on the DSP's output volume instead? */ |
445 | 450 | ||