OpenTTD

cocoa_m.cpp
Go to the documentation of this file.
1 /* $Id: cocoa_m.cpp 27675 2016-10-31 19:29:01Z 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
16 #ifdef WITH_COCOA
17
18 #include "../stdafx.h"
19 #include "../os/macosx/macos.h"
20 #include "cocoa_m.h"
21 #include "../debug.h"
22
23 #define Rect OTTDRect
24 #define Point OTTDPoint
25 #include <CoreServices/CoreServices.h>
26 #include <AudioUnit/AudioUnit.h>
27 #include <AudioToolbox/AudioToolbox.h>
28 #undef Rect
29 #undef Point
30
31 #include "../safeguards.h"
32
33 #if !defined(HAVE_OSX_1011_SDK)
34 #define kMusicSequenceFile_AnyType 0
35 #endif
36
37 static FMusicDriver_Cocoa iFMusicDriver_Cocoa;
38
39
40 static MusicPlayer _player = NULL;
41 static MusicSequence _sequence = NULL;
42 static MusicTimeStamp _seq_length = 0;
43 static bool _playing = false;
44 static byte _volume = 127;
45
46
48 static void DoSetVolume()
49 {
50 if (_sequence == NULL) return;
51
52 AUGraph graph;
53 MusicSequenceGetAUGraph(_sequence, &graph);
54
55 AudioUnit output_unit = NULL;
56
57 /* Get output audio unit */
58 UInt32 node_count = 0;
59 AUGraphGetNodeCount(graph, &node_count);
60 for (UInt32 i = 0; i < node_count; i++) {
61 AUNode node;
62 AUGraphGetIndNode(graph, i, &node);
63
64 AudioUnit unit;
65 OSType comp_type = 0;
66
67 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
68 if (MacOSVersionIsAtLeast(10, 5, 0)) {
69 /* The 10.6 SDK has changed the function prototype of
70 * AUGraphNodeInfo. This is a binary compatible change,
71 * but we need to get the type declaration right or
72 * risk compilation errors. The header AudioComponent.h
73 * was introduced in 10.6 so use it to decide which
74 * type definition to use. */
75 #if defined(__AUDIOCOMPONENT_H__) || defined(HAVE_OSX_107_SDK)
76 AudioComponentDescription desc;
77 #else
78 ComponentDescription desc;
79 #endif
80 AUGraphNodeInfo(graph, node, &desc, &unit);
81 comp_type = desc.componentType;
82 } else
83 #endif
84 {
85 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
86 ComponentDescription desc;
87 AUGraphGetNodeInfo(graph, node, &desc, NULL, NULL, &unit);
88 comp_type = desc.componentType;
89 #endif
90 }
91
92 if (comp_type == kAudioUnitType_Output) {
93 output_unit = unit;
94 break;
95 }
96 }
97 if (output_unit == NULL) {
98 DEBUG(driver, 1, "cocoa_m: Failed to get output node to set volume");
99 return;
100 }
101
102 Float32 vol = _volume / 127.0f; // 0 - +127 -> 0.0 - 1.0
103 AudioUnitSetParameter(output_unit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, vol, 0);
104 }
105
106
110 const char *MusicDriver_Cocoa::Start(const char * const *parm)
111 {
112 if (NewMusicPlayer(&_player) != noErr) return "failed to create music player";
113
114 return NULL;
115 }
116
117
121 bool MusicDriver_Cocoa::IsSongPlaying()
122 {
123 if (!_playing) return false;
124
125 MusicTimeStamp time = 0;
126 MusicPlayerGetTime(_player, &time);
127 return time < _seq_length;
128 }
129
130
134 void MusicDriver_Cocoa::Stop()
135 {
136 if (_player != NULL) DisposeMusicPlayer(_player);
137 if (_sequence != NULL) DisposeMusicSequence(_sequence);
138 }
139
140
146 void MusicDriver_Cocoa::PlaySong(const char *filename)
147 {
148 DEBUG(driver, 2, "cocoa_m: trying to play '%s'", filename);
149
150 this->StopSong();
151 if (_sequence != NULL) {
152 DisposeMusicSequence(_sequence);
153 _sequence = NULL;
154 }
155
156 if (NewMusicSequence(&_sequence) != noErr) {
157 DEBUG(driver, 0, "cocoa_m: Failed to create music sequence");
158 return;
159 }
160
161 const char *os_file = OTTD2FS(filename);
162 CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8*)os_file, strlen(os_file), false);
163
164 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
165 if (MacOSVersionIsAtLeast(10, 5, 0)) {
166 if (MusicSequenceFileLoad(_sequence, url, kMusicSequenceFile_AnyType, 0) != noErr) {
167 DEBUG(driver, 0, "cocoa_m: Failed to load MIDI file");
168 CFRelease(url);
169 return;
170 }
171 } else
172 #endif
173 {
174 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
175 FSRef ref_file;
176 if (!CFURLGetFSRef(url, &ref_file)) {
177 DEBUG(driver, 0, "cocoa_m: Failed to make FSRef");
178 CFRelease(url);
179 return;
180 }
181 if (MusicSequenceLoadSMFWithFlags(_sequence, &ref_file, 0) != noErr) {
182 DEBUG(driver, 0, "cocoa_m: Failed to load MIDI file old style");
183 CFRelease(url);
184 return;
185 }
186 #endif
187 }
188 CFRelease(url);
189
190 /* Construct audio graph */
191 AUGraph graph = NULL;
192
193 MusicSequenceGetAUGraph(_sequence, &graph);
194 AUGraphOpen(graph);
195 if (AUGraphInitialize(graph) != noErr) {
196 DEBUG(driver, 0, "cocoa_m: Failed to initialize AU graph");
197 return;
198 }
199
200 /* Figure out sequence length */
201 UInt32 num_tracks;
202 MusicSequenceGetTrackCount(_sequence, &num_tracks);
203 _seq_length = 0;
204 for (UInt32 i = 0; i < num_tracks; i++) {
205 MusicTrack track = NULL;
206 MusicTimeStamp track_length = 0;
207 UInt32 prop_size = sizeof(MusicTimeStamp);
208 MusicSequenceGetIndTrack(_sequence, i, &track);
209 MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, &track_length, &prop_size);
210 if (track_length > _seq_length) _seq_length = track_length;
211 }
212 /* Add 8 beats for reverb/long note release */
213 _seq_length += 8;
214
215 DoSetVolume();
216 MusicPlayerSetSequence(_player, _sequence);
217 MusicPlayerPreroll(_player);
218 if (MusicPlayerStart(_player) != noErr) return;
219 _playing = true;
220
221 DEBUG(driver, 3, "cocoa_m: playing '%s'", filename);
222 }
223
224
228 void MusicDriver_Cocoa::StopSong()
229 {
230 MusicPlayerStop(_player);
231 MusicPlayerSetSequence(_player, NULL);
232 _playing = false;
233 }
234
235
241 void MusicDriver_Cocoa::SetVolume(byte vol)
242 {
243 _volume = vol;
244 DoSetVolume();
245 }
246
247 #endif /* WITH_COCOA */
MusicDriver_Cocoa::Start
const char * Start(const char *const *param)
Start this driver.
MusicDriver_Cocoa::StopSong
void StopSong()
Stop playing the current song.
MacOSVersionIsAtLeast
static bool MacOSVersionIsAtLeast(long major, long minor, long bugfix)
Check if we are at least running on the specified version of Mac OS.
Definition: macos.h:27
FMusicDriver_Cocoa
Definition: cocoa_m.h:33
cocoa_m.h
Base of music playback via CoreAudio.
OTTD2FS
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
DEBUG
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:39
MusicDriver_Cocoa::PlaySong
void PlaySong(const char *filename)
Play a particular song.
MusicDriver_Cocoa::IsSongPlaying
bool IsSongPlaying()
Are we currently playing a song?
MusicDriver_Cocoa::SetVolume
void SetVolume(byte vol)
Set the volume, if possible.
MusicDriver_Cocoa::Stop
void Stop()
Stop this driver.