# win32_m.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "../stdafx.h"
    00013 #include "../string_func.h"
    00014 #include "win32_m.h"
    00015 #include <windows.h>
    00016 #include <mmsystem.h>
    00017
    00018 static struct {
    00019   bool stop_song;
    00020   bool terminate;
    00021   bool playing;
    00022   int new_vol;
    00023   HANDLE wait_obj;
    00024   HANDLE thread;
    00025   UINT_PTR devid;
    00026   char start_song[MAX_PATH];
    00027 } _midi;
    00028
    00029 static FMusicDriver_Win32 iFMusicDriver_Win32;
    00030
    00031 void MusicDriver_Win32::PlaySong(const char *filename)
    00032 {
    00033   assert(filename != NULL);
    00034   strecpy(_midi.start_song, filename, lastof(_midi.start_song));
    00035   _midi.playing = true;
    00036   _midi.stop_song = false;
    00037   SetEvent(_midi.wait_obj);
    00038 }
    00039
    00040 void MusicDriver_Win32::StopSong()
    00041 {
    00042   if (_midi.playing) {
    00043     _midi.stop_song = true;
    00044     _midi.start_song[0] = '\0';
    00045     SetEvent(_midi.wait_obj);
    00046   }
    00047 }
    00048
    00049 bool MusicDriver_Win32::IsSongPlaying()
    00050 {
    00051   return _midi.playing;
    00052 }
    00053
    00054 void MusicDriver_Win32::SetVolume(byte vol)
    00055 {
    00056   _midi.new_vol = vol;
    00057   SetEvent(_midi.wait_obj);
    00058 }
    00059
    00060 static MCIERROR CDECL MidiSendCommand(const TCHAR *cmd, ...)
    00061 {
    00062   va_list va;
    00063   TCHAR buf[512];
    00064
    00065   va_start(va, cmd);
    00066   _vsntprintf(buf, lengthof(buf), cmd, va);
    00067   va_end(va);
    00068   return mciSendString(buf, NULL, 0, 0);
    00069 }
    00070
    00071 static bool MidiIntPlaySong(const char *filename)
    00072 {
    00073   MidiSendCommand(_T("close all"));
    00074
    00075   if (MidiSendCommand(_T("open \"%s\" type sequencer alias song"), OTTD2FS(filename)) != 0) {
    00076     /* Let's try the "short name" */
    00077     TCHAR buf[MAX_PATH];
    00078     if (GetShortPathName(OTTD2FS(filename), buf, MAX_PATH) == 0) return false;
    00079     if (MidiSendCommand(_T("open \"%s\" type sequencer alias song"), buf) != 0) return false;
    00080   }
    00081
    00082   MidiSendCommand(_T("seek song to start wait"));
    00083   return MidiSendCommand(_T("play song")) == 0;
    00084 }
    00085
    00086 static void MidiIntStopSong()
    00087 {
    00088   MidiSendCommand(_T("close all"));
    00089 }
    00090
    00091 static void MidiIntSetVolume(int vol)
    00092 {
    00093   DWORD v = (vol * 65535 / 127);
    00094   midiOutSetVolume((HMIDIOUT)_midi.devid, v + (v << 16));
    00095 }
    00096
    00097 static bool MidiIntIsSongPlaying()
    00098 {
    00099   char buf[16];
    00100   mciSendStringA("status song mode", buf, sizeof(buf), 0);
    00101   return strcmp(buf, "playing") == 0 || strcmp(buf, "seeking") == 0;
    00102 }
    00103
    00104 static DWORD WINAPI MidiThread(LPVOID arg)
    00105 {
    00106   do {
    00107     char *s;
    00108     int vol;
    00109
    00110     vol = _midi.new_vol;
    00111     if (vol != -1) {
    00112       _midi.new_vol = -1;
    00113       MidiIntSetVolume(vol);
    00114     }
    00115
    00116     s = _midi.start_song;
    00117     if (s[0] != '\0') {
    00118       _midi.playing = MidiIntPlaySong(s);
    00119       s[0] = '\0';
    00120
    00121       /* Delay somewhat in case we don't manage to play. */
    00122       if (!_midi.playing) WaitForMultipleObjects(1, &_midi.wait_obj, FALSE, 5000);
    00123     }
    00124
    00125     if (_midi.stop_song && _midi.playing) {
    00126       _midi.stop_song = false;
    00127       _midi.playing = false;
    00128       MidiIntStopSong();
    00129     }
    00130
    00131     if (_midi.playing && !MidiIntIsSongPlaying()) _midi.playing = false;
    00132
    00133     WaitForMultipleObjects(1, &_midi.wait_obj, FALSE, 1000);
    00134   } while (!_midi.terminate);
    00135
    00136   MidiIntStopSong();
    00137   return 0;
    00138 }
    00139
    00140 const char *MusicDriver_Win32::Start(const char * const *parm)
    00141 {
    00142   MIDIOUTCAPS midicaps;
    00143   UINT nbdev;
    00144   UINT_PTR dev;
    00145   char buf[16];
    00146
    00147   mciSendStringA("capability sequencer has audio", buf, lengthof(buf), 0);
    00148   if (strcmp(buf, "true") != 0) return "MCI sequencer can't play audio";
    00149
    00150   memset(&_midi, 0, sizeof(_midi));
    00151   _midi.new_vol = -1;
    00152
    00153   /* Get midi device */
    00154   _midi.devid = MIDI_MAPPER;
    00155   for (dev = 0, nbdev = midiOutGetNumDevs(); dev < nbdev; dev++) {
    00156     if (midiOutGetDevCaps(dev, &midicaps, sizeof(midicaps)) == 0 && (midicaps.dwSupport & MIDICAPS_VOLUME)) {
    00157       _midi.devid = dev;
    00158       break;
    00159     }
    00160   }
    00161
    00162   if (NULL == (_midi.wait_obj = CreateEvent(NULL, FALSE, FALSE, NULL))) return "Failed to create event";
    00163
    00164   /* The lpThreadId parameter of CreateThread (the last parameter)
    00165    * may NOT be NULL on Windows 95, 98 and ME. */
    00166   DWORD threadId;
    00167   if (NULL == (_midi.thread = CreateThread(NULL, 8192, MidiThread, 0, 0, &threadId))) return "Failed to create thread";
    00168
    00169   return NULL;
    00170 }
    00171
    00172 void MusicDriver_Win32::Stop()
    00173 {
    00174   _midi.terminate = true;
    00175   SetEvent(_midi.wait_obj);
    00176   WaitForMultipleObjects(1, &_midi.thread, true, INFINITE);
    00177   CloseHandle(_midi.wait_obj);
    00178   CloseHandle(_midi.thread);
    00179 }