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 */