ft2_midi.c (11112B)
1 // this implements MIDI input only! 2 3 #ifdef HAS_MIDI 4 5 // for finding memory leaks in debug mode with Visual Studio 6 #if defined _DEBUG && defined _MSC_VER 7 #include <crtdbg.h> 8 #endif 9 10 #include <stdio.h> 11 #include <stdbool.h> 12 #include "ft2_header.h" 13 #include "ft2_edit.h" 14 #include "ft2_config.h" 15 #include "ft2_gui.h" 16 #include "ft2_midi.h" 17 #include "ft2_audio.h" 18 #include "ft2_mouse.h" 19 #include "ft2_pattern_ed.h" 20 #include "ft2_structs.h" 21 #include "rtmidi/rtmidi_c.h" 22 23 // hide POSIX warnings 24 #ifdef _MSC_VER 25 #pragma warning(disable: 4996) 26 #endif 27 28 midi_t midi; // globalized 29 30 static volatile bool midiDeviceOpened; 31 static bool recMIDIValidChn = true; 32 static volatile RtMidiPtr midiInDev; 33 34 static inline void midiInSetChannel(uint8_t status) 35 { 36 recMIDIValidChn = (config.recMIDIAllChn || (status & 0xF) == config.recMIDIChn-1); 37 } 38 39 static inline void midiInKeyAction(int8_t m, uint8_t mv) 40 { 41 int16_t vol = (mv * 64 * config.recMIDIVolSens) / (127 * 100); 42 if (vol > 64) 43 vol = 64; 44 45 // FT2 bugfix: If velocity>0, and sensitivity made vol=0, set vol to 1 (prevent key off) 46 if (mv > 0 && vol == 0) 47 vol = 1; 48 49 if (mv > 0 && !config.recMIDIVelocity) 50 vol = -1; // don't record volume (velocity) 51 52 m -= 11; 53 if (config.recMIDITransp) 54 m += (int8_t)config.recMIDITranspVal; 55 56 if ((mv == 0 || vol != 0) && m > 0 && m < 96 && recMIDIValidChn) 57 recordNote(m, (int8_t)vol); 58 } 59 60 static inline void midiInControlChange(uint8_t data1, uint8_t data2) 61 { 62 if (data1 != 1) // 1 = modulation wheel 63 return; 64 65 midi.currMIDIVibDepth = data2 << 6; 66 67 if (recMIDIValidChn) // real FT2 forgot to check this here.. 68 { 69 for (uint8_t i = 0; i < song.numChannels; i++) 70 { 71 if (channel[i].midiVibDepth != 0 || editor.keyOnTab[i] != 0) 72 channel[i].midiVibDepth = midi.currMIDIVibDepth; 73 } 74 } 75 76 const uint8_t vibDepth = (midi.currMIDIVibDepth >> 9) & 0x0F; 77 if (vibDepth > 0 && recMIDIValidChn) 78 recordMIDIEffect(0x04, 0xA0 | vibDepth); 79 } 80 81 static inline void midiInPitchBendChange(uint8_t data1, uint8_t data2) 82 { 83 int16_t pitch = (int16_t)((data2 << 7) | data1) - 8192; // -8192..8191 84 pitch >>= 6; // -128..127 85 86 midi.currMIDIPitch = pitch; 87 if (recMIDIValidChn) 88 { 89 channel_t *ch = channel; 90 for (uint8_t i = 0; i < song.numChannels; i++, ch++) 91 { 92 if (ch->midiPitch != 0 || editor.keyOnTab[i] != 0) 93 ch->midiPitch = midi.currMIDIPitch; 94 } 95 } 96 } 97 98 static void midiInCallback(double timeStamp, const unsigned char *message, size_t messageSize, void *userData) 99 { 100 uint8_t byte[3]; 101 102 if (!midi.enable || messageSize < 2) 103 return; 104 105 midi.callbackBusy = true; 106 107 byte[0] = message[0]; 108 if (byte[0] > 127 && byte[0] < 240) 109 { 110 byte[1] = message[1] & 0x7F; 111 112 if (messageSize >= 3) 113 byte[2] = message[2] & 0x7F; 114 else 115 byte[2] = 0; 116 117 midiInSetChannel(byte[0]); 118 119 if (byte[0] >= 128 && byte[0] <= 128+15) midiInKeyAction(byte[1], 0); 120 else if (byte[0] >= 144 && byte[0] <= 144+15) midiInKeyAction(byte[1], byte[2]); 121 else if (byte[0] >= 176 && byte[0] <= 176+15) midiInControlChange(byte[1], byte[2]); 122 else if (byte[0] >= 224 && byte[0] <= 224+15) midiInPitchBendChange(byte[1], byte[2]); 123 } 124 125 midi.callbackBusy = false; 126 127 (void)timeStamp; 128 (void)userData; 129 } 130 131 static uint32_t getNumMidiInDevices(void) 132 { 133 if (midiInDev == NULL) 134 return 0; 135 136 return rtmidi_get_port_count(midiInDev); 137 } 138 139 static char *getMidiInDeviceName(uint32_t deviceID) 140 { 141 if (midiInDev == NULL) 142 return NULL; // MIDI not initialized 143 144 char *devStr = (char *)rtmidi_get_port_name(midiInDev, deviceID); 145 if (devStr == NULL || !midiInDev->ok) 146 return NULL; 147 148 return devStr; 149 } 150 151 void closeMidiInDevice(void) 152 { 153 if (midiDeviceOpened) 154 { 155 if (midiInDev != NULL) 156 { 157 rtmidi_in_cancel_callback(midiInDev); 158 rtmidi_close_port(midiInDev); 159 } 160 161 midiDeviceOpened = false; 162 } 163 } 164 165 void freeMidiIn(void) 166 { 167 if (midiInDev != NULL) 168 { 169 rtmidi_in_free(midiInDev); 170 midiInDev = NULL; 171 } 172 } 173 174 bool initMidiIn(void) 175 { 176 midiInDev = rtmidi_in_create_default(); 177 if (midiInDev == NULL) 178 return false; 179 180 if (!midiInDev->ok) 181 { 182 rtmidi_in_free(midiInDev); 183 midiInDev = NULL; 184 return false; 185 } 186 187 return true; 188 } 189 190 bool openMidiInDevice(uint32_t deviceID) 191 { 192 if (midiDeviceOpened || midiInDev == NULL || midi.numInputDevices == 0) 193 return false; 194 195 rtmidi_open_port(midiInDev, deviceID, "FT2 Clone MIDI Port"); 196 if (!midiInDev->ok) 197 return false; 198 199 rtmidi_in_set_callback(midiInDev, midiInCallback, NULL); 200 if (!midiInDev->ok) 201 { 202 rtmidi_close_port(midiInDev); 203 return false; 204 } 205 206 rtmidi_in_ignore_types(midiInDev, true, true, true); 207 208 midiDeviceOpened = true; 209 return true; 210 } 211 212 void recordMIDIEffect(uint8_t efx, uint8_t efxData) 213 { 214 // only handle this in record mode 215 if (!midi.enable || (playMode != PLAYMODE_RECSONG && playMode != PLAYMODE_RECPATT)) 216 return; 217 218 if (config.multiRec) 219 { 220 note_t *p = &pattern[editor.editPattern][editor.row * MAX_CHANNELS]; 221 for (int32_t i = 0; i < song.numChannels; i++, p++) 222 { 223 if (config.multiRecChn[i] && !editor.channelMuted[i]) 224 { 225 if (!allocatePattern(editor.editPattern)) 226 return; 227 228 if (p->efx == 0) 229 { 230 p->efx = efx; 231 p->efxData = efxData; 232 setSongModifiedFlag(); 233 } 234 } 235 } 236 } 237 else 238 { 239 if (!allocatePattern(editor.editPattern)) 240 return; 241 242 note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch]; 243 if (p->efx != efx || p->efxData != efxData) 244 setSongModifiedFlag(); 245 246 p->efx = efx; 247 p->efxData = efxData; 248 } 249 } 250 251 bool saveMidiInputDeviceToConfig(void) 252 { 253 if (!midi.initThreadDone || midiInDev == NULL || !midiDeviceOpened) 254 return false; 255 256 const uint32_t numDevices = getNumMidiInDevices(); 257 if (numDevices == 0) 258 return false; 259 260 char *midiInStr = getMidiInDeviceName(midi.inputDevice); 261 if (midiInStr == NULL) 262 return false; 263 264 FILE *f = UNICHAR_FOPEN(editor.midiConfigFileLocationU, "w"); 265 if (f == NULL) 266 { 267 free(midiInStr); 268 return false; 269 } 270 271 fputs(midiInStr, f); 272 free(midiInStr); 273 274 fclose(f); 275 return true; 276 } 277 278 bool setMidiInputDeviceFromConfig(void) 279 { 280 uint32_t i; 281 282 if (midiInDev == NULL || editor.midiConfigFileLocationU == NULL) 283 goto setDefMidiInputDev; 284 285 const uint32_t numDevices = getNumMidiInDevices(); 286 if (numDevices == 0) 287 goto setDefMidiInputDev; 288 289 FILE *f = UNICHAR_FOPEN(editor.midiConfigFileLocationU, "r"); 290 if (f == NULL) 291 goto setDefMidiInputDev; 292 293 char *devString = (char *)malloc(1024+2); 294 if (devString == NULL) 295 { 296 fclose(f); 297 goto setDefMidiInputDev; 298 } 299 devString[0] = '\0'; 300 301 if (fgets(devString, 1024, f) == NULL) 302 { 303 fclose(f); 304 free(devString); 305 goto setDefMidiInputDev; 306 } 307 308 fclose(f); 309 310 // scan for device in list 311 char *midiInStr = NULL; 312 for (i = 0; i < numDevices; i++) 313 { 314 midiInStr = getMidiInDeviceName(i); 315 if (midiInStr == NULL) 316 continue; 317 318 if (!_stricmp(devString, midiInStr)) 319 break; // device matched 320 321 free(midiInStr); 322 midiInStr = NULL; 323 } 324 325 free(devString); 326 327 // device not found in list, set default 328 if (i == numDevices) 329 goto setDefMidiInputDev; 330 331 if (midi.inputDeviceName != NULL) 332 { 333 free(midi.inputDeviceName); 334 midi.inputDeviceName = NULL; 335 } 336 337 midi.inputDevice = i; 338 midi.inputDeviceName = midiInStr; 339 midi.numInputDevices = numDevices; 340 341 return true; 342 343 // couldn't load device, set default 344 setDefMidiInputDev: 345 if (midi.inputDeviceName != NULL) 346 { 347 free(midi.inputDeviceName); 348 midi.inputDeviceName = NULL; 349 } 350 351 midi.inputDevice = 0; 352 midi.inputDeviceName = strdup("Error configuring MIDI..."); 353 midi.numInputDevices = 1; 354 355 return false; 356 } 357 358 void freeMidiInputDeviceList(void) 359 { 360 for (int32_t i = 0; i < MAX_MIDI_DEVICES; i++) 361 { 362 if (midi.inputDeviceNames[i] != NULL) 363 { 364 free(midi.inputDeviceNames[i]); 365 midi.inputDeviceNames[i] = NULL; 366 } 367 } 368 369 midi.numInputDevices = 0; 370 } 371 372 void rescanMidiInputDevices(void) 373 { 374 freeMidiInputDeviceList(); 375 376 midi.numInputDevices = getNumMidiInDevices(); 377 if (midi.numInputDevices > MAX_MIDI_DEVICES) 378 midi.numInputDevices = MAX_MIDI_DEVICES; 379 380 for (uint32_t i = 0; i < midi.numInputDevices; i++) 381 { 382 char *deviceName = getMidiInDeviceName(i); 383 if (deviceName == NULL) 384 { 385 if (midi.numInputDevices > 0) 386 midi.numInputDevices--; // hide device 387 388 continue; 389 } 390 391 midi.inputDeviceNames[i] = deviceName; 392 } 393 394 setScrollBarEnd(SB_MIDI_INPUT_SCROLL, midi.numInputDevices); 395 setScrollBarPos(SB_MIDI_INPUT_SCROLL, 0, false); 396 } 397 398 void drawMidiInputList(void) 399 { 400 clearRect(114, 4, 365, 165); 401 402 if (!midi.initThreadDone || midiInDev == NULL || midi.numInputDevices == 0) 403 { 404 textOut(114, 4 + (0 * 11), PAL_FORGRND, "No MIDI input devices found!"); 405 textOut(114, 4 + (1 * 11), PAL_FORGRND, "Either wait a few seconds for MIDI to initialize, or restart the"); 406 textOut(114, 4 + (2 * 11), PAL_FORGRND, "tracker if you recently plugged in a MIDI device."); 407 return; 408 } 409 410 for (uint16_t i = 0; i < 15; i++) 411 { 412 uint32_t deviceEntry = getScrollBarPos(SB_MIDI_INPUT_SCROLL) + i; 413 if (deviceEntry > MAX_MIDI_DEVICES) 414 deviceEntry = MAX_MIDI_DEVICES; 415 416 if (deviceEntry < midi.numInputDevices) 417 { 418 if (midi.inputDeviceNames[deviceEntry] == NULL) 419 continue; 420 421 const uint16_t y = 4 + (i * 11); 422 423 if (midi.inputDeviceName != NULL) 424 { 425 if (_stricmp(midi.inputDeviceName, midi.inputDeviceNames[deviceEntry]) == 0) 426 fillRect(114, y, 365, 10, PAL_BOXSLCT); // selection background color 427 } 428 429 char *tmpString = utf8ToCp850(midi.inputDeviceNames[deviceEntry], true); 430 if (tmpString != NULL) 431 { 432 textOutClipX(114, y, PAL_FORGRND, tmpString, 479); 433 free(tmpString); 434 } 435 } 436 } 437 } 438 439 void scrollMidiInputDevListUp(void) 440 { 441 scrollBarScrollUp(SB_MIDI_INPUT_SCROLL, 1); 442 } 443 444 void scrollMidiInputDevListDown(void) 445 { 446 scrollBarScrollDown(SB_MIDI_INPUT_SCROLL, 1); 447 } 448 449 void sbMidiInputSetPos(uint32_t pos) 450 { 451 if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_MIDI_INPUT) 452 drawMidiInputList(); 453 454 (void)pos; 455 } 456 457 bool testMidiInputDeviceListMouseDown(void) 458 { 459 if (!ui.configScreenShown || editor.currConfigScreen != CONFIG_SCREEN_MIDI_INPUT) 460 return false; 461 462 if (mouse.x < 114 || mouse.x > 479 || mouse.y < 4 || mouse.y > 166) 463 return false; // we didn't click inside the list area 464 465 if (!midi.initThreadDone) 466 return true; 467 468 uint32_t deviceNum = (uint32_t)scrollBars[SB_MIDI_INPUT_SCROLL].pos + ((mouse.y - 4) / 11); 469 if (deviceNum > MAX_MIDI_DEVICES) 470 deviceNum = MAX_MIDI_DEVICES; 471 472 if (midi.numInputDevices == 0 || deviceNum >= midi.numInputDevices) 473 return true; 474 475 if (midi.inputDeviceName != NULL) 476 { 477 if (!_stricmp(midi.inputDeviceName, midi.inputDeviceNames[deviceNum])) 478 return true; // we clicked the currently selected device, do nothing 479 480 free(midi.inputDeviceName); 481 midi.inputDeviceName = NULL; 482 } 483 484 midi.inputDeviceName = strdup(midi.inputDeviceNames[deviceNum]); 485 midi.inputDevice = deviceNum; 486 487 closeMidiInDevice(); 488 freeMidiIn(); 489 initMidiIn(); 490 openMidiInDevice(midi.inputDevice); 491 492 drawMidiInputList(); 493 return true; 494 } 495 496 int32_t initMidiFunc(void *ptr) 497 { 498 initMidiIn(); 499 setMidiInputDeviceFromConfig(); 500 openMidiInDevice(midi.inputDevice); 501 midi.rescanDevicesFlag = true; 502 midi.initThreadDone = true; 503 504 return true; 505 (void)ptr; 506 } 507 508 #else 509 typedef int prevent_compiler_warning; // kludge: prevent warning about empty .c file if HAS_MIDI is not defined 510 #endif