OpenTTD
cocoa_m.cpp
Go to the documentation of this file.
1 /* $Id: cocoa_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 
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 static FMusicDriver_Cocoa iFMusicDriver_Cocoa;
34 
35 
36 static MusicPlayer _player = NULL;
37 static MusicSequence _sequence = NULL;
38 static MusicTimeStamp _seq_length = 0;
39 static bool _playing = false;
40 static byte _volume = 127;
41 
42 
44 static void DoSetVolume()
45 {
46  if (_sequence == NULL) return;
47 
48  AUGraph graph;
49  MusicSequenceGetAUGraph(_sequence, &graph);
50 
51  AudioUnit output_unit = NULL;
52 
53  /* Get output audio unit */
54  UInt32 node_count = 0;
55  AUGraphGetNodeCount(graph, &node_count);
56  for (UInt32 i = 0; i < node_count; i++) {
57  AUNode node;
58  AUGraphGetIndNode(graph, i, &node);
59 
60  AudioUnit unit;
61  OSType comp_type = 0;
62 
63 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
64  if (MacOSVersionIsAtLeast(10, 5, 0)) {
65  /* The 10.6 SDK has changed the function prototype of
66  * AUGraphNodeInfo. This is a binary compatible change,
67  * but we need to get the type declaration right or
68  * risk compilation errors. The header AudioComponent.h
69  * was introduced in 10.6 so use it to decide which
70  * type definition to use. */
71 #ifdef __AUDIOCOMPONENT_H__
72  AudioComponentDescription desc;
73 #else
74  ComponentDescription desc;
75 #endif
76  AUGraphNodeInfo(graph, node, &desc, &unit);
77  comp_type = desc.componentType;
78  } else
79 #endif
80  {
81 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
82  ComponentDescription desc;
83  AUGraphGetNodeInfo(graph, node, &desc, NULL, NULL, &unit);
84  comp_type = desc.componentType;
85 #endif
86  }
87 
88  if (comp_type == kAudioUnitType_Output) {
89  output_unit = unit;
90  break;
91  }
92  }
93  if (output_unit == NULL) {
94  DEBUG(driver, 1, "cocoa_m: Failed to get output node to set volume");
95  return;
96  }
97 
98  Float32 vol = _volume / 127.0f; // 0 - +127 -> 0.0 - 1.0
99  AudioUnitSetParameter(output_unit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, vol, 0);
100 }
101 
102 
106 const char *MusicDriver_Cocoa::Start(const char * const *parm)
107 {
108  if (NewMusicPlayer(&_player) != noErr) return "failed to create music player";
109 
110  return NULL;
111 }
112 
113 
118 {
119  if (!_playing) return false;
120 
121  MusicTimeStamp time = 0;
122  MusicPlayerGetTime(_player, &time);
123  return time < _seq_length;
124 }
125 
126 
131 {
132  if (_player != NULL) DisposeMusicPlayer(_player);
133  if (_sequence != NULL) DisposeMusicSequence(_sequence);
134 }
135 
136 
142 void MusicDriver_Cocoa::PlaySong(const char *filename)
143 {
144  DEBUG(driver, 2, "cocoa_m: trying to play '%s'", filename);
145 
146  this->StopSong();
147  if (_sequence != NULL) {
148  DisposeMusicSequence(_sequence);
149  _sequence = NULL;
150  }
151 
152  if (NewMusicSequence(&_sequence) != noErr) {
153  DEBUG(driver, 0, "cocoa_m: Failed to create music sequence");
154  return;
155  }
156 
157  const char *os_file = OTTD2FS(filename);
158  CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8*)os_file, strlen(os_file), false);
159 
160 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
161  if (MacOSVersionIsAtLeast(10, 5, 0)) {
162  if (MusicSequenceFileLoad(_sequence, url, 0, 0) != noErr) {
163  DEBUG(driver, 0, "cocoa_m: Failed to load MIDI file");
164  CFRelease(url);
165  return;
166  }
167  } else
168 #endif
169  {
170 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
171  FSRef ref_file;
172  if (!CFURLGetFSRef(url, &ref_file)) {
173  DEBUG(driver, 0, "cocoa_m: Failed to make FSRef");
174  CFRelease(url);
175  return;
176  }
177  if (MusicSequenceLoadSMFWithFlags(_sequence, &ref_file, 0) != noErr) {
178  DEBUG(driver, 0, "cocoa_m: Failed to load MIDI file old style");
179  CFRelease(url);
180  return;
181  }
182 #endif
183  }
184  CFRelease(url);
185 
186  /* Construct audio graph */
187  AUGraph graph = NULL;
188 
189  MusicSequenceGetAUGraph(_sequence, &graph);
190  AUGraphOpen(graph);
191  if (AUGraphInitialize(graph) != noErr) {
192  DEBUG(driver, 0, "cocoa_m: Failed to initialize AU graph");
193  return;
194  }
195 
196  /* Figure out sequence length */
197  UInt32 num_tracks;
198  MusicSequenceGetTrackCount(_sequence, &num_tracks);
199  _seq_length = 0;
200  for (UInt32 i = 0; i < num_tracks; i++) {
201  MusicTrack track = NULL;
202  MusicTimeStamp track_length = 0;
203  UInt32 prop_size = sizeof(MusicTimeStamp);
204  MusicSequenceGetIndTrack(_sequence, i, &track);
205  MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, &track_length, &prop_size);
206  if (track_length > _seq_length) _seq_length = track_length;
207  }
208  /* Add 8 beats for reverb/long note release */
209  _seq_length += 8;
210 
211  DoSetVolume();
212  MusicPlayerSetSequence(_player, _sequence);
213  MusicPlayerPreroll(_player);
214  if (MusicPlayerStart(_player) != noErr) return;
215  _playing = true;
216 
217  DEBUG(driver, 3, "cocoa_m: playing '%s'", filename);
218 }
219 
220 
225 {
226  MusicPlayerStop(_player);
227  MusicPlayerSetSequence(_player, NULL);
228  _playing = false;
229 }
230 
231 
237 void MusicDriver_Cocoa::SetVolume(byte vol)
238 {
239  _volume = vol;
240  DoSetVolume();
241 }
242 
243 #endif /* WITH_COCOA */