(svn r17710) -Feature [FS#3223]: [OSX] Add a MIDI driver using Cocoa/CoreAudio.
This commit is contained in:
		@@ -1320,7 +1320,7 @@ make_cflags_and_ldflags() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if [ "$with_cocoa" != "0" ]; then
 | 
						if [ "$with_cocoa" != "0" ]; then
 | 
				
			||||||
		CFLAGS="$CFLAGS -DWITH_COCOA"
 | 
							CFLAGS="$CFLAGS -DWITH_COCOA"
 | 
				
			||||||
		LIBS="$LIBS -F/System/Library/Frameworks -framework Cocoa -framework Carbon -framework AudioUnit"
 | 
							LIBS="$LIBS -F/System/Library/Frameworks -framework Cocoa -framework Carbon -framework AudioUnit -framework AudioToolbox"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if [ "$enable_cocoa_quartz" != "0" ]; then
 | 
							if [ "$enable_cocoa_quartz" != "0" ]; then
 | 
				
			||||||
			CFLAGS="$CFLAGS -DENABLE_COCOA_QUARTZ"
 | 
								CFLAGS="$CFLAGS -DENABLE_COCOA_QUARTZ"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -327,6 +327,7 @@ zoom_type.h
 | 
				
			|||||||
#if WIN32
 | 
					#if WIN32
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
music/bemidi.h
 | 
					music/bemidi.h
 | 
				
			||||||
 | 
					music/cocoa_m.h
 | 
				
			||||||
music/extmidi.h
 | 
					music/extmidi.h
 | 
				
			||||||
music/libtimidity.h
 | 
					music/libtimidity.h
 | 
				
			||||||
music/os2_m.h
 | 
					music/os2_m.h
 | 
				
			||||||
@@ -919,6 +920,7 @@ sound/null_s.cpp
 | 
				
			|||||||
		video/cocoa/fullscreen.mm
 | 
							video/cocoa/fullscreen.mm
 | 
				
			||||||
		video/cocoa/wnd_quartz.mm
 | 
							video/cocoa/wnd_quartz.mm
 | 
				
			||||||
		video/cocoa/wnd_quickdraw.mm
 | 
							video/cocoa/wnd_quickdraw.mm
 | 
				
			||||||
 | 
							music/cocoa_m.cpp
 | 
				
			||||||
		sound/cocoa_s.cpp
 | 
							sound/cocoa_s.cpp
 | 
				
			||||||
		os/macosx/splash.cpp
 | 
							os/macosx/splash.cpp
 | 
				
			||||||
	#end
 | 
						#end
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										241
									
								
								src/music/cocoa_m.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								src/music/cocoa_m.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,241 @@
 | 
				
			|||||||
 | 
					/* $Id$ */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of OpenTTD.
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 * 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/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @file cocoa_m.cpp
 | 
				
			||||||
 | 
					 * @brief MIDI music player for MacOS X using CoreAudio.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef WITH_COCOA
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../stdafx.h"
 | 
				
			||||||
 | 
					#include "../os/macosx/macos.h"
 | 
				
			||||||
 | 
					#include "cocoa_m.h"
 | 
				
			||||||
 | 
					#include "../debug.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define Rect        OTTDRect
 | 
				
			||||||
 | 
					#define Point       OTTDPoint
 | 
				
			||||||
 | 
					#include <CoreServices/CoreServices.h>
 | 
				
			||||||
 | 
					#include <AudioUnit/AudioUnit.h>
 | 
				
			||||||
 | 
					#include <AudioToolbox/AudioToolbox.h>
 | 
				
			||||||
 | 
					#undef Rect
 | 
				
			||||||
 | 
					#undef Point
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static FMusicDriver_Cocoa iFMusicDriver_Cocoa;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static MusicPlayer    _player = NULL;
 | 
				
			||||||
 | 
					static MusicSequence  _sequence = NULL;
 | 
				
			||||||
 | 
					static MusicTimeStamp _seq_length = 0;
 | 
				
			||||||
 | 
					static bool           _playing = false;
 | 
				
			||||||
 | 
					static byte           _volume = 127;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Set the volume of the current sequence. */
 | 
				
			||||||
 | 
					static void DoSetVolume()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (_sequence == NULL) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AUGraph graph;
 | 
				
			||||||
 | 
						MusicSequenceGetAUGraph(_sequence, &graph);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AudioUnit output_unit = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Get output audio unit */
 | 
				
			||||||
 | 
						UInt32 node_count = 0;
 | 
				
			||||||
 | 
						AUGraphGetNodeCount(graph, &node_count);
 | 
				
			||||||
 | 
						for (UInt32 i = 0; i < node_count; i++) {
 | 
				
			||||||
 | 
							AUNode node;
 | 
				
			||||||
 | 
							AUGraphGetIndNode(graph, i, &node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							AudioUnit unit;
 | 
				
			||||||
 | 
							OSType comp_type = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
 | 
				
			||||||
 | 
							if (MacOSVersionIsAtLeast(10, 5, 0)) {
 | 
				
			||||||
 | 
								/* The 10.6 SDK has changed the function prototype of
 | 
				
			||||||
 | 
								 * AUGraphNodeInfo. This is a binary compatible change,
 | 
				
			||||||
 | 
								 * but we need to get the type declaration right or
 | 
				
			||||||
 | 
								 * risk compilation errors. The header AudioComponent.h
 | 
				
			||||||
 | 
								 * was introduced in 10.6 so use it to decide which
 | 
				
			||||||
 | 
								 * type definition to use. */
 | 
				
			||||||
 | 
					#ifdef __AUDIOCOMPONENT_H__
 | 
				
			||||||
 | 
								AudioComponentDescription desc;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
								ComponentDescription desc;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
								AUGraphNodeInfo(graph, node, &desc, &unit);
 | 
				
			||||||
 | 
								comp_type = desc.componentType;
 | 
				
			||||||
 | 
							} else
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
					#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
 | 
				
			||||||
 | 
								ComponentDescription desc;
 | 
				
			||||||
 | 
								AUGraphGetNodeInfo(graph, node, &desc, NULL, NULL, &unit);
 | 
				
			||||||
 | 
								comp_type = desc.componentType;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (comp_type == kAudioUnitType_Output) {
 | 
				
			||||||
 | 
								output_unit = unit;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (output_unit == NULL) {
 | 
				
			||||||
 | 
							DEBUG(driver, 1, "cocoa_m: Failed to get output node to set volume");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Float32 vol = _volume / 127.0f;  // 0 - +127 -> 0.0 - 1.0
 | 
				
			||||||
 | 
						AudioUnitSetParameter(output_unit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, vol, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Initialized the MIDI player, including QuickTime initialization.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const char *MusicDriver_Cocoa::Start(const char * const *parm)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (NewMusicPlayer(&_player) != noErr) return "failed to create music player";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Checks wether the player is active.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					bool MusicDriver_Cocoa::IsSongPlaying()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!_playing) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						MusicTimeStamp time = 0;
 | 
				
			||||||
 | 
						MusicPlayerGetTime(_player, &time);
 | 
				
			||||||
 | 
						return time < _seq_length;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Stops the MIDI player.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void MusicDriver_Cocoa::Stop()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (_player != NULL) DisposeMusicPlayer(_player);
 | 
				
			||||||
 | 
						if (_sequence != NULL) DisposeMusicSequence(_sequence);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Starts playing a new song.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param filename Path to a MIDI file.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void MusicDriver_Cocoa::PlaySong(const char *filename)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						DEBUG(driver, 2, "cocoa_m: trying to play '%s'", filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this->StopSong();
 | 
				
			||||||
 | 
						if (_sequence != NULL) {
 | 
				
			||||||
 | 
							DisposeMusicSequence(_sequence);
 | 
				
			||||||
 | 
							_sequence = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (NewMusicSequence(&_sequence) != noErr) {
 | 
				
			||||||
 | 
							DEBUG(driver, 0, "cocoa_m: Failed to create music sequence");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char *os_file = OTTD2FS(filename);
 | 
				
			||||||
 | 
						CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8*)os_file, strlen(os_file), false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
 | 
				
			||||||
 | 
						if (MacOSVersionIsAtLeast(10, 5, 0)) {
 | 
				
			||||||
 | 
							if (MusicSequenceFileLoad(_sequence, url, 0, 0) != noErr) {
 | 
				
			||||||
 | 
								DEBUG(driver, 0, "cocoa_m: Failed to load MIDI file");
 | 
				
			||||||
 | 
								CFRelease(url);
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
					#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
 | 
				
			||||||
 | 
							FSRef ref_file;
 | 
				
			||||||
 | 
							if (!CFURLGetFSRef(url, &ref_file)) {
 | 
				
			||||||
 | 
								DEBUG(driver, 0, "cocoa_m: Failed to make FSRef");
 | 
				
			||||||
 | 
								CFRelease(url);
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (MusicSequenceLoadSMFWithFlags(_sequence, &ref_file, 0) != noErr) {
 | 
				
			||||||
 | 
								DEBUG(driver, 0, "cocoa_m: Failed to load MIDI file old style");
 | 
				
			||||||
 | 
								CFRelease(url);
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						CFRelease(url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Construct audio graph */
 | 
				
			||||||
 | 
						AUGraph graph = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						MusicSequenceGetAUGraph(_sequence, &graph);
 | 
				
			||||||
 | 
						AUGraphOpen(graph);
 | 
				
			||||||
 | 
						if (AUGraphInitialize(graph) != noErr) {
 | 
				
			||||||
 | 
							DEBUG(driver, 0, "cocoa_m: Failed to initialize AU graph");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Figure out sequence length */
 | 
				
			||||||
 | 
						UInt32 num_tracks;
 | 
				
			||||||
 | 
						MusicSequenceGetTrackCount(_sequence, &num_tracks);
 | 
				
			||||||
 | 
						_seq_length = 0;
 | 
				
			||||||
 | 
						for (UInt32 i = 0; i < num_tracks; i++) {
 | 
				
			||||||
 | 
							MusicTrack     track = NULL;
 | 
				
			||||||
 | 
							MusicTimeStamp track_length = 0;
 | 
				
			||||||
 | 
							UInt32         prop_size = sizeof(MusicTimeStamp);
 | 
				
			||||||
 | 
							MusicSequenceGetIndTrack(_sequence, i, &track);
 | 
				
			||||||
 | 
							MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, &track_length, &prop_size);
 | 
				
			||||||
 | 
							if (track_length > _seq_length) _seq_length = track_length;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						/* Add 8 beats for reverb/long note release */
 | 
				
			||||||
 | 
						_seq_length += 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DoSetVolume();
 | 
				
			||||||
 | 
						MusicPlayerSetSequence(_player, _sequence);
 | 
				
			||||||
 | 
						MusicPlayerPreroll(_player);
 | 
				
			||||||
 | 
						if (MusicPlayerStart(_player) != noErr) return;
 | 
				
			||||||
 | 
						_playing = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DEBUG(driver, 3, "cocoa_m: playing '%s'", filename);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Stops playing the current song, if the player is active.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void MusicDriver_Cocoa::StopSong()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						MusicPlayerStop(_player);
 | 
				
			||||||
 | 
						MusicPlayerSetSequence(_player, NULL);
 | 
				
			||||||
 | 
						_playing = false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Changes the playing volume of the MIDI player.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param vol The desired volume, range of the value is @c 0-127
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void MusicDriver_Cocoa::SetVolume(byte vol)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						_volume = vol;
 | 
				
			||||||
 | 
						DoSetVolume();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* WITH_COCOA */
 | 
				
			||||||
							
								
								
									
										41
									
								
								src/music/cocoa_m.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/music/cocoa_m.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					/* $Id$ */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of OpenTTD.
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 * 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/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** @file cocoa_m.h Base of music playback via CoreAudio. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef MUSIC_MACOSX_COCOA_H
 | 
				
			||||||
 | 
					#define MUSIC_MACOSX_COCOA_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "music_driver.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MusicDriver_Cocoa: public MusicDriver {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						/* virtual */ const char *Start(const char * const *param);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* virtual */ void Stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* virtual */ void PlaySong(const char *filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* virtual */ void StopSong();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* virtual */ bool IsSongPlaying();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* virtual */ void SetVolume(byte vol);
 | 
				
			||||||
 | 
						/* virtual */ const char *GetName() const { return "cocoa"; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FMusicDriver_Cocoa: public MusicDriverFactory<FMusicDriver_Cocoa> {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						static const int priority = 10;
 | 
				
			||||||
 | 
						/* virtual */ const char *GetName() { return "cocoa"; }
 | 
				
			||||||
 | 
						/* virtual */ const char *GetDescription() { return "Cocoa MIDI Driver"; }
 | 
				
			||||||
 | 
						/* virtual */ Driver *CreateInstance() { return new MusicDriver_Cocoa(); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* MUSIC_MACOSX_COCOA_H */
 | 
				
			||||||
@@ -32,7 +32,7 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class FMusicDriver_QtMidi: public MusicDriverFactory<FMusicDriver_QtMidi> {
 | 
					class FMusicDriver_QtMidi: public MusicDriverFactory<FMusicDriver_QtMidi> {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	static const int priority = 10;
 | 
						static const int priority = 5;
 | 
				
			||||||
	/* virtual */ const char *GetName() { return "qt"; }
 | 
						/* virtual */ const char *GetName() { return "qt"; }
 | 
				
			||||||
	/* virtual */ const char *GetDescription() { return "QuickTime MIDI Driver"; }
 | 
						/* virtual */ const char *GetDescription() { return "QuickTime MIDI Driver"; }
 | 
				
			||||||
	/* virtual */ Driver *CreateInstance() { return new MusicDriver_QtMidi(); }
 | 
						/* virtual */ Driver *CreateInstance() { return new MusicDriver_QtMidi(); }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user