OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

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