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 
122 {
123  if (!_playing) return false;
124 
125  MusicTimeStamp time = 0;
126  MusicPlayerGetTime(_player, &time);
127  return time < _seq_length;
128 }
129 
130 
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 
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 */
const char * Start(const char *const *param)
Start this driver.
void StopSong()
Stop playing the current song.
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
Base of music playback via CoreAudio.
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
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:39
void PlaySong(const char *filename)
Play a particular song.
bool IsSongPlaying()
Are we currently playing a song?
void SetVolume(byte vol)
Set the volume, if possible.
void Stop()
Stop this driver.