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 
compareMIDIEvent(const MIDIEvent & a,const MIDIEvent & b)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:
MidiEventsStore()45   MidiEventsStore()
46   {
47     fPlaying = false;
48     fLoops = 0;
49   }
Import(SDL_RWops * src)50   virtual status_t Import(SDL_RWops *src)
51   {
52     fEvs = CreateMIDIEventList(src, &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   }
Run()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           if (fLoops > 0) --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   }
~MidiEventsStore()87   virtual ~MidiEventsStore()
88   {
89     if (!fEvs) return;
90     FreeMIDIEventList(fEvs);
91     fEvs = 0;
92   }
93 
IsPlaying()94   bool IsPlaying()
95   {
96     return fPlaying;
97   }
98 
SetLoops(int loops)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 
SprayEvent(MIDIEvent * ev,uint32 time)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 
sort_events()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 
native_midi_detect(void)209 int native_midi_detect(void)
210 {
211   status_t res = synth.EnableInput(true, false);
212   return res == B_OK;
213 }
214 
native_midi_setvolume(int volume)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 
native_midi_loadsong_RW(SDL_RWops * src,int freesrc)222 NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *src, int freesrc)
223 {
224   NativeMidiSong *song = new NativeMidiSong;
225   song->store = new MidiEventsStore;
226   status_t res = song->store->Import(src);
227 
228   if (res != B_OK)
229   {
230     snprintf(lasterr, sizeof lasterr, "Cannot Import() midi file: status_t=%d", res);
231     delete song->store;
232     delete song;
233     return NULL;
234   }
235   else
236   {
237     if (freesrc) {
238       SDL_RWclose(src);
239     }
240   }
241   return song;
242 }
243 
native_midi_freesong(NativeMidiSong * song)244 void native_midi_freesong(NativeMidiSong *song)
245 {
246   if (song == NULL) return;
247   song->store->Stop();
248   song->store->Disconnect(&synth);
249   if (currentSong == song)
250   {
251     currentSong = NULL;
252   }
253   delete song->store;
254   delete song; song = 0;
255 }
256 
native_midi_start(NativeMidiSong * song,int loops)257 void native_midi_start(NativeMidiSong *song, int loops)
258 {
259   native_midi_stop();
260   song->store->Connect(&synth);
261   song->store->SetLoops(loops);
262   song->store->Start();
263   currentSong = song;
264 }
265 
native_midi_pause(void)266 void native_midi_pause(void)
267 {
268 }
269 
native_midi_resume(void)270 void native_midi_resume(void)
271 {
272 }
273 
native_midi_stop(void)274 void native_midi_stop(void)
275 {
276   if (currentSong == NULL) return;
277   currentSong->store->Stop();
278   currentSong->store->Disconnect(&synth);
279   while (currentSong->store->IsPlaying())
280     usleep(1000);
281   currentSong = NULL;
282 }
283 
native_midi_active(void)284 int native_midi_active(void)
285 {
286   if (currentSong == NULL) return 0;
287   return currentSong->store->IsPlaying();
288 }
289 
native_midi_error(void)290 const char* native_midi_error(void)
291 {
292   return lasterr;
293 }
294 
295 #endif /* __HAIKU__ */
296