mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-11-25 05:21:39 +00:00
358 lines
9.5 KiB
C++
358 lines
9.5 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "Precompiled.h"
|
|
#include "globaldata.h"
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include "idlib/sys/sys_defines.h"
|
|
// mus header
|
|
|
|
|
|
// reads a variable length integer
|
|
unsigned long ReadVarLen( char* buffer ) {
|
|
unsigned long value;
|
|
byte c;
|
|
|
|
if ((value = *buffer++) & 0x80) {
|
|
value &= 0x7f;
|
|
do {
|
|
value = (value << 7) + ((c = *buffer++) & 0x7f);
|
|
} while (c & 0x80);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
// Writes a variable length integer to a buffer, and returns bytes written
|
|
int WriteVarLen( long value, byte* out )
|
|
{
|
|
long buffer, count = 0;
|
|
|
|
buffer = value & 0x7f;
|
|
while ((value >>= 7) > 0) {
|
|
buffer <<= 8;
|
|
buffer += 0x80;
|
|
buffer += (value & 0x7f);
|
|
}
|
|
|
|
while (1) {
|
|
++count;
|
|
*out = (byte)buffer;
|
|
++out;
|
|
if (buffer & 0x80)
|
|
buffer >>= 8;
|
|
else
|
|
break;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
// writes a byte, and returns the buffer
|
|
unsigned char* WriteByte(void* buf, byte b)
|
|
{
|
|
unsigned char* buffer = (unsigned char*)buf;
|
|
*buffer++ = b;
|
|
return buffer;
|
|
}
|
|
|
|
unsigned char* WriteShort(void* b, unsigned short s)
|
|
{
|
|
unsigned char* buffer = (unsigned char*)b;
|
|
*buffer++ = (s >> 8);
|
|
*buffer++ = (s & 0x00FF);
|
|
return buffer;
|
|
}
|
|
|
|
unsigned char* WriteInt(void* b, unsigned int i)
|
|
{
|
|
unsigned char* buffer = (unsigned char*)b;
|
|
*buffer++ = (i & 0xff000000) >> 24;
|
|
*buffer++ = (i & 0x00ff0000) >> 16;
|
|
*buffer++ = (i & 0x0000ff00) >> 8;
|
|
*buffer++ = (i & 0x000000ff);
|
|
return buffer;
|
|
}
|
|
|
|
// Format - 0(1 track only), 1(1 or more tracks, each play same time), 2(1 or more, each play seperatly)
|
|
void Midi_CreateHeader(MidiHeaderChunk_t* header, short format, short track_count, short division)
|
|
{
|
|
WriteInt(header->name, 'MThd');
|
|
WriteInt(&header->length, 6);
|
|
WriteShort(&header->format, format);
|
|
WriteShort(&header->ntracks, track_count);
|
|
WriteShort(&header->division, division);
|
|
}
|
|
|
|
unsigned char* Midi_WriteTempo(unsigned char* buffer, int tempo)
|
|
{
|
|
buffer = WriteByte(buffer, 0x00); // delta time
|
|
buffer = WriteByte(buffer, 0xff); // sys command
|
|
buffer = WriteShort(buffer, 0x5103); // command - set tempo
|
|
|
|
buffer = WriteByte(buffer, tempo & 0x000000ff);
|
|
buffer = WriteByte(buffer, (tempo & 0x0000ff00) >> 8);
|
|
buffer = WriteByte(buffer, (tempo & 0x00ff0000) >> 16);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
int Midi_UpdateBytesWritten(int* bytes_written, int to_add, int max)
|
|
{
|
|
*bytes_written += to_add;
|
|
if (max && *bytes_written > max)
|
|
{
|
|
assert(0);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
unsigned char MidiMap[] =
|
|
{
|
|
0, // prog change
|
|
0, // bank sel
|
|
1, //2 // mod pot
|
|
0x07, //3 // volume
|
|
0x0A, //4 // pan pot
|
|
0x0B, //5 // expression pot
|
|
0x5B, //6 // reverb depth
|
|
0x5D, //7 // chorus depth
|
|
0x40, //8 // sustain pedal
|
|
0x43, //9 // soft pedal
|
|
0x78, //10 // all sounds off
|
|
0x7B, //11 // all notes off
|
|
0x7E, //12 // mono(use numchannels + 1)
|
|
0x7F, //13 // poly
|
|
0x79, //14 // reset all controllers
|
|
};
|
|
|
|
// The MUS data is stored in little-endian.
|
|
namespace {
|
|
unsigned short LittleToNative( const unsigned short value ) {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
int Mus2Midi(unsigned char* bytes, unsigned char* out, int* len)
|
|
{
|
|
// mus header and instruments
|
|
MUSheader_t header;
|
|
|
|
// current position in read buffer
|
|
unsigned char* cur = bytes,* end;
|
|
|
|
// Midi header(format 0)
|
|
MidiHeaderChunk_t midiHeader;
|
|
// Midi track header, only 1 needed(format 0)
|
|
MidiTrackChunk_t midiTrackHeader;
|
|
// Stores the position of the midi track header(to change the size)
|
|
byte* midiTrackHeaderOut;
|
|
|
|
// Delta time for midi event
|
|
int delta_time = 0;
|
|
int temp;
|
|
int channel_volume[MIDI_MAXCHANNELS] = {0};
|
|
int bytes_written = 0;
|
|
int channelMap[MIDI_MAXCHANNELS], currentChannel = 0;
|
|
byte last_status = 0;
|
|
|
|
// read the mus header
|
|
memcpy(&header, cur, sizeof(header));
|
|
cur += sizeof(header);
|
|
|
|
header.scoreLen = LittleToNative( header.scoreLen );
|
|
header.scoreStart = LittleToNative( header.scoreStart );
|
|
header.channels = LittleToNative( header.channels );
|
|
header.sec_channels = LittleToNative( header.sec_channels );
|
|
header.instrCnt = LittleToNative( header.instrCnt );
|
|
header.dummy = LittleToNative( header.dummy );
|
|
|
|
// only 15 supported
|
|
if (header.channels > MIDI_MAXCHANNELS - 1)
|
|
return 0;
|
|
|
|
// Map channel 15 to 9(percussions)
|
|
for (temp = 0; temp < MIDI_MAXCHANNELS; ++temp) {
|
|
channelMap[temp] = -1;
|
|
channel_volume[temp] = 0x40;
|
|
}
|
|
channelMap[15] = 9;
|
|
|
|
// Get current position, and end of position
|
|
cur = bytes + header.scoreStart;
|
|
end = cur + header.scoreLen;
|
|
|
|
// Write out midi header
|
|
Midi_CreateHeader(&midiHeader, 0, 1, 0x0059);
|
|
Midi_UpdateBytesWritten(&bytes_written, MIDIHEADERSIZE, *len);
|
|
memcpy(out, &midiHeader, MIDIHEADERSIZE); // cannot use sizeof(packs it to 16 bytes)
|
|
out += MIDIHEADERSIZE;
|
|
|
|
// Store this position, for later filling in the midiTrackHeader
|
|
Midi_UpdateBytesWritten(&bytes_written, sizeof(midiTrackHeader), *len);
|
|
midiTrackHeaderOut = out;
|
|
out += sizeof(midiTrackHeader);
|
|
|
|
|
|
// microseconds per quarter note(yikes)
|
|
Midi_UpdateBytesWritten(&bytes_written, 7, *len);
|
|
out = Midi_WriteTempo(out, 0x001aa309);
|
|
|
|
// Percussions channel starts out at full volume
|
|
Midi_UpdateBytesWritten(&bytes_written, 4, *len);
|
|
out = WriteByte(out, 0x00);
|
|
out = WriteByte(out, 0xB9);
|
|
out = WriteByte(out, 0x07);
|
|
out = WriteByte(out, 127);
|
|
|
|
// Main Loop
|
|
while (cur < end) {
|
|
byte channel;
|
|
byte event;
|
|
byte temp_buffer[32]; // temp buffer for current iterator
|
|
byte *out_local = temp_buffer;
|
|
byte status, bit1, bit2, bitc = 2;
|
|
|
|
// Read in current bit
|
|
event = *cur++;
|
|
channel = (event & 15); // current channel
|
|
|
|
// Write variable length delta time
|
|
out_local += WriteVarLen(delta_time, out_local);
|
|
|
|
if (channelMap[channel] < 0) {
|
|
// Set all channels to 127 volume
|
|
out_local = WriteByte(out_local, 0xB0 + currentChannel);
|
|
out_local = WriteByte(out_local, 0x07);
|
|
out_local = WriteByte(out_local, 127);
|
|
out_local = WriteByte(out_local, 0x00);
|
|
|
|
channelMap[channel] = currentChannel++;
|
|
if (currentChannel == 9)
|
|
++currentChannel;
|
|
}
|
|
|
|
status = channelMap[channel];
|
|
|
|
// Handle ::g->events
|
|
switch ((event & 122) >> 4)
|
|
{
|
|
default:
|
|
assert(0);
|
|
break;
|
|
case MUSEVENT_KEYOFF:
|
|
status |= 0x80;
|
|
bit1 = *cur++;
|
|
bit2 = 0x40;
|
|
break;
|
|
case MUSEVENT_KEYON:
|
|
status |= 0x90;
|
|
bit1 = *cur & 127;
|
|
if (*cur++ & 128) // volume bit?
|
|
channel_volume[channelMap[channel]] = *cur++;
|
|
bit2 = channel_volume[channelMap[channel]];
|
|
break;
|
|
case MUSEVENT_PITCHWHEEL:
|
|
status |= 0xE0;
|
|
bit1 = (*cur & 1) >> 6;
|
|
bit2 = (*cur++ >> 1) & 127;
|
|
break;
|
|
case MUSEVENT_CHANNELMODE:
|
|
status |= 0xB0;
|
|
assert(*cur < sizeof(MidiMap) / sizeof(MidiMap[0]));
|
|
bit1 = MidiMap[*cur++];
|
|
bit2 = (*cur++ == 12) ? header.channels + 1 : 0x00;
|
|
break;
|
|
case MUSEVENT_CONTROLLERCHANGE:
|
|
if (*cur == 0) {
|
|
cur++;
|
|
status |= 0xC0;
|
|
bit1 = *cur++;
|
|
bitc = 1;
|
|
} else {
|
|
status |= 0xB0;
|
|
assert(*cur < sizeof(MidiMap) / sizeof(MidiMap[0]));
|
|
bit1 = MidiMap[*cur++];
|
|
bit2 = *cur++;
|
|
}
|
|
break;
|
|
case 5: // Unknown
|
|
assert(0);
|
|
break;
|
|
case MUSEVENT_END: // End
|
|
status = 0xff;
|
|
bit1 = 0x2f;
|
|
bit2 = 0x00;
|
|
assert(cur == end);
|
|
break;
|
|
case 7: // Unknown
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
// Write it out
|
|
out_local = WriteByte(out_local, status);
|
|
out_local = WriteByte(out_local, bit1);
|
|
if (bitc == 2)
|
|
out_local = WriteByte(out_local, bit2);
|
|
|
|
|
|
// Write out temp stuff
|
|
if (out_local != temp_buffer)
|
|
{
|
|
Midi_UpdateBytesWritten(&bytes_written, out_local - temp_buffer, *len);
|
|
memcpy(out, temp_buffer, out_local - temp_buffer);
|
|
out += out_local - temp_buffer;
|
|
}
|
|
|
|
if (event & 128) {
|
|
delta_time = 0;
|
|
do {
|
|
delta_time = delta_time * 128 + (*cur & 127);
|
|
} while ((*cur++ & 128));
|
|
} else {
|
|
delta_time = 0;
|
|
}
|
|
}
|
|
|
|
// Write out track header
|
|
WriteInt(midiTrackHeader.name, 'MTrk');
|
|
WriteInt(&midiTrackHeader.length, out - midiTrackHeaderOut - sizeof(midiTrackHeader));
|
|
memcpy(midiTrackHeaderOut, &midiTrackHeader, sizeof(midiTrackHeader));
|
|
|
|
// Store length written
|
|
*len = bytes_written;
|
|
/*{
|
|
FILE* file = f o pen("d:\\test.midi", "wb");
|
|
fwrite(midiTrackHeaderOut - sizeof(MidiHeaderChunk_t), bytes_written, 1, file);
|
|
fclose(file);
|
|
}*/
|
|
return 1;
|
|
}
|
|
|