OpenTTD
win32_m.cpp
Go to the documentation of this file.
1 /* $Id: win32_m.cpp 27673 2016-10-30 18:22:55Z michi_cc $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #include "../stdafx.h"
13 #include "../string_func.h"
14 #include "win32_m.h"
15 #include <windows.h>
16 #include <mmsystem.h>
17 #include "../os/windows/win32.h"
18 
19 #include "../safeguards.h"
20 
21 static struct {
22  bool stop_song;
23  bool terminate;
24  bool playing;
25  int new_vol;
26  HANDLE wait_obj;
27  HANDLE thread;
28  UINT_PTR devid;
29  char start_song[MAX_PATH];
30 } _midi;
31 
32 static FMusicDriver_Win32 iFMusicDriver_Win32;
33 
34 void MusicDriver_Win32::PlaySong(const char *filename)
35 {
36  assert(filename != NULL);
37  strecpy(_midi.start_song, filename, lastof(_midi.start_song));
38  _midi.playing = true;
39  _midi.stop_song = false;
40  SetEvent(_midi.wait_obj);
41 }
42 
44 {
45  if (_midi.playing) {
46  _midi.stop_song = true;
47  _midi.start_song[0] = '\0';
48  SetEvent(_midi.wait_obj);
49  }
50 }
51 
53 {
54  return _midi.playing;
55 }
56 
58 {
59  _midi.new_vol = vol;
60  SetEvent(_midi.wait_obj);
61 }
62 
63 static MCIERROR CDECL MidiSendCommand(const TCHAR *cmd, ...)
64 {
65  va_list va;
66  TCHAR buf[512];
67 
68  va_start(va, cmd);
69  _vsntprintf(buf, lengthof(buf), cmd, va);
70  va_end(va);
71  return mciSendString(buf, NULL, 0, 0);
72 }
73 
74 static bool MidiIntPlaySong(const char *filename)
75 {
76  MidiSendCommand(_T("close all"));
77 
78  if (MidiSendCommand(_T("open \"%s\" type sequencer alias song"), OTTD2FS(filename)) != 0) {
79  /* Let's try the "short name" */
80  TCHAR buf[MAX_PATH];
81  if (GetShortPathName(OTTD2FS(filename), buf, MAX_PATH) == 0) return false;
82  if (MidiSendCommand(_T("open \"%s\" type sequencer alias song"), buf) != 0) return false;
83  }
84 
85  MidiSendCommand(_T("seek song to start wait"));
86  return MidiSendCommand(_T("play song")) == 0;
87 }
88 
89 static void MidiIntStopSong()
90 {
91  MidiSendCommand(_T("close all"));
92 }
93 
94 static void MidiIntSetVolume(int vol)
95 {
96  DWORD v = (vol * 65535 / 127);
97  midiOutSetVolume((HMIDIOUT)_midi.devid, v + (v << 16));
98 }
99 
100 static bool MidiIntIsSongPlaying()
101 {
102  char buf[16];
103  mciSendStringA("status song mode", buf, sizeof(buf), 0);
104  return strcmp(buf, "playing") == 0 || strcmp(buf, "seeking") == 0;
105 }
106 
107 static DWORD WINAPI MidiThread(LPVOID arg)
108 {
109  SetWin32ThreadName(-1, "ottd:win-midi");
110 
111  do {
112  char *s;
113  int vol;
114 
115  vol = _midi.new_vol;
116  if (vol != -1) {
117  _midi.new_vol = -1;
118  MidiIntSetVolume(vol);
119  }
120 
121  s = _midi.start_song;
122  if (s[0] != '\0') {
123  _midi.playing = MidiIntPlaySong(s);
124  s[0] = '\0';
125 
126  /* Delay somewhat in case we don't manage to play. */
127  if (!_midi.playing) WaitForMultipleObjects(1, &_midi.wait_obj, FALSE, 5000);
128  }
129 
130  if (_midi.stop_song && _midi.playing) {
131  _midi.stop_song = false;
132  _midi.playing = false;
133  MidiIntStopSong();
134  }
135 
136  if (_midi.playing && !MidiIntIsSongPlaying()) _midi.playing = false;
137 
138  WaitForMultipleObjects(1, &_midi.wait_obj, FALSE, 1000);
139  } while (!_midi.terminate);
140 
141  MidiIntStopSong();
142  return 0;
143 }
144 
145 const char *MusicDriver_Win32::Start(const char * const *parm)
146 {
147  MIDIOUTCAPS midicaps;
148  UINT nbdev;
149  UINT_PTR dev;
150  char buf[16];
151 
152  mciSendStringA("capability sequencer has audio", buf, lengthof(buf), 0);
153  if (strcmp(buf, "true") != 0) return "MCI sequencer can't play audio";
154 
155  memset(&_midi, 0, sizeof(_midi));
156  _midi.new_vol = -1;
157 
158  /* Get midi device */
159  _midi.devid = MIDI_MAPPER;
160  for (dev = 0, nbdev = midiOutGetNumDevs(); dev < nbdev; dev++) {
161  if (midiOutGetDevCaps(dev, &midicaps, sizeof(midicaps)) == 0 && (midicaps.dwSupport & MIDICAPS_VOLUME)) {
162  _midi.devid = dev;
163  break;
164  }
165  }
166 
167  if (NULL == (_midi.wait_obj = CreateEvent(NULL, FALSE, FALSE, NULL))) return "Failed to create event";
168 
169  /* The lpThreadId parameter of CreateThread (the last parameter)
170  * may NOT be NULL on Windows 95, 98 and ME. */
171  DWORD threadId;
172  if (NULL == (_midi.thread = CreateThread(NULL, 8192, MidiThread, 0, 0, &threadId))) return "Failed to create thread";
173 
174  return NULL;
175 }
176 
178 {
179  _midi.terminate = true;
180  SetEvent(_midi.wait_obj);
181  WaitForMultipleObjects(1, &_midi.thread, true, INFINITE);
182  CloseHandle(_midi.wait_obj);
183  CloseHandle(_midi.thread);
184 }
void Stop()
Stop this driver.
Definition: win32_m.cpp:177
Factory for Windows&#39; music player.
Definition: win32_m.h:35
Base for Windows music playback.
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:50
void PlaySong(const char *filename)
Play a particular song.
Definition: win32_m.cpp:34
static long CDECL MidiSendCommand(const char *cmd,...)
Send a midi command.
Definition: os2_m.cpp:39
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:42
const char * Start(const char *const *param)
Start this driver.
Definition: win32_m.cpp:145
bool IsSongPlaying()
Are we currently playing a song?
Definition: win32_m.cpp:52
const TCHAR * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD&#39;s encoding to that of the local environment.
Definition: win32.cpp:631
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: depend.cpp:68
void SetVolume(byte vol)
Set the volume, if possible.
Definition: win32_m.cpp:57
void StopSong()
Stop playing the current song.
Definition: win32_m.cpp:43
static struct @24 _midi
Metadata about the midi we&#39;re playing.