| | 1 | /* |
| | 2 | native_midi_haiku: Native Midi support on HaikuOS for the SDL_mixer library |
| | 3 | Copyright (C) 2010 Egor Suvorov |
| | 4 | |
| | 5 | This library is free software; you can redistribute it and/or |
| | 6 | modify it under the terms of the GNU Library General Public |
| | 7 | License as published by the Free Software Foundation; either |
| | 8 | version 2 of the License, or (at your option) any later version. |
| | 9 | |
| | 10 | This library is distributed in the hope that it will be useful, |
| | 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| | 13 | Library General Public License for more details. |
| | 14 | |
| | 15 | You should have received a copy of the GNU Library General Public |
| | 16 | License along with this library; if not, write to the Free |
| | 17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| | 18 | |
| | 19 | Egor Suvorov |
| | 20 | egor_suvorov@mail.ru |
| | 21 | */ |
| | 22 | #include "SDL_config.h" |
| | 23 | |
| | 24 | #ifdef __HAIKU__ |
| | 25 | #include <stdio.h> |
| | 26 | #include <stdlib.h> |
| | 27 | #include <string.h> |
| | 28 | #include <MidiStore.h> |
| | 29 | #include <MidiDefs.h> |
| | 30 | #include <MidiSynthFile.h> |
| | 31 | #include <algorithm> |
| | 32 | #include <assert.h> |
| | 33 | extern "C" { |
| | 34 | #include "native_midi.h" |
| | 35 | #include "native_midi_common.h" |
| | 36 | } |
| | 37 | |
| | 38 | bool compareMIDIEvent(const MIDIEvent &a, const MIDIEvent &b) |
| | 39 | { |
| | 40 | return a.time < b.time; |
| | 41 | } |
| | 42 | |
| | 43 | class MidiEventsStore : public BMidi |
| | 44 | { |
| | 45 | public: |
| | 46 | MidiEventsStore() |
| | 47 | { |
| | 48 | fPlaying = false; |
| | 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() && ev) |
| | 71 | { |
| | 72 | SprayEvent(ev, ev->time + startTime); |
| | 73 | ev = ev->next; |
| | 74 | fPos++; |
| | 75 | } |
| | 76 | fPos = fTotal; |
| | 77 | fPlaying = false; |
| | 78 | } |
| | 79 | virtual ~MidiEventsStore() |
| | 80 | { |
| | 81 | if (!fEvs) return; |
| | 82 | FreeMIDIEventList(fEvs); |
| | 83 | fEvs = 0; |
| | 84 | } |
| | 85 | |
| | 86 | int CurrentEvent() |
| | 87 | { |
| | 88 | return fPos; |
| | 89 | } |
| | 90 | int CountEvents() |
| | 91 | { |
| | 92 | return fTotal; |
| | 93 | } |
| | 94 | |
| | 95 | bool IsPlaying() |
| | 96 | { |
| | 97 | return fPlaying; |
| | 98 | } |
| | 99 | |
| | 100 | protected: |
| | 101 | MIDIEvent *fEvs; |
| | 102 | Uint16 fDivision; |
| | 103 | |
| | 104 | int fPos, fTotal; |
| | 105 | bool fPlaying; |
| | 106 | |
| | 107 | void SprayEvent(MIDIEvent *ev, uint32 time) |
| | 108 | { |
| | 109 | switch (ev->status & 0xF0) |
| | 110 | { |
| | 111 | case B_NOTE_OFF: |
| | 112 | SprayNoteOff((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); |
| | 113 | break; |
| | 114 | case B_NOTE_ON: |
| | 115 | SprayNoteOn((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); |
| | 116 | break; |
| | 117 | case B_KEY_PRESSURE: |
| | 118 | SprayKeyPressure((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); |
| | 119 | break; |
| | 120 | case B_CONTROL_CHANGE: |
| | 121 | SprayControlChange((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); |
| | 122 | break; |
| | 123 | case B_PROGRAM_CHANGE: |
| | 124 | SprayProgramChange((ev->status & 0x0F) + 1, ev->data[0], time); |
| | 125 | break; |
| | 126 | case B_CHANNEL_PRESSURE: |
| | 127 | SprayChannelPressure((ev->status & 0x0F) + 1, ev->data[0], time); |
| | 128 | break; |
| | 129 | case B_PITCH_BEND: |
| | 130 | SprayPitchBend((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); |
| | 131 | break; |
| | 132 | case 0xF: |
| | 133 | switch (ev->status) |
| | 134 | { |
| | 135 | case B_SYS_EX_START: |
| | 136 | SpraySystemExclusive(ev->extraData, ev->extraLen, time); |
| | 137 | break; |
| | 138 | case B_MIDI_TIME_CODE: |
| | 139 | case B_SONG_POSITION: |
| | 140 | case B_SONG_SELECT: |
| | 141 | case B_CABLE_MESSAGE: |
| | 142 | case B_TUNE_REQUEST: |
| | 143 | case B_SYS_EX_END: |
| | 144 | SpraySystemCommon(ev->status, ev->data[0], ev->data[1], time); |
| | 145 | break; |
| | 146 | case B_TIMING_CLOCK: |
| | 147 | case B_START: |
| | 148 | case B_STOP: |
| | 149 | case B_CONTINUE: |
| | 150 | case B_ACTIVE_SENSING: |
| | 151 | SpraySystemRealTime(ev->status, time); |
| | 152 | break; |
| | 153 | case B_SYSTEM_RESET: |
| | 154 | if (ev->data[0] == 0x51 && ev->data[1] == 0x03) |
| | 155 | { |
| | 156 | assert(ev->extraLen == 3); |
| | 157 | int val = (ev->extraData[0] << 16) | (ev->extraData[1] << 8) | ev->extraData[2]; |
| | 158 | int tempo = 60000000 / val; |
| | 159 | SprayTempoChange(tempo, time); |
| | 160 | } |
| | 161 | else |
| | 162 | { |
| | 163 | SpraySystemRealTime(ev->status, time); |
| | 164 | } |
| | 165 | } |
| | 166 | break; |
| | 167 | } |
| | 168 | } |
| | 169 | |
| | 170 | void sort_events() |
| | 171 | { |
| | 172 | MIDIEvent *items = new MIDIEvent[fTotal]; |
| | 173 | MIDIEvent *x = fEvs; |
| | 174 | for (int i = 0; i < fTotal; i++) |
| | 175 | { |
| | 176 | memcpy(items + i, x, sizeof(MIDIEvent)); |
| | 177 | x = x->next; |
| | 178 | } |
| | 179 | std::sort(items, items + fTotal, compareMIDIEvent); |
| | 180 | |
| | 181 | x = fEvs; |
| | 182 | for (int i = 0; i < fTotal; i++) |
| | 183 | { |
| | 184 | MIDIEvent *ne = x->next; |
| | 185 | memcpy(x, items + i, sizeof(MIDIEvent)); |
| | 186 | x->next = ne; |
| | 187 | x = ne; |
| | 188 | } |
| | 189 | |
| | 190 | for (x = fEvs; x && x->next; x = x->next) |
| | 191 | assert(x->time <= x->next->time); |
| | 192 | |
| | 193 | delete[] items; |
| | 194 | } |
| | 195 | }; |
| | 196 | |
| | 197 | BMidiSynth synth; |
| | 198 | struct _NativeMidiSong { |
| | 199 | MidiEventsStore *store; |
| | 200 | } *currentSong = NULL; |
| | 201 | |
| | 202 | char lasterr[1024]; |
| | 203 | |
| | 204 | int native_midi_detect() |
| | 205 | { |
| | 206 | status_t res = synth.EnableInput(true, false); |
| | 207 | return res == B_OK; |
| | 208 | } |
| | 209 | |
| | 210 | void native_midi_setvolume(int volume) |
| | 211 | { |
| | 212 | if (volume < 0) volume = 0; |
| | 213 | if (volume > 128) volume = 128; |
| | 214 | synth.SetVolume(volume / 128.0); |
| | 215 | } |
| | 216 | |
| | 217 | NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw) |
| | 218 | { |
| | 219 | NativeMidiSong *song = new NativeMidiSong; |
| | 220 | song->store = new MidiEventsStore; |
| | 221 | status_t res = song->store->Import(rw); |
| | 222 | |
| | 223 | if (res != B_OK) |
| | 224 | { |
| | 225 | snprintf(lasterr, sizeof lasterr, "Cannot Import() midi file: status_t=%d", res); |
| | 226 | delete song->store; |
| | 227 | delete song; |
| | 228 | return NULL; |
| | 229 | } |
| | 230 | return song; |
| | 231 | } |
| | 232 | |
| | 233 | NativeMidiSong *native_midi_loadsong(const char *midifile) |
| | 234 | { |
| | 235 | SDL_RWops *rw = SDL_RWFromFile(midifile, "rb"); |
| | 236 | if (!rw) return NULL; |
| | 237 | NativeMidiSong *song = native_midi_loadsong_RW(rw); |
| | 238 | SDL_RWclose(rw); |
| | 239 | return song; |
| | 240 | } |
| | 241 | |
| | 242 | void native_midi_freesong(NativeMidiSong *song) |
| | 243 | { |
| | 244 | if (song == NULL) return; |
| | 245 | song->store->Stop(); |
| | 246 | song->store->Disconnect(&synth); |
| | 247 | if (currentSong == song) |
| | 248 | { |
| | 249 | currentSong = NULL; |
| | 250 | } |
| | 251 | delete song->store; |
| | 252 | delete song; song = 0; |
| | 253 | } |
| | 254 | void native_midi_start(NativeMidiSong *song) |
| | 255 | { |
| | 256 | native_midi_stop(); |
| | 257 | song->store->Connect(&synth); |
| | 258 | song->store->Start(); |
| | 259 | currentSong = song; |
| | 260 | } |
| | 261 | void native_midi_stop() |