(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
 | 
			
		||||
		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
 | 
			
		||||
			CFLAGS="$CFLAGS -DENABLE_COCOA_QUARTZ"
 | 
			
		||||
 
 | 
			
		||||
@@ -327,6 +327,7 @@ zoom_type.h
 | 
			
		||||
#if WIN32
 | 
			
		||||
#else
 | 
			
		||||
music/bemidi.h
 | 
			
		||||
music/cocoa_m.h
 | 
			
		||||
music/extmidi.h
 | 
			
		||||
music/libtimidity.h
 | 
			
		||||
music/os2_m.h
 | 
			
		||||
@@ -919,6 +920,7 @@ sound/null_s.cpp
 | 
			
		||||
		video/cocoa/fullscreen.mm
 | 
			
		||||
		video/cocoa/wnd_quartz.mm
 | 
			
		||||
		video/cocoa/wnd_quickdraw.mm
 | 
			
		||||
		music/cocoa_m.cpp
 | 
			
		||||
		sound/cocoa_s.cpp
 | 
			
		||||
		os/macosx/splash.cpp
 | 
			
		||||
	#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> {
 | 
			
		||||
public:
 | 
			
		||||
	static const int priority = 10;
 | 
			
		||||
	static const int priority = 5;
 | 
			
		||||
	/* virtual */ const char *GetName() { return "qt"; }
 | 
			
		||||
	/* virtual */ const char *GetDescription() { return "QuickTime MIDI Driver"; }
 | 
			
		||||
	/* virtual */ Driver *CreateInstance() { return new MusicDriver_QtMidi(); }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user