# win32_m.cpp

    00001 /* $Id: win32_m.cpp 14426 2008-10-01 11:48:57Z rubidium $ */
    00002
    00003 #include "../stdafx.h"
    00004 #include "win32_m.h"
    00005 #include <windows.h>
    00006 #include <mmsystem.h>
    00007
    00008 static struct {
    00009   bool stop_song;
    00010   bool terminate;
    00011   bool playing;
    00012   int new_vol;
    00013   HANDLE wait_obj;
    00014   HANDLE thread;
    00015   UINT_PTR devid;
    00016   char start_song[MAX_PATH];
    00017 } _midi;
    00018
    00019 static FMusicDriver_Win32 iFMusicDriver_Win32;
    00020
    00021 void MusicDriver_Win32::PlaySong(const char *filename)
    00022 {
    00023   assert(filename != NULL);
    00024   strcpy(_midi.start_song, filename);
    00025   _midi.playing = true;
    00026   _midi.stop_song = false;
    00027   SetEvent(_midi.wait_obj);
    00028 }
    00029
    00030 void MusicDriver_Win32::StopSong()
    00031 {
    00032   if (_midi.playing) {
    00033     _midi.stop_song = true;
    00034     _midi.start_song[0] = '\0';
    00035     SetEvent(_midi.wait_obj);
    00036   }
    00037 }
    00038
    00039 bool MusicDriver_Win32::IsSongPlaying()
    00040 {
    00041   return _midi.playing;
    00042 }
    00043
    00044 void MusicDriver_Win32::SetVolume(byte vol)
    00045 {
    00046   _midi.new_vol = vol;
    00047   SetEvent(_midi.wait_obj);
    00048 }
    00049
    00050 static MCIERROR CDECL MidiSendCommand(const TCHAR* cmd, ...)
    00051 {
    00052   va_list va;
    00053   TCHAR buf[512];
    00054
    00055   va_start(va, cmd);
    00056   _vsntprintf(buf, lengthof(buf), cmd, va);
    00057   va_end(va);
    00058   return mciSendString(buf, NULL, 0, 0);
    00059 }
    00060
    00061 static bool MidiIntPlaySong(const char *filename)
    00062 {
    00063   MidiSendCommand(_T("close all"));
    00064
    00065   if (MidiSendCommand(_T("open \"%s\" type sequencer alias song"), OTTD2FS(filename)) != 0) {
    00066     /* Let's try the "short name" */
    00067     TCHAR buf[MAX_PATH];
    00068     if (GetShortPathName(OTTD2FS(filename), buf, MAX_PATH) == 0) return false;
    00069     if (MidiSendCommand(_T("open \"%s\" type sequencer alias song"), buf) != 0) return false;
    00070   }
    00071
    00072   return MidiSendCommand(_T("play song from 0")) == 0;
    00073 }
    00074
    00075 static void MidiIntStopSong()
    00076 {
    00077   MidiSendCommand(_T("close all"));
    00078 }
    00079
    00080 static void MidiIntSetVolume(int vol)
    00081 {
    00082   DWORD v = (vol * 65535 / 127);
    00083   midiOutSetVolume((HMIDIOUT)_midi.devid, v + (v << 16));
    00084 }
    00085
    00086 static bool MidiIntIsSongPlaying()
    00087 {
    00088   char buf[16];
    00089   mciSendStringA("status song mode", buf, sizeof(buf), 0);
    00090   return strcmp(buf, "playing") == 0 || strcmp(buf, "seeking") == 0;
    00091 }
    00092
    00093 static DWORD WINAPI MidiThread(LPVOID arg)
    00094 {
    00095   do {
    00096     char *s;
    00097     int vol;
    00098
    00099     vol = _midi.new_vol;
    00100     if (vol != -1) {
    00101       _midi.new_vol = -1;
    00102       MidiIntSetVolume(vol);
    00103     }
    00104
    00105     s = _midi.start_song;
    00106     if (s[0] != '\0') {
    00107       _midi.playing = MidiIntPlaySong(s);
    00108       s[0] = '\0';
    00109
    00110       // Delay somewhat in case we don't manage to play.
    00111       if (!_midi.playing) WaitForMultipleObjects(1, &_midi.wait_obj, FALSE, 5000);
    00112     }
    00113
    00114     if (_midi.stop_song && _midi.playing) {
    00115       _midi.stop_song = false;
    00116       _midi.playing = false;
    00117       MidiIntStopSong();
    00118     }
    00119
    00120     if (_midi.playing && !MidiIntIsSongPlaying()) _midi.playing = false;
    00121
    00122     WaitForMultipleObjects(1, &_midi.wait_obj, FALSE, 1000);
    00123   } while (!_midi.terminate);
    00124
    00125   MidiIntStopSong();
    00126   return 0;
    00127 }
    00128
    00129 const char *MusicDriver_Win32::Start(const char * const *parm)
    00130 {
    00131   MIDIOUTCAPS midicaps;
    00132   UINT nbdev;
    00133   UINT_PTR dev;
    00134   char buf[16];
    00135
    00136   mciSendStringA("capability sequencer has audio", buf, lengthof(buf), 0);
    00137   if (strcmp(buf, "true") != 0) return "MCI sequencer can't play audio";
    00138
    00139   memset(&_midi, 0, sizeof(_midi));
    00140   _midi.new_vol = -1;
    00141
    00142   /* Get midi device */
    00143   _midi.devid = MIDI_MAPPER;
    00144   for (dev = 0, nbdev = midiOutGetNumDevs(); dev < nbdev; dev++) {
    00145     if (midiOutGetDevCaps(dev, &midicaps, sizeof(midicaps)) == 0 && (midicaps.dwSupport & MIDICAPS_VOLUME)) {
    00146       _midi.devid = dev;
    00147       break;
    00148     }
    00149   }
    00150
    00151   if (NULL == (_midi.wait_obj = CreateEvent(NULL, FALSE, FALSE, NULL))) return "Failed to create event";
    00152
    00153   /* The lpThreadId parameter of CreateThread (the last parameter)
    00154    * may NOT be NULL on Windows 95, 98 and ME. */
    00155   DWORD threadId;
    00156   if (NULL == (_midi.thread = CreateThread(NULL, 8192, MidiThread, 0, 0, &threadId))) return "Failed to create thread";
    00157
    00158   return NULL;
    00159 }
    00160
    00161 void MusicDriver_Win32::Stop()
    00162 {
    00163   _midi.terminate = true;
    00164   SetEvent(_midi.wait_obj);
    00165   WaitForMultipleObjects(1, &_midi.thread, true, INFINITE);
    00166   CloseHandle(_midi.wait_obj);
    00167   CloseHandle(_midi.thread);
    00168 }