Moved all files one dir upwards (should've done that long ago)

This commit is contained in:
pelya
2010-08-16 13:15:59 +03:00
parent a8ae1c2295
commit 51bc7a8e3e
3206 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
/*
native_midi: Hardware Midi support for the SDL_mixer library
Copyright (C) 2000 Florian 'Proff' Schulze
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Florian 'Proff' Schulze
florian.proff.schulze@gmx.net
*/
#ifndef _NATIVE_MIDI_H_
#define _NATIVE_MIDI_H_
#include <SDL_rwops.h>
typedef struct _NativeMidiSong NativeMidiSong;
int native_midi_detect();
NativeMidiSong *native_midi_loadsong(const char *midifile);
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw);
void native_midi_freesong(NativeMidiSong *song);
void native_midi_start(NativeMidiSong *song);
void native_midi_stop();
int native_midi_active();
void native_midi_setvolume(int volume);
const char *native_midi_error(void);
#endif /* _NATIVE_MIDI_H_ */

View File

@@ -0,0 +1,410 @@
/*
native_midi: Hardware Midi support for the SDL_mixer library
Copyright (C) 2000,2001 Florian 'Proff' Schulze
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Florian 'Proff' Schulze
florian.proff.schulze@gmx.net
*/
#include "native_midi_common.h"
#include "../SDL_mixer.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
/* The maximum number of midi tracks that we can handle
#define MIDI_TRACKS 32 */
/* A single midi track as read from the midi file */
typedef struct
{
Uint8 *data; /* MIDI message stream */
int len; /* length of the track data */
} MIDITrack;
/* A midi file, stripped down to the absolute minimum - divison & track data */
typedef struct
{
int division; /* number of pulses per quarter note (ppqn) */
int nTracks; /* number of tracks */
MIDITrack *track; /* tracks */
} MIDIFile;
/* Some macros that help us stay endianess-independant */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define BE_SHORT(x) (x)
#define BE_LONG(x) (x)
#else
#define BE_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
#define BE_LONG(x) ((((x)&0x0000FF)<<24) | \
(((x)&0x00FF00)<<8) | \
(((x)&0xFF0000)>>8) | \
(((x)>>24)&0xFF))
#endif
/* Get Variable Length Quantity */
static int GetVLQ(MIDITrack *track, int *currentPos)
{
int l = 0;
Uint8 c;
while(1)
{
c = track->data[*currentPos];
(*currentPos)++;
l += (c & 0x7f);
if (!(c & 0x80))
return l;
l <<= 7;
}
}
/* Create a single MIDIEvent */
static MIDIEvent *CreateEvent(Uint32 time, Uint8 event, Uint8 a, Uint8 b)
{
MIDIEvent *newEvent;
newEvent = calloc(1, sizeof(MIDIEvent));
if (newEvent)
{
newEvent->time = time;
newEvent->status = event;
newEvent->data[0] = a;
newEvent->data[1] = b;
}
else
Mix_SetError("Out of memory");
return newEvent;
}
/* Convert a single midi track to a list of MIDIEvents */
static MIDIEvent *MIDITracktoStream(MIDITrack *track)
{
Uint32 atime = 0;
Uint32 len = 0;
Uint8 event,type,a,b;
Uint8 laststatus = 0;
Uint8 lastchan = 0;
int currentPos = 0;
int end = 0;
MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */
MIDIEvent *currentEvent = head;
while (!end)
{
if (currentPos >= track->len)
break; /* End of data stream reached */
atime += GetVLQ(track, &currentPos);
event = track->data[currentPos++];
/* Handle SysEx seperatly */
if (((event>>4) & 0x0F) == MIDI_STATUS_SYSEX)
{
if (event == 0xFF)
{
type = track->data[currentPos];
currentPos++;
switch(type)
{
case 0x2f: /* End of data marker */
end = 1;
case 0x51: /* Tempo change */
/*
a=track->data[currentPos];
b=track->data[currentPos+1];
c=track->data[currentPos+2];
AddEvent(song, atime, MEVT_TEMPO, c, b, a);
*/
break;
}
}
else
type = 0;
len = GetVLQ(track, &currentPos);
/* Create an event and attach the extra data, if any */
currentEvent->next = CreateEvent(atime, event, type, 0);
currentEvent = currentEvent->next;
if (NULL == currentEvent)
{
FreeMIDIEventList(head);
return NULL;
}
if (len)
{
currentEvent->extraLen = len;
currentEvent->extraData = malloc(len);
memcpy(currentEvent->extraData, &(track->data[currentPos]), len);
currentPos += len;
}
}
else
{
a = event;
if (a & 0x80) /* It's a status byte */
{
/* Extract channel and status information */
lastchan = a & 0x0F;
laststatus = (a>>4) & 0x0F;
/* Read the next byte which should always be a data byte */
a = track->data[currentPos++] & 0x7F;
}
switch(laststatus)
{
case MIDI_STATUS_NOTE_OFF:
case MIDI_STATUS_NOTE_ON: /* Note on */
case MIDI_STATUS_AFTERTOUCH: /* Key Pressure */
case MIDI_STATUS_CONTROLLER: /* Control change */
case MIDI_STATUS_PITCH_WHEEL: /* Pitch wheel */
b = track->data[currentPos++] & 0x7F;
currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, b);
currentEvent = currentEvent->next;
if (NULL == currentEvent)
{
FreeMIDIEventList(head);
return NULL;
}
break;
case MIDI_STATUS_PROG_CHANGE: /* Program change */
case MIDI_STATUS_PRESSURE: /* Channel pressure */
a &= 0x7f;
currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, 0);
currentEvent = currentEvent->next;
if (NULL == currentEvent)
{
FreeMIDIEventList(head);
return NULL;
}
break;
default: /* Sysex already handled above */
break;
}
}
}
currentEvent = head->next;
free(head); /* release the dummy head event */
return currentEvent;
}
/*
* Convert a midi song, consisting of up to 32 tracks, to a list of MIDIEvents.
* To do so, first convert the tracks seperatly, then interweave the resulting
* MIDIEvent-Lists to one big list.
*/
static MIDIEvent *MIDItoStream(MIDIFile *mididata)
{
MIDIEvent **track;
MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */
MIDIEvent *currentEvent = head;
int trackID;
if (NULL == head)
return NULL;
track = (MIDIEvent**) calloc(1, sizeof(MIDIEvent*) * mididata->nTracks);
if (NULL == head)
return NULL;
/* First, convert all tracks to MIDIEvent lists */
for (trackID = 0; trackID < mididata->nTracks; trackID++)
track[trackID] = MIDITracktoStream(&mididata->track[trackID]);
/* Now, merge the lists. */
/* TODO */
while(1)
{
Uint32 lowestTime = INT_MAX;
int currentTrackID = -1;
/* Find the next event */
for (trackID = 0; trackID < mididata->nTracks; trackID++)
{
if (track[trackID] && (track[trackID]->time < lowestTime))
{
currentTrackID = trackID;
lowestTime = track[currentTrackID]->time;
}
}
/* Check if we processes all events */
if (currentTrackID == -1)
break;
currentEvent->next = track[currentTrackID];
track[currentTrackID] = track[currentTrackID]->next;
currentEvent = currentEvent->next;
lowestTime = 0;
}
/* Make sure the list is properly terminated */
currentEvent->next = 0;
currentEvent = head->next;
free(track);
free(head); /* release the dummy head event */
return currentEvent;
}
static int ReadMIDIFile(MIDIFile *mididata, SDL_RWops *rw)
{
int i = 0;
Uint32 ID;
Uint32 size;
Uint16 format;
Uint16 tracks;
Uint16 division;
if (!mididata)
return 0;
if (!rw)
return 0;
/* Make sure this is really a MIDI file */
SDL_RWread(rw, &ID, 1, 4);
if (BE_LONG(ID) != 'MThd')
return 0;
/* Header size must be 6 */
SDL_RWread(rw, &size, 1, 4);
size = BE_LONG(size);
if (size != 6)
return 0;
/* We only support format 0 and 1, but not 2 */
SDL_RWread(rw, &format, 1, 2);
format = BE_SHORT(format);
if (format != 0 && format != 1)
return 0;
SDL_RWread(rw, &tracks, 1, 2);
tracks = BE_SHORT(tracks);
mididata->nTracks = tracks;
/* Allocate tracks */
mididata->track = (MIDITrack*) calloc(1, sizeof(MIDITrack) * mididata->nTracks);
if (NULL == mididata->track)
{
Mix_SetError("Out of memory");
goto bail;
}
/* Retrieve the PPQN value, needed for playback */
SDL_RWread(rw, &division, 1, 2);
mididata->division = BE_SHORT(division);
for (i=0; i<tracks; i++)
{
SDL_RWread(rw, &ID, 1, 4); /* We might want to verify this is MTrk... */
SDL_RWread(rw, &size, 1, 4);
size = BE_LONG(size);
mididata->track[i].len = size;
mididata->track[i].data = malloc(size);
if (NULL == mididata->track[i].data)
{
Mix_SetError("Out of memory");
goto bail;
}
SDL_RWread(rw, mididata->track[i].data, 1, size);
}
return 1;
bail:
for(;i >= 0; i--)
{
if (mididata->track[i].data)
free(mididata->track[i].data);
}
return 0;
}
MIDIEvent *CreateMIDIEventList(SDL_RWops *rw, Uint16 *division)
{
MIDIFile *mididata = NULL;
MIDIEvent *eventList;
int trackID;
mididata = calloc(1, sizeof(MIDIFile));
if (!mididata)
return NULL;
/* Open the file */
if ( rw != NULL )
{
/* Read in the data */
if ( ! ReadMIDIFile(mididata, rw))
{
free(mididata);
return NULL;
}
}
else
{
free(mididata);
return NULL;
}
if (division)
*division = mididata->division;
eventList = MIDItoStream(mididata);
for(trackID = 0; trackID < mididata->nTracks; trackID++)
{
if (mididata->track[trackID].data)
free(mididata->track[trackID].data);
}
free(mididata->track);
free(mididata);
return eventList;
}
void FreeMIDIEventList(MIDIEvent *head)
{
MIDIEvent *cur, *next;
cur = head;
while (cur)
{
next = cur->next;
if (cur->extraData)
free (cur->extraData);
free (cur);
cur = next;
}
}

View File

@@ -0,0 +1,67 @@
/*
native_midi: Hardware Midi support for the SDL_mixer library
Copyright (C) 2000,2001 Florian 'Proff' Schulze & Max Horn
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Florian 'Proff' Schulze
florian.proff.schulze@gmx.net
Max Horn
max@quendi.de
*/
#ifndef _NATIVE_MIDI_COMMON_H_
#define _NATIVE_MIDI_COMMON_H_
#include "SDL.h"
/* Midi Status Bytes */
#define MIDI_STATUS_NOTE_OFF 0x8
#define MIDI_STATUS_NOTE_ON 0x9
#define MIDI_STATUS_AFTERTOUCH 0xA
#define MIDI_STATUS_CONTROLLER 0xB
#define MIDI_STATUS_PROG_CHANGE 0xC
#define MIDI_STATUS_PRESSURE 0xD
#define MIDI_STATUS_PITCH_WHEEL 0xE
#define MIDI_STATUS_SYSEX 0xF
/* We store the midi events in a linked list; this way it is
easy to shuffle the tracks together later on; and we are
flexible in the size of each elemnt.
*/
typedef struct MIDIEvent
{
Uint32 time; /* Time at which this midi events occurs */
Uint8 status; /* Status byte */
Uint8 data[2]; /* 1 or 2 bytes additional data for most events */
Uint32 extraLen; /* For some SysEx events, we need additional storage */
Uint8 *extraData;
struct MIDIEvent *next;
} MIDIEvent;
/* Load a midifile to memory, converting it to a list of MIDIEvents.
This function returns a linked lists of MIDIEvents, 0 if an error occured.
*/
MIDIEvent *CreateMIDIEventList(SDL_RWops *rw, Uint16 *division);
/* Release a MIDIEvent list after usage. */
void FreeMIDIEventList(MIDIEvent *head);
#endif /* _NATIVE_MIDI_COMMON_H_ */

View File

@@ -0,0 +1,704 @@
/*
native_midi_mac: Native Midi support on MacOS for the SDL_mixer library
Copyright (C) 2001 Max Horn
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Max Horn
max@quendi.de
*/
#include "SDL_config.h"
#include "SDL_endian.h"
#if __MACOS__ /*|| __MACOSX__ */
#include "native_midi.h"
#include "native_midi_common.h"
#if __MACOSX__
#include <QuickTime/QuickTimeMusic.h>
#else
#include <QuickTimeMusic.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <string.h>
/* Native Midi song */
struct _NativeMidiSong
{
Uint32 *tuneSequence;
Uint32 *tuneHeader;
};
enum
{
/* number of (32-bit) long words in a note request event */
kNoteRequestEventLength = ((sizeof(NoteRequest)/sizeof(long)) + 2),
/* number of (32-bit) long words in a marker event */
kMarkerEventLength = 1,
/* number of (32-bit) long words in a general event, minus its data */
kGeneralEventLength = 2
};
#define ERROR_BUF_SIZE 256
#define BUFFER_INCREMENT 5000
#define REST_IF_NECESSARY() do {\
int timeDiff = eventPos->time - lastEventTime; \
if(timeDiff) \
{ \
timeDiff = (int)(timeDiff*tick); \
qtma_StuffRestEvent(*tunePos, timeDiff); \
tunePos++; \
lastEventTime = eventPos->time; \
} \
} while(0)
static Uint32 *BuildTuneSequence(MIDIEvent *evntlist, int ppqn, int part_poly_max[32], int part_to_inst[32], int *numParts);
static Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts);
/* The global TunePlayer instance */
static TunePlayer gTunePlayer = NULL;
static int gInstaceCount = 0;
static Uint32 *gCurrentTuneSequence = NULL;
static char gErrorBuffer[ERROR_BUF_SIZE] = "";
/* Check whether QuickTime is available */
int native_midi_detect()
{
/* TODO */
return 1;
}
NativeMidiSong *native_midi_loadsong(const char *midifile)
{
NativeMidiSong *song = NULL;
MIDIEvent *evntlist = NULL;
int part_to_inst[32];
int part_poly_max[32];
int numParts = 0;
Uint16 ppqn;
SDL_RWops *rw;
/* Init the arrays */
memset(part_poly_max,0,sizeof(part_poly_max));
memset(part_to_inst,-1,sizeof(part_to_inst));
/* Attempt to load the midi file */
rw = SDL_RWFromFile(midifile, "rb");
if (rw) {
evntlist = CreateMIDIEventList(rw, &ppqn);
SDL_RWclose(rw);
if (!evntlist)
goto bail;
}
/* Allocate memory for the song struct */
song = malloc(sizeof(NativeMidiSong));
if (!song)
goto bail;
/* Build a tune sequence from the event list */
song->tuneSequence = BuildTuneSequence(evntlist, ppqn, part_poly_max, part_to_inst, &numParts);
if(!song->tuneSequence)
goto bail;
/* Now build a tune header from the data we collect above, create
all parts as needed and assign them the correct instrument.
*/
song->tuneHeader = BuildTuneHeader(part_poly_max, part_to_inst, numParts);
if(!song->tuneHeader)
goto bail;
/* Increment the instance count */
gInstaceCount++;
if (gTunePlayer == NULL)
gTunePlayer = OpenDefaultComponent(kTunePlayerComponentType, 0);
/* Finally, free the event list */
FreeMIDIEventList(evntlist);
return song;
bail:
if (evntlist)
FreeMIDIEventList(evntlist);
if (song)
{
if(song->tuneSequence)
free(song->tuneSequence);
if(song->tuneHeader)
DisposePtr((Ptr)song->tuneHeader);
free(song);
}
return NULL;
}
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
{
NativeMidiSong *song = NULL;
MIDIEvent *evntlist = NULL;
int part_to_inst[32];
int part_poly_max[32];
int numParts = 0;
Uint16 ppqn;
/* Init the arrays */
memset(part_poly_max,0,sizeof(part_poly_max));
memset(part_to_inst,-1,sizeof(part_to_inst));
/* Attempt to load the midi file */
evntlist = CreateMIDIEventList(rw, &ppqn);
if (!evntlist)
goto bail;
/* Allocate memory for the song struct */
song = malloc(sizeof(NativeMidiSong));
if (!song)
goto bail;
/* Build a tune sequence from the event list */
song->tuneSequence = BuildTuneSequence(evntlist, ppqn, part_poly_max, part_to_inst, &numParts);
if(!song->tuneSequence)
goto bail;
/* Now build a tune header from the data we collect above, create
all parts as needed and assign them the correct instrument.
*/
song->tuneHeader = BuildTuneHeader(part_poly_max, part_to_inst, numParts);
if(!song->tuneHeader)
goto bail;
/* Increment the instance count */
gInstaceCount++;
if (gTunePlayer == NULL)
gTunePlayer = OpenDefaultComponent(kTunePlayerComponentType, 0);
/* Finally, free the event list */
FreeMIDIEventList(evntlist);
return song;
bail:
if (evntlist)
FreeMIDIEventList(evntlist);
if (song)
{
if(song->tuneSequence)
free(song->tuneSequence);
if(song->tuneHeader)
DisposePtr((Ptr)song->tuneHeader);
free(song);
}
return NULL;
}
void native_midi_freesong(NativeMidiSong *song)
{
if(!song || !song->tuneSequence)
return;
/* If this is the currently playing song, stop it now */
if (song->tuneSequence == gCurrentTuneSequence)
native_midi_stop();
/* Finally, free the data storage */
free(song->tuneSequence);
DisposePtr((Ptr)song->tuneHeader);
free(song);
/* Increment the instance count */
gInstaceCount--;
if ((gTunePlayer != NULL) && (gInstaceCount == 0))
{
CloseComponent(gTunePlayer);
gTunePlayer = NULL;
}
}
void native_midi_start(NativeMidiSong *song)
{
UInt32 queueFlags = 0;
ComponentResult tpError;
assert (gTunePlayer != NULL);
SDL_PauseAudio(1);
SDL_UnlockAudio();
/* First, stop the currently playing music */
native_midi_stop();
/* Set up the queue flags */
queueFlags = kTuneStartNow;
/* Set the time scale (units per second), we want milliseconds */
tpError = TuneSetTimeScale(gTunePlayer, 1000);
if (tpError != noErr)
{
strncpy (gErrorBuffer, "MIDI error during TuneSetTimeScale", ERROR_BUF_SIZE);
goto done;
}
/* Set the header, to tell what instruments are used */
tpError = TuneSetHeader(gTunePlayer, (UInt32 *)song->tuneHeader);
if (tpError != noErr)
{
strncpy (gErrorBuffer, "MIDI error during TuneSetHeader", ERROR_BUF_SIZE);
goto done;
}
/* Have it allocate whatever resources are needed */
tpError = TunePreroll(gTunePlayer);
if (tpError != noErr)
{
strncpy (gErrorBuffer, "MIDI error during TunePreroll", ERROR_BUF_SIZE);
goto done;
}
/* We want to play at normal volume */
tpError = TuneSetVolume(gTunePlayer, 0x00010000);
if (tpError != noErr)
{
strncpy (gErrorBuffer, "MIDI error during TuneSetVolume", ERROR_BUF_SIZE);
goto done;
}
/* Finally, start playing the full song */
gCurrentTuneSequence = song->tuneSequence;
tpError = TuneQueue(gTunePlayer, (UInt32 *)song->tuneSequence, 0x00010000, 0, 0xFFFFFFFF, queueFlags, NULL, 0);
if (tpError != noErr)
{
strncpy (gErrorBuffer, "MIDI error during TuneQueue", ERROR_BUF_SIZE);
goto done;
}
done:
SDL_LockAudio();
SDL_PauseAudio(0);
}
void native_midi_stop()
{
if (gTunePlayer == NULL)
return;
/* Stop music */
TuneStop(gTunePlayer, 0);
/* Deallocate all instruments */
TuneUnroll(gTunePlayer);
}
int native_midi_active()
{
if (gTunePlayer != NULL)
{
TuneStatus ts;
TuneGetStatus(gTunePlayer,&ts);
return ts.queueTime != 0;
}
else
return 0;
}
void native_midi_setvolume(int volume)
{
if (gTunePlayer == NULL)
return;
/* QTMA olume may range from 0.0 to 1.0 (in 16.16 fixed point encoding) */
TuneSetVolume(gTunePlayer, (0x00010000 * volume)/SDL_MIX_MAXVOLUME);
}
const char *native_midi_error(void)
{
return gErrorBuffer;
}
Uint32 *BuildTuneSequence(MIDIEvent *evntlist, int ppqn, int part_poly_max[32], int part_to_inst[32], int *numParts)
{
int part_poly[32];
int channel_to_part[16];
int channel_pan[16];
int channel_vol[16];
int channel_pitch_bend[16];
int lastEventTime = 0;
int tempo = 500000;
double Ippqn = 1.0 / (1000*ppqn);
double tick = tempo * Ippqn;
MIDIEvent *eventPos = evntlist;
MIDIEvent *noteOffPos;
Uint32 *tunePos, *endPos;
Uint32 *tuneSequence;
size_t tuneSize;
/* allocate space for the tune header */
tuneSize = 5000;
tuneSequence = (Uint32 *)malloc(tuneSize * sizeof(Uint32));
if (tuneSequence == NULL)
return NULL;
/* Set starting position in our tune memory */
tunePos = tuneSequence;
endPos = tuneSequence + tuneSize;
/* Initialise the arrays */
memset(part_poly,0,sizeof(part_poly));
memset(channel_to_part,-1,sizeof(channel_to_part));
memset(channel_pan,-1,sizeof(channel_pan));
memset(channel_vol,-1,sizeof(channel_vol));
memset(channel_pitch_bend,-1,sizeof(channel_pitch_bend));
*numParts = 0;
/*
* Now the major work - iterate over all GM events,
* and turn them into QuickTime Music format.
* At the same time, calculate the max. polyphony for each part,
* and also the part->instrument mapping.
*/
while(eventPos)
{
int status = (eventPos->status&0xF0)>>4;
int channel = eventPos->status&0x0F;
int part = channel_to_part[channel];
int velocity, pitch;
int value, controller;
int bend;
int newInst;
/* Check if we are running low on space... */
if((tunePos+16) > endPos)
{
/* Resize our data storage. */
Uint32 *oldTuneSequence = tuneSequence;
tuneSize += BUFFER_INCREMENT;
tuneSequence = (Uint32 *)realloc(tuneSequence, tuneSize * sizeof(Uint32));
if(oldTuneSequence != tuneSequence)
tunePos += tuneSequence - oldTuneSequence;
endPos = tuneSequence + tuneSize;
}
switch (status)
{
case MIDI_STATUS_NOTE_OFF:
assert(part>=0 && part<=31);
/* Keep track of the polyphony of the current part */
part_poly[part]--;
break;
case MIDI_STATUS_NOTE_ON:
if (part < 0)
{
/* If no part is specified yet, we default to the first instrument, which
is piano (or the first drum kit if we are on the drum channel)
*/
int newInst;
if (channel == 9)
newInst = kFirstDrumkit + 1; /* the first drum kit is the "no drum" kit! */
else
newInst = kFirstGMInstrument;
part = channel_to_part[channel] = *numParts;
part_to_inst[(*numParts)++] = newInst;
}
/* TODO - add support for more than 32 parts using eXtended QTMA events */
assert(part<=31);
/* Decode pitch & velocity */
pitch = eventPos->data[0];
velocity = eventPos->data[1];
if (velocity == 0)
{
/* was a NOTE OFF in disguise, so we decrement the polyphony */
part_poly[part]--;
}
else
{
/* Keep track of the polyphony of the current part */
int foo = ++part_poly[part];
if (part_poly_max[part] < foo)
part_poly_max[part] = foo;
/* Now scan forward to find the matching NOTE OFF event */
for(noteOffPos = eventPos; noteOffPos; noteOffPos = noteOffPos->next)
{
if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_OFF
&& channel == (eventPos->status&0x0F)
&& pitch == noteOffPos->data[0])
break;
/* NOTE ON with velocity == 0 is the same as a NOTE OFF */
if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_ON
&& channel == (eventPos->status&0x0F)
&& pitch == noteOffPos->data[0]
&& 0 == noteOffPos->data[1])
break;
}
/* Did we find a note off? Should always be the case, but who knows... */
if (noteOffPos)
{
/* We found a NOTE OFF, now calculate the note duration */
int duration = (int)((noteOffPos->time - eventPos->time)*tick);
REST_IF_NECESSARY();
/* Now we need to check if we get along with a normal Note Event, or if we need an extended one... */
if (duration < 2048 && pitch>=32 && pitch<=95 && velocity>=0 && velocity<=127)
{
qtma_StuffNoteEvent(*tunePos, part, pitch, velocity, duration);
tunePos++;
}
else
{
qtma_StuffXNoteEvent(*tunePos, *(tunePos+1), part, pitch, velocity, duration);
tunePos+=2;
}
}
}
break;
case MIDI_STATUS_AFTERTOUCH:
/* NYI - use kControllerAfterTouch. But how are the parameters to be mapped? */
break;
case MIDI_STATUS_CONTROLLER:
controller = eventPos->data[0];
value = eventPos->data[1];
switch(controller)
{
case 0: /* bank change - igore for now */
break;
case kControllerVolume:
if(channel_vol[channel] != value<<8)
{
channel_vol[channel] = value<<8;
if(part>=0 && part<=31)
{
REST_IF_NECESSARY();
qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]);
tunePos++;
}
}
break;
case kControllerPan:
if(channel_pan[channel] != (value << 1) + 256)
{
channel_pan[channel] = (value << 1) + 256;
if(part>=0 && part<=31)
{
REST_IF_NECESSARY();
qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]);
tunePos++;
}
}
break;
default:
/* No other controllers implemented yet */;
break;
}
break;
case MIDI_STATUS_PROG_CHANGE:
/* Instrument changed */
newInst = eventPos->data[0];
/* Channel 9 (the 10th channel) is different, it indicates a drum kit */
if (channel == 9)
newInst += kFirstDrumkit;
else
newInst += kFirstGMInstrument;
/* Only if the instrument for this channel *really* changed, add a new part. */
if(newInst != part_to_inst[part])
{
/* TODO maybe make use of kGeneralEventPartChange here,
to help QT reuse note channels?
*/
part = channel_to_part[channel] = *numParts;
part_to_inst[(*numParts)++] = newInst;
if(channel_vol[channel] >= 0)
{
REST_IF_NECESSARY();
qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]);
tunePos++;
}
if(channel_pan[channel] >= 0)
{
REST_IF_NECESSARY();
qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]);
tunePos++;
}
if(channel_pitch_bend[channel] >= 0)
{
REST_IF_NECESSARY();
qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, channel_pitch_bend[channel]);
tunePos++;
}
}
break;
case MIDI_STATUS_PRESSURE:
/* NYI */
break;
case MIDI_STATUS_PITCH_WHEEL:
/* In the midi spec, 0x2000 = center, 0x0000 = - 2 semitones, 0x3FFF = +2 semitones
but for QTMA, we specify it as a 8.8 fixed point of semitones
TODO: detect "pitch bend range changes" & honor them!
*/
bend = (eventPos->data[0] & 0x7f) | ((eventPos->data[1] & 0x7f) << 7);
/* "Center" the bend */
bend -= 0x2000;
/* Move it to our format: */
bend <<= 4;
/* If it turns out the pitch bend didn't change, stop here */
if(channel_pitch_bend[channel] == bend)
break;
channel_pitch_bend[channel] = bend;
if(part>=0 && part<=31)
{
/* Stuff a control event */
REST_IF_NECESSARY();
qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, bend);
tunePos++;
}
break;
case MIDI_STATUS_SYSEX:
if (eventPos->status == 0xFF && eventPos->data[0] == 0x51) /* Tempo change */
{
tempo = (eventPos->extraData[0] << 16) +
(eventPos->extraData[1] << 8) +
eventPos->extraData[2];
tick = tempo * Ippqn;
}
break;
}
/* on to the next event */
eventPos = eventPos->next;
}
/* Finally, place an end marker */
*tunePos = kEndMarkerValue;
return tuneSequence;
}
Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts)
{
Uint32 *myHeader;
Uint32 *myPos1, *myPos2; /* pointers to the head and tail long words of a music event */
NoteRequest *myNoteRequest;
NoteAllocator myNoteAllocator; /* for the NAStuffToneDescription call */
ComponentResult myErr = noErr;
int part;
myHeader = NULL;
myNoteAllocator = NULL;
/*
* Open up the Note Allocator
*/
myNoteAllocator = OpenDefaultComponent(kNoteAllocatorComponentType,0);
if (myNoteAllocator == NULL)
goto bail;
/*
* Allocate space for the tune header
*/
myHeader = (Uint32 *)
NewPtrClear((numParts * kNoteRequestEventLength + kMarkerEventLength) * sizeof(Uint32));
if (myHeader == NULL)
goto bail;
myPos1 = myHeader;
/*
* Loop over all parts
*/
for(part = 0; part < numParts; ++part)
{
/*
* Stuff request for the instrument with the given polyphony
*/
myPos2 = myPos1 + (kNoteRequestEventLength - 1); /* last longword of general event */
qtma_StuffGeneralEvent(*myPos1, *myPos2, part, kGeneralEventNoteRequest, kNoteRequestEventLength);
myNoteRequest = (NoteRequest *)(myPos1 + 1);
myNoteRequest->info.flags = 0;
/* I'm told by the Apple people that the Quicktime types were poorly designed and it was
* too late to change them. On little endian, the BigEndian(Short|Fixed) types are structs
* while on big endian they are primitive types. Furthermore, Quicktime failed to
* provide setter and getter functions. To get this to work, we need to case the
* code for the two possible situations.
* My assumption is that the right-side value was always expected to be BigEndian
* as it was written way before the Universal Binary transition. So in the little endian
* case, OSSwap is used.
*/
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
myNoteRequest->info.polyphony.bigEndianValue = OSSwapHostToBigInt16(part_poly_max[part]);
myNoteRequest->info.typicalPolyphony.bigEndianValue = OSSwapHostToBigInt32(0x00010000);
#else
myNoteRequest->info.polyphony = part_poly_max[part];
myNoteRequest->info.typicalPolyphony = 0x00010000;
#endif
myErr = NAStuffToneDescription(myNoteAllocator,part_to_inst[part],&myNoteRequest->tone);
if (myErr != noErr)
goto bail;
/* move pointer to beginning of next event */
myPos1 += kNoteRequestEventLength;
}
*myPos1 = kEndMarkerValue; /* end of sequence marker */
bail:
if(myNoteAllocator)
CloseComponent(myNoteAllocator);
/* if we encountered an error, dispose of the storage we allocated and return NULL */
if (myErr != noErr) {
DisposePtr((Ptr)myHeader);
myHeader = NULL;
}
return myHeader;
}
#endif /* MacOS native MIDI support */

View File

@@ -0,0 +1,317 @@
/*
native_midi_macosx: Native Midi support on Mac OS X for the SDL_mixer library
Copyright (C) 2009 Ryan C. Gordon
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Ryan C. Gordon
icculus@icculus.org
*/
/* This is Mac OS X only, using Core MIDI.
Mac OS 9 support via QuickTime is in native_midi_mac.c */
#include "SDL_config.h"
#if __MACOSX__
#include <Carbon/Carbon.h>
#include <AudioToolbox/AudioToolbox.h>
#include <AvailabilityMacros.h>
#include "../SDL_mixer.h"
#include "SDL_endian.h"
#include "native_midi.h"
/* Native Midi song */
struct _NativeMidiSong
{
MusicPlayer player;
MusicSequence sequence;
MusicTimeStamp endTime;
AudioUnit audiounit;
};
static NativeMidiSong *currentsong = NULL;
static int latched_volume = MIX_MAX_VOLUME;
static OSStatus
GetSequenceLength(MusicSequence sequence, MusicTimeStamp *_sequenceLength)
{
// http://lists.apple.com/archives/Coreaudio-api/2003/Jul/msg00370.html
// figure out sequence length
UInt32 ntracks, i;
MusicTimeStamp sequenceLength = 0;
OSStatus err;
err = MusicSequenceGetTrackCount(sequence, &ntracks);
if (err != noErr)
return err;
for (i = 0; i < ntracks; ++i)
{
MusicTrack track;
MusicTimeStamp tracklen = 0;
UInt32 tracklenlen = sizeof (tracklen);
err = MusicSequenceGetIndTrack(sequence, i, &track);
if (err != noErr)
return err;
err = MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength,
&tracklen, &tracklenlen);
if (err != noErr)
return err;
if (sequenceLength < tracklen)
sequenceLength = tracklen;
}
*_sequenceLength = sequenceLength;
return noErr;
}
/* we're looking for the sequence output audiounit. */
static OSStatus
GetSequenceAudioUnit(MusicSequence sequence, AudioUnit *aunit)
{
AUGraph graph;
UInt32 nodecount, i;
OSStatus err;
err = MusicSequenceGetAUGraph(sequence, &graph);
if (err != noErr)
return err;
err = AUGraphGetNodeCount(graph, &nodecount);
if (err != noErr)
return err;
for (i = 0; i < nodecount; i++) {
AUNode node;
if (AUGraphGetIndNode(graph, i, &node) != noErr)
continue; /* better luck next time. */
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 /* this is deprecated, but works back to 10.0 */
{
struct ComponentDescription desc;
UInt32 classdatasize = 0;
void *classdata = NULL;
err = AUGraphGetNodeInfo(graph, node, &desc, &classdatasize,
&classdata, aunit);
if (err != noErr)
continue;
else if (desc.componentType != kAudioUnitType_Output)
continue;
else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput)
continue;
}
#else /* not deprecated, but requires 10.5 or later */
{
AudioComponentDescription desc;
if (AUGraphNodeInfo(graph, node, &desc, aunit) != noErr)
continue;
else if (desc.componentType != kAudioUnitType_Output)
continue;
else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput)
continue;
}
#endif
return noErr; /* found it! */
}
return kAUGraphErr_NodeNotFound;
}
int native_midi_detect()
{
return 1; /* always available. */
}
NativeMidiSong *native_midi_loadsong(const char *midifile)
{
NativeMidiSong *retval = NULL;
SDL_RWops *rw = SDL_RWFromFile(midifile, "rb");
if (rw != NULL) {
retval = native_midi_loadsong_RW(rw);
SDL_RWclose(rw);
}
return retval;
}
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
{
NativeMidiSong *retval = NULL;
void *buf = NULL;
int len = 0;
CFDataRef data = NULL;
if (SDL_RWseek(rw, 0, RW_SEEK_END) < 0)
goto fail;
len = SDL_RWtell(rw);
if (len < 0)
goto fail;
if (SDL_RWseek(rw, 0, RW_SEEK_SET) < 0)
goto fail;
buf = malloc(len);
if (buf == NULL)
goto fail;
if (SDL_RWread(rw, buf, len, 1) != 1)
goto fail;
retval = malloc(sizeof(NativeMidiSong));
if (retval == NULL)
goto fail;
memset(retval, '\0', sizeof (*retval));
if (NewMusicPlayer(&retval->player) != noErr)
goto fail;
if (NewMusicSequence(&retval->sequence) != noErr)
goto fail;
data = CFDataCreate(NULL, (const UInt8 *) buf, len);
if (data == NULL)
goto fail;
free(buf);
buf = NULL;
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 /* this is deprecated, but works back to 10.3 */
if (MusicSequenceLoadSMFDataWithFlags(retval->sequence, data, 0) != noErr)
goto fail;
#else /* not deprecated, but requires 10.5 or later */
if (MusicSequenceFileLoadData(retval->sequence, data, 0, 0) != noErr)
goto fail;
#endif
CFRelease(data);
data = NULL;
if (GetSequenceLength(retval->sequence, &retval->endTime) != noErr)
goto fail;
if (MusicPlayerSetSequence(retval->player, retval->sequence) != noErr)
goto fail;
return retval;
fail:
if (retval) {
if (retval->sequence)
DisposeMusicSequence(retval->sequence);
if (retval->player)
DisposeMusicPlayer(retval->player);
free(retval);
}
if (data)
CFRelease(data);
if (buf)
free(buf);
return NULL;
}
void native_midi_freesong(NativeMidiSong *song)
{
if (song != NULL) {
if (currentsong == song)
currentsong = NULL;
MusicPlayerStop(song->player);
DisposeMusicSequence(song->sequence);
DisposeMusicPlayer(song->player);
free(song);
}
}
void native_midi_start(NativeMidiSong *song)
{
int vol;
if (song == NULL)
return;
SDL_PauseAudio(1);
SDL_UnlockAudio();
if (currentsong)
MusicPlayerStop(currentsong->player);
currentsong = song;
MusicPlayerStart(song->player);
GetSequenceAudioUnit(song->sequence, &song->audiounit);
vol = latched_volume;
latched_volume++; /* just make this not match. */
native_midi_setvolume(vol);
SDL_LockAudio();
SDL_PauseAudio(0);
}
void native_midi_stop()
{
if (currentsong) {
SDL_PauseAudio(1);
SDL_UnlockAudio();
MusicPlayerStop(currentsong->player);
currentsong = NULL;
SDL_LockAudio();
SDL_PauseAudio(0);
}
}
int native_midi_active()
{
MusicTimeStamp currentTime = 0;
if (currentsong == NULL)
return 0;
MusicPlayerGetTime(currentsong->player, &currentTime);
return ((currentTime < currentsong->endTime) ||
(currentTime >= kMusicTimeStamp_EndOfTrack));
}
void native_midi_setvolume(int volume)
{
if (latched_volume == volume)
return;
latched_volume = volume;
if ((currentsong) && (currentsong->audiounit)) {
const float floatvol = ((float) volume) / ((float) MIX_MAX_VOLUME);
AudioUnitSetParameter(currentsong->audiounit, kHALOutputParam_Volume,
kAudioUnitScope_Global, 0, floatvol, 0);
}
}
const char *native_midi_error(void)
{
return ""; /* !!! FIXME */
}
#endif /* Mac OS X native MIDI support */

View File

@@ -0,0 +1,319 @@
/*
native_midi: Hardware Midi support for the SDL_mixer library
Copyright (C) 2000,2001 Florian 'Proff' Schulze
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Florian 'Proff' Schulze
florian.proff.schulze@gmx.net
*/
#include "SDL_config.h"
/* everything below is currently one very big bad hack ;) Proff */
#if __WIN32__
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include "native_midi.h"
#include "native_midi_common.h"
struct _NativeMidiSong {
int MusicLoaded;
int MusicPlaying;
MIDIHDR MidiStreamHdr;
MIDIEVENT *NewEvents;
Uint16 ppqn;
int Size;
int NewPos;
};
static UINT MidiDevice=MIDI_MAPPER;
static HMIDISTRM hMidiStream;
static NativeMidiSong *currentsong;
static int BlockOut(NativeMidiSong *song)
{
MMRESULT err;
int BlockSize;
if ((song->MusicLoaded) && (song->NewEvents))
{
// proff 12/8/98: Added for savety
midiOutUnprepareHeader((HMIDIOUT)hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR));
if (song->NewPos>=song->Size)
return 0;
BlockSize=(song->Size-song->NewPos);
if (BlockSize<=0)
return 0;
if (BlockSize>36000)
BlockSize=36000;
song->MidiStreamHdr.lpData=(void *)((unsigned char *)song->NewEvents+song->NewPos);
song->NewPos+=BlockSize;
song->MidiStreamHdr.dwBufferLength=BlockSize;
song->MidiStreamHdr.dwBytesRecorded=BlockSize;
song->MidiStreamHdr.dwFlags=0;
err=midiOutPrepareHeader((HMIDIOUT)hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR));
if (err!=MMSYSERR_NOERROR)
return 0;
err=midiStreamOut(hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR));
return 0;
}
return 1;
}
static void MIDItoStream(NativeMidiSong *song, MIDIEvent *evntlist)
{
int eventcount;
MIDIEvent *event;
MIDIEVENT *newevent;
eventcount=0;
event=evntlist;
while (event)
{
eventcount++;
event=event->next;
}
song->NewEvents=malloc(eventcount*3*sizeof(DWORD));
if (!song->NewEvents)
return;
memset(song->NewEvents,0,(eventcount*3*sizeof(DWORD)));
eventcount=0;
event=evntlist;
newevent=song->NewEvents;
while (event)
{
int status = (event->status&0xF0)>>4;
switch (status)
{
case MIDI_STATUS_NOTE_OFF:
case MIDI_STATUS_NOTE_ON:
case MIDI_STATUS_AFTERTOUCH:
case MIDI_STATUS_CONTROLLER:
case MIDI_STATUS_PROG_CHANGE:
case MIDI_STATUS_PRESSURE:
case MIDI_STATUS_PITCH_WHEEL:
newevent->dwDeltaTime=event->time;
newevent->dwEvent=(event->status|0x80)|(event->data[0]<<8)|(event->data[1]<<16)|(MEVT_SHORTMSG<<24);
newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD)));
eventcount++;
break;
case MIDI_STATUS_SYSEX:
if (event->status == 0xFF && event->data[0] == 0x51) /* Tempo change */
{
int tempo = (event->extraData[0] << 16) |
(event->extraData[1] << 8) |
event->extraData[2];
newevent->dwDeltaTime=event->time;
newevent->dwEvent=(MEVT_TEMPO<<24) | tempo;
newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD)));
eventcount++;
}
break;
}
event=event->next;
}
song->Size=eventcount*3*sizeof(DWORD);
{
int time;
int temptime;
song->NewPos=0;
time=0;
newevent=song->NewEvents;
while (song->NewPos<song->Size)
{
temptime=newevent->dwDeltaTime;
newevent->dwDeltaTime-=time;
time=temptime;
if ((song->NewPos+12)>=song->Size)
newevent->dwEvent |= MEVT_F_CALLBACK;
newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD)));
song->NewPos+=12;
}
}
song->NewPos=0;
song->MusicLoaded=1;
}
void CALLBACK MidiProc( HMIDIIN hMidi, UINT uMsg, DWORD_PTR dwInstance,
DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
{
switch( uMsg )
{
case MOM_DONE:
if ((currentsong->MusicLoaded) && (dwParam1 == (DWORD_PTR)&currentsong->MidiStreamHdr))
BlockOut(currentsong);
break;
case MOM_POSITIONCB:
if ((currentsong->MusicLoaded) && (dwParam1 == (DWORD_PTR)&currentsong->MidiStreamHdr))
currentsong->MusicPlaying=0;
break;
default:
break;
}
}
int native_midi_detect()
{
MMRESULT merr;
HMIDISTRM MidiStream;
merr=midiStreamOpen(&MidiStream,&MidiDevice,(DWORD)1,(DWORD_PTR)MidiProc,(DWORD_PTR)0,CALLBACK_FUNCTION);
if (merr!=MMSYSERR_NOERROR)
return 0;
midiStreamClose(MidiStream);
return 1;
}
NativeMidiSong *native_midi_loadsong(const char *midifile)
{
NativeMidiSong *newsong;
MIDIEvent *evntlist = NULL;
SDL_RWops *rw;
newsong=malloc(sizeof(NativeMidiSong));
if (!newsong)
return NULL;
memset(newsong,0,sizeof(NativeMidiSong));
/* Attempt to load the midi file */
rw = SDL_RWFromFile(midifile, "rb");
if (rw) {
evntlist = CreateMIDIEventList(rw, &newsong->ppqn);
SDL_RWclose(rw);
if (!evntlist)
{
free(newsong);
return NULL;
}
}
MIDItoStream(newsong, evntlist);
FreeMIDIEventList(evntlist);
return newsong;
}
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
{
NativeMidiSong *newsong;
MIDIEvent *evntlist = NULL;
newsong=malloc(sizeof(NativeMidiSong));
if (!newsong)
return NULL;
memset(newsong,0,sizeof(NativeMidiSong));
/* Attempt to load the midi file */
evntlist = CreateMIDIEventList(rw, &newsong->ppqn);
if (!evntlist)
{
free(newsong);
return NULL;
}
MIDItoStream(newsong, evntlist);
FreeMIDIEventList(evntlist);
return newsong;
}
void native_midi_freesong(NativeMidiSong *song)
{
if (hMidiStream)
{
midiStreamStop(hMidiStream);
midiStreamClose(hMidiStream);
}
if (song)
{
if (song->NewEvents)
free(song->NewEvents);
free(song);
}
}
void native_midi_start(NativeMidiSong *song)
{
MMRESULT merr;
MIDIPROPTIMEDIV mptd;
native_midi_stop();
if (!hMidiStream)
{
merr=midiStreamOpen(&hMidiStream,&MidiDevice,1,(DWORD)&MidiProc,0,CALLBACK_FUNCTION);
if (merr!=MMSYSERR_NOERROR)
{
hMidiStream=0;
return;
}
//midiStreamStop(hMidiStream);
currentsong=song;
currentsong->NewPos=0;
currentsong->MusicPlaying=1;
mptd.cbStruct=sizeof(MIDIPROPTIMEDIV);
mptd.dwTimeDiv=currentsong->ppqn;
merr=midiStreamProperty(hMidiStream,(LPBYTE)&mptd,MIDIPROP_SET | MIDIPROP_TIMEDIV);
BlockOut(song);
merr=midiStreamRestart(hMidiStream);
}
}
void native_midi_stop()
{
if (!hMidiStream)
return;
midiStreamStop(hMidiStream);
midiStreamClose(hMidiStream);
currentsong=NULL;
hMidiStream = 0;
}
int native_midi_active()
{
return currentsong->MusicPlaying;
}
void native_midi_setvolume(int volume)
{
int calcVolume;
if (volume > 128)
volume = 128;
if (volume < 0)
volume = 0;
calcVolume = (65535 * volume / 128);
midiOutSetVolume((HMIDIOUT)hMidiStream, MAKELONG(calcVolume , calcVolume));
}
const char *native_midi_error(void)
{
return "";
}
#endif /* Windows native MIDI support */