# win32_m.cpp

Go to the documentation of this file.

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