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