00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #ifdef HAVE_CONFIG_H
00020 # include <config.h>
00021 #endif
00022
00023
00024
00025 #if defined(WIN32) && !defined(UNDER_CE)
00026
00027 #ifndef WIN32_LEAN_AND_MEAN
00028 #define WIN32_LEAN_AND_MEAN
00029 #endif
00030
00031
00032 #define MMNODRV // Installable driver support
00033 #define MMNOSOUND // Sound support
00034 #define MMNOWAVE // Waveform support
00035 #define MMNOAUX // Auxiliary audio support
00036 #define MMNOMIXER // Mixer support
00037 #define MMNOTIMER // Timer support
00038 #define MMNOJOY // Joystick support
00039 #define MMNOMCI // MCI support
00040 #define MMNOMMIO // Multimedia file I/O support
00041
00042 #include <windows.h>
00043 #include <mmsystem.h>
00044 #include <winbase.h>
00045
00046 #include <string>
00047
00048 #include "win_midiout.h"
00049 #include "../xmidi.h"
00050
00051 #include "../../files/utils.h"
00052 #include "../../conf/Configuration.h"
00053
00054 extern Configuration *config;
00055
00056 #define W32MO_THREAD_COM_READY 0
00057 #define W32MO_THREAD_COM_PLAY 1
00058 #define W32MO_THREAD_COM_STOP 2
00059 #define W32MO_THREAD_COM_INIT 3
00060 #define W32MO_THREAD_COM_INIT_FAILED 4
00061 #define W32MO_THREAD_COM_EXIT -1
00062
00063 const unsigned short Windows_MidiOut::centre_value = 0x2000;
00064 const unsigned char Windows_MidiOut::fine_value = centre_value & 127;
00065 const unsigned char Windows_MidiOut::coarse_value = centre_value >> 7;
00066 const unsigned short Windows_MidiOut::combined_value = (coarse_value << 8) | fine_value;
00067
00068
00069
00070 #ifdef DO_SMP_TEST
00071 #define giveinfo() std::cerr << __FILE__ << ":" << __LINE__ << std::endl; std::cerr.flush();
00072 #else
00073 #define giveinfo()
00074 #endif
00075
00076 using std::string;
00077 using std::cout;
00078 using std::cerr;
00079 using std::endl;
00080
00081 Windows_MidiOut::Windows_MidiOut() : dev_num(-1)
00082 {
00083 giveinfo();
00084 InterlockedExchange (&playing, false);
00085 InterlockedExchange (&s_playing, false);
00086 InterlockedExchange (&is_available, false);
00087 giveinfo();
00088 init_device();
00089 giveinfo();
00090 }
00091
00092 Windows_MidiOut::~Windows_MidiOut()
00093 {
00094 giveinfo();
00095 if (!is_available) return;
00096
00097 giveinfo();
00098 while (thread_com != W32MO_THREAD_COM_READY) Sleep (1);
00099
00100 giveinfo();
00101 InterlockedExchange (&thread_com, W32MO_THREAD_COM_EXIT);
00102
00103 giveinfo();
00104 int count = 0;
00105
00106 giveinfo();
00107 while (count < 100)
00108 {
00109 giveinfo();
00110 DWORD code;
00111 GetExitCodeThread (thread_handle, &code);
00112
00113 giveinfo();
00114
00115 if (code == STILL_ACTIVE) Sleep (1);
00116 else break;
00117 giveinfo();
00118
00119 count++;
00120 }
00121
00122
00123 giveinfo();
00124 if (count == 100 && is_available)
00125 TerminateThread (thread_handle, 1);
00126
00127 giveinfo();
00128 InterlockedExchange (&is_available, false);
00129 giveinfo();
00130 }
00131
00132 void Windows_MidiOut::init_device()
00133 {
00134 string s;
00135
00136
00137 giveinfo();
00138 InterlockedExchange (&thread_com, W32MO_THREAD_COM_INIT);
00139
00140
00141 config->value("config/audio/midi/win32_device", dev_num, -1);
00142
00143 giveinfo();
00144 thread_handle = (HANDLE*) CreateThread (NULL, 0, thread_start, this, 0, &thread_id);
00145
00146 giveinfo();
00147 while (thread_com == W32MO_THREAD_COM_INIT) Sleep (1);
00148
00149
00150 config->set("config/audio/midi/win32_device", dev_num, true);
00151
00152 giveinfo();
00153 if (thread_com == W32MO_THREAD_COM_INIT_FAILED) cerr << "Failure to initialize midi playing thread" << endl;
00154 giveinfo();
00155 }
00156
00157 DWORD __stdcall Windows_MidiOut::thread_start(void *data)
00158 {
00159 giveinfo();
00160 Windows_MidiOut *ptr=static_cast<Windows_MidiOut *>(data);
00161 giveinfo();
00162 return ptr->thread_main();
00163 }
00164
00165 DWORD Windows_MidiOut::thread_main()
00166 {
00167 int i;
00168 thread_data = NULL;
00169 giveinfo();
00170 InterlockedExchange (&playing, false);
00171 InterlockedExchange (&s_playing, false);
00172
00173 giveinfo();
00174
00175
00176 MIDIOUTCAPS caps;
00177 signed long dev_count = (signed long) midiOutGetNumDevs();
00178 std::cout << dev_count << " Midi Devices Detected" << endl;
00179 std::cout << "Listing midi devices:" << endl;
00180
00181 for (i = -1; i < dev_count; i++)
00182 {
00183 midiOutGetDevCaps ((UINT) i, &caps, sizeof(caps));
00184 std::cout << i << ": " << caps.szPname << endl;
00185 }
00186
00187 if (dev_num < -1 || dev_num >= dev_count)
00188 {
00189 std::cerr << "Warning Midi device in config is out of range." << endl;
00190 dev_num = -1;
00191 }
00192 midiOutGetDevCaps ((UINT) dev_num, &caps, sizeof(caps));
00193 std::cout << "Using device " << dev_num << ": "<< caps.szPname << endl;
00194
00195 UINT mmsys_err = midiOutOpen (&midi_port, dev_num, 0, 0, 0);
00196
00197 giveinfo();
00198 if (mmsys_err != MMSYSERR_NOERROR)
00199 {
00200 TCHAR buf[512];
00201
00202 giveinfo();
00203 mciGetErrorString(mmsys_err, buf, 512);
00204
00205 #ifdef UNICODE
00206 std::wcerr << TEXT("Unable to open device: ") << buf << endl;
00207 #else
00208 cerr << "Unable to open device: " << buf << endl;
00209 #endif
00210 giveinfo();
00211 InterlockedExchange (&thread_com, W32MO_THREAD_COM_INIT_FAILED);
00212 giveinfo();
00213 return 1;
00214 }
00215 giveinfo();
00216 InterlockedExchange (&is_available, true);
00217
00218
00219 giveinfo();
00220 SetThreadPriority (thread_handle, THREAD_PRIORITY_TIME_CRITICAL);
00221
00222 giveinfo();
00223 InterlockedExchange (&thread_com, W32MO_THREAD_COM_READY);
00224 InterlockedExchange (&sfx_com, W32MO_THREAD_COM_READY);
00225
00226 giveinfo();
00227 thread_play();
00228 giveinfo();
00229
00230 giveinfo();
00231 midiOutClose (midi_port);
00232 giveinfo();
00233 InterlockedExchange (&is_available, false);
00234 giveinfo();
00235 return 0;
00236 }
00237
00238 void Windows_MidiOut::thread_play ()
00239 {
00240 int repeat = false;
00241 uint32 aim = 0;
00242 sint32 diff = 0;
00243 uint32 last_tick = 0;
00244 XMIDIEventList *evntlist = NULL;
00245 midi_event *event = NULL;
00246 NoteStack notes_on;
00247 midi_event *note = NULL;
00248
00249
00250
00251
00252
00253
00254 midi_event *loop_event[XMIDI_MAX_FOR_LOOP_COUNT];
00255
00256
00257 int loop_count[XMIDI_MAX_FOR_LOOP_COUNT];
00258
00259
00260 int loop_num = -1;
00261
00262 giveinfo();
00263
00264 int s_track = 0;
00265 uint32 s_aim = 0;
00266 sint32 s_diff = 0;
00267 uint32 s_last_tick = 0;
00268 NoteStack s_notes_on;
00269 XMIDIEventList *s_evntlist = NULL;
00270 midi_event *s_event = NULL;
00271
00272 giveinfo();
00273
00274
00275 while (1)
00276 {
00277 if (thread_com == W32MO_THREAD_COM_EXIT && !playing && !s_playing) break;
00278
00279 if (thread_com == W32MO_THREAD_COM_STOP)
00280 {
00281 giveinfo();
00282 InterlockedExchange (&playing, FALSE);
00283 InterlockedExchange (&thread_com, W32MO_THREAD_COM_READY);
00284
00285
00286 while (note = notes_on.Pop())
00287 midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
00288
00289 giveinfo();
00290
00291 for (int i = 0; i < 16; i++) reset_channel (i);
00292 midiOutReset (midi_port);
00293 giveinfo();
00294 if (evntlist) evntlist->DecerementCounter();
00295 giveinfo();
00296 evntlist = NULL;
00297 event = NULL;
00298 giveinfo();
00299
00300
00301
00302 loop_num = -1;
00303
00304 wmoInitClock ();
00305 last_tick = 0;
00306 }
00307
00308
00309 while (note = notes_on.PopTime(wmoGetRealTime()))
00310 midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
00311
00312 while (note = s_notes_on.PopTime(wmoGetRealTime()))
00313 midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
00314
00315 while (event && thread_com != W32MO_THREAD_COM_STOP)
00316 {
00317 aim = (event->time-last_tick)*50;
00318 diff = aim - wmoGetTime ();
00319
00320 if (diff > 0) break;
00321
00322 last_tick = event->time;
00323 wmoAddOffset(aim);
00324
00325
00326 if ((event->status >> 4) == MIDI_STATUS_CONTROLLER && event->data[0] == XMIDI_CONTROLLER_FOR_LOOP)
00327 {
00328 if (loop_num < XMIDI_MAX_FOR_LOOP_COUNT) loop_num++;
00329
00330 loop_count[loop_num] = event->data[1];
00331 loop_event[loop_num] = event;
00332
00333 }
00334 else if ((event->status >> 4) == MIDI_STATUS_CONTROLLER && event->data[0] == XMIDI_CONTROLLER_NEXT_BREAK)
00335 {
00336 if (loop_num != -1)
00337 {
00338 if (event->data[1] < 64)
00339 {
00340 loop_num--;
00341 }
00342 }
00343 event = NULL;
00344
00345 }
00346 else if (event->status < 0xF0)
00347 {
00348 int type = event->status >> 4;
00349
00350 if ((type != MIDI_STATUS_NOTE_ON || event->data[1]) && type != MIDI_STATUS_NOTE_OFF) {
00351 if (type == MIDI_STATUS_NOTE_ON) {
00352 notes_on.Remove(event);
00353 notes_on.Push (event, event->duration * 50 + wmoGetStart());
00354 }
00355
00356 midiOutShortMsg (midi_port, event->status + (event->data[0] << 8) + (event->data[1] << 16));
00357 }
00358 }
00359
00360 if (event) event = event->next;
00361
00362 if (!event || thread_com != W32MO_THREAD_COM_READY)
00363 {
00364 bool clean = !repeat || (thread_com != W32MO_THREAD_COM_READY) || last_tick == 0;
00365
00366 if (clean)
00367 {
00368 InterlockedExchange (&playing, FALSE);
00369 if (thread_com == W32MO_THREAD_COM_STOP)
00370 InterlockedExchange (&thread_com, W32MO_THREAD_COM_READY);
00371
00372
00373 while (note = notes_on.Pop())
00374 midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
00375
00376
00377 for (int i = 0; i < 16; i++) reset_channel (i);
00378 midiOutReset (midi_port);
00379 if (evntlist) evntlist->DecerementCounter();
00380 evntlist = NULL;
00381 event = NULL;
00382
00383 loop_num = -1;
00384 wmoInitClock ();
00385 }
00386
00387 last_tick = 0;
00388
00389 if (evntlist)
00390 {
00391 if (loop_num == -1) event = evntlist->events;
00392 else
00393 {
00394 event = loop_event[loop_num]->next;
00395 last_tick = loop_event[loop_num]->time;
00396
00397 if (loop_count[loop_num])
00398 if (!--loop_count[loop_num])
00399 loop_num--;
00400 }
00401 }
00402 }
00403 }
00404
00405
00406
00407
00408 if (thread_com == W32MO_THREAD_COM_PLAY)
00409 {
00410
00411 while (note = notes_on.Pop())
00412 midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
00413
00414
00415 giveinfo();
00416 for (int i = 0; i < 16; i++) reset_channel (i);
00417 midiOutReset (midi_port);
00418
00419 if (evntlist) evntlist->DecerementCounter();
00420 evntlist = NULL;
00421 event = NULL;
00422 InterlockedExchange (&playing, FALSE);
00423
00424
00425 giveinfo();
00426 while (!thread_data) Sleep(1);
00427
00428 giveinfo();
00429 evntlist = thread_data->list;
00430 repeat = thread_data->repeat;
00431
00432 giveinfo();
00433 InterlockedExchange ((LONG*) &thread_data, (LONG) NULL);
00434 giveinfo();
00435 InterlockedExchange (&thread_com, W32MO_THREAD_COM_READY);
00436
00437 giveinfo();
00438 if (evntlist) event = evntlist->events;
00439 else event = 0;
00440
00441 giveinfo();
00442 last_tick = 0;
00443
00444 giveinfo();
00445 wmoInitClock ();
00446
00447
00448 loop_num = -1;
00449
00450 giveinfo();
00451 InterlockedExchange (&playing, true);
00452 }
00453
00454 if (s_event)
00455 {
00456 s_aim = (s_event->time-s_last_tick)*50;
00457 s_diff = s_aim - wmoGetSFXTime ();
00458 }
00459 else
00460 s_diff = 1;
00461
00462 if (s_diff <= 0)
00463 {
00464 s_last_tick = s_event->time;
00465 wmoAddSFXOffset(s_aim);
00466
00467
00468 if ((s_event->status >> 4) != MIDI_STATUS_SYSEX)
00469 {
00470 int type = s_event->status >> 4;
00471
00472 if ((type != MIDI_STATUS_NOTE_ON || s_event->data[1]) && type != MIDI_STATUS_NOTE_OFF) {
00473 if (type == MIDI_STATUS_NOTE_ON) {
00474 s_notes_on.Remove(s_event);
00475 s_notes_on.Push (s_event, s_event->duration * 50 + wmoGetSFXStart());
00476 }
00477
00478 midiOutShortMsg (midi_port, s_event->status + (s_event->data[0] << 8) + (s_event->data[1] << 16));
00479 }
00480 s_track |= 1 << (s_event->status & 0xF);
00481 }
00482
00483 s_event = s_event->next;
00484 }
00485 if (s_evntlist && (!s_event || thread_com == W32MO_THREAD_COM_EXIT || sfx_com != W32MO_THREAD_COM_READY))
00486 {
00487
00488 while (note = s_notes_on.Pop())
00489 midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
00490
00491
00492 for (int i = 0; i < 16; i++) if ((s_track >> i)&1) reset_channel (i);
00493
00494 s_evntlist->DecerementCounter();
00495 s_evntlist = NULL;
00496 s_event = NULL;
00497 InterlockedExchange (&s_playing, false);
00498 if (sfx_com != W32MO_THREAD_COM_PLAY) InterlockedExchange (&sfx_com, W32MO_THREAD_COM_READY);
00499 }
00500
00501
00502
00503 if (!s_evntlist && sfx_com == W32MO_THREAD_COM_PLAY)
00504 {
00505 giveinfo();
00506 cout << "Play sfx command" << endl;
00507
00508
00509 while (note = s_notes_on.Pop())
00510 midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
00511
00512
00513 while (!sfx_data) Sleep(1);
00514
00515 giveinfo();
00516 s_evntlist = sfx_data->list;
00517
00518 giveinfo();
00519 InterlockedExchange ((LONG*) &sfx_data, (LONG) NULL);
00520 InterlockedExchange (&sfx_com, W32MO_THREAD_COM_READY);
00521 giveinfo();
00522
00523 if (s_evntlist) s_event = s_evntlist->events;
00524 else s_event = 0;
00525
00526 giveinfo();
00527
00528 s_last_tick = 0;
00529
00530 giveinfo();
00531 wmoInitSFXClock ();
00532
00533 giveinfo();
00534 InterlockedExchange (&s_playing, true);
00535
00536 giveinfo();
00537
00538 s_track = 0;
00539 }
00540
00541 if (event)
00542 {
00543 aim = (event->time-last_tick)*50;
00544 diff = aim - wmoGetTime ();
00545 }
00546 else
00547 diff = 6;
00548
00549 if (s_event)
00550 {
00551 s_aim = (s_event->time-s_last_tick)*50;
00552 s_diff = s_aim - wmoGetSFXTime ();
00553 }
00554 else
00555 s_diff = 6;
00556
00557
00558
00559 if (diff > 5 && s_diff > 5) Sleep (1);
00560 }
00561
00562 while (note = notes_on.Pop())
00563 midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
00564
00565
00566 while (note = s_notes_on.PopTime(wmoGetRealTime()))
00567 midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
00568
00569 if (evntlist) evntlist->DecerementCounter();
00570 evntlist = NULL;
00571 if (s_evntlist) s_evntlist->DecerementCounter();
00572 s_evntlist = NULL;
00573 for (int i = 0; i < 16; i++) reset_channel (i);
00574 midiOutReset (midi_port);
00575 }
00576
00577 void Windows_MidiOut::reset_channel (int i)
00578 {
00579
00580 midiOutShortMsg (midi_port, i | (MIDI_STATUS_PITCH_WHEEL << 4) | (combined_value << 8));
00581
00582
00583 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (121 << 8));
00584
00585
00586 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (123 << 8));
00587
00588
00589 midiOutShortMsg (midi_port, i | (MIDI_STATUS_PROG_CHANGE << 4) | (0 << 8));
00590 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (0 << 8));
00591 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (32 << 8));
00592
00593
00594 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (1 << 8) | (coarse_value << 16));
00595 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (33 << 8) | (fine_value << 16));
00596
00597
00598 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (7 << 8) | (coarse_value << 16));
00599 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (39 << 8) | (fine_value << 16));
00600
00601
00602 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (8 << 8) | (coarse_value << 16));
00603 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (40 << 8) | (fine_value << 16));
00604
00605
00606 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (10 << 8) | (coarse_value << 16));
00607 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (42 << 8) | (fine_value << 16));
00608
00609
00610 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (91 << 8));
00611
00612
00613 midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (93 << 8));
00614 }
00615
00616 void Windows_MidiOut::start_track (XMIDIEventList *xmidi, bool repeat)
00617 {
00618 giveinfo();
00619 if (!is_available)
00620 init_device();
00621
00622 giveinfo();
00623 if (!is_available)
00624 return;
00625
00626 giveinfo();
00627 while (thread_com != W32MO_THREAD_COM_READY) Sleep (1);
00628
00629 giveinfo();
00630 xmidi->IncerementCounter();
00631 data.list = xmidi;
00632 data.repeat = repeat;
00633
00634
00635 giveinfo();
00636 InterlockedExchange ((LONG*) &thread_data, (LONG) &data);
00637 giveinfo();
00638 InterlockedExchange (&thread_com, W32MO_THREAD_COM_PLAY);
00639 giveinfo();
00640 }
00641
00642 void Windows_MidiOut::start_sfx(XMIDIEventList *xmidi)
00643 {
00644 giveinfo();
00645 if (!is_available)
00646 init_device();
00647
00648 giveinfo();
00649 if (!is_available)
00650 return;
00651
00652 giveinfo();
00653 while (sfx_com != W32MO_THREAD_COM_READY) Sleep (1);
00654
00655 giveinfo();
00656 xmidi->IncerementCounter();
00657 sdata.list = xmidi;
00658 sdata.repeat;
00659
00660 giveinfo();
00661 InterlockedExchange ((LONG*) &sfx_data, (LONG) &sdata);
00662 giveinfo();
00663 InterlockedExchange (&sfx_com, W32MO_THREAD_COM_PLAY);
00664 giveinfo();
00665 }
00666
00667
00668 void Windows_MidiOut::stop_track(void)
00669 {
00670 giveinfo();
00671 if (!is_available)
00672 return;
00673
00674 giveinfo();
00675 if (!playing) return;
00676
00677 giveinfo();
00678 while (thread_com != W32MO_THREAD_COM_READY) Sleep (1);
00679 giveinfo();
00680 InterlockedExchange (&thread_com, W32MO_THREAD_COM_STOP);
00681 giveinfo();
00682 while (thread_com != W32MO_THREAD_COM_READY) Sleep (1);
00683 giveinfo();
00684 }
00685
00686 void Windows_MidiOut::stop_sfx(void)
00687 {
00688 giveinfo();
00689 if (!is_available)
00690 return;
00691
00692 giveinfo();
00693 if (!s_playing) return;
00694
00695 giveinfo();
00696 while (sfx_com != W32MO_THREAD_COM_READY) Sleep (1);
00697 giveinfo();
00698 InterlockedExchange (&sfx_com, W32MO_THREAD_COM_STOP);
00699 giveinfo();
00700 }
00701
00702 bool Windows_MidiOut::is_playing(void)
00703 {
00704 giveinfo();
00705 return playing!=0;
00706 }
00707
00708 const char *Windows_MidiOut::copyright(void)
00709 {
00710 giveinfo();
00711 return "Internal Win32 Midiout Midi Player for Exult and Pentagram.";
00712 }
00713
00714
00715 #endif