Built SDL2_image and _mixer static
This commit is contained in:
40
libsdl2_mixer/native_midi/native_midi.h
Normal file
40
libsdl2_mixer/native_midi/native_midi.h
Normal 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_ */
|
414
libsdl2_mixer/native_midi/native_midi_common.c
Normal file
414
libsdl2_mixer/native_midi/native_midi_common.c
Normal 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, ¤tPos);
|
||||
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, ¤tPos);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
63
libsdl2_mixer/native_midi/native_midi_common.h
Normal file
63
libsdl2_mixer/native_midi/native_midi_common.h
Normal 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_ */
|
295
libsdl2_mixer/native_midi/native_midi_haiku.cpp
Normal file
295
libsdl2_mixer/native_midi/native_midi_haiku.cpp
Normal 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__ */
|
649
libsdl2_mixer/native_midi/native_midi_mac.c
Normal file
649
libsdl2_mixer/native_midi/native_midi_mac.c
Normal 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 */
|
347
libsdl2_mixer/native_midi/native_midi_macosx.c
Normal file
347
libsdl2_mixer/native_midi/native_midi_macosx.c
Normal 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, ¤tTime);
|
||||
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 */
|
||||
|
321
libsdl2_mixer/native_midi/native_midi_win32.c
Normal file
321
libsdl2_mixer/native_midi/native_midi_win32.c
Normal 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)¤tsong->MidiStreamHdr[currentsong->CurrentHdr]))
|
||||
BlockOut(currentsong);
|
||||
break;
|
||||
case MOM_POSITIONCB:
|
||||
if ((currentsong->MusicLoaded) && (dwParam1 == (DWORD_PTR)¤tsong->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 */
|
Reference in New Issue
Block a user