Built SDL2_image and _mixer static

This commit is contained in:
2022-09-30 15:49:16 -04:00
parent e2605bf6c1
commit 1dec4347e0
4473 changed files with 1964551 additions and 9 deletions

View File

@ -0,0 +1,40 @@
/*
native_midi: Hardware Midi support for the SDL_mixer library
Copyright (C) 2000 Florian 'Proff' Schulze <florian.proff.schulze@gmx.net>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef _NATIVE_MIDI_H_
#define _NATIVE_MIDI_H_
#include "SDL_rwops.h"
typedef struct _NativeMidiSong NativeMidiSong;
int native_midi_detect(void);
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *src, int freesrc);
void native_midi_freesong(NativeMidiSong *song);
void native_midi_start(NativeMidiSong *song, int loops);
void native_midi_pause(void);
void native_midi_resume(void);
void native_midi_stop(void);
int native_midi_active(void);
void native_midi_setvolume(int volume);
const char *native_midi_error(void);
#endif /* _NATIVE_MIDI_H_ */

View File

@ -0,0 +1,414 @@
/*
native_midi: Hardware Midi support for the SDL_mixer library
Copyright (C) 2000,2001 Florian 'Proff' Schulze <florian.proff.schulze@gmx.net>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "native_midi_common.h"
#include "../SDL_mixer.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
/* The constant 'MThd' */
#define MIDI_MAGIC 0x4d546864
/* 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 == track)
{
free(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 *src)
{
int i = 0;
Uint32 ID;
Uint32 size;
Uint16 format;
Uint16 tracks;
Uint16 division;
if (!mididata)
return 0;
if (!src)
return 0;
/* Make sure this is really a MIDI file */
SDL_RWread(src, &ID, 1, 4);
if (BE_LONG(ID) != MIDI_MAGIC)
return 0;
/* Header size must be 6 */
SDL_RWread(src, &size, 1, 4);
size = BE_LONG(size);
if (size != 6)
return 0;
/* We only support format 0 and 1, but not 2 */
SDL_RWread(src, &format, 1, 2);
format = BE_SHORT(format);
if (format != 0 && format != 1)
return 0;
SDL_RWread(src, &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(src, &division, 1, 2);
mididata->division = BE_SHORT(division);
for (i=0; i<tracks; i++)
{
SDL_RWread(src, &ID, 1, 4); /* We might want to verify this is MTrk... */
SDL_RWread(src, &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(src, 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 *src, Uint16 *division)
{
MIDIFile *mididata = NULL;
MIDIEvent *eventList;
int trackID;
mididata = calloc(1, sizeof(MIDIFile));
if (!mididata)
return NULL;
/* Open the file */
if ( src != NULL )
{
/* Read in the data */
if ( ! ReadMIDIFile(mididata, src))
{
free(mididata);
return NULL;
}
}
else
{
free(mididata);
return NULL;
}
if (division)
*division = mididata->division;
eventList = MIDItoStream(mididata);
if (eventList == NULL)
{
free(mididata);
return NULL;
}
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,63 @@
/*
native_midi: Hardware Midi support for the SDL_mixer library
Copyright (C) 2000,2001 Florian 'Proff' Schulze <florian.proff.schulze@gmx.net>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#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,295 @@
/*
native_midi_haiku: Native Midi support on Haiku for the SDL_mixer library
Copyright (C) 2010 Egor Suvorov <egor_suvorov@mail.ru>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_config.h"
#ifdef __HAIKU__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <MidiStore.h>
#include <MidiDefs.h>
#include <MidiSynthFile.h>
#include <algorithm>
#include <assert.h>
extern "C" {
#include "native_midi.h"
#include "native_midi_common.h"
}
bool compareMIDIEvent(const MIDIEvent &a, const MIDIEvent &b)
{
return a.time < b.time;
}
class MidiEventsStore : public BMidi
{
public:
MidiEventsStore()
{
fPlaying = false;
fLoops = 0;
}
virtual status_t Import(SDL_RWops *src)
{
fEvs = CreateMIDIEventList(src, &fDivision);
if (!fEvs) {
return B_BAD_MIDI_DATA;
}
fTotal = 0;
for (MIDIEvent *x = fEvs; x; x = x->next) fTotal++;
fPos = fTotal;
sort_events();
return B_OK;
}
virtual void Run()
{
fPlaying = true;
fPos = 0;
MIDIEvent *ev = fEvs;
uint32 startTime = B_NOW;
while (KeepRunning())
{
if (!ev) {
if (fLoops && fEvs) {
if (fLoops > 0) --fLoops;
fPos = 0;
ev = fEvs;
} else
break;
}
SprayEvent(ev, ev->time + startTime);
ev = ev->next;
fPos++;
}
fPos = fTotal;
fPlaying = false;
}
virtual ~MidiEventsStore()
{
if (!fEvs) return;
FreeMIDIEventList(fEvs);
fEvs = 0;
}
bool IsPlaying()
{
return fPlaying;
}
void SetLoops(int loops)
{
fLoops = loops;
}
protected:
MIDIEvent *fEvs;
Uint16 fDivision;
int fPos, fTotal;
int fLoops;
bool fPlaying;
void SprayEvent(MIDIEvent *ev, uint32 time)
{
switch (ev->status & 0xF0)
{
case B_NOTE_OFF:
SprayNoteOff((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time);
break;
case B_NOTE_ON:
SprayNoteOn((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time);
break;
case B_KEY_PRESSURE:
SprayKeyPressure((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time);
break;
case B_CONTROL_CHANGE:
SprayControlChange((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time);
break;
case B_PROGRAM_CHANGE:
SprayProgramChange((ev->status & 0x0F) + 1, ev->data[0], time);
break;
case B_CHANNEL_PRESSURE:
SprayChannelPressure((ev->status & 0x0F) + 1, ev->data[0], time);
break;
case B_PITCH_BEND:
SprayPitchBend((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time);
break;
case 0xF:
switch (ev->status)
{
case B_SYS_EX_START:
SpraySystemExclusive(ev->extraData, ev->extraLen, time);
break;
case B_MIDI_TIME_CODE:
case B_SONG_POSITION:
case B_SONG_SELECT:
case B_CABLE_MESSAGE:
case B_TUNE_REQUEST:
case B_SYS_EX_END:
SpraySystemCommon(ev->status, ev->data[0], ev->data[1], time);
break;
case B_TIMING_CLOCK:
case B_START:
case B_STOP:
case B_CONTINUE:
case B_ACTIVE_SENSING:
SpraySystemRealTime(ev->status, time);
break;
case B_SYSTEM_RESET:
if (ev->data[0] == 0x51 && ev->data[1] == 0x03)
{
assert(ev->extraLen == 3);
int val = (ev->extraData[0] << 16) | (ev->extraData[1] << 8) | ev->extraData[2];
int tempo = 60000000 / val;
SprayTempoChange(tempo, time);
}
else
{
SpraySystemRealTime(ev->status, time);
}
}
break;
}
}
void sort_events()
{
MIDIEvent *items = new MIDIEvent[fTotal];
MIDIEvent *x = fEvs;
for (int i = 0; i < fTotal; i++)
{
memcpy(items + i, x, sizeof(MIDIEvent));
x = x->next;
}
std::sort(items, items + fTotal, compareMIDIEvent);
x = fEvs;
for (int i = 0; i < fTotal; i++)
{
MIDIEvent *ne = x->next;
memcpy(x, items + i, sizeof(MIDIEvent));
x->next = ne;
x = ne;
}
for (x = fEvs; x && x->next; x = x->next)
assert(x->time <= x->next->time);
delete[] items;
}
};
BMidiSynth synth;
struct _NativeMidiSong {
MidiEventsStore *store;
} *currentSong = NULL;
char lasterr[1024];
int native_midi_detect(void)
{
status_t res = synth.EnableInput(true, false);
return res == B_OK;
}
void native_midi_setvolume(int volume)
{
if (volume < 0) volume = 0;
if (volume > 128) volume = 128;
synth.SetVolume(volume / 128.0);
}
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *src, int freesrc)
{
NativeMidiSong *song = new NativeMidiSong;
song->store = new MidiEventsStore;
status_t res = song->store->Import(src);
if (res != B_OK)
{
snprintf(lasterr, sizeof lasterr, "Cannot Import() midi file: status_t=%d", res);
delete song->store;
delete song;
return NULL;
}
else
{
if (freesrc) {
SDL_RWclose(src);
}
}
return song;
}
void native_midi_freesong(NativeMidiSong *song)
{
if (song == NULL) return;
song->store->Stop();
song->store->Disconnect(&synth);
if (currentSong == song)
{
currentSong = NULL;
}
delete song->store;
delete song; song = 0;
}
void native_midi_start(NativeMidiSong *song, int loops)
{
native_midi_stop();
song->store->Connect(&synth);
song->store->SetLoops(loops);
song->store->Start();
currentSong = song;
}
void native_midi_pause(void)
{
}
void native_midi_resume(void)
{
}
void native_midi_stop(void)
{
if (currentSong == NULL) return;
currentSong->store->Stop();
currentSong->store->Disconnect(&synth);
while (currentSong->store->IsPlaying())
usleep(1000);
currentSong = NULL;
}
int native_midi_active(void)
{
if (currentSong == NULL) return 0;
return currentSong->store->IsPlaying();
}
const char* native_midi_error(void)
{
return lasterr;
}
#endif /* __HAIKU__ */

View File

@ -0,0 +1,649 @@
/*
native_midi_mac: Native Midi support on MacOS for the SDL_mixer library
Copyright (C) 2001 Max Horn <max@quendi.de>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_config.h"
#include "SDL_endian.h"
#include "../mixer.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(void)
{
/* TODO */
return 1;
}
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *src, int freesrc)
{
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(src, &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);
if (freerw) {
SDL_RWclose(rw);
}
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, int loops)
{
UInt32 queueFlags = 0;
ComponentResult tpError;
assert (gTunePlayer != NULL);
/* FIXME: is this code even used anymore? */
assert (loops == 0);
SDL_PauseAudio(1);
Mix_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:
Mix_LockAudio();
SDL_PauseAudio(0);
}
void native_midi_pause(void)
{
}
void native_midi_resume(void)
{
}
void native_midi_stop(void)
{
if (gTunePlayer == NULL)
return;
/* Stop music */
TuneStop(gTunePlayer, 0);
/* Deallocate all instruments */
TuneUnroll(gTunePlayer);
}
int native_midi_active(void)
{
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,347 @@
/*
native_midi_macosx: Native Midi support on Mac OS X for the SDL_mixer library
Copyright (C) 2009 Ryan C. Gordon <icculus@icculus.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* 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 <CoreServices/CoreServices.h> /* ComponentDescription */
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
#include <AvailabilityMacros.h>
#include "SDL_endian.h"
#include "../SDL_mixer.h"
#include "../mixer.h"
#include "native_midi.h"
/* Native Midi song */
struct _NativeMidiSong
{
MusicPlayer player;
MusicSequence sequence;
MusicTimeStamp endTime;
AudioUnit audiounit;
int loops;
};
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 < 1050 /* 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 */
{
# if !defined(AUDIO_UNIT_VERSION) || ((AUDIO_UNIT_VERSION + 0) < 1060)
/* AUGraphAddNode () is changed to take an AudioComponentDescription*
* desc parameter instead of a ComponentDescription* in the 10.6 SDK.
* AudioComponentDescription is in 10.6 or newer, but it is actually
* the same as struct ComponentDescription with 20 bytes of size and
* the same offsets of all members, therefore, is binary compatible. */
# define AudioComponentDescription ComponentDescription
# endif
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(void)
{
return 1; /* always available. */
}
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *src, int freesrc)
{
NativeMidiSong *retval = NULL;
void *buf = NULL;
Sint64 len = 0;
CFDataRef data = NULL;
if (SDL_RWseek(src, 0, RW_SEEK_END) < 0)
goto fail;
len = SDL_RWtell(src);
if (len < 0)
goto fail;
if (SDL_RWseek(src, 0, RW_SEEK_SET) < 0)
goto fail;
buf = malloc(len);
if (buf == NULL)
goto fail;
if (SDL_RWread(src, 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 < 1050
/* MusicSequenceLoadSMFData() (avail. in 10.2, no 64 bit) is
* equivalent to calling MusicSequenceLoadSMFDataWithFlags()
* with a flags value of 0 (avail. in 10.3, avail. 64 bit).
* So, we use MusicSequenceLoadSMFData() for powerpc versions
* but the *WithFlags() on intel which require 10.4 anyway. */
# if defined(__ppc__) || defined(__POWERPC__)
if (MusicSequenceLoadSMFData(song->sequence, data) != noErr)
goto fail;
# else
if (MusicSequenceLoadSMFDataWithFlags(retval->sequence, data, 0) != noErr)
goto fail;
# endif
#else /* MusicSequenceFileLoadData() 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;
if (freesrc)
SDL_RWclose(src);
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 loops)
{
int vol;
if (song == NULL)
return;
SDL_PauseAudio(1);
Mix_UnlockAudio();
if (currentsong)
MusicPlayerStop(currentsong->player);
currentsong = song;
currentsong->loops = loops;
MusicPlayerPreroll(song->player);
MusicPlayerSetTime(song->player, 0);
MusicPlayerStart(song->player);
GetSequenceAudioUnit(song->sequence, &song->audiounit);
vol = latched_volume;
latched_volume++; /* just make this not match. */
native_midi_setvolume(vol);
Mix_LockAudio();
SDL_PauseAudio(0);
}
void native_midi_pause(void)
{
}
void native_midi_resume(void)
{
}
void native_midi_stop(void)
{
if (currentsong) {
SDL_PauseAudio(1);
Mix_UnlockAudio();
MusicPlayerStop(currentsong->player);
currentsong = NULL;
Mix_LockAudio();
SDL_PauseAudio(0);
}
}
int native_midi_active(void)
{
MusicTimeStamp currentTime = 0;
if (currentsong == NULL)
return 0;
MusicPlayerGetTime(currentsong->player, &currentTime);
if ((currentTime < currentsong->endTime) ||
(currentTime >= kMusicTimeStamp_EndOfTrack)) {
return 1;
} else if (currentsong->loops) {
--currentsong->loops;
MusicPlayerSetTime(currentsong->player, 0);
return 1;
}
return 0;
}
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,321 @@
/*
native_midi: Hardware Midi support for the SDL_mixer library
Copyright (C) 2000,2001 Florian 'Proff' Schulze <florian.proff.schulze@gmx.net>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#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;
int Loops;
int CurrentHdr;
MIDIHDR MidiStreamHdr[2];
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;
MIDIHDR *hdr;
if ((song->MusicLoaded) && (song->NewEvents))
{
// proff 12/8/98: Added for safety
song->CurrentHdr = !song->CurrentHdr;
hdr = &song->MidiStreamHdr[song->CurrentHdr];
midiOutUnprepareHeader((HMIDIOUT)hMidiStream,hdr,sizeof(MIDIHDR));
if (song->NewPos>=song->Size)
return 0;
BlockSize=(song->Size-song->NewPos);
if (BlockSize<=0)
return 0;
if (BlockSize>36000)
BlockSize=36000;
hdr->lpData=(void *)((unsigned char *)song->NewEvents+song->NewPos);
song->NewPos+=BlockSize;
hdr->dwBufferLength=BlockSize;
hdr->dwBytesRecorded=BlockSize;
hdr->dwFlags=0;
hdr->dwOffset=0;
err=midiOutPrepareHeader((HMIDIOUT)hMidiStream,hdr,sizeof(MIDIHDR));
if (err!=MMSYSERR_NOERROR)
return 0;
err=midiStreamOut(hMidiStream,hdr,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[currentsong->CurrentHdr]))
BlockOut(currentsong);
break;
case MOM_POSITIONCB:
if ((currentsong->MusicLoaded) && (dwParam1 == (DWORD_PTR)&currentsong->MidiStreamHdr[currentsong->CurrentHdr])) {
if (currentsong->Loops) {
if (currentsong->Loops > 0)
--currentsong->Loops;
currentsong->NewPos=0;
BlockOut(currentsong);
} else {
currentsong->MusicPlaying=0;
}
}
break;
default:
break;
}
}
int native_midi_detect(void)
{
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_RW(SDL_RWops *src, int freesrc)
{
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(src, &newsong->ppqn);
if (!evntlist)
{
free(newsong);
return NULL;
}
MIDItoStream(newsong, evntlist);
FreeMIDIEventList(evntlist);
if (freesrc) {
SDL_RWclose(src);
}
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, int loops)
{
MMRESULT merr;
MIDIPROPTIMEDIV mptd;
native_midi_stop();
if (!hMidiStream)
{
merr=midiStreamOpen(&hMidiStream,&MidiDevice,(DWORD)1,(DWORD_PTR)MidiProc,(DWORD_PTR)0,CALLBACK_FUNCTION);
if (merr!=MMSYSERR_NOERROR)
{
hMidiStream = NULL; // should I do midiStreamClose(hMidiStream) before?
return;
}
//midiStreamStop(hMidiStream);
currentsong=song;
currentsong->NewPos=0;
currentsong->MusicPlaying=1;
currentsong->Loops=loops;
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_pause(void)
{
if (!hMidiStream)
return;
midiStreamPause(hMidiStream);
}
void native_midi_resume(void)
{
if (!hMidiStream)
return;
midiStreamRestart(hMidiStream);
}
void native_midi_stop(void)
{
if (!hMidiStream)
return;
midiStreamStop(hMidiStream);
midiStreamClose(hMidiStream);
currentsong=NULL;
hMidiStream = NULL;
}
int native_midi_active(void)
{
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 */