This is a fork of Zandronum used on servers hosted by The Sentinels Playground (TSPG).
Revision | ede6f20d8c5858c026366aeb59d444401a72a5eb (tree) |
---|---|
Time | 2008-04-20 06:36:53 |
Author | Randy Heit <rheit@zdoo...> |
Commiter | Randy Heit |
- Added the writewave command to write the internal TiMidity's output to a
- Changed the default channel velocity for MUS files from 64 to 100 to
- Changed the mus2midi channel assignments to match the internal player's.
- Fixed: apply_envelope_to_amp() should clamp the mix levels to 0.
SVN r926 (trunk)
@@ -1,3 +1,12 @@ | ||
1 | +April 19, 2008 | |
2 | +- Added the writewave command to write the internal TiMidity's output to a | |
3 | + wave file. | |
4 | +- Changed the default channel velocity for MUS files from 64 to 100 to | |
5 | + better match apparent MIDI practice. (Would like to know what this is | |
6 | + supposed to be.) | |
7 | +- Changed the mus2midi channel assignments to match the internal player's. | |
8 | +- Fixed: apply_envelope_to_amp() should clamp the mix levels to 0. | |
9 | + | |
1 | 10 | April 18, 2008 |
2 | 11 | - Made the maximum number of TiMidity voices configurable through the |
3 | 12 | timidity_voices cvar. |
@@ -33,10 +33,7 @@ | ||
33 | 33 | ** MUS files are essentially format 0 MIDI files with some |
34 | 34 | ** space-saving modifications. Conversion is quite straight-forward. |
35 | 35 | ** If you were to hook a main() into this that calls ProduceMIDI, |
36 | -** you could create a self-contained MUS->MIDI converter. However, if | |
37 | -** you want to do that, you would be better off using qmus2mid, since | |
38 | -** it creates multitrack files that usually maintain running status | |
39 | -** better than single track files and are thus smaller. | |
36 | +** you could create a self-contained MUS->MIDI converter. | |
40 | 37 | */ |
41 | 38 | |
42 | 39 |
@@ -52,9 +49,7 @@ | ||
52 | 49 | 0, 70, // 70 divisions |
53 | 50 | 'M','T','r','k', 0, 0, 0, 0, |
54 | 51 | // The first event sets the tempo to 500,000 microsec/quarter note |
55 | -0, 255, 81, 3, 0x07, 0xa1, 0x20, | |
56 | -// Set the percussion channel to full volume | |
57 | -0, 0xB9, 7, 127 | |
52 | +0, 255, 81, 3, 0x07, 0xa1, 0x20 | |
58 | 53 | }; |
59 | 54 | |
60 | 55 | static const BYTE MUSMagic[4] = { 'M','U','S',0x1a }; |
@@ -122,9 +117,8 @@ | ||
122 | 117 | int deltaTime; |
123 | 118 | const MUSHeader *musHead = (const MUSHeader *)musBuf; |
124 | 119 | BYTE status; |
120 | + BYTE chanUsed[16]; | |
125 | 121 | BYTE lastVel[16]; |
126 | - SBYTE chanMap[16]; | |
127 | - int chanCount; | |
128 | 122 | long trackLen; |
129 | 123 | |
130 | 124 | // Do some validation of the MUS file |
@@ -143,10 +137,8 @@ | ||
143 | 137 | maxmus_p = LittleShort(musHead->SongLen); |
144 | 138 | mus_p = 0; |
145 | 139 | |
146 | - memset (lastVel, 64, 16); | |
147 | - memset (chanMap, -1, 15); | |
148 | - chanMap[15] = 9; | |
149 | - chanCount = 0; | |
140 | + memset (lastVel, 100, 16); | |
141 | + memset (chanUsed, 0, 16); | |
150 | 142 | event = 0; |
151 | 143 | deltaTime = 0; |
152 | 144 | status = 0; |
@@ -163,21 +155,27 @@ | ||
163 | 155 | t = musBuf[mus_p++]; |
164 | 156 | } |
165 | 157 | channel = event & 15; |
158 | + if (channel == 15) | |
159 | + { | |
160 | + channel = 9; | |
161 | + } | |
162 | + else if (channel >= 9) | |
163 | + { | |
164 | + channel++; | |
165 | + } | |
166 | 166 | |
167 | - if (chanMap[channel] < 0) | |
167 | + if (!chanUsed[channel]) | |
168 | 168 | { |
169 | 169 | // This is the first time this channel has been used, |
170 | 170 | // so sets its volume to 127. |
171 | + chanUsed[channel] = 1; | |
171 | 172 | outFile.Push(0); |
172 | - outFile.Push(0xB0 | chanCount); | |
173 | + outFile.Push(0xB0 | channel); | |
173 | 174 | outFile.Push(7); |
174 | 175 | outFile.Push(127); |
175 | - chanMap[channel] = chanCount++; | |
176 | - if (chanCount == 9) | |
177 | - ++chanCount; | |
178 | 176 | } |
179 | 177 | |
180 | - midStatus = channel = chanMap[channel]; | |
178 | + midStatus = channel; | |
181 | 179 | midArgs = 0; // Most events have two args (0 means 2, 1 means 1) |
182 | 180 | |
183 | 181 | switch (event & 0x70) |
@@ -72,7 +72,6 @@ | ||
72 | 72 | |
73 | 73 | #define SPECTRUM_SIZE 256 |
74 | 74 | |
75 | - | |
76 | 75 | // TYPES ------------------------------------------------------------------- |
77 | 76 | |
78 | 77 | struct FEnumList |
@@ -151,6 +151,11 @@ | ||
151 | 151 | return NULL; |
152 | 152 | } |
153 | 153 | |
154 | +MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate) | |
155 | +{ | |
156 | + return NULL; | |
157 | +} | |
158 | + | |
154 | 159 | void I_InitMusic (void) |
155 | 160 | { |
156 | 161 | static bool setatterm = false; |
@@ -629,3 +634,41 @@ | ||
629 | 634 | Printf ("Usage: writeopl <filename>"); |
630 | 635 | } |
631 | 636 | } |
637 | + | |
638 | +//========================================================================== | |
639 | +// | |
640 | +// CCMD writewave | |
641 | +// | |
642 | +// If the current song can be represented as a waveform, dump it to | |
643 | +// the specified file on disk. The sample rate parameter is merely a | |
644 | +// suggestion, and the dumper is free to ignore it. | |
645 | +// | |
646 | +//========================================================================== | |
647 | + | |
648 | +CCMD (writewave) | |
649 | +{ | |
650 | + if (argv.argc() >= 2 && argv.argc() <= 3) | |
651 | + { | |
652 | + if (currSong == NULL) | |
653 | + { | |
654 | + Printf ("No song is currently playing.\n"); | |
655 | + } | |
656 | + else | |
657 | + { | |
658 | + MusInfo *dumper = currSong->GetWaveDumper(argv[1], argv.argc() == 3 ? atoi(argv[2]) : 0); | |
659 | + if (dumper == NULL) | |
660 | + { | |
661 | + Printf ("Current song cannot be saved as wave data.\n"); | |
662 | + } | |
663 | + else | |
664 | + { | |
665 | + dumper->Play(false); | |
666 | + delete dumper; | |
667 | + } | |
668 | + } | |
669 | + } | |
670 | + else | |
671 | + { | |
672 | + Printf ("Usage: writewave <filename> [sample rate]"); | |
673 | + } | |
674 | +} |
@@ -49,6 +49,7 @@ | ||
49 | 49 | virtual void Update(); |
50 | 50 | virtual FString GetStats(); |
51 | 51 | virtual MusInfo *GetOPLDumper(const char *filename); |
52 | + virtual MusInfo *GetWaveDumper(const char *filename, int rate); | |
52 | 53 | |
53 | 54 | enum EState |
54 | 55 | { |
@@ -225,6 +226,7 @@ | ||
225 | 226 | { |
226 | 227 | public: |
227 | 228 | TimidityMIDIDevice(); |
229 | + TimidityMIDIDevice(int rate); | |
228 | 230 | ~TimidityMIDIDevice(); |
229 | 231 | |
230 | 232 | int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); |
@@ -268,6 +270,20 @@ | ||
268 | 270 | DWORD Position; |
269 | 271 | }; |
270 | 272 | |
273 | +// Internal TiMidity disk writing version of a MIDI device ------------------ | |
274 | + | |
275 | +class TimidityWaveWriterMIDIDevice : public TimidityMIDIDevice | |
276 | +{ | |
277 | +public: | |
278 | + TimidityWaveWriterMIDIDevice(const char *filename, int rate); | |
279 | + ~TimidityWaveWriterMIDIDevice(); | |
280 | + int Resume(); | |
281 | + void Stop(); | |
282 | + | |
283 | +protected: | |
284 | + FILE *File; | |
285 | +}; | |
286 | + | |
271 | 287 | // Base class for streaming MUS and MIDI files ------------------------------ |
272 | 288 | |
273 | 289 | // MIDI device selection. |
@@ -297,7 +313,7 @@ | ||
297 | 313 | FString GetStats(); |
298 | 314 | |
299 | 315 | protected: |
300 | - MIDIStreamer(const char *dumpname); | |
316 | + MIDIStreamer(const char *dumpname, EMIDIDevice type); | |
301 | 317 | |
302 | 318 | void OutputVolume (DWORD volume); |
303 | 319 | int FillBuffer(int buffer_num, int max_events, DWORD max_time); |
@@ -363,9 +379,10 @@ | ||
363 | 379 | ~MUSSong2(); |
364 | 380 | |
365 | 381 | MusInfo *GetOPLDumper(const char *filename); |
382 | + MusInfo *GetWaveDumper(const char *filename, int rate); | |
366 | 383 | |
367 | 384 | protected: |
368 | - MUSSong2(const MUSSong2 *original, const char *filename); //OPL dump constructor | |
385 | + MUSSong2(const MUSSong2 *original, const char *filename, EMIDIDevice type); // file dump constructor | |
369 | 386 | |
370 | 387 | void DoInitialSetup(); |
371 | 388 | void DoRestart(); |
@@ -388,9 +405,10 @@ | ||
388 | 405 | ~MIDISong2(); |
389 | 406 | |
390 | 407 | MusInfo *GetOPLDumper(const char *filename); |
408 | + MusInfo *GetWaveDumper(const char *filename, int rate); | |
391 | 409 | |
392 | 410 | protected: |
393 | - MIDISong2(const MIDISong2 *original, const char *filename); // OPL dump constructor | |
411 | + MIDISong2(const MIDISong2 *original, const char *filename, EMIDIDevice type); // file dump constructor | |
394 | 412 | |
395 | 413 | void CheckCaps(); |
396 | 414 | void DoInitialSetup(); |
@@ -891,17 +891,28 @@ | ||
891 | 891 | |
892 | 892 | MusInfo *MIDISong2::GetOPLDumper(const char *filename) |
893 | 893 | { |
894 | - return new MIDISong2(this, filename); | |
894 | + return new MIDISong2(this, filename, MIDI_OPL); | |
895 | 895 | } |
896 | 896 | |
897 | 897 | //========================================================================== |
898 | 898 | // |
899 | -// MIDISong2 OPL Dumping Constructor | |
899 | +// MIDISong2 :: GetWaveDumper | |
900 | 900 | // |
901 | 901 | //========================================================================== |
902 | 902 | |
903 | -MIDISong2::MIDISong2(const MIDISong2 *original, const char *filename) | |
904 | -: MIDIStreamer(filename) | |
903 | +MusInfo *MIDISong2::GetWaveDumper(const char *filename, int rate) | |
904 | +{ | |
905 | + return new MIDISong2(this, filename, MIDI_Timidity); | |
906 | +} | |
907 | + | |
908 | +//========================================================================== | |
909 | +// | |
910 | +// MIDISong2 File Dumping Constructor | |
911 | +// | |
912 | +//========================================================================== | |
913 | + | |
914 | +MIDISong2::MIDISong2(const MIDISong2 *original, const char *filename, EMIDIDevice type) | |
915 | +: MIDIStreamer(filename, type) | |
905 | 916 | { |
906 | 917 | SongLen = original->SongLen; |
907 | 918 | MusHeader = new BYTE[original->SongLen]; |
@@ -97,12 +97,12 @@ | ||
97 | 97 | // |
98 | 98 | //========================================================================== |
99 | 99 | |
100 | -MIDIStreamer::MIDIStreamer(const char *dumpname) | |
100 | +MIDIStreamer::MIDIStreamer(const char *dumpname, EMIDIDevice type) | |
101 | 101 | : |
102 | 102 | #ifdef _WIN32 |
103 | 103 | PlayerThread(0), ExitEvent(0), BufferDoneEvent(0), |
104 | 104 | #endif |
105 | - MIDI(0), Division(0), InitialTempo(500000), DeviceType(MIDI_OPL), DumpFilename(dumpname) | |
105 | + MIDI(0), Division(0), InitialTempo(500000), DeviceType(type), DumpFilename(dumpname) | |
106 | 106 | { |
107 | 107 | #ifdef _WIN32 |
108 | 108 | BufferDoneEvent = NULL; |
@@ -196,7 +196,14 @@ | ||
196 | 196 | assert(MIDI == NULL); |
197 | 197 | if (DumpFilename.IsNotEmpty()) |
198 | 198 | { |
199 | - MIDI = new OPLDumperMIDIDevice(DumpFilename); | |
199 | + if (DeviceType == MIDI_OPL) | |
200 | + { | |
201 | + MIDI = new OPLDumperMIDIDevice(DumpFilename); | |
202 | + } | |
203 | + else if (DeviceType == MIDI_Timidity) | |
204 | + { | |
205 | + MIDI = new TimidityWaveWriterMIDIDevice(DumpFilename, 0); | |
206 | + } | |
200 | 207 | } |
201 | 208 | else switch(DeviceType) |
202 | 209 | { |
@@ -150,7 +150,7 @@ | ||
150 | 150 | { |
151 | 151 | for (int i = 0; i < 16; ++i) |
152 | 152 | { |
153 | - LastVelocity[i] = 64; | |
153 | + LastVelocity[i] = 100; | |
154 | 154 | ChannelVolumes[i] = 127; |
155 | 155 | } |
156 | 156 | } |
@@ -340,7 +340,18 @@ | ||
340 | 340 | |
341 | 341 | MusInfo *MUSSong2::GetOPLDumper(const char *filename) |
342 | 342 | { |
343 | - return new MUSSong2(this, filename); | |
343 | + return new MUSSong2(this, filename, MIDI_OPL); | |
344 | +} | |
345 | + | |
346 | +//========================================================================== | |
347 | +// | |
348 | +// MUSSong2 :: GetWaveDumper | |
349 | +// | |
350 | +//========================================================================== | |
351 | + | |
352 | +MusInfo *MUSSong2::GetWaveDumper(const char *filename, int rate) | |
353 | +{ | |
354 | + return new MUSSong2(this, filename, MIDI_Timidity); | |
344 | 355 | } |
345 | 356 | |
346 | 357 | //========================================================================== |
@@ -349,8 +360,8 @@ | ||
349 | 360 | // |
350 | 361 | //========================================================================== |
351 | 362 | |
352 | -MUSSong2::MUSSong2(const MUSSong2 *original, const char *filename) | |
353 | -: MIDIStreamer(filename) | |
363 | +MUSSong2::MUSSong2(const MUSSong2 *original, const char *filename, EMIDIDevice type) | |
364 | +: MIDIStreamer(filename, type) | |
354 | 365 | { |
355 | 366 | int songstart = LittleShort(original->MusHeader->SongStart); |
356 | 367 | MaxMusP = original->MaxMusP; |
@@ -44,6 +44,27 @@ | ||
44 | 44 | |
45 | 45 | // MACROS ------------------------------------------------------------------ |
46 | 46 | |
47 | +// TYPES ------------------------------------------------------------------- | |
48 | + | |
49 | +struct FmtChunk | |
50 | +{ | |
51 | + DWORD ChunkID; | |
52 | + DWORD ChunkLen; | |
53 | + WORD FormatTag; | |
54 | + WORD Channels; | |
55 | + DWORD SamplesPerSec; | |
56 | + DWORD AvgBytesPerSec; | |
57 | + WORD BlockAlign; | |
58 | + WORD BitsPerSample; | |
59 | + WORD ExtensionSize; | |
60 | + WORD ValidBitsPerSample; | |
61 | + DWORD ChannelMask; | |
62 | + DWORD SubFormatA; | |
63 | + WORD SubFormatB; | |
64 | + WORD SubFormatC; | |
65 | + BYTE SubFormatD[8]; | |
66 | +}; | |
67 | + | |
47 | 68 | // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- |
48 | 69 | |
49 | 70 | // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- |
@@ -79,6 +100,26 @@ | ||
79 | 100 | |
80 | 101 | //========================================================================== |
81 | 102 | // |
103 | +// TimidityMIDIDevice Constructor with rate parameter | |
104 | +// | |
105 | +//========================================================================== | |
106 | + | |
107 | +TimidityMIDIDevice::TimidityMIDIDevice(int rate) | |
108 | +{ | |
109 | + // Need to support multiple instances with different playback rates | |
110 | + // before we can use this parameter. | |
111 | + rate = (int)GSnd->GetOutputRate(); | |
112 | + Stream = NULL; | |
113 | + Tempo = 0; | |
114 | + Division = 0; | |
115 | + Events = NULL; | |
116 | + Started = false; | |
117 | + Renderer = NULL; | |
118 | + Renderer = new Timidity::Renderer((float)rate); | |
119 | +} | |
120 | + | |
121 | +//========================================================================== | |
122 | +// | |
82 | 123 | // TimidityMIDIDevice Destructor |
83 | 124 | // |
84 | 125 | //========================================================================== |
@@ -509,7 +550,7 @@ | ||
509 | 550 | { // end of song |
510 | 551 | if (numsamples > 0) |
511 | 552 | { |
512 | - Renderer->ComputeOutput(samples1, samplesleft); | |
553 | + Renderer->ComputeOutput(samples1, numsamples); | |
513 | 554 | } |
514 | 555 | res = false; |
515 | 556 | break; |
@@ -521,6 +562,10 @@ | ||
521 | 562 | } |
522 | 563 | } |
523 | 564 | } |
565 | + if (Events == NULL) | |
566 | + { | |
567 | + res = false; | |
568 | + } | |
524 | 569 | CritSec.Leave(); |
525 | 570 | return res; |
526 | 571 | } |
@@ -596,3 +641,124 @@ | ||
596 | 641 | } |
597 | 642 | return out; |
598 | 643 | } |
644 | + | |
645 | +//========================================================================== | |
646 | +// | |
647 | +// TimidityWaveWriterMIDIDevice Constructor | |
648 | +// | |
649 | +//========================================================================== | |
650 | + | |
651 | +TimidityWaveWriterMIDIDevice::TimidityWaveWriterMIDIDevice(const char *filename, int rate) | |
652 | +{ | |
653 | + File = fopen(filename, "wb"); | |
654 | + if (File != NULL) | |
655 | + { // Write wave header | |
656 | + DWORD work[3]; | |
657 | + FmtChunk fmt; | |
658 | + | |
659 | + work[0] = MAKE_ID('R','I','F','F'); | |
660 | + work[1] = 0; // filled in later | |
661 | + work[2] = MAKE_ID('W','A','V','E'); | |
662 | + if (3 != fwrite(work, 4, 3, File)) goto fail; | |
663 | + | |
664 | + fmt.ChunkID = MAKE_ID('f','m','t',' '); | |
665 | + fmt.ChunkLen = LittleLong(sizeof(fmt) - 8); | |
666 | + fmt.FormatTag = LittleShort(0xFFFE); // WAVE_FORMAT_EXTENSIBLE | |
667 | + fmt.Channels = LittleShort(2); | |
668 | + fmt.SamplesPerSec = LittleLong((int)Renderer->rate); | |
669 | + fmt.AvgBytesPerSec = LittleLong((int)Renderer->rate * 8); | |
670 | + fmt.BlockAlign = LittleShort(8); | |
671 | + fmt.BitsPerSample = LittleShort(32); | |
672 | + fmt.ExtensionSize = LittleShort(2 + 4 + 16); | |
673 | + fmt.ValidBitsPerSample = LittleShort(32); | |
674 | + fmt.ChannelMask = LittleLong(3); | |
675 | + fmt.SubFormatA = LittleLong(0x00000003); // Set subformat to KSDATAFORMAT_SUBTYPE_IEEE_FLOAT | |
676 | + fmt.SubFormatB = LittleShort(0x0000); | |
677 | + fmt.SubFormatC = LittleShort(0x0010); | |
678 | + fmt.SubFormatD[0] = 0x80; | |
679 | + fmt.SubFormatD[1] = 0x00; | |
680 | + fmt.SubFormatD[2] = 0x00; | |
681 | + fmt.SubFormatD[3] = 0xaa; | |
682 | + fmt.SubFormatD[4] = 0x00; | |
683 | + fmt.SubFormatD[5] = 0x38; | |
684 | + fmt.SubFormatD[6] = 0x9b; | |
685 | + fmt.SubFormatD[7] = 0x71; | |
686 | + if (1 != fwrite(&fmt, sizeof(fmt), 1, File)) goto fail; | |
687 | + | |
688 | + work[0] = MAKE_ID('d','a','t','a'); | |
689 | + work[1] = 0; // filled in later | |
690 | + if (2 != fwrite(work, 4, 2, File)) goto fail; | |
691 | + | |
692 | + return; | |
693 | +fail: | |
694 | + Printf("Failed to write %s: %s\n", filename, strerror(errno)); | |
695 | + fclose(File); | |
696 | + File = NULL; | |
697 | + } | |
698 | +} | |
699 | + | |
700 | +//========================================================================== | |
701 | +// | |
702 | +// TimidityWaveWriterMIDIDevice Destructor | |
703 | +// | |
704 | +//========================================================================== | |
705 | + | |
706 | +TimidityWaveWriterMIDIDevice::~TimidityWaveWriterMIDIDevice() | |
707 | +{ | |
708 | + if (File != NULL) | |
709 | + { | |
710 | + long pos = ftell(File); | |
711 | + DWORD size; | |
712 | + | |
713 | + // data chunk size | |
714 | + size = LittleLong(pos - 8); | |
715 | + if (0 == fseek(File, 4, SEEK_SET)) | |
716 | + { | |
717 | + if (1 == fwrite(&size, 4, 1, File)) | |
718 | + { | |
719 | + size = LittleLong(pos - 12 - sizeof(FmtChunk) - 8); | |
720 | + if (0 == fseek(File, 4 + sizeof(FmtChunk) + 4, SEEK_CUR)) | |
721 | + { | |
722 | + if (1 == fwrite(&size, 4, 1, File)) | |
723 | + { | |
724 | + fclose(File); | |
725 | + return; | |
726 | + } | |
727 | + } | |
728 | + } | |
729 | + } | |
730 | + Printf("Could not finish writing wave file: %s\n", strerror(errno)); | |
731 | + fclose(File); | |
732 | + } | |
733 | +} | |
734 | + | |
735 | +//========================================================================== | |
736 | +// | |
737 | +// TimidityWaveWriterMIDIDevice :: Resume | |
738 | +// | |
739 | +//========================================================================== | |
740 | + | |
741 | +int TimidityWaveWriterMIDIDevice::Resume() | |
742 | +{ | |
743 | + float writebuffer[4096]; | |
744 | + | |
745 | + while (ServiceStream(writebuffer, sizeof(writebuffer))) | |
746 | + { | |
747 | + if (fwrite(writebuffer, sizeof(writebuffer), 1, File) != 1) | |
748 | + { | |
749 | + Printf("Could not write entire wave file: %s\n", strerror(errno)); | |
750 | + return 1; | |
751 | + } | |
752 | + } | |
753 | + return 0; | |
754 | +} | |
755 | + | |
756 | +//========================================================================== | |
757 | +// | |
758 | +// TimidityWaveWriterMIDIDevice Stop | |
759 | +// | |
760 | +//========================================================================== | |
761 | + | |
762 | +void TimidityWaveWriterMIDIDevice::Stop() | |
763 | +{ | |
764 | +} |
@@ -82,8 +82,8 @@ | ||
82 | 82 | env_vol *= v->envelope_volume / float(1 << 30); |
83 | 83 | } |
84 | 84 | // Note: The pan offsets are negative. |
85 | - v->left_mix = (float)calc_gf1_amp(env_vol + v->left_offset) * final_amp; | |
86 | - v->right_mix = (float)calc_gf1_amp(env_vol + v->right_offset) * final_amp; | |
85 | + v->left_mix = MAX(0.f, (float)calc_gf1_amp(env_vol + v->left_offset) * final_amp); | |
86 | + v->right_mix = MAX(0.f, (float)calc_gf1_amp(env_vol + v->right_offset) * final_amp); | |
87 | 87 | } |
88 | 88 | |
89 | 89 | static int update_envelope(Voice *v) |
@@ -440,7 +440,7 @@ | ||
440 | 440 | { |
441 | 441 | return; |
442 | 442 | } |
443 | - if (v->left_offset == 0) // All the way to the left | |
443 | + if (v->right_mix == 0) // All the way to the left | |
444 | 444 | { |
445 | 445 | if (v->envelope_increment != 0 || v->tremolo_phase_increment != 0) |
446 | 446 | { |
@@ -451,7 +451,7 @@ | ||
451 | 451 | mix_single_left(sp, buf, v, count); |
452 | 452 | } |
453 | 453 | } |
454 | - else if (v->right_offset == 0) // All the way to the right | |
454 | + else if (v->left_mix == 0) // All the way to the right | |
455 | 455 | { |
456 | 456 | if (v->envelope_increment != 0 || v->tremolo_phase_increment != 0) |
457 | 457 | { |
@@ -215,6 +215,17 @@ | ||
215 | 215 | |
216 | 216 | void Renderer::compute_pan(int panning, float &left_offset, float &right_offset) |
217 | 217 | { |
218 | + // Round the left- and right-most positions to their extremes, since | |
219 | + // most songs only do coarse panning. | |
220 | + if (panning < 128) | |
221 | + { | |
222 | + panning = 0; | |
223 | + } | |
224 | + else if (panning > 127*128) | |
225 | + { | |
226 | + panning = 32767; | |
227 | + } | |
228 | + | |
218 | 229 | if (panning == 0) |
219 | 230 | { |
220 | 231 | left_offset = 0; |
@@ -355,6 +366,12 @@ | ||
355 | 366 | /* Only one instance of a note can be playing on a single channel. */ |
356 | 367 | void Renderer::note_on(int chan, int note, int vel) |
357 | 368 | { |
369 | + if (vel == 0) | |
370 | + { | |
371 | + note_off(chan, note, 0); | |
372 | + return; | |
373 | + } | |
374 | + | |
358 | 375 | int i = voices, lowest = -1; |
359 | 376 | float lv = 1e10, v; |
360 | 377 |
@@ -574,14 +591,7 @@ | ||
574 | 591 | switch (command) |
575 | 592 | { |
576 | 593 | case ME_NOTEON: |
577 | - if (parm2 == 0) | |
578 | - { | |
579 | - note_off(chan, parm1, 0); | |
580 | - } | |
581 | - else | |
582 | - { | |
583 | - note_on(chan, parm1, parm2); | |
584 | - } | |
594 | + note_on(chan, parm1, parm2); | |
585 | 595 | break; |
586 | 596 | |
587 | 597 | case ME_NOTEOFF: |
@@ -66,7 +66,7 @@ | ||
66 | 66 | /* For some reason the sample volume is always set to maximum in all |
67 | 67 | patch files. Define this for a crude adjustment that may help |
68 | 68 | equalize instrument volumes. */ |
69 | -#define ADJUST_SAMPLE_VOLUMES | |
69 | +//#define ADJUST_SAMPLE_VOLUMES | |
70 | 70 | |
71 | 71 | /* The number of samples to use for ramping out a dying note. Affects |
72 | 72 | click removal. */ |