/
playmidi.c
801 lines (690 loc) · 21 KB
1
/*
2
3
4
5
6
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
7
it under the terms of the Perl Artistic License, available in COPYING.
8
9
10
11
12
13
14
15
playmidi.c -- random stuff in need of rearrangement
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
21
#include "SDL.h"
22
23
24
#include "timidity.h"
#include "options.h"
25
26
27
28
29
30
#include "instrum.h"
#include "playmidi.h"
#include "output.h"
#include "mix.h"
#include "tables.h"
31
static void adjust_amplification(MidiSong *song)
32
{
33
song->master_volume = (float)(song->amplification) / (float)100.0;
34
35
}
36
static void reset_voices(MidiSong *song)
37
38
39
{
int i;
for (i=0; i<MAX_VOICES; i++)
40
song->voice[i].status=VOICE_FREE;
41
42
43
}
/* Process the Reset All Controllers event */
44
static void reset_controllers(MidiSong *song, int c)
45
{
46
47
48
49
50
song->channel[c].volume=90; /* Some standard says, although the SCC docs say 0. */
song->channel[c].expression=127; /* SCC-1 does this. */
song->channel[c].sustain=0;
song->channel[c].pitchbend=0x2000;
song->channel[c].pitchfactor=0; /* to be computed */
51
52
}
53
static void reset_midi(MidiSong *song)
54
55
{
int i;
56
for (i=0; i<MAXCHAN; i++)
57
{
58
reset_controllers(song, i);
59
/* The rest of these are unaffected by the Reset All Controllers event */
60
61
62
63
song->channel[i].program=song->default_program;
song->channel[i].panning=NO_PANNING;
song->channel[i].pitchsens=2;
song->channel[i].bank=0; /* tone bank or drum set */
64
}
65
reset_voices(song);
66
67
}
68
static void select_sample(MidiSong *song, int v, Instrument *ip, int vel)
69
{
70
Sint32 f, cdiff, diff;
71
72
73
74
75
76
77
78
int s,i;
Sample *sp, *closest;
s=ip->samples;
sp=ip->sample;
if (s==1)
{
79
song->voice[v].sample=sp;
80
81
82
return;
}
83
84
85
86
87
88
89
90
91
92
93
94
f=song->voice[v].orig_frequency;
for (i=0; i<s; i++)
{
if (sp->low_vel <= vel && sp->high_vel >= vel &&
sp->low_freq <= f && sp->high_freq >= f)
{
song->voice[v].sample=sp;
return;
}
sp++;
}
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/*
No suitable sample found! We'll select the sample whose root
frequency is closest to the one we want. (Actually we should
probably convert the low, high, and root frequencies to MIDI note
values and compare those.) */
cdiff=0x7FFFFFFF;
closest=sp=ip->sample;
for(i=0; i<s; i++)
{
diff=sp->root_freq - f;
if (diff<0) diff=-diff;
if (diff<cdiff)
{
cdiff=diff;
closest=sp;
}
sp++;
}
114
song->voice[v].sample=closest;
115
116
117
return;
}
118
static void recompute_freq(MidiSong *song, int v)
119
120
{
int
121
122
sign=(song->voice[v].sample_increment < 0), /* for bidirectional loops */
pb=song->channel[song->voice[v].channel].pitchbend;
123
124
double a;
125
if (!song->voice[v].sample->sample_rate)
126
127
return;
128
if (song->voice[v].vibrato_control_ratio)
129
130
131
132
133
134
{
/* This instrument has vibrato. Invalidate any precomputed
sample_increments. */
int i=VIBRATO_SAMPLE_INCREMENTS;
while (i--)
135
song->voice[v].vibrato_sample_increment[i]=0;
136
137
138
}
if (pb==0x2000 || pb<0 || pb>0x3FFF)
139
song->voice[v].frequency = song->voice[v].orig_frequency;
140
141
142
else
{
pb-=0x2000;
143
if (!(song->channel[song->voice[v].channel].pitchfactor))
144
145
{
/* Damn. Somebody bent the pitch. */
146
Sint32 i=pb*song->channel[song->voice[v].channel].pitchsens;
147
148
if (pb<0)
i=-i;
149
150
song->channel[song->voice[v].channel].pitchfactor=
(float)(bend_fine[(i>>5) & 0xFF] * bend_coarse[i>>13]);
151
152
}
if (pb>0)
153
154
155
song->voice[v].frequency=
(Sint32)(song->channel[song->voice[v].channel].pitchfactor *
(double)(song->voice[v].orig_frequency));
156
else
157
158
159
song->voice[v].frequency=
(Sint32)((double)(song->voice[v].orig_frequency) /
song->channel[song->voice[v].channel].pitchfactor);
160
161
}
162
163
164
165
a = FSCALE(((double)(song->voice[v].sample->sample_rate) *
(double)(song->voice[v].frequency)) /
((double)(song->voice[v].sample->root_freq) *
(double)(song->rate)),
166
167
168
169
170
FRACTION_BITS);
if (sign)
a = -a; /* need to preserve the loop direction */
171
song->voice[v].sample_increment = (Sint32)(a);
172
173
}
174
static void recompute_amp(MidiSong *song, int v)
175
{
176
Sint32 tempamp;
177
178
179
/* TODO: use fscale */
180
181
182
183
184
tempamp= (song->voice[v].velocity *
song->channel[song->voice[v].channel].volume *
song->channel[song->voice[v].channel].expression); /* 21 bits */
if (!(song->encoding & PE_MONO))
185
{
186
if (song->voice[v].panning > 60 && song->voice[v].panning < 68)
187
{
188
189
190
191
192
song->voice[v].panned=PANNED_CENTER;
song->voice[v].left_amp=
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
21);
193
}
194
else if (song->voice[v].panning<5)
195
{
196
song->voice[v].panned = PANNED_LEFT;
197
198
199
song->voice[v].left_amp=
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
200
201
20);
}
202
else if (song->voice[v].panning>123)
203
{
204
song->voice[v].panned = PANNED_RIGHT;
205
206
207
song->voice[v].left_amp= /* left_amp will be used */
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
208
209
210
211
20);
}
else
{
212
song->voice[v].panned = PANNED_MYSTERY;
213
214
215
216
217
218
song->voice[v].left_amp=
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
27);
song->voice[v].right_amp = song->voice[v].left_amp * (song->voice[v].panning);
song->voice[v].left_amp *= (float)(127 - song->voice[v].panning);
219
220
221
222
}
}
else
{
223
song->voice[v].panned = PANNED_CENTER;
224
225
226
song->voice[v].left_amp=
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
227
228
229
230
21);
}
}
231
static void start_note(MidiSong *song, MidiEvent *e, int i)
232
233
{
Instrument *ip;
234
235
236
int j;
if (ISDRUMCHANNEL(song, e->channel))
237
{
238
if (!(ip=song->drumset[song->channel[e->channel].bank]->instrument[e->a]))
239
{
240
if (!(ip=song->drumset[0]->instrument[e->a]))
241
242
return; /* No instrument? Then we can't play. */
}
243
if (ip->samples != 1)
244
{
245
246
SNDDBG(("Strange: percussion instrument with %d samples!",
ip->samples));
247
248
249
}
if (ip->sample->note_to_use) /* Do we have a fixed pitch? */
250
song->voice[i].orig_frequency = freq_table[(int)(ip->sample->note_to_use)];
251
else
252
253
254
255
song->voice[i].orig_frequency = freq_table[e->a & 0x7F];
/* drums are supposed to have only one sample */
song->voice[i].sample = ip->sample;
256
257
258
}
else
{
259
260
261
262
if (song->channel[e->channel].program == SPECIAL_PROGRAM)
ip=song->default_instrument;
else if (!(ip=song->tonebank[song->channel[e->channel].bank]->
instrument[song->channel[e->channel].program]))
263
{
264
if (!(ip=song->tonebank[0]->instrument[song->channel[e->channel].program]))
265
266
return; /* No instrument? Then we can't play. */
}
267
268
if (ip->sample->note_to_use) /* Fixed-pitch instrument? */
269
song->voice[i].orig_frequency = freq_table[(int)(ip->sample->note_to_use)];
270
else
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
song->voice[i].orig_frequency = freq_table[e->a & 0x7F];
select_sample(song, i, ip, e->b);
}
song->voice[i].status = VOICE_ON;
song->voice[i].channel = e->channel;
song->voice[i].note = e->a;
song->voice[i].velocity = e->b;
song->voice[i].sample_offset = 0;
song->voice[i].sample_increment = 0; /* make sure it isn't negative */
song->voice[i].tremolo_phase = 0;
song->voice[i].tremolo_phase_increment = song->voice[i].sample->tremolo_phase_increment;
song->voice[i].tremolo_sweep = song->voice[i].sample->tremolo_sweep_increment;
song->voice[i].tremolo_sweep_position = 0;
song->voice[i].vibrato_sweep = song->voice[i].sample->vibrato_sweep_increment;
song->voice[i].vibrato_sweep_position = 0;
song->voice[i].vibrato_control_ratio = song->voice[i].sample->vibrato_control_ratio;
song->voice[i].vibrato_control_counter = song->voice[i].vibrato_phase = 0;
291
for (j=0; j<VIBRATO_SAMPLE_INCREMENTS; j++)
292
song->voice[i].vibrato_sample_increment[j] = 0;
293
294
295
if (song->channel[e->channel].panning != NO_PANNING)
song->voice[i].panning = song->channel[e->channel].panning;
296
else
297
song->voice[i].panning = song->voice[i].sample->panning;
298
299
300
301
recompute_freq(song, i);
recompute_amp(song, i);
if (song->voice[i].sample->modes & MODES_ENVELOPE)
302
303
{
/* Ramp up from 0 */
304
305
306
307
308
song->voice[i].envelope_stage = 0;
song->voice[i].envelope_volume = 0;
song->voice[i].control_counter = 0;
recompute_envelope(song, i);
apply_envelope_to_amp(song, i);
309
310
311
}
else
{
312
313
song->voice[i].envelope_increment = 0;
apply_envelope_to_amp(song, i);
314
315
316
}
}
317
static void kill_note(MidiSong *song, int i)
318
{
319
song->voice[i].status = VOICE_DIE;
320
321
322
}
/* Only one instance of a note can be playing on a single channel. */
323
static void note_on(MidiSong *song)
324
{
325
326
327
int i = song->voices, lowest=-1;
Sint32 lv=0x7FFFFFFF, v;
MidiEvent *e = song->current_event;
328
329
330
while (i--)
{
331
if (song->voice[i].status == VOICE_FREE)
332
lowest=i; /* Can't get a lower volume than silence */
333
334
335
else if (song->voice[i].channel==e->channel &&
(song->voice[i].note==e->a || song->channel[song->voice[i].channel].mono))
kill_note(song, i);
336
337
338
339
340
}
if (lowest != -1)
{
/* Found a free voice. */
341
start_note(song,e,lowest);
342
343
344
345
return;
}
/* Look for the decaying note with the lowest volume */
346
i = song->voices;
347
348
while (i--)
{
349
350
if ((song->voice[i].status != VOICE_ON) &&
(song->voice[i].status != VOICE_DIE))
351
{
352
353
354
355
v = song->voice[i].left_mix;
if ((song->voice[i].panned == PANNED_MYSTERY)
&& (song->voice[i].right_mix > v))
v = song->voice[i].right_mix;
356
357
358
359
360
361
362
363
364
365
366
367
368
369
if (v<lv)
{
lv=v;
lowest=i;
}
}
}
if (lowest != -1)
{
/* This can still cause a click, but if we had a free voice to
spare for ramping down this note, we wouldn't need to kill it
in the first place... Still, this needs to be fixed. Perhaps
we could use a reserve of voices to play dying notes only. */
370
371
372
373
song->cut_notes++;
song->voice[lowest].status=VOICE_FREE;
start_note(song,e,lowest);
374
375
}
else
376
song->lost_notes++;
377
378
}
379
static void finish_note(MidiSong *song, int i)
380
{
381
if (song->voice[i].sample->modes & MODES_ENVELOPE)
382
383
{
/* We need to get the envelope out of Sustain stage */
384
385
386
387
song->voice[i].envelope_stage = 3;
song->voice[i].status = VOICE_OFF;
recompute_envelope(song, i);
apply_envelope_to_amp(song, i);
388
389
390
391
392
393
}
else
{
/* Set status to OFF so resample_voice() will let this voice out
of its loop, if any. In any case, this voice dies when it
hits the end of its data (ofs>=data_length). */
394
song->voice[i].status = VOICE_OFF;
395
396
397
}
}
398
static void note_off(MidiSong *song)
399
{
400
401
402
int i = song->voices;
MidiEvent *e = song->current_event;
403
while (i--)
404
405
406
if (song->voice[i].status == VOICE_ON &&
song->voice[i].channel == e->channel &&
song->voice[i].note == e->a)
407
{
408
if (song->channel[e->channel].sustain)
409
{
410
song->voice[i].status = VOICE_SUSTAINED;
411
412
}
else
413
finish_note(song, i);
414
415
416
417
418
return;
}
}
/* Process the All Notes Off event */
419
static void all_notes_off(MidiSong *song)
420
{
421
422
423
424
int i = song->voices;
int c = song->current_event->channel;
SNDDBG(("All notes off on channel %d", c));
425
while (i--)
426
427
if (song->voice[i].status == VOICE_ON &&
song->voice[i].channel == c)
428
{
429
430
if (song->channel[c].sustain)
song->voice[i].status = VOICE_SUSTAINED;
431
else
432
finish_note(song, i);
433
434
435
436
}
}
/* Process the All Sounds Off event */
437
static void all_sounds_off(MidiSong *song)
438
{
439
440
441
int i = song->voices;
int c = song->current_event->channel;
442
while (i--)
443
444
445
if (song->voice[i].channel == c &&
song->voice[i].status != VOICE_FREE &&
song->voice[i].status != VOICE_DIE)
446
{
447
kill_note(song, i);
448
449
450
}
}
451
static void adjust_pressure(MidiSong *song)
452
{
453
454
455
MidiEvent *e = song->current_event;
int i = song->voices;
456
while (i--)
457
458
459
if (song->voice[i].status == VOICE_ON &&
song->voice[i].channel == e->channel &&
song->voice[i].note == e->a)
460
{
461
462
463
song->voice[i].velocity = e->b;
recompute_amp(song, i);
apply_envelope_to_amp(song, i);
464
465
466
467
return;
}
}
468
static void drop_sustain(MidiSong *song)
469
{
470
471
int i = song->voices;
int c = song->current_event->channel;
472
473
while (i--)
474
475
if (song->voice[i].status == VOICE_SUSTAINED && song->voice[i].channel == c)
finish_note(song, i);
476
477
}
478
static void adjust_pitchbend(MidiSong *song)
479
{
480
481
482
int c = song->current_event->channel;
int i = song->voices;
483
while (i--)
484
if (song->voice[i].status != VOICE_FREE && song->voice[i].channel == c)
485
{
486
recompute_freq(song, i);
487
488
489
}
}
490
static void adjust_volume(MidiSong *song)
491
{
492
493
494
int c = song->current_event->channel;
int i = song->voices;
495
while (i--)
496
497
if (song->voice[i].channel == c &&
(song->voice[i].status==VOICE_ON || song->voice[i].status==VOICE_SUSTAINED))
498
{
499
500
recompute_amp(song, i);
apply_envelope_to_amp(song, i);
501
502
503
}
}
504
static void seek_forward(MidiSong *song, Sint32 until_time)
505
{
506
507
reset_voices(song);
while (song->current_event->time < until_time)
508
{
509
switch(song->current_event->type)
510
511
512
513
{
/* All notes stay off. Just handle the parameter changes. */
case ME_PITCH_SENS:
514
515
516
song->channel[song->current_event->channel].pitchsens =
song->current_event->a;
song->channel[song->current_event->channel].pitchfactor = 0;
517
518
519
break;
case ME_PITCHWHEEL:
520
521
522
song->channel[song->current_event->channel].pitchbend =
song->current_event->a + song->current_event->b * 128;
song->channel[song->current_event->channel].pitchfactor = 0;
523
524
525
break;
case ME_MAINVOLUME:
526
527
song->channel[song->current_event->channel].volume =
song->current_event->a;
528
529
break;
530
case ME_PAN:
531
532
song->channel[song->current_event->channel].panning =
song->current_event->a;
533
534
535
break;
case ME_EXPRESSION:
536
537
song->channel[song->current_event->channel].expression =
song->current_event->a;
538
539
540
break;
case ME_PROGRAM:
541
if (ISDRUMCHANNEL(song, song->current_event->channel))
542
/* Change drum set */
543
544
song->channel[song->current_event->channel].bank =
song->current_event->a;
545
else
546
547
song->channel[song->current_event->channel].program =
song->current_event->a;
548
549
550
break;
case ME_SUSTAIN:
551
552
song->channel[song->current_event->channel].sustain =
song->current_event->a;
553
554
break;
555
case ME_RESET_CONTROLLERS:
556
reset_controllers(song, song->current_event->channel);
557
558
559
break;
case ME_TONE_BANK:
560
561
song->channel[song->current_event->channel].bank =
song->current_event->a;
562
563
564
break;
case ME_EOT:
565
song->current_sample = song->current_event->time;
566
567
return;
}
568
song->current_event++;
569
}
570
571
572
573
/*song->current_sample=song->current_event->time;*/
if (song->current_event != song->events)
song->current_event--;
song->current_sample=until_time;
574
575
}
576
static void skip_to(MidiSong *song, Sint32 until_time)
577
{
578
579
if (song->current_sample > until_time)
song->current_sample = 0;
580
581
582
583
584
reset_midi(song);
song->buffered_count = 0;
song->buffer_pointer = song->common_buffer;
song->current_event = song->events;
585
586
if (until_time)
587
seek_forward(song, until_time);
588
589
}
590
static void do_compute_data(MidiSong *song, Sint32 count)
591
592
{
int i;
593
594
595
memset(song->buffer_pointer, 0,
(song->encoding & PE_MONO) ? (count * 4) : (count * 8));
for (i = 0; i < song->voices; i++)
596
{
597
598
if(song->voice[i].status != VOICE_FREE)
mix_voice(song, song->buffer_pointer, i, count);
599
}
600
song->current_sample += count;
601
602
603
604
}
/* count=0 means flush remaining buffered data to output device, then
flush the device itself */
605
static void compute_data(MidiSong *song, void *stream, Sint32 count)
606
{
607
int channels;
608
609
if ( song->encoding & PE_MONO )
610
611
channels = 1;
else
612
channels = 2;
613
614
615
if (!count)
{
616
617
618
619
620
if (song->buffered_count)
song->write(stream, song->common_buffer, channels * song->buffered_count);
song->buffer_pointer = song->common_buffer;
song->buffered_count = 0;
return;
621
622
}
623
while ((count + song->buffered_count) >= song->buffer_size)
624
{
625
626
627
628
629
do_compute_data(song, song->buffer_size - song->buffered_count);
count -= song->buffer_size - song->buffered_count;
song->write(stream, song->common_buffer, channels * song->buffer_size);
song->buffer_pointer = song->common_buffer;
song->buffered_count = 0;
630
631
632
}
if (count>0)
{
633
634
635
do_compute_data(song, count);
song->buffered_count += count;
song->buffer_pointer += (song->encoding & PE_MONO) ? count : count*2;
636
637
638
}
}
639
640
641
642
643
644
645
646
void Timidity_Start(MidiSong *song)
{
song->playing = 1;
adjust_amplification(song);
skip_to(song, 0);
}
void Timidity_Seek(MidiSong *song, Uint32 ms)
647
{
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
skip_to(song, (ms * song->rate) / 1000);
}
Uint32 Timidity_GetSongLength(MidiSong *song)
{
MidiEvent *last_event = &song->events[song->groomed_event_count - 1];
/* We want last_event->time * 1000 / song->rate */
Uint32 retvalue = (last_event->time / song->rate) * 1000;
retvalue += (last_event->time % song->rate) * 1000 / song->rate;
return retvalue;
}
int Timidity_PlaySome(MidiSong *song, void *stream, Sint32 len)
{
Sint32 start_sample, end_sample, samples;
int bytes_per_sample;
if (!song->playing)
return 0;
667
668
669
670
671
672
673
674
675
bytes_per_sample =
((song->encoding & PE_MONO) ? 1 : 2)
* ((song->encoding & PE_16BIT) ? 2 : 1);
samples = len / bytes_per_sample;
start_sample = song->current_sample;
end_sample = song->current_sample+samples;
while ( song->current_sample < end_sample ) {
676
/* Handle all events that should happen at this time */
677
678
while (song->current_event->time <= song->current_sample) {
switch(song->current_event->type) {
679
680
681
682
/* Effects affecting a single note */
case ME_NOTEON:
683
684
if (!(song->current_event->b)) /* Velocity 0? */
note_off(song);
685
else
686
note_on(song);
687
688
689
break;
case ME_NOTEOFF:
690
note_off(song);
691
692
693
break;
case ME_KEYPRESSURE:
694
adjust_pressure(song);
695
696
697
698
699
break;
/* Effects affecting a single channel */
case ME_PITCH_SENS:
700
701
702
song->channel[song->current_event->channel].pitchsens =
song->current_event->a;
song->channel[song->current_event->channel].pitchfactor = 0;
703
704
705
break;
case ME_PITCHWHEEL:
706
707
708
song->channel[song->current_event->channel].pitchbend =
song->current_event->a + song->current_event->b * 128;
song->channel[song->current_event->channel].pitchfactor = 0;
709
/* Adjust pitch for notes already playing */
710
adjust_pitchbend(song);
711
712
713
break;
case ME_MAINVOLUME:
714
715
716
song->channel[song->current_event->channel].volume =
song->current_event->a;
adjust_volume(song);
717
break;
718
719
case ME_PAN:
720
721
song->channel[song->current_event->channel].panning =
song->current_event->a;
722
723
724
break;
case ME_EXPRESSION:
725
726
727
song->channel[song->current_event->channel].expression =
song->current_event->a;
adjust_volume(song);
728
729
730
break;
case ME_PROGRAM:
731
if (ISDRUMCHANNEL(song, song->current_event->channel)) {
732
/* Change drum set */
733
734
song->channel[song->current_event->channel].bank =
song->current_event->a;
735
736
}
else
737
738
song->channel[song->current_event->channel].program =
song->current_event->a;
739
740
741
break;
case ME_SUSTAIN:
742
743
744
745
song->channel[song->current_event->channel].sustain =
song->current_event->a;
if (!song->current_event->a)
drop_sustain(song);
746
747
748
break;
case ME_RESET_CONTROLLERS:
749
reset_controllers(song, song->current_event->channel);
750
751
752
break;
case ME_ALL_NOTES_OFF:
753
all_notes_off(song);
754
755
756
break;
case ME_ALL_SOUNDS_OFF:
757
all_sounds_off(song);
758
break;
759
760
case ME_TONE_BANK:
761
762
song->channel[song->current_event->channel].bank =
song->current_event->a;
763
break;
764
765
766
case ME_EOT:
/* Give the last notes a couple of seconds to decay */
767
768
769
770
771
772
SNDDBG(("Playing time: ~%d seconds\n",
song->current_sample/song->rate+2));
SNDDBG(("Notes cut: %d\n", song->cut_notes));
SNDDBG(("Notes lost totally: %d\n", song->lost_notes));
song->playing = 0;
return (song->current_sample - start_sample) * bytes_per_sample;
773
}
774
song->current_event++;
775
}
776
777
if (song->current_event->time > end_sample)
compute_data(song, stream, end_sample-song->current_sample);
778
else
779
compute_data(song, stream, song->current_event->time-song->current_sample);
780
}
781
return samples * bytes_per_sample;
782
783
}
784
void Timidity_SetVolume(MidiSong *song, int volume)
785
786
787
{
int i;
if (volume > MAX_AMPLIFICATION)
788
song->amplification = MAX_AMPLIFICATION;
789
790
else
if (volume < 0)
791
song->amplification = 0;
792
else
793
794
795
796
song->amplification = volume;
adjust_amplification(song);
for (i = 0; i < song->voices; i++)
if (song->voice[i].status != VOICE_FREE)
797
{
798
799
recompute_amp(song, i);
apply_envelope_to_amp(song, i);
800
801
}
}