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