native_midi/native_midi_win32.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 03 Oct 2009 20:43:33 +0000
changeset 419 e27fe0bfe470
parent 401 ee71829d80e7
child 485 e2c905c12312
permissions -rw-r--r--
Sam Lantinga - Sat Oct 3 13:33:36 PDT 2009
* MOD support uses libmikmod and is dynamically loaded by default
slouken@98
     1
/*
slouken@98
     2
    native_midi:  Hardware Midi support for the SDL_mixer library
slouken@98
     3
    Copyright (C) 2000,2001  Florian 'Proff' Schulze
slouken@98
     4
slouken@98
     5
    This library is free software; you can redistribute it and/or
slouken@98
     6
    modify it under the terms of the GNU Library General Public
slouken@98
     7
    License as published by the Free Software Foundation; either
slouken@98
     8
    version 2 of the License, or (at your option) any later version.
slouken@98
     9
slouken@98
    10
    This library is distributed in the hope that it will be useful,
slouken@98
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@98
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@98
    13
    Library General Public License for more details.
slouken@98
    14
slouken@98
    15
    You should have received a copy of the GNU Library General Public
slouken@98
    16
    License along with this library; if not, write to the Free
slouken@98
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@98
    18
slouken@98
    19
    Florian 'Proff' Schulze
slouken@98
    20
    florian.proff.schulze@gmx.net
slouken@98
    21
*/
slouken@294
    22
#include "SDL_config.h"
slouken@98
    23
slouken@98
    24
/* everything below is currently one very big bad hack ;) Proff */
slouken@98
    25
slouken@294
    26
#if __WIN32__
slouken@101
    27
#define WIN32_LEAN_AND_MEAN
slouken@101
    28
#include <windows.h>
slouken@101
    29
#include <windowsx.h>
slouken@101
    30
#include <mmsystem.h>
slouken@98
    31
#include <stdio.h>
slouken@98
    32
#include <stdlib.h>
slouken@98
    33
#include <limits.h>
slouken@98
    34
#include "native_midi.h"
slouken@108
    35
#include "native_midi_common.h"
slouken@101
    36
slouken@101
    37
struct _NativeMidiSong {
slouken@101
    38
  int MusicLoaded;
slouken@101
    39
  int MusicPlaying;
slouken@101
    40
  MIDIHDR MidiStreamHdr;
slouken@101
    41
  MIDIEVENT *NewEvents;
slouken@108
    42
	Uint16 ppqn;
slouken@108
    43
  int Size;
slouken@101
    44
  int NewPos;
slouken@101
    45
};
slouken@101
    46
slouken@98
    47
static UINT MidiDevice=MIDI_MAPPER;
slouken@98
    48
static HMIDISTRM hMidiStream;
slouken@98
    49
static NativeMidiSong *currentsong;
slouken@98
    50
slouken@98
    51
static int BlockOut(NativeMidiSong *song)
slouken@98
    52
{
slouken@98
    53
  MMRESULT err;
slouken@98
    54
  int BlockSize;
slouken@98
    55
slouken@98
    56
  if ((song->MusicLoaded) && (song->NewEvents))
slouken@98
    57
  {
slouken@98
    58
    // proff 12/8/98: Added for savety
slouken@108
    59
    midiOutUnprepareHeader((HMIDIOUT)hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR));
slouken@108
    60
    if (song->NewPos>=song->Size)
slouken@98
    61
      return 0;
slouken@108
    62
    BlockSize=(song->Size-song->NewPos);
slouken@98
    63
    if (BlockSize<=0)
slouken@98
    64
      return 0;
slouken@98
    65
    if (BlockSize>36000)
slouken@98
    66
      BlockSize=36000;
slouken@108
    67
    song->MidiStreamHdr.lpData=(void *)((unsigned char *)song->NewEvents+song->NewPos);
slouken@98
    68
    song->NewPos+=BlockSize;
slouken@98
    69
    song->MidiStreamHdr.dwBufferLength=BlockSize;
slouken@98
    70
    song->MidiStreamHdr.dwBytesRecorded=BlockSize;
slouken@98
    71
    song->MidiStreamHdr.dwFlags=0;
slouken@108
    72
    err=midiOutPrepareHeader((HMIDIOUT)hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR));
slouken@98
    73
    if (err!=MMSYSERR_NOERROR)
slouken@98
    74
      return 0;
slouken@98
    75
    err=midiStreamOut(hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR));
slouken@98
    76
      return 0;
slouken@98
    77
  }
slouken@98
    78
  return 1;
slouken@98
    79
}
slouken@98
    80
slouken@108
    81
static void MIDItoStream(NativeMidiSong *song, MIDIEvent *evntlist)
slouken@98
    82
{
slouken@108
    83
  int eventcount;
slouken@108
    84
  MIDIEvent *event;
slouken@108
    85
  MIDIEVENT *newevent;
slouken@98
    86
slouken@108
    87
  eventcount=0;
slouken@108
    88
  event=evntlist;
slouken@108
    89
  while (event)
slouken@98
    90
  {
slouken@108
    91
    eventcount++;
slouken@108
    92
    event=event->next;
slouken@98
    93
  }
slouken@108
    94
  song->NewEvents=malloc(eventcount*3*sizeof(DWORD));
slouken@108
    95
  if (!song->NewEvents)
slouken@108
    96
    return;
slouken@108
    97
  memset(song->NewEvents,0,(eventcount*3*sizeof(DWORD)));
slouken@108
    98
slouken@108
    99
  eventcount=0;
slouken@108
   100
  event=evntlist;
slouken@108
   101
  newevent=song->NewEvents;
slouken@108
   102
  while (event)
slouken@98
   103
  {
slouken@108
   104
		int status = (event->status&0xF0)>>4;
slouken@108
   105
		switch (status)
slouken@108
   106
		{
slouken@108
   107
		case MIDI_STATUS_NOTE_OFF:
slouken@108
   108
		case MIDI_STATUS_NOTE_ON:
slouken@108
   109
		case MIDI_STATUS_AFTERTOUCH:
slouken@108
   110
		case MIDI_STATUS_CONTROLLER:
slouken@108
   111
		case MIDI_STATUS_PROG_CHANGE:
slouken@108
   112
		case MIDI_STATUS_PRESSURE:
slouken@108
   113
		case MIDI_STATUS_PITCH_WHEEL:
slouken@108
   114
      newevent->dwDeltaTime=event->time;
slouken@108
   115
		  newevent->dwEvent=(event->status|0x80)|(event->data[0]<<8)|(event->data[1]<<16)|(MEVT_SHORTMSG<<24);
slouken@108
   116
      newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD)));
slouken@108
   117
      eventcount++;
slouken@108
   118
			break;
slouken@108
   119
slouken@108
   120
		case MIDI_STATUS_SYSEX:
slouken@108
   121
			if (event->status == 0xFF && event->data[0] == 0x51) /* Tempo change */
slouken@108
   122
			{
slouken@108
   123
				int tempo = (event->extraData[0] << 16) |
slouken@108
   124
					          (event->extraData[1] << 8) |
slouken@108
   125
					           event->extraData[2];
slouken@108
   126
        newevent->dwDeltaTime=event->time;
slouken@108
   127
				newevent->dwEvent=(MEVT_TEMPO<<24) | tempo;
slouken@108
   128
        newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD)));
slouken@108
   129
        eventcount++;
slouken@108
   130
			}
slouken@108
   131
			break;
slouken@108
   132
    }
slouken@108
   133
slouken@108
   134
    event=event->next;
slouken@108
   135
  }
slouken@108
   136
slouken@108
   137
  song->Size=eventcount*3*sizeof(DWORD);
slouken@108
   138
slouken@108
   139
  {
slouken@108
   140
    int time;
slouken@108
   141
    int temptime;
slouken@108
   142
slouken@98
   143
    song->NewPos=0;
slouken@108
   144
    time=0;
slouken@108
   145
    newevent=song->NewEvents;
slouken@108
   146
    while (song->NewPos<song->Size)
slouken@98
   147
    {
slouken@108
   148
      temptime=newevent->dwDeltaTime;
slouken@108
   149
      newevent->dwDeltaTime-=time;
slouken@108
   150
      time=temptime;
slouken@108
   151
      if ((song->NewPos+12)>=song->Size)
slouken@108
   152
        newevent->dwEvent |= MEVT_F_CALLBACK;
slouken@108
   153
      newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD)));
slouken@98
   154
      song->NewPos+=12;
slouken@98
   155
    }
slouken@98
   156
  }
slouken@108
   157
  song->NewPos=0;
slouken@108
   158
  song->MusicLoaded=1;
slouken@98
   159
}
slouken@98
   160
slouken@397
   161
void CALLBACK MidiProc( HMIDIIN hMidi, UINT uMsg, DWORD_PTR dwInstance,
slouken@397
   162
                        DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
slouken@98
   163
{
slouken@98
   164
    switch( uMsg )
slouken@98
   165
    {
slouken@98
   166
    case MOM_DONE:
slouken@397
   167
      if ((currentsong->MusicLoaded) && (dwParam1 == (DWORD_PTR)&currentsong->MidiStreamHdr))
slouken@98
   168
        BlockOut(currentsong);
slouken@98
   169
      break;
slouken@98
   170
    case MOM_POSITIONCB:
slouken@397
   171
      if ((currentsong->MusicLoaded) && (dwParam1 == (DWORD_PTR)&currentsong->MidiStreamHdr))
slouken@98
   172
        currentsong->MusicPlaying=0;
slouken@98
   173
      break;
slouken@98
   174
    default:
slouken@98
   175
      break;
slouken@98
   176
    }
slouken@98
   177
}
slouken@98
   178
slouken@101
   179
int native_midi_detect()
slouken@98
   180
{
slouken@98
   181
  MMRESULT merr;
slouken@98
   182
  HMIDISTRM MidiStream;
slouken@98
   183
slouken@397
   184
  merr=midiStreamOpen(&MidiStream,&MidiDevice,(DWORD)1,(DWORD_PTR)MidiProc,(DWORD_PTR)0,CALLBACK_FUNCTION);
slouken@98
   185
  if (merr!=MMSYSERR_NOERROR)
slouken@399
   186
    return 0;
slouken@98
   187
  midiStreamClose(MidiStream);
slouken@399
   188
  return 1;
slouken@98
   189
}
slouken@98
   190
slouken@401
   191
NativeMidiSong *native_midi_loadsong(const char *midifile)
slouken@98
   192
{
patmandin@274
   193
	NativeMidiSong *newsong;
slouken@108
   194
	MIDIEvent		*evntlist = NULL;
patmandin@274
   195
	SDL_RWops	*rw;
slouken@98
   196
patmandin@274
   197
	newsong=malloc(sizeof(NativeMidiSong));
patmandin@274
   198
	if (!newsong)
patmandin@274
   199
		return NULL;
patmandin@274
   200
	memset(newsong,0,sizeof(NativeMidiSong));
slouken@108
   201
patmandin@274
   202
	/* Attempt to load the midi file */
patmandin@274
   203
	rw = SDL_RWFromFile(midifile, "rb");
patmandin@274
   204
	if (rw) {
patmandin@274
   205
		evntlist = CreateMIDIEventList(rw, &newsong->ppqn);
patmandin@274
   206
		SDL_RWclose(rw);
patmandin@274
   207
		if (!evntlist)
patmandin@274
   208
		{
patmandin@274
   209
			free(newsong);
patmandin@274
   210
			return NULL;
patmandin@274
   211
		}
patmandin@274
   212
	}
slouken@108
   213
patmandin@274
   214
	MIDItoStream(newsong, evntlist);
slouken@108
   215
slouken@108
   216
	FreeMIDIEventList(evntlist);
slouken@98
   217
patmandin@274
   218
	return newsong;
slouken@98
   219
}
slouken@98
   220
patmandin@269
   221
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
patmandin@269
   222
{
patmandin@274
   223
	NativeMidiSong *newsong;
patmandin@274
   224
	MIDIEvent		*evntlist = NULL;
patmandin@274
   225
patmandin@274
   226
	newsong=malloc(sizeof(NativeMidiSong));
patmandin@274
   227
	if (!newsong)
patmandin@274
   228
		return NULL;
patmandin@274
   229
	memset(newsong,0,sizeof(NativeMidiSong));
patmandin@274
   230
patmandin@274
   231
	/* Attempt to load the midi file */
patmandin@274
   232
	evntlist = CreateMIDIEventList(rw, &newsong->ppqn);
patmandin@274
   233
	if (!evntlist)
patmandin@274
   234
	{
patmandin@274
   235
		free(newsong);
patmandin@274
   236
		return NULL;
patmandin@274
   237
	}
patmandin@274
   238
patmandin@274
   239
	MIDItoStream(newsong, evntlist);
patmandin@274
   240
patmandin@274
   241
	FreeMIDIEventList(evntlist);
patmandin@274
   242
patmandin@274
   243
	return newsong;
patmandin@269
   244
}
patmandin@269
   245
slouken@98
   246
void native_midi_freesong(NativeMidiSong *song)
slouken@98
   247
{
slouken@98
   248
  if (hMidiStream)
slouken@98
   249
  {
slouken@98
   250
    midiStreamStop(hMidiStream);
slouken@98
   251
    midiStreamClose(hMidiStream);
slouken@98
   252
  }
slouken@98
   253
  if (song)
slouken@98
   254
  {
slouken@98
   255
    if (song->NewEvents)
slouken@98
   256
      free(song->NewEvents);
slouken@98
   257
    free(song);
slouken@98
   258
  }
slouken@98
   259
}
slouken@98
   260
slouken@98
   261
void native_midi_start(NativeMidiSong *song)
slouken@98
   262
{
slouken@98
   263
  MMRESULT merr;
slouken@98
   264
  MIDIPROPTIMEDIV mptd;
slouken@98
   265
slouken@98
   266
  native_midi_stop();
slouken@98
   267
  if (!hMidiStream)
slouken@98
   268
  {
slouken@98
   269
    merr=midiStreamOpen(&hMidiStream,&MidiDevice,1,(DWORD)&MidiProc,0,CALLBACK_FUNCTION);
slouken@98
   270
    if (merr!=MMSYSERR_NOERROR)
slouken@98
   271
    {
slouken@98
   272
      hMidiStream=0;
slouken@98
   273
      return;
slouken@98
   274
    }
slouken@98
   275
    //midiStreamStop(hMidiStream);
slouken@98
   276
    currentsong=song;
slouken@98
   277
    currentsong->NewPos=0;
slouken@98
   278
    currentsong->MusicPlaying=1;
slouken@98
   279
    mptd.cbStruct=sizeof(MIDIPROPTIMEDIV);
slouken@108
   280
    mptd.dwTimeDiv=currentsong->ppqn;
slouken@98
   281
    merr=midiStreamProperty(hMidiStream,(LPBYTE)&mptd,MIDIPROP_SET | MIDIPROP_TIMEDIV);
slouken@98
   282
    BlockOut(song);
slouken@98
   283
    merr=midiStreamRestart(hMidiStream);
slouken@98
   284
  }
slouken@98
   285
}
slouken@98
   286
slouken@98
   287
void native_midi_stop()
slouken@98
   288
{
slouken@98
   289
  if (!hMidiStream)
slouken@98
   290
    return;
slouken@98
   291
  midiStreamStop(hMidiStream);
slouken@98
   292
  midiStreamClose(hMidiStream);
slouken@98
   293
  currentsong=NULL;
slouken@98
   294
  hMidiStream = 0;
slouken@98
   295
}
slouken@98
   296
slouken@98
   297
int native_midi_active()
slouken@98
   298
{
slouken@98
   299
  return currentsong->MusicPlaying;
slouken@98
   300
}
slouken@98
   301
slouken@98
   302
void native_midi_setvolume(int volume)
slouken@98
   303
{
slouken@236
   304
  int calcVolume;
slouken@236
   305
  if (volume > 128)
slouken@236
   306
    volume = 128;
slouken@236
   307
  if (volume < 0)
slouken@236
   308
    volume = 0;
slouken@236
   309
  calcVolume = (65535 * volume / 128);
slouken@236
   310
slouken@236
   311
  midiOutSetVolume((HMIDIOUT)hMidiStream, MAKELONG(calcVolume , calcVolume));
slouken@98
   312
}
slouken@98
   313
slouken@400
   314
const char *native_midi_error(void)
slouken@98
   315
{
slouken@98
   316
  return "";
slouken@98
   317
}
slouken@98
   318
slouken@99
   319
#endif /* Windows native MIDI support */