native_midi/native_midi_win32.c
author Ozkan Sezer <sezeroz@gmail.com>
Sat, 13 Oct 2018 23:02:04 +0300
branchSDL-1.2
changeset 908 6b860486ce24
parent 542 3de4970b36d4
child 617 87116a42526e
permissions -rw-r--r--
Mix_InitMP3: unload dll if mpg123_init() fails.
slouken@98
     1
/*
slouken@518
     2
  native_midi:  Hardware Midi support for the SDL_mixer library
slouken@518
     3
  Copyright (C) 2000,2001  Florian 'Proff' Schulze <florian.proff.schulze@gmx.net>
slouken@98
     4
slouken@518
     5
  This software is provided 'as-is', without any express or implied
slouken@518
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@518
     7
  arising from the use of this software.
slouken@98
     8
slouken@518
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@518
    10
  including commercial applications, and to alter it and redistribute it
slouken@518
    11
  freely, subject to the following restrictions:
slouken@98
    12
slouken@518
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@518
    14
     claim that you wrote the original software. If you use this software
slouken@518
    15
     in a product, an acknowledgment in the product documentation would be
slouken@518
    16
     appreciated but is not required.
slouken@518
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@518
    18
     misrepresented as being the original software.
slouken@518
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@98
    20
*/
slouken@294
    21
#include "SDL_config.h"
slouken@98
    22
slouken@98
    23
/* everything below is currently one very big bad hack ;) Proff */
slouken@98
    24
slouken@294
    25
#if __WIN32__
slouken@101
    26
#define WIN32_LEAN_AND_MEAN
slouken@101
    27
#include <windows.h>
slouken@101
    28
#include <windowsx.h>
slouken@101
    29
#include <mmsystem.h>
slouken@98
    30
#include <stdio.h>
slouken@98
    31
#include <stdlib.h>
slouken@98
    32
#include <limits.h>
slouken@98
    33
#include "native_midi.h"
slouken@108
    34
#include "native_midi_common.h"
slouken@101
    35
slouken@101
    36
struct _NativeMidiSong {
slouken@101
    37
  int MusicLoaded;
slouken@101
    38
  int MusicPlaying;
slouken@533
    39
  int Loops;
slouken@533
    40
  int CurrentHdr;
slouken@533
    41
  MIDIHDR MidiStreamHdr[2];
slouken@101
    42
  MIDIEVENT *NewEvents;
slouken@533
    43
  Uint16 ppqn;
slouken@108
    44
  int Size;
slouken@101
    45
  int NewPos;
slouken@101
    46
};
slouken@101
    47
slouken@98
    48
static UINT MidiDevice=MIDI_MAPPER;
slouken@98
    49
static HMIDISTRM hMidiStream;
slouken@98
    50
static NativeMidiSong *currentsong;
slouken@98
    51
slouken@98
    52
static int BlockOut(NativeMidiSong *song)
slouken@98
    53
{
slouken@98
    54
  MMRESULT err;
slouken@98
    55
  int BlockSize;
slouken@533
    56
  MIDIHDR *hdr;
slouken@98
    57
slouken@98
    58
  if ((song->MusicLoaded) && (song->NewEvents))
slouken@98
    59
  {
slouken@533
    60
    // proff 12/8/98: Added for safety
slouken@533
    61
    song->CurrentHdr = !song->CurrentHdr;
slouken@533
    62
    hdr = &song->MidiStreamHdr[song->CurrentHdr];
slouken@533
    63
    midiOutUnprepareHeader((HMIDIOUT)hMidiStream,hdr,sizeof(MIDIHDR));
slouken@108
    64
    if (song->NewPos>=song->Size)
slouken@98
    65
      return 0;
slouken@108
    66
    BlockSize=(song->Size-song->NewPos);
slouken@98
    67
    if (BlockSize<=0)
slouken@98
    68
      return 0;
slouken@98
    69
    if (BlockSize>36000)
slouken@98
    70
      BlockSize=36000;
slouken@533
    71
    hdr->lpData=(void *)((unsigned char *)song->NewEvents+song->NewPos);
slouken@98
    72
    song->NewPos+=BlockSize;
slouken@533
    73
    hdr->dwBufferLength=BlockSize;
slouken@533
    74
    hdr->dwBytesRecorded=BlockSize;
slouken@533
    75
    hdr->dwFlags=0;
slouken@533
    76
    hdr->dwOffset=0;
slouken@533
    77
    err=midiOutPrepareHeader((HMIDIOUT)hMidiStream,hdr,sizeof(MIDIHDR));
slouken@98
    78
    if (err!=MMSYSERR_NOERROR)
slouken@98
    79
      return 0;
slouken@533
    80
    err=midiStreamOut(hMidiStream,hdr,sizeof(MIDIHDR));
slouken@98
    81
      return 0;
slouken@98
    82
  }
slouken@98
    83
  return 1;
slouken@98
    84
}
slouken@98
    85
slouken@108
    86
static void MIDItoStream(NativeMidiSong *song, MIDIEvent *evntlist)
slouken@98
    87
{
slouken@108
    88
  int eventcount;
slouken@108
    89
  MIDIEvent *event;
slouken@108
    90
  MIDIEVENT *newevent;
slouken@98
    91
slouken@108
    92
  eventcount=0;
slouken@108
    93
  event=evntlist;
slouken@108
    94
  while (event)
slouken@98
    95
  {
slouken@108
    96
    eventcount++;
slouken@108
    97
    event=event->next;
slouken@98
    98
  }
slouken@108
    99
  song->NewEvents=malloc(eventcount*3*sizeof(DWORD));
slouken@108
   100
  if (!song->NewEvents)
slouken@108
   101
    return;
slouken@108
   102
  memset(song->NewEvents,0,(eventcount*3*sizeof(DWORD)));
slouken@108
   103
slouken@108
   104
  eventcount=0;
slouken@108
   105
  event=evntlist;
slouken@108
   106
  newevent=song->NewEvents;
slouken@108
   107
  while (event)
slouken@98
   108
  {
slouken@108
   109
		int status = (event->status&0xF0)>>4;
slouken@108
   110
		switch (status)
slouken@108
   111
		{
slouken@108
   112
		case MIDI_STATUS_NOTE_OFF:
slouken@108
   113
		case MIDI_STATUS_NOTE_ON:
slouken@108
   114
		case MIDI_STATUS_AFTERTOUCH:
slouken@108
   115
		case MIDI_STATUS_CONTROLLER:
slouken@108
   116
		case MIDI_STATUS_PROG_CHANGE:
slouken@108
   117
		case MIDI_STATUS_PRESSURE:
slouken@108
   118
		case MIDI_STATUS_PITCH_WHEEL:
slouken@108
   119
      newevent->dwDeltaTime=event->time;
slouken@108
   120
		  newevent->dwEvent=(event->status|0x80)|(event->data[0]<<8)|(event->data[1]<<16)|(MEVT_SHORTMSG<<24);
slouken@108
   121
      newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD)));
slouken@108
   122
      eventcount++;
slouken@108
   123
			break;
slouken@108
   124
slouken@108
   125
		case MIDI_STATUS_SYSEX:
slouken@108
   126
			if (event->status == 0xFF && event->data[0] == 0x51) /* Tempo change */
slouken@108
   127
			{
slouken@108
   128
				int tempo = (event->extraData[0] << 16) |
slouken@108
   129
					          (event->extraData[1] << 8) |
slouken@108
   130
					           event->extraData[2];
slouken@108
   131
        newevent->dwDeltaTime=event->time;
slouken@108
   132
				newevent->dwEvent=(MEVT_TEMPO<<24) | tempo;
slouken@108
   133
        newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD)));
slouken@108
   134
        eventcount++;
slouken@108
   135
			}
slouken@108
   136
			break;
slouken@108
   137
    }
slouken@108
   138
slouken@108
   139
    event=event->next;
slouken@108
   140
  }
slouken@108
   141
slouken@108
   142
  song->Size=eventcount*3*sizeof(DWORD);
slouken@108
   143
slouken@108
   144
  {
slouken@108
   145
    int time;
slouken@108
   146
    int temptime;
slouken@108
   147
slouken@98
   148
    song->NewPos=0;
slouken@108
   149
    time=0;
slouken@108
   150
    newevent=song->NewEvents;
slouken@108
   151
    while (song->NewPos<song->Size)
slouken@98
   152
    {
slouken@108
   153
      temptime=newevent->dwDeltaTime;
slouken@108
   154
      newevent->dwDeltaTime-=time;
slouken@108
   155
      time=temptime;
slouken@108
   156
      if ((song->NewPos+12)>=song->Size)
slouken@108
   157
        newevent->dwEvent |= MEVT_F_CALLBACK;
slouken@108
   158
      newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD)));
slouken@98
   159
      song->NewPos+=12;
slouken@98
   160
    }
slouken@98
   161
  }
slouken@108
   162
  song->NewPos=0;
slouken@108
   163
  song->MusicLoaded=1;
slouken@98
   164
}
slouken@98
   165
slouken@397
   166
void CALLBACK MidiProc( HMIDIIN hMidi, UINT uMsg, DWORD_PTR dwInstance,
slouken@397
   167
                        DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
slouken@98
   168
{
slouken@98
   169
    switch( uMsg )
slouken@98
   170
    {
slouken@98
   171
    case MOM_DONE:
slouken@533
   172
      if ((currentsong->MusicLoaded) && (dwParam1 == (DWORD_PTR)&currentsong->MidiStreamHdr[currentsong->CurrentHdr]))
slouken@98
   173
        BlockOut(currentsong);
slouken@98
   174
      break;
slouken@98
   175
    case MOM_POSITIONCB:
slouken@533
   176
      if ((currentsong->MusicLoaded) && (dwParam1 == (DWORD_PTR)&currentsong->MidiStreamHdr[currentsong->CurrentHdr])) {
slouken@533
   177
        if (currentsong->Loops) {
slouken@533
   178
          --currentsong->Loops;
slouken@533
   179
          currentsong->NewPos=0;
slouken@533
   180
          BlockOut(currentsong);
slouken@533
   181
        } else {
slouken@533
   182
          currentsong->MusicPlaying=0;
slouken@533
   183
        }
slouken@533
   184
      }
slouken@98
   185
      break;
slouken@98
   186
    default:
slouken@98
   187
      break;
slouken@98
   188
    }
slouken@98
   189
}
slouken@98
   190
slouken@101
   191
int native_midi_detect()
slouken@98
   192
{
slouken@98
   193
  MMRESULT merr;
slouken@98
   194
  HMIDISTRM MidiStream;
slouken@98
   195
slouken@397
   196
  merr=midiStreamOpen(&MidiStream,&MidiDevice,(DWORD)1,(DWORD_PTR)MidiProc,(DWORD_PTR)0,CALLBACK_FUNCTION);
slouken@98
   197
  if (merr!=MMSYSERR_NOERROR)
slouken@399
   198
    return 0;
slouken@98
   199
  midiStreamClose(MidiStream);
slouken@399
   200
  return 1;
slouken@98
   201
}
slouken@98
   202
slouken@521
   203
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw, int freerw)
patmandin@269
   204
{
patmandin@274
   205
	NativeMidiSong *newsong;
patmandin@274
   206
	MIDIEvent		*evntlist = NULL;
patmandin@274
   207
patmandin@274
   208
	newsong=malloc(sizeof(NativeMidiSong));
slouken@521
   209
	if (!newsong) {
slouken@521
   210
		if (freerw) {
slouken@521
   211
			SDL_RWclose(rw);
slouken@521
   212
		}
patmandin@274
   213
		return NULL;
slouken@521
   214
	}
patmandin@274
   215
	memset(newsong,0,sizeof(NativeMidiSong));
patmandin@274
   216
patmandin@274
   217
	/* Attempt to load the midi file */
patmandin@274
   218
	evntlist = CreateMIDIEventList(rw, &newsong->ppqn);
patmandin@274
   219
	if (!evntlist)
patmandin@274
   220
	{
patmandin@274
   221
		free(newsong);
slouken@521
   222
		if (freerw) {
slouken@521
   223
			SDL_RWclose(rw);
slouken@521
   224
		}
patmandin@274
   225
		return NULL;
patmandin@274
   226
	}
patmandin@274
   227
patmandin@274
   228
	MIDItoStream(newsong, evntlist);
patmandin@274
   229
patmandin@274
   230
	FreeMIDIEventList(evntlist);
patmandin@274
   231
slouken@521
   232
	if (freerw) {
slouken@521
   233
		SDL_RWclose(rw);
slouken@521
   234
	}
patmandin@274
   235
	return newsong;
patmandin@269
   236
}
patmandin@269
   237
slouken@98
   238
void native_midi_freesong(NativeMidiSong *song)
slouken@98
   239
{
slouken@98
   240
  if (hMidiStream)
slouken@98
   241
  {
slouken@98
   242
    midiStreamStop(hMidiStream);
slouken@98
   243
    midiStreamClose(hMidiStream);
slouken@98
   244
  }
slouken@98
   245
  if (song)
slouken@98
   246
  {
slouken@98
   247
    if (song->NewEvents)
slouken@98
   248
      free(song->NewEvents);
slouken@98
   249
    free(song);
slouken@98
   250
  }
slouken@98
   251
}
slouken@98
   252
slouken@533
   253
void native_midi_start(NativeMidiSong *song, int loops)
slouken@98
   254
{
slouken@98
   255
  MMRESULT merr;
slouken@98
   256
  MIDIPROPTIMEDIV mptd;
slouken@98
   257
slouken@98
   258
  native_midi_stop();
slouken@98
   259
  if (!hMidiStream)
slouken@98
   260
  {
icculus@485
   261
    merr=midiStreamOpen(&hMidiStream,&MidiDevice,(DWORD)1,(DWORD_PTR)MidiProc,(DWORD_PTR)0,CALLBACK_FUNCTION);
slouken@98
   262
    if (merr!=MMSYSERR_NOERROR)
slouken@98
   263
    {
icculus@485
   264
      hMidiStream = NULL; // should I do midiStreamClose(hMidiStream) before?
slouken@98
   265
      return;
slouken@98
   266
    }
slouken@98
   267
    //midiStreamStop(hMidiStream);
slouken@98
   268
    currentsong=song;
slouken@98
   269
    currentsong->NewPos=0;
slouken@98
   270
    currentsong->MusicPlaying=1;
slouken@533
   271
    currentsong->Loops=loops;
slouken@98
   272
    mptd.cbStruct=sizeof(MIDIPROPTIMEDIV);
slouken@108
   273
    mptd.dwTimeDiv=currentsong->ppqn;
slouken@98
   274
    merr=midiStreamProperty(hMidiStream,(LPBYTE)&mptd,MIDIPROP_SET | MIDIPROP_TIMEDIV);
slouken@98
   275
    BlockOut(song);
slouken@98
   276
    merr=midiStreamRestart(hMidiStream);
slouken@98
   277
  }
slouken@98
   278
}
slouken@98
   279
slouken@98
   280
void native_midi_stop()
slouken@98
   281
{
slouken@98
   282
  if (!hMidiStream)
slouken@98
   283
    return;
slouken@98
   284
  midiStreamStop(hMidiStream);
slouken@98
   285
  midiStreamClose(hMidiStream);
slouken@98
   286
  currentsong=NULL;
icculus@485
   287
  hMidiStream = NULL;
slouken@98
   288
}
slouken@98
   289
slouken@98
   290
int native_midi_active()
slouken@98
   291
{
slouken@98
   292
  return currentsong->MusicPlaying;
slouken@98
   293
}
slouken@98
   294
slouken@98
   295
void native_midi_setvolume(int volume)
slouken@98
   296
{
slouken@236
   297
  int calcVolume;
slouken@236
   298
  if (volume > 128)
slouken@236
   299
    volume = 128;
slouken@236
   300
  if (volume < 0)
slouken@236
   301
    volume = 0;
slouken@236
   302
  calcVolume = (65535 * volume / 128);
slouken@236
   303
slouken@236
   304
  midiOutSetVolume((HMIDIOUT)hMidiStream, MAKELONG(calcVolume , calcVolume));
slouken@98
   305
}
slouken@98
   306
slouken@400
   307
const char *native_midi_error(void)
slouken@98
   308
{
slouken@98
   309
  return "";
slouken@98
   310
}
slouken@98
   311
slouken@99
   312
#endif /* Windows native MIDI support */