From b09ff6f00346a6aacf09930888523ab8a68115e2 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 5 Sep 2005 22:50:56 +0000 Subject: Add mmus2mid code from prboom. Use 'void *' for music handles. Pass length of data when registering music. Subversion-branch: /trunk/chocolate-doom Subversion-revision: 75 --- src/Makefile.am | 4 +- src/i_sound.c | 27 +- src/i_sound.h | 34 ++- src/m_swap.h | 10 +- src/mmus2mid.c | 858 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/mmus2mid.h | 99 +++++++ src/s_sound.c | 10 +- src/sounds.h | 8 +- 8 files changed, 1020 insertions(+), 30 deletions(-) create mode 100644 src/mmus2mid.c create mode 100644 src/mmus2mid.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index e872dbc9..b6252898 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,8 +24,8 @@ doomstat.h info.c m_random.h p_setup.h r_segs.c w_wad.c \ doomtype.h info.h m_swap.c p_sight.c r_segs.h w_wad.h \ d_player.h i_sound.c m_swap.h p_spec.c r_sky.c z_zone.c \ dstrings.c i_sound.h p_ceilng.c p_spec.h r_sky.h z_zone.h \ -dstrings.h i_system.c p_doors.c p_switch.c r_state.h \ -d_textur.h i_system.h p_enemy.c p_telept.c r_things.c +dstrings.h i_system.c p_doors.c p_switch.c r_state.h mmus2mid.c \ +d_textur.h i_system.h p_enemy.c p_telept.c r_things.c mmus2mid.h bin_PROGRAMS = chocolate-doom diff --git a/src/i_sound.c b/src/i_sound.c index e97ccc29..e20463d9 100644 --- a/src/i_sound.c +++ b/src/i_sound.c @@ -1,7 +1,7 @@ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // -// $Id: i_sound.c 74 2005-09-05 21:03:43Z fraggle $ +// $Id: i_sound.c 75 2005-09-05 22:50:56Z fraggle $ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005 Simon Howard @@ -22,6 +22,10 @@ // 02111-1307, USA. // // $Log$ +// Revision 1.13 2005/09/05 22:50:56 fraggle +// Add mmus2mid code from prboom. Use 'void *' for music handles. Pass +// length of data when registering music. +// // Revision 1.12 2005/09/05 21:03:43 fraggle // 16-bit sound // @@ -70,7 +74,7 @@ //----------------------------------------------------------------------------- static const char -rcsid[] = "$Id: i_sound.c 74 2005-09-05 21:03:43Z fraggle $"; +rcsid[] = "$Id: i_sound.c 75 2005-09-05 22:50:56Z fraggle $"; #include #include @@ -363,26 +367,26 @@ void I_ShutdownMusic(void) { } static int looping=0; static int musicdies=-1; -void I_PlaySong(int handle, int looping) +void I_PlaySong(void *handle, int looping) { // UNUSED. - handle = looping = 0; + looping = 0; musicdies = gametic + TICRATE*30; } -void I_PauseSong (int handle) +void I_PauseSong (void *handle) { // UNUSED. handle = 0; } -void I_ResumeSong (int handle) +void I_ResumeSong (void *handle) { // UNUSED. handle = 0; } -void I_StopSong(int handle) +void I_StopSong(void *handle) { // UNUSED. handle = 0; @@ -391,22 +395,21 @@ void I_StopSong(int handle) musicdies = 0; } -void I_UnRegisterSong(int handle) +void I_UnRegisterSong(void *handle) { // UNUSED. handle = 0; } -int I_RegisterSong(void* data) +void *I_RegisterSong(void *data, int len) { - // UNUSED. - data = NULL; + // write the return 1; } // Is the song playing? -int I_QrySongPlaying(int handle) +int I_QrySongPlaying(void *handle) { // UNUSED. handle = 0; diff --git a/src/i_sound.h b/src/i_sound.h index e0162a77..53d3ef66 100644 --- a/src/i_sound.h +++ b/src/i_sound.h @@ -1,7 +1,7 @@ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // -// $Id: i_sound.h 73 2005-09-05 20:32:18Z fraggle $ +// $Id: i_sound.h 75 2005-09-05 22:50:56Z fraggle $ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005 Simon Howard @@ -91,27 +91,37 @@ I_UpdateSoundParams // // MUSIC I/O // + void I_InitMusic(void); void I_ShutdownMusic(void); + // Volume. + void I_SetMusicVolume(int volume); + // PAUSE game handling. -void I_PauseSong(int handle); -void I_ResumeSong(int handle); + +void I_PauseSong(void *handle); +void I_ResumeSong(void *handle); + // Registers a song handle to song data. -int I_RegisterSong(void *data); + +void *I_RegisterSong(void *data, int length); + // Called by anything that wishes to start music. // plays a song, and when the song is done, // starts playing it again in an endless loop. // Horrible thing to do, considering. -void -I_PlaySong -( int handle, - int looping ); + +void I_PlaySong(void *handle, int looping); + // Stops a song over 3 seconds. -void I_StopSong(int handle); + +void I_StopSong(void *handle); + // See above (register), then think backwards -void I_UnRegisterSong(int handle); + +void I_UnRegisterSong(void *handle); @@ -119,6 +129,10 @@ void I_UnRegisterSong(int handle); //----------------------------------------------------------------------------- // // $Log$ +// Revision 1.5 2005/09/05 22:50:56 fraggle +// Add mmus2mid code from prboom. Use 'void *' for music handles. Pass +// length of data when registering music. +// // Revision 1.4 2005/09/05 20:32:18 fraggle // Use the system-nonspecific sound code to assign the channel number used // by SDL. Remove handle tagging stuff. diff --git a/src/m_swap.h b/src/m_swap.h index af296d62..881574ea 100644 --- a/src/m_swap.h +++ b/src/m_swap.h @@ -1,7 +1,7 @@ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // -// $Id: m_swap.h 18 2005-07-23 18:56:07Z fraggle $ +// $Id: m_swap.h 75 2005-09-05 22:50:56Z fraggle $ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005 Simon Howard @@ -43,6 +43,10 @@ long SwapLONG(long); #else #define SHORT(x) (x) #define LONG(x) (x) +#define doom_wtohs(x) ((short int) (x)) +#define doom_htows(x) ((short int) (x)) +#define doom_wtohl(x) ((long int) (x)) +#define doom_htowl(x) ((long int) (x)) #endif @@ -52,6 +56,10 @@ long SwapLONG(long); //----------------------------------------------------------------------------- // // $Log$ +// Revision 1.4 2005/09/05 22:50:56 fraggle +// Add mmus2mid code from prboom. Use 'void *' for music handles. Pass +// length of data when registering music. +// // Revision 1.3 2005/07/23 18:56:07 fraggle // Remove unneccessary pragmas // diff --git a/src/mmus2mid.c b/src/mmus2mid.c new file mode 100644 index 00000000..6db59308 --- /dev/null +++ b/src/mmus2mid.c @@ -0,0 +1,858 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id: mmus2mid.c 75 2005-09-05 22:50:56Z fraggle $ +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright (C) 1999 by +// id Software, Chi Hoang, Lee Killough, Jim Flynn, +// Rand Phares, Ty Halderman +// Copyright (C) 1999-2000 by +// Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze +// Copyright(C) 2005 Simon Howard +// +// This program 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 2 +// of the License, or (at your option) any later version. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// $Log$ +// Revision 1.1 2005/09/05 22:50:56 fraggle +// Add mmus2mid code from prboom. Use 'void *' for music handles. Pass +// length of data when registering music. +// +// +// DESCRIPTION: +// MUS to MID conversion utility from PrBoom. +// +// This file supports conversion of MUS format music in memory +// to MIDI format 1 music in memory. +// +// The primary routine, mmus2mid, converts a block of memory in MUS format +// to an Allegro MIDI structure. This supports playing MUS lumps in a wad +// file with BOOM. +// +// Another routine, Midi2MIDI, converts a block of memory in MIDI format 1 to +// an Allegro MIDI structure. This supports playing MIDI lumps in a wad +// file with BOOM. +// +// For testing purposes, and to make a utility if desired, if the symbol +// STANDALONE is defined by uncommenting the definition below, a main +// routine is compiled that will convert a possibly wildcarded set of MUS +// files to a similarly named set of MIDI files. +// +// Much of the code here is thanks to S. Bacquet's source for QMUS2MID.C +// + +#include +#include +#include +#include +#include +#include +#include +#include "mmus2mid.h" + +//#define STANDALONE /* uncomment this to make MMUS2MID.EXE */ +#ifndef STANDALONE +#include "m_swap.h" +#include "z_zone.h" +#endif + +// some macros to decode mus event bit fields + +#define last(e) ((UBYTE)((e) & 0x80)) +#define event_type(e) ((UBYTE)(((e) & 0x7F) >> 4)) +#define channel(e) ((UBYTE)((e) & 0x0F)) + +// event types + +typedef enum +{ + RELEASE_NOTE, + PLAY_NOTE, + BEND_NOTE, + SYS_EVENT, + CNTL_CHANGE, + UNKNOWN_EVENT1, + SCORE_END, + UNKNOWN_EVENT2, +} mus_event_t; + +// MUS format header structure + +typedef struct +{ + char ID[4]; // identifier "MUS"0x1A + UWORD ScoreLength; // length of music portion + UWORD ScoreStart; // offset of music portion + UWORD channels; // count of primary channels + UWORD SecChannels; // count of secondary channels + UWORD InstrCnt; // number of instruments +} MUSheader; + +// to keep track of information in a MIDI track + +typedef struct Track +{ + char velocity; + long deltaT; + UBYTE lastEvt; + long alloced; +} TrackInfo; + +// array of info about tracks + +static TrackInfo track[MIDI_TRACKS]; + +// initial track size allocation +#define TRACKBUFFERSIZE 1024 + +// lookup table MUS -> MID controls +static UBYTE MUS2MIDcontrol[15] = +{ + 0, // Program change - not a MIDI control change + 0x00, // Bank select + 0x01, // Modulation pot + 0x07, // Volume + 0x0A, // Pan pot + 0x0B, // Expression pot + 0x5B, // Reverb depth + 0x5D, // Chorus depth + 0x40, // Sustain pedal + 0x43, // Soft pedal + 0x78, // All sounds off + 0x7B, // All notes off + 0x7E, // Mono + 0x7F, // Poly + 0x79 // Reset all controllers +}; + +// some strings of bytes used in the midi format + +static UBYTE midikey[] = +{0x00,0xff,0x59,0x02,0x00,0x00}; // C major +static UBYTE miditempo[] = +{0x00,0xff,0x51,0x03,0x09,0xa3,0x1a}; // uS/qnote +static UBYTE midihdr[] = +{'M','T','h','d',0,0,0,6,0,1,0,0,0,0}; // header (length 6, format 1) +static UBYTE trackhdr[] = +{'M','T','r','k'}; // track header + +// static routine prototypes + +static int TWriteByte(MIDI *mididata, int MIDItrack, UBYTE byte); +static int TWriteVarLen(MIDI *mididata, int MIDItrack, register ULONG value); +static ULONG ReadTime(const UBYTE **musptrp); +static int FirstChannelAvailable(int MUS2MIDchannel[]); +static UBYTE MidiEvent(MIDI *mididata,UBYTE midicode,UBYTE MIDIchannel, + UBYTE MIDItrack,int nocomp); + +// +// TWriteByte() +// +// write one byte to the selected MIDItrack, update current position +// if track allocation exceeded, double it +// if track not allocated, initially allocate TRACKBUFFERSIZE bytes +// +// Passed pointer to Allegro MIDI structure, number of the MIDI track being +// written, and the byte to write. +// +// Returns 0 on success, MEMALLOC if a memory allocation error occurs +// +static int TWriteByte(MIDI *mididata, int MIDItrack, UBYTE byte) +{ + ULONG pos ; + + pos = mididata->track[MIDItrack].len; + if (pos >= (ULONG)track[MIDItrack].alloced) + { + track[MIDItrack].alloced = // double allocation + track[MIDItrack].alloced? // or set initial TRACKBUFFERSIZE + 2*track[MIDItrack].alloced : + TRACKBUFFERSIZE; + + if (!(mididata->track[MIDItrack].data = // attempt to reallocate + realloc(mididata->track[MIDItrack].data, + track[MIDItrack].alloced))) + return MEMALLOC; + } + mididata->track[MIDItrack].data[pos] = byte; + mididata->track[MIDItrack].len++; + return 0; +} + +// +// TWriteVarLen() +// +// write the ULONG value to tracknum-th track, in midi format, which is +// big endian, 7 bits per byte, with all bytes but the last flagged by +// bit 8 being set, allowing the length to vary. +// +// Passed the Allegro MIDI structure, the track number to write, +// and the ULONG value to encode in midi format there +// +// Returns 0 if sucessful, MEMALLOC if a memory allocation error occurs +// +static int TWriteVarLen(MIDI *mididata, int tracknum, register ULONG value) +{ + register ULONG buffer; + + buffer = value & 0x7f; + while ((value >>= 7)) // terminates because value unsigned + { + buffer <<= 8; // note first value shifted in has bit 8 clear + buffer |= 0x80; // all succeeding values do not + buffer += (value & 0x7f); + } + while (1) // write bytes out in opposite order + { + if (TWriteByte(mididata, tracknum, (UBYTE)(buffer&0xff))) // insure buffer masked + return MEMALLOC; + + if (buffer & 0x80) + buffer >>= 8; + else // terminate on the byte with bit 8 clear + break; + } + return 0; +} + +// +// ReadTime() +// +// Read a time value from the MUS buffer, advancing the position in it +// +// A time value is a variable length sequence of 8 bit bytes, with all +// but the last having bit 8 set. +// +// Passed a pointer to the pointer to the MUS buffer +// Returns the integer unsigned long time value there and advances the pointer +// +static ULONG ReadTime(const UBYTE **musptrp) +{ + register ULONG timeval = 0; + int byte; + + do // shift each byte read up in the result until a byte with bit 8 clear + { + byte = *(*musptrp)++; + timeval = (timeval << 7) + (byte & 0x7F); + } + while(byte & 0x80); + + return timeval; +} + +// +// FirstChannelAvailable() +// +// Return the next unassigned MIDI channel number +// +// The assignment for MUS channel 15 is not counted in the caculation, that +// being percussion and always assigned to MIDI channel 9 (base 0). +// +// Passed the array of MIDI channels assigned to MUS channels +// Returns the maximum channel number unassigned unless that is 9 in which +// case 10 is returned. +// +// killough 10/7/98: changed char parameter, return values to int + +static int FirstChannelAvailable(int MUS2MIDchannel[]) +{ + int i ; + int max = -1 ; + + // find the largest MIDI channel assigned so far + for (i = 0; i < 15; i++) + if (MUS2MIDchannel[i] > max) + max = MUS2MIDchannel[i]; + + return (max == 8 ? 10 : max+1); // skip MIDI channel 9 (percussion) +} + +// +// MidiEvent() +// +// Constructs a MIDI event code, and writes it to the current MIDI track +// unless its the same as the last event code and compressio is enabled +// in which case nothing is written. +// +// Passed the Allegro MIDI structure, the midi event code, the current +// MIDI channel number, the current MIDI track number, and whether compression +// (running status) is enabled. +// +// Returns the new event code if successful, 0 if a memory allocation error +// +static UBYTE MidiEvent(MIDI *mididata,UBYTE midicode,UBYTE MIDIchannel, + UBYTE MIDItrack,int nocomp) +{ + UBYTE newevent; + + newevent = midicode | MIDIchannel; + if ((newevent != track[MIDItrack].lastEvt) || nocomp) + { + if (TWriteByte(mididata,MIDItrack, newevent)) + return 0; // indicates MEMALLOC error + track[MIDItrack].lastEvt = newevent; + } + return newevent; +} + +// +// mmus2mid() +// +// Convert a memory buffer contain MUS data to an Allegro MIDI structure +// with specified time division and compression. +// +// Passed a pointer to the buffer containing MUS data, a pointer to the +// Allegro MIDI structure, the divisions, and a flag whether to compress. +// +// Returns 0 if successful, otherwise an error code (see mmus2mid.h). +// +int mmus2mid(const UBYTE *mus, MIDI *mididata, UWORD division, int nocomp) +{ + UWORD TrackCnt = 0; + UBYTE evt, MUSchannel, MIDIchannel, MIDItrack=0, NewEvent; + int i, event, data; + const UBYTE *musptr; + size_t muslen; + static MUSheader MUSh; + UBYTE MIDIchan2track[MIDI_TRACKS]; // killough 10/7/98: fix too small array + int MUS2MIDchannel[MIDI_TRACKS]; // killough 10/7/98: fix too small array + + // copy the MUS header from the MUS buffer to the MUSh header structure + + memcpy(&MUSh,mus,sizeof(MUSheader)); + MUSh.ScoreLength = doom_wtohs(MUSh.ScoreLength); + MUSh.ScoreStart = doom_wtohs(MUSh.ScoreStart); + MUSh.channels = doom_wtohs(MUSh.channels); + MUSh.SecChannels = doom_wtohs(MUSh.SecChannels); + MUSh.InstrCnt = doom_wtohs(MUSh.InstrCnt); + + // check some things and set length of MUS buffer from internal data + + if (!(muslen = MUSh.ScoreLength + MUSh.ScoreStart)) + return MUSDATAMT; // MUS file empty + + if (MUSh.channels > 15) // MUSchannels + drum channel > 16 + return TOOMCHAN ; + + musptr = mus+MUSh.ScoreStart; // init musptr to start of score + + for (i = 0; i < MIDI_TRACKS; i++) // init the track structure's tracks + { + MUS2MIDchannel[i] = -1; // flag for channel not used yet + track[i].velocity = 64; + track[i].deltaT = 0; + track[i].lastEvt = 0; + free(mididata->track[i].data);//jff 3/5/98 remove old allocations + mididata->track[i].data=NULL; + track[i].alloced = 0; + mididata->track[i].len = 0; + } + + if (!division) + division = 70; + + // allocate the first track which is a special tempo/key track + // note multiple tracks means midi format 1 + + // set the divisions (ticks per quarter note) + mididata->divisions = division; + + // allocate for midi tempo/key track, allow for end of track + if (!(mididata->track[0].data = + realloc(mididata->track[0].data,sizeof(midikey)+sizeof(miditempo)+4))) + return MEMALLOC; + + // key C major + memcpy(mididata->track[0].data,midikey,sizeof(midikey)); + // tempo uS/qnote + memcpy(mididata->track[0].data+sizeof(midikey),miditempo,sizeof(miditempo)); + mididata->track[0].len = sizeof(midikey)+sizeof(miditempo); + + TrackCnt++; // music tracks start at 1 + + // process the MUS events in the MUS buffer + + do + { + // get a mus event, decode its type and channel fields + + event = *musptr++; + if ((evt = event_type(event)) == SCORE_END) //jff 1/23/98 use symbol + break; // if end of score event, leave + MUSchannel = channel(event); + + // if this channel not initialized, do so + + if (MUS2MIDchannel[MUSchannel] == -1) + { + // set MIDIchannel and MIDItrack + + MIDIchannel = MUS2MIDchannel[MUSchannel] = + (MUSchannel == 15 ? 9 : FirstChannelAvailable(MUS2MIDchannel)); + MIDItrack = MIDIchan2track[MIDIchannel] = (UBYTE)TrackCnt++; + } + else // channel already allocated as a track, use those values + { + MIDIchannel = MUS2MIDchannel[MUSchannel]; + MIDItrack = MIDIchan2track[MIDIchannel]; + } + + if (TWriteVarLen(mididata, MIDItrack, track[MIDItrack].deltaT)) + return MEMALLOC; + track[MIDItrack].deltaT = 0; + + switch(evt) + { + case RELEASE_NOTE: + // killough 10/7/98: Fix noise problems by not allowing compression + if (!(NewEvent=MidiEvent(mididata,0x90,MIDIchannel,MIDItrack,1))) + return MEMALLOC; + + data = *musptr++; + if (TWriteByte(mididata, MIDItrack, (UBYTE)(data & 0x7F))) + return MEMALLOC; + if (TWriteByte(mididata, MIDItrack, 0)) + return MEMALLOC; + break; + + case PLAY_NOTE: + if (!(NewEvent=MidiEvent(mididata,0x90,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + + data = *musptr++; + if (TWriteByte(mididata, MIDItrack, (UBYTE)(data & 0x7F))) + return MEMALLOC; + if( data & 0x80 ) + track[MIDItrack].velocity = (*musptr++) & 0x7f; + if (TWriteByte(mididata, MIDItrack, track[MIDItrack].velocity)) + return MEMALLOC; + break; + + case BEND_NOTE: + if (!(NewEvent=MidiEvent(mididata,0xE0,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + + data = *musptr++; + if (TWriteByte(mididata, MIDItrack, (UBYTE)((data & 1) << 6))) + return MEMALLOC; + if (TWriteByte(mididata, MIDItrack, (UBYTE)(data >> 1))) + return MEMALLOC; + break; + + case SYS_EVENT: + if (!(NewEvent=MidiEvent(mididata,0xB0,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + + data = *musptr++; + if (data<10 || data>14) + return BADSYSEVT; + + if (TWriteByte(mididata, MIDItrack, MUS2MIDcontrol[data])) + return MEMALLOC; + if (data == 12) + { + if (TWriteByte(mididata, MIDItrack, (UBYTE)(MUSh.channels+1))) + return MEMALLOC; + } + else + if (TWriteByte(mididata, MIDItrack, 0)) + return MEMALLOC; + break; + + case CNTL_CHANGE: + data = *musptr++; + if (data>9) + return BADCTLCHG; + + if (data) + { + if (!(NewEvent=MidiEvent(mididata,0xB0,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + + if (TWriteByte(mididata, MIDItrack, MUS2MIDcontrol[data])) + return MEMALLOC; + } + else + { + if (!(NewEvent=MidiEvent(mididata,0xC0,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + } + data = *musptr++; + if (TWriteByte(mididata, MIDItrack, (UBYTE)(data & 0x7F))) + return MEMALLOC; + break; + + case UNKNOWN_EVENT1: // mus events 5 and 7 + case UNKNOWN_EVENT2: // meaning not known + return BADMUSCTL; + + case SCORE_END: + break; + + default: + return BADMUSCTL; // exit with error + } + if (last(event)) + { + ULONG DeltaTime = ReadTime(&musptr); // killough 10/7/98: make local + for (i = 0;i < MIDI_TRACKS; i++) //jff 3/13/98 update all tracks + track[i].deltaT += DeltaTime; //whether allocated yet or not + } + + } + while ((evt != SCORE_END) && ((size_t)(musptr-mus) < muslen)); + + if (evt!=SCORE_END) + return MUSDATACOR; + + // Now add an end of track to each mididata track, correct allocation + + for (i = 0; i < MIDI_TRACKS; i++) + if (mididata->track[i].len) + { // killough 10/7/98: simplify code + if (TWriteByte(mididata, i, 0x00) || // midi end of track code + TWriteByte(mididata, i, 0xFF) || + TWriteByte(mididata, i, 0x2F) || + TWriteByte(mididata, i, 0x00)) + return MEMALLOC; + + // jff 1/23/98 fix failure to set data NULL, len 0 for unused tracks + // shorten allocation to proper length (important for Allegro) + if (!(mididata->track[i].data = + realloc(mididata->track[i].data,mididata->track[i].len))) + return MEMALLOC; + } + else + { + free(mididata->track[i].data); + mididata->track[i].data = NULL; + } + + return 0; +} + +// +// ReadLength() +// +// Reads the length of a chunk in a midi buffer, advancing the pointer +// 4 bytes, bigendian +// +// Passed a pointer to the pointer to a MIDI buffer +// Returns the chunk length at the pointer position +// +size_t ReadLength(UBYTE **mid) +{ + UBYTE *midptr = *mid; + + size_t length = (*midptr++)<<24; + length += (*midptr++)<<16; + length += (*midptr++)<<8; + length += *midptr++; + *mid = midptr; + return length; +} + +// +// MidiToMIDI() +// +// Convert an in-memory copy of a MIDI format 0 or 1 file to +// an Allegro MIDI structure, that is valid or has been zeroed +// +// Passed a pointer to a memory buffer with MIDI format music in it and a +// pointer to an Allegro MIDI structure. +// +// Returns 0 if successful, BADMIDHDR if the buffer is not MIDI format +// +int MidiToMIDI(UBYTE *mid,MIDI *mididata) +{ + int i; + int ntracks; + + // read the midi header + + if (memcmp(mid,midihdr,4)) + return BADMIDHDR; + + mididata->divisions = (mid[12]<<8)+mid[13]; + ntracks = (mid[10]<<8)+mid[11]; + + if (ntracks>=MIDI_TRACKS) + return BADMIDHDR; + + mid += 4; + { // killough 10/7/98: fix mid from being modified twice before sequence pt. + size_t t = ReadLength(&mid); // seek past header + mid += t; + } + + // now read each track + + for (i=0;itrack[i].len = ReadLength(&mid); // get length, move mid past it + + // read a track + mididata->track[i].data = realloc(mididata->track[i].data,mididata->track[i].len); + memcpy(mididata->track[i].data,mid,mididata->track[i].len); + mid += mididata->track[i].len; + } + for (;itrack[i].len) + { + free(mididata->track[i].data); + mididata->track[i].data = NULL; + mididata->track[i].len = 0; + } + return 0; +} + +//#ifdef STANDALONE /* this code unused by BOOM provided for future portability */ +// /* it also provides a MUS to MID file converter*/ +// proff: I moved this down, because I need MIDItoMidi + +static void FreeTracks(MIDI *mididata); +static void TWriteLength(UBYTE **midiptr,ULONG length); + +// +// FreeTracks() +// +// Free all track allocations in the MIDI structure +// +// Passed a pointer to an Allegro MIDI structure +// Returns nothing +// +static void FreeTracks(MIDI *mididata) +{ + int i; + + for (i=0; itrack[i].data); + mididata->track[i].data = NULL; + mididata->track[i].len = 0; + } +} + +// +// TWriteLength() +// +// Write the length of a MIDI chunk to a midi buffer. The length is four +// bytes and is written byte-reversed for bigendian. The pointer to the +// midi buffer is advanced. +// +// Passed a pointer to the pointer to a midi buffer, and the length to write +// Returns nothing +// +static void TWriteLength(UBYTE **midiptr,ULONG length) +{ +// proff: Added typecast to avoid warning + *(*midiptr)++ = (unsigned char)((length>>24)&0xff); + *(*midiptr)++ = (unsigned char)((length>>16)&0xff); + *(*midiptr)++ = (unsigned char)((length>>8)&0xff); + *(*midiptr)++ = (unsigned char)((length)&0xff); +} + +// +// MIDIToMidi() +// +// This routine converts an Allegro MIDI structure to a midi 1 format file +// in memory. It is used to support memory MUS -> MIDI conversion +// +// Passed a pointer to an Allegro MIDI structure, a pointer to a pointer to +// a buffer containing midi data, and a pointer to a length return. +// Returns 0 if successful, MEMALLOC if a memory allocation error occurs +// +int MIDIToMidi(MIDI *mididata,UBYTE **mid,int *midlen) +{ + size_t total; + int i,ntrks; + UBYTE *midiptr; + + // calculate how long the mid buffer must be, and allocate + + total = sizeof(midihdr); + for (i=0,ntrks=0;itrack[i].len) + { + total += 8 + mididata->track[i].len; // Track hdr + track length + ntrks++; + } + if ((*mid = malloc(total))==NULL) + return MEMALLOC; + + + // fill in number of tracks and bigendian divisions (ticks/qnote) + + midihdr[10] = 0; + midihdr[11] = (UBYTE)ntrks; // set number of tracks in header + midihdr[12] = (mididata->divisions>>8) & 0x7f; + midihdr[13] = (mididata->divisions) & 0xff; + + // write the midi header + + midiptr = *mid; + memcpy(midiptr,midihdr,sizeof(midihdr)); + midiptr += sizeof(midihdr); + + // write the tracks + + for (i=0;itrack[i].len) + { + memcpy(midiptr,trackhdr,sizeof(trackhdr)); // header + midiptr += sizeof(trackhdr); + TWriteLength(&midiptr,mididata->track[i].len); // track length + // data + memcpy(midiptr,mididata->track[i].data,mididata->track[i].len); + midiptr += mididata->track[i].len; + } + } + + // return length information + + *midlen = midiptr - *mid; + + return 0; +} + +#ifdef STANDALONE /* this code unused by BOOM provided for future portability */ + /* it also provides a MUS to MID file converter*/ +// proff: I moved this down, because I need MIDItoMidi + +// +// main() +// +// Main routine that will convert a globbed set of MUS files to the +// correspondingly named MID files using mmus2mid(). Only compiled +// if the STANDALONE symbol is defined. +// +// Passed the command line arguments, returns 0 if successful +// +int main(int argc,char **argv) +{ + FILE *musst,*midst; + char musfile[FILENAME_MAX],midfile[FILENAME_MAX]; + MUSheader MUSh; + UBYTE *mus,*mid; + static MIDI mididata; + int err,midlen; + char *p,*q; + int i; + + if (argc<2) + { + //jff 8/3/98 use logical output routine + lprintf(LO_INFO,"Usage: MMUS2MID musfile[.MUS]\n"); + lprintf(LO_INFO,"writes musfile.MID as output\n"); + lprintf(LO_INFO,"musfile may contain wildcards\n"); + exit(1); + } + + for (i=1;idata = (void *) W_CacheLumpNum(music->lumpnum, PU_MUSIC); - music->handle = I_RegisterSong(music->data); + music->handle = I_RegisterSong(music->data, W_LumpLength(music->lumpnum)); // play it I_PlaySong(music->handle, looping); diff --git a/src/sounds.h b/src/sounds.h index 9481bee5..1e3e7757 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -1,7 +1,7 @@ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // -// $Id: sounds.h 8 2005-07-23 16:44:57Z fraggle $ +// $Id: sounds.h 75 2005-09-05 22:50:56Z fraggle $ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005 Simon Howard @@ -86,7 +86,7 @@ typedef struct void* data; // music handle once registered - int handle; + void *handle; } musicinfo_t; @@ -299,6 +299,10 @@ typedef enum //----------------------------------------------------------------------------- // // $Log$ +// Revision 1.3 2005/09/05 22:50:56 fraggle +// Add mmus2mid code from prboom. Use 'void *' for music handles. Pass +// length of data when registering music. +// // Revision 1.2 2005/07/23 16:44:57 fraggle // Update copyright to GNU GPL // -- cgit v1.2.3