native_midi/native_midi_haiku.cpp
author Ozkan Sezer <sezeroz@gmail.com>
Sun, 07 Oct 2018 00:08:00 +0300
branchSDL-1.2
changeset 871 284d10d87cc9
parent 546 83de6eb0d6d2
child 617 87116a42526e
permissions -rw-r--r--
backport fix for bug #1833. (from 2.0 branch commit 7ead8213dfb0).
     1 /*
     2   native_midi_haiku:  Native Midi support on Haiku for the SDL_mixer library
     3   Copyright (C) 2010  Egor Suvorov <egor_suvorov@mail.ru>
     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 #ifdef __HAIKU__
    24 #include <stdio.h>
    25 #include <stdlib.h>
    26 #include <string.h>
    27 #include <MidiStore.h>
    28 #include <MidiDefs.h>
    29 #include <MidiSynthFile.h>
    30 #include <algorithm>
    31 #include <assert.h>
    32 extern "C" {
    33 #include "native_midi.h"
    34 #include "native_midi_common.h"
    35 }
    36 
    37 bool compareMIDIEvent(const MIDIEvent &a, const MIDIEvent &b)
    38 {
    39   return a.time < b.time;
    40 }
    41 
    42 class MidiEventsStore : public BMidi
    43 {
    44   public:
    45   MidiEventsStore()
    46   {
    47     fPlaying = false;
    48     fLoops = 0;
    49   }
    50   virtual status_t Import(SDL_RWops *rw)
    51   {
    52     fEvs = CreateMIDIEventList(rw, &fDivision);
    53     if (!fEvs) {
    54       return B_BAD_MIDI_DATA;
    55     }
    56     fTotal = 0;
    57     for (MIDIEvent *x = fEvs; x; x = x->next) fTotal++;
    58     fPos = fTotal;
    59 
    60     sort_events();
    61     return B_OK;
    62   }
    63   virtual void Run()
    64   {
    65     fPlaying = true;
    66     fPos = 0;
    67     MIDIEvent *ev = fEvs;
    68 
    69     uint32 startTime = B_NOW;
    70     while (KeepRunning())
    71     {
    72       if (!ev) {
    73         if (fLoops && fEvs) {
    74           --fLoops;
    75           fPos = 0;
    76           ev = fEvs;
    77         } else
    78           break;
    79       }
    80       SprayEvent(ev, ev->time + startTime);
    81       ev = ev->next;
    82       fPos++;
    83     }
    84     fPos = fTotal;
    85     fPlaying = false;
    86   }
    87   virtual ~MidiEventsStore()
    88   {
    89     if (!fEvs) return;
    90     FreeMIDIEventList(fEvs);
    91     fEvs = 0;
    92   }
    93 
    94   bool IsPlaying()
    95   {
    96     return fPlaying;
    97   }
    98 
    99   void SetLoops(int loops)
   100   {
   101     fLoops = loops;
   102   }
   103 
   104   protected:
   105   MIDIEvent *fEvs;
   106   Uint16 fDivision;
   107 
   108   int fPos, fTotal;
   109   int fLoops;
   110   bool fPlaying;
   111 
   112   void SprayEvent(MIDIEvent *ev, uint32 time)
   113   {
   114     switch (ev->status & 0xF0)
   115     {
   116     case B_NOTE_OFF:
   117       SprayNoteOff((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time);
   118       break;
   119     case B_NOTE_ON:
   120       SprayNoteOn((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time);
   121       break;
   122     case B_KEY_PRESSURE:
   123       SprayKeyPressure((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time);
   124       break;
   125     case B_CONTROL_CHANGE:
   126       SprayControlChange((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time);
   127       break;
   128     case B_PROGRAM_CHANGE:
   129       SprayProgramChange((ev->status & 0x0F) + 1, ev->data[0], time);
   130       break;
   131     case B_CHANNEL_PRESSURE:
   132       SprayChannelPressure((ev->status & 0x0F) + 1, ev->data[0], time);
   133       break;
   134     case B_PITCH_BEND:
   135       SprayPitchBend((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time);
   136       break;
   137     case 0xF:
   138       switch (ev->status)
   139       {
   140       case B_SYS_EX_START:
   141         SpraySystemExclusive(ev->extraData, ev->extraLen, time);
   142 	break;
   143       case B_MIDI_TIME_CODE:
   144       case B_SONG_POSITION:
   145       case B_SONG_SELECT:
   146       case B_CABLE_MESSAGE:
   147       case B_TUNE_REQUEST:
   148       case B_SYS_EX_END:
   149         SpraySystemCommon(ev->status, ev->data[0], ev->data[1], time);
   150 	break;
   151       case B_TIMING_CLOCK:
   152       case B_START:
   153       case B_STOP:
   154       case B_CONTINUE:
   155       case B_ACTIVE_SENSING:
   156         SpraySystemRealTime(ev->status, time);
   157 	break;
   158       case B_SYSTEM_RESET:
   159         if (ev->data[0] == 0x51 && ev->data[1] == 0x03)
   160 	{
   161 	  assert(ev->extraLen == 3);
   162 	  int val = (ev->extraData[0] << 16) | (ev->extraData[1] << 8) | ev->extraData[2];
   163 	  int tempo = 60000000 / val;
   164 	  SprayTempoChange(tempo, time);
   165 	}
   166 	else
   167 	{
   168 	  SpraySystemRealTime(ev->status, time);
   169 	}
   170       }
   171       break;
   172     }
   173   }
   174 
   175   void sort_events()
   176   {
   177     MIDIEvent *items = new MIDIEvent[fTotal];
   178     MIDIEvent *x = fEvs;
   179     for (int i = 0; i < fTotal; i++)
   180     {
   181       memcpy(items + i, x, sizeof(MIDIEvent));
   182       x = x->next;
   183     }
   184     std::sort(items, items + fTotal, compareMIDIEvent);
   185 
   186     x = fEvs;
   187     for (int i = 0; i < fTotal; i++)
   188     {
   189       MIDIEvent *ne = x->next;
   190       memcpy(x, items + i, sizeof(MIDIEvent));
   191       x->next = ne;
   192       x = ne;
   193     }
   194 
   195     for (x = fEvs; x && x->next; x = x->next)
   196       assert(x->time <= x->next->time);
   197 
   198     delete[] items;
   199   }
   200 };
   201 
   202 BMidiSynth synth;
   203 struct _NativeMidiSong {
   204   MidiEventsStore *store;
   205 } *currentSong = NULL;
   206 
   207 char lasterr[1024];
   208 
   209 int native_midi_detect()
   210 {
   211   status_t res = synth.EnableInput(true, false);
   212   return res == B_OK;
   213 }
   214 
   215 void native_midi_setvolume(int volume)
   216 {
   217   if (volume < 0) volume = 0;
   218   if (volume > 128) volume = 128;
   219   synth.SetVolume(volume / 128.0);
   220 }
   221 
   222 NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw, int freerw)
   223 {
   224   NativeMidiSong *song = new NativeMidiSong;
   225   song->store = new MidiEventsStore;
   226   status_t res = song->store->Import(rw);
   227 
   228   if (freerw) {
   229     SDL_RWclose(rw);
   230   }
   231   if (res != B_OK)
   232   {
   233     snprintf(lasterr, sizeof lasterr, "Cannot Import() midi file: status_t=%d", res);
   234     delete song->store;
   235     delete song;
   236     return NULL;
   237   }
   238   return song;
   239 }
   240 
   241 void native_midi_freesong(NativeMidiSong *song)
   242 {
   243   if (song == NULL) return;
   244   song->store->Stop();
   245   song->store->Disconnect(&synth);
   246   if (currentSong == song) 
   247   {
   248     currentSong = NULL;
   249   }
   250   delete song->store;
   251   delete song; song = 0;
   252 }
   253 void native_midi_start(NativeMidiSong *song, int loops)
   254 {
   255   native_midi_stop();
   256   song->store->Connect(&synth);
   257   song->store->SetLoops(loops);
   258   song->store->Start();
   259   currentSong = song;
   260 }
   261 void native_midi_stop()
   262 {
   263   if (currentSong == NULL) return;
   264   currentSong->store->Stop();
   265   currentSong->store->Disconnect(&synth);
   266   while (currentSong->store->IsPlaying())
   267     usleep(1000);
   268   currentSong = NULL;
   269 }
   270 int native_midi_active()
   271 {
   272   if (currentSong == NULL) return 0;
   273   return currentSong->store->IsPlaying();
   274 }
   275 
   276 const char* native_midi_error(void)
   277 {
   278   return lasterr;
   279 }
   280 
   281 #endif /* __HAIKU__ */