2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 /** @file win32_m.cpp Music playback for Windows. */
10 #include "../stdafx.h"
11 #include "../string_func.h"
15 #include "../os/windows/win32.h"
17 #include "midifile.hpp"
19 #include "../base_media_base.h"
22 #include "../safeguards.h"
24 struct PlaybackSegment
{
31 UINT time_period
; ///< obtained timer precision value
32 HMIDIOUT midi_out
; ///< handle to open midiOut
33 UINT timer_id
; ///< ID of active multimedia timer
34 std::mutex lock
; ///< synchronization for playback status fields
36 bool playing
; ///< flag indicating that playback is active
37 int do_start
; ///< flag for starting playback of next_file at next opportunity
38 bool do_stop
; ///< flag for stopping playback at next opportunity
39 byte current_volume
; ///< current effective volume setting
40 byte new_volume
; ///< volume setting to change to
42 MidiFile current_file
; ///< file currently being played from
43 PlaybackSegment current_segment
; ///< segment info for current playback
44 DWORD playback_start_time
; ///< timestamp current file began playback
45 size_t current_block
; ///< next block index to send
46 MidiFile next_file
; ///< upcoming file to play
47 PlaybackSegment next_segment
; ///< segment info for upcoming file
49 byte channel_volumes
[16]; ///< last seen volume controller values in raw data
52 static FMusicDriver_Win32 iFMusicDriver_Win32
;
55 static byte
ScaleVolume(byte original
, byte scale
)
57 return original
* scale
/ 127;
61 void CALLBACK
MidiOutProc(HMIDIOUT hmo
, UINT wMsg
, DWORD_PTR dwInstance
, DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
63 if (wMsg
== MOM_DONE
) {
64 MIDIHDR
*hdr
= (LPMIDIHDR
)dwParam1
;
65 midiOutUnprepareHeader(hmo
, hdr
, sizeof(*hdr
));
70 static void TransmitChannelMsg(byte status
, byte p1
, byte p2
= 0)
72 midiOutShortMsg(_midi
.midi_out
, status
| (p1
<< 8) | (p2
<< 16));
75 static void TransmitSysex(const byte
*&msg_start
, size_t &remaining
)
77 /* find end of message */
78 const byte
*msg_end
= msg_start
;
79 while (*msg_end
!= MIDIST_ENDSYSEX
) msg_end
++;
80 msg_end
++; /* also include sysex end byte */
83 MIDIHDR
*hdr
= CallocT
<MIDIHDR
>(1);
84 hdr
->lpData
= reinterpret_cast<LPSTR
>(const_cast<byte
*>(msg_start
));
85 hdr
->dwBufferLength
= msg_end
- msg_start
;
86 if (midiOutPrepareHeader(_midi
.midi_out
, hdr
, sizeof(*hdr
)) == MMSYSERR_NOERROR
) {
87 /* transmit - just point directly into the data buffer */
88 hdr
->dwBytesRecorded
= hdr
->dwBufferLength
;
89 midiOutLongMsg(_midi
.midi_out
, hdr
, sizeof(*hdr
));
94 /* update position in buffer */
95 remaining
-= msg_end
- msg_start
;
99 static void TransmitStandardSysex(MidiSysexMessage msg
)
102 const byte
*data
= MidiGetStandardSysexMessage(msg
, length
);
103 TransmitSysex(data
, length
);
107 * Realtime MIDI playback service routine.
108 * This is called by the multimedia timer.
110 void CALLBACK
TimerCallback(UINT uTimerID
, UINT
, DWORD_PTR dwUser
, DWORD_PTR
, DWORD_PTR
)
112 /* Ensure only one timer callback is running at once, and prevent races on status flags */
113 std::unique_lock
<std::mutex
> mutex_lock(_midi
.lock
, std::defer_lock
);
114 if (!mutex_lock
.try_lock()) return;
118 Debug(driver
, 2, "Win32-MIDI: timer: do_stop is set");
119 midiOutReset(_midi
.midi_out
);
120 _midi
.playing
= false;
121 _midi
.do_stop
= false;
125 /* check for start/restart/change song */
126 if (_midi
.do_start
!= 0) {
127 /* Have a delay between playback start steps, prevents jumbled-together notes at the start of song */
128 if (timeGetTime() - _midi
.playback_start_time
< 50) {
131 Debug(driver
, 2, "Win32-MIDI: timer: do_start step {}", _midi
.do_start
);
133 if (_midi
.do_start
== 1) {
134 /* Send "all notes off" */
135 midiOutReset(_midi
.midi_out
);
136 _midi
.playback_start_time
= timeGetTime();
140 } else if (_midi
.do_start
== 2) {
141 /* Reset the device to General MIDI defaults */
142 TransmitStandardSysex(MidiSysexMessage::ResetGM
);
143 _midi
.playback_start_time
= timeGetTime();
147 } else if (_midi
.do_start
== 3) {
148 /* Set up device-specific effects */
149 TransmitStandardSysex(MidiSysexMessage::RolandSetReverb
);
150 _midi
.playback_start_time
= timeGetTime();
154 } else if (_midi
.do_start
== 4) {
155 /* Load the new file */
156 _midi
.current_file
.MoveFrom(_midi
.next_file
);
157 std::swap(_midi
.next_segment
, _midi
.current_segment
);
158 _midi
.current_segment
.start_block
= 0;
159 _midi
.playback_start_time
= timeGetTime();
160 _midi
.playing
= true;
162 _midi
.current_block
= 0;
164 MemSetT
<byte
>(_midi
.channel_volumes
, 127, lengthof(_midi
.channel_volumes
));
166 } else if (!_midi
.playing
) {
167 /* not playing, stop the timer */
168 Debug(driver
, 2, "Win32-MIDI: timer: not playing, stopping timer");
169 timeKillEvent(uTimerID
);
174 /* check for volume change */
175 static int volume_throttle
= 0;
176 if (_midi
.current_volume
!= _midi
.new_volume
) {
177 if (volume_throttle
== 0) {
178 Debug(driver
, 2, "Win32-MIDI: timer: volume change");
179 _midi
.current_volume
= _midi
.new_volume
;
180 volume_throttle
= 20 / _midi
.time_period
;
181 for (int ch
= 0; ch
< 16; ch
++) {
182 byte vol
= ScaleVolume(_midi
.channel_volumes
[ch
], _midi
.current_volume
);
183 TransmitChannelMsg(MIDIST_CONTROLLER
| ch
, MIDICT_CHANVOLUME
, vol
);
190 /* skip beginning of file? */
191 if (_midi
.current_segment
.start
> 0 && _midi
.current_block
== 0 && _midi
.current_segment
.start_block
== 0) {
192 /* find first block after start time and pretend playback started earlier
193 * this is to allow all blocks prior to the actual start to still affect playback,
194 * as they may contain important controller and program changes */
195 size_t preload_bytes
= 0;
196 for (size_t bl
= 0; bl
< _midi
.current_file
.blocks
.size(); bl
++) {
197 MidiFile::DataBlock
&block
= _midi
.current_file
.blocks
[bl
];
198 preload_bytes
+= block
.data
.size();
199 if (block
.ticktime
>= _midi
.current_segment
.start
) {
200 if (_midi
.current_segment
.loop
) {
201 Debug(driver
, 2, "Win32-MIDI: timer: loop from block {} (ticktime {}, realtime {:.3f}, bytes {})", bl
, block
.ticktime
, ((int)block
.realtime
)/1000.0, preload_bytes
);
202 _midi
.current_segment
.start_block
= bl
;
205 /* Calculate offset start time for playback.
206 * The preload_bytes are used to compensate for delay in transmission over traditional serial MIDI interfaces,
207 * which have a bitrate of 31,250 bits/sec, and transmit 1+8+1 start/data/stop bits per byte.
208 * The delay compensation is needed to avoid time-compression of following messages.
210 Debug(driver
, 2, "Win32-MIDI: timer: start from block {} (ticktime {}, realtime {:.3f}, bytes {})", bl
, block
.ticktime
, ((int)block
.realtime
) / 1000.0, preload_bytes
);
211 _midi
.playback_start_time
-= block
.realtime
/ 1000 - (DWORD
)(preload_bytes
* 1000 / 3125);
219 /* play pending blocks */
220 DWORD current_time
= timeGetTime();
221 DWORD playback_time
= current_time
- _midi
.playback_start_time
;
222 while (_midi
.current_block
< _midi
.current_file
.blocks
.size()) {
223 MidiFile::DataBlock
&block
= _midi
.current_file
.blocks
[_midi
.current_block
];
225 /* check that block isn't at end-of-song override */
226 if (_midi
.current_segment
.end
> 0 && block
.ticktime
>= _midi
.current_segment
.end
) {
227 if (_midi
.current_segment
.loop
) {
228 _midi
.current_block
= _midi
.current_segment
.start_block
;
229 _midi
.playback_start_time
= timeGetTime() - _midi
.current_file
.blocks
[_midi
.current_block
].realtime
/ 1000;
231 _midi
.do_stop
= true;
235 /* check that block is not in the future */
236 if (block
.realtime
/ 1000 > playback_time
) {
240 const byte
*data
= block
.data
.data();
241 size_t remaining
= block
.data
.size();
242 byte last_status
= 0;
243 while (remaining
> 0) {
244 /* MidiFile ought to have converted everything out of running status,
245 * but handle it anyway just to be safe */
246 byte status
= data
[0];
248 last_status
= status
;
252 status
= last_status
;
254 switch (status
& 0xF0) {
256 case MIDIST_CHANPRESS
:
257 /* 2 byte channel messages */
258 TransmitChannelMsg(status
, data
[0]);
264 case MIDIST_POLYPRESS
:
265 case MIDIST_PITCHBEND
:
266 /* 3 byte channel messages */
267 TransmitChannelMsg(status
, data
[0], data
[1]);
271 case MIDIST_CONTROLLER
:
272 /* controller change */
273 if (data
[0] == MIDICT_CHANVOLUME
) {
274 /* volume controller, adjust for user volume */
275 _midi
.channel_volumes
[status
& 0x0F] = data
[1];
276 int vol
= ScaleVolume(data
[1], _midi
.current_volume
);
277 TransmitChannelMsg(status
, data
[0], vol
);
279 /* handle other controllers normally */
280 TransmitChannelMsg(status
, data
[0], data
[1]);
286 /* system messages */
288 case MIDIST_SYSEX
: /* system exclusive */
289 TransmitSysex(data
, remaining
);
291 case MIDIST_TC_QFRAME
: /* time code quarter frame */
292 case MIDIST_SONGSEL
: /* song select */
296 case MIDIST_SONGPOSPTR
: /* song position pointer */
300 default: /* remaining have no data bytes */
307 _midi
.current_block
++;
311 if (_midi
.current_block
== _midi
.current_file
.blocks
.size()) {
312 if (_midi
.current_segment
.loop
) {
313 _midi
.current_block
= _midi
.current_segment
.start_block
;
314 _midi
.playback_start_time
= timeGetTime() - _midi
.current_file
.blocks
[_midi
.current_block
].realtime
/ 1000;
316 _midi
.do_stop
= true;
321 void MusicDriver_Win32::PlaySong(const MusicSongInfo
&song
)
323 Debug(driver
, 2, "Win32-MIDI: PlaySong: entry");
326 if (!new_song
.LoadSong(song
)) return;
327 Debug(driver
, 2, "Win32-MIDI: PlaySong: Loaded song");
329 std::lock_guard
<std::mutex
> mutex_lock(_midi
.lock
);
331 _midi
.next_file
.MoveFrom(new_song
);
332 _midi
.next_segment
.start
= song
.override_start
;
333 _midi
.next_segment
.end
= song
.override_end
;
334 _midi
.next_segment
.loop
= song
.loop
;
336 Debug(driver
, 2, "Win32-MIDI: PlaySong: setting flag");
337 _midi
.do_stop
= _midi
.playing
;
340 if (_midi
.timer_id
== 0) {
341 Debug(driver
, 2, "Win32-MIDI: PlaySong: starting timer");
342 _midi
.timer_id
= timeSetEvent(_midi
.time_period
, _midi
.time_period
, TimerCallback
, (DWORD_PTR
)this, TIME_PERIODIC
| TIME_CALLBACK_FUNCTION
);
346 void MusicDriver_Win32::StopSong()
348 Debug(driver
, 2, "Win32-MIDI: StopSong: entry");
349 std::lock_guard
<std::mutex
> mutex_lock(_midi
.lock
);
350 Debug(driver
, 2, "Win32-MIDI: StopSong: setting flag");
351 _midi
.do_stop
= true;
354 bool MusicDriver_Win32::IsSongPlaying()
356 return _midi
.playing
|| (_midi
.do_start
!= 0);
359 void MusicDriver_Win32::SetVolume(byte vol
)
361 std::lock_guard
<std::mutex
> mutex_lock(_midi
.lock
);
362 _midi
.new_volume
= vol
;
365 const char *MusicDriver_Win32::Start(const StringList
&parm
)
367 Debug(driver
, 2, "Win32-MIDI: Start: initializing");
369 int resolution
= GetDriverParamInt(parm
, "resolution", 5);
370 uint port
= (uint
)GetDriverParamInt(parm
, "port", UINT_MAX
);
371 const char *portname
= GetDriverParam(parm
, "portname");
373 /* Enumerate ports either for selecting port by name, or for debug output */
374 if (portname
!= nullptr || _debug_driver_level
> 0) {
375 uint numports
= midiOutGetNumDevs();
376 Debug(driver
, 1, "Win32-MIDI: Found {} output devices:", numports
);
377 for (uint tryport
= 0; tryport
< numports
; tryport
++) {
379 if (midiOutGetDevCaps(tryport
, &moc
, sizeof(moc
)) == MMSYSERR_NOERROR
) {
380 char tryportname
[128];
381 convert_from_fs(moc
.szPname
, tryportname
, lengthof(tryportname
));
383 /* Compare requested and detected port name.
384 * If multiple ports have the same name, this will select the last matching port, and the debug output will be confusing. */
385 if (portname
!= nullptr && strncmp(tryportname
, portname
, lengthof(tryportname
)) == 0) port
= tryport
;
387 Debug(driver
, 1, "MIDI port {:2d}: {}{}", tryport
, tryportname
, (tryport
== port
) ? " [selected]" : "");
393 if (port
== UINT_MAX
) {
399 resolution
= Clamp(resolution
, 1, 20);
401 if (midiOutOpen(&_midi
.midi_out
, devid
, (DWORD_PTR
)&MidiOutProc
, (DWORD_PTR
)this, CALLBACK_FUNCTION
) != MMSYSERR_NOERROR
) {
402 return "could not open midi device";
405 midiOutReset(_midi
.midi_out
);
407 /* prepare multimedia timer */
409 if (timeGetDevCaps(&timecaps
, sizeof(timecaps
)) == MMSYSERR_NOERROR
) {
410 _midi
.time_period
= std::min(std::max((UINT
)resolution
, timecaps
.wPeriodMin
), timecaps
.wPeriodMax
);
411 if (timeBeginPeriod(_midi
.time_period
) == MMSYSERR_NOERROR
) {
413 Debug(driver
, 2, "Win32-MIDI: Start: timer resolution is {}", _midi
.time_period
);
417 midiOutClose(_midi
.midi_out
);
418 return "could not set timer resolution";
421 void MusicDriver_Win32::Stop()
423 std::lock_guard
<std::mutex
> mutex_lock(_midi
.lock
);
425 if (_midi
.timer_id
) {
426 timeKillEvent(_midi
.timer_id
);
430 timeEndPeriod(_midi
.time_period
);
431 midiOutReset(_midi
.midi_out
);
432 midiOutClose(_midi
.midi_out
);