mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-03-16 15:41:16 +00:00
392 lines
9.9 KiB
C++
392 lines
9.9 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>
|
|
// 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;
|
|
if( cur[0] != 'M' || cur[1] != 'U' || cur[2] != 'S' )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|