Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

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