|
// |
|
// AudioAddOn.m |
|
// AudioDemo |
|
// |
|
// Created by David Such on 13/04/13. |
|
// Copyright (c) 2013 Reefwing Software. All rights reserved. |
|
// |
|
// Version: 1.0 - Original (13/04/13) |
|
// 1.1 - Volume control & monitoring added, metering enabled (14/04/13) |
|
// 1.2 - Minor refactoring (19/04/13) |
|
|
|
#import "AudioAddOn.h" |
|
#import "lua.h" |
|
|
|
@implementation AudioAddOn |
|
|
|
#pragma mark - Initialisation |
|
|
|
- (id)init |
|
{ |
|
self = [super init]; |
|
if (self) |
|
{ |
|
// Initialise the Audio Player with your mp3 file. |
|
|
|
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] |
|
pathForResource:@"LunarLander" |
|
ofType:@"mp3"]]; |
|
|
|
NSError *error; |
|
|
|
_player = [[AVAudioPlayer alloc] initWithContentsOfURL: url error: &error]; |
|
|
|
if (error) |
|
NSLog(@"Error initialiasing Audio Add On: %@", [error localizedDescription]); |
|
else |
|
{ |
|
// Player initialised, assign delegate to this class |
|
|
|
[_player setDelegate: self]; |
|
|
|
// Calling prepareToPlay preloads buffers and acquires the audio hardware needed for playback, |
|
// which minimizes the lag between calling the play method and the start of sound output. |
|
|
|
[_player prepareToPlay]; |
|
|
|
// A value of 0, which is the default, means to play the sound once. Set a positive integer |
|
// value to specify the number of times to return to the start and play again. For example, |
|
// specifying a value of 1 results in a total of two plays of the sound. Set any negative |
|
// integer value to loop the sound indefinitely until you call the stop method. |
|
|
|
[_player setNumberOfLoops: -1]; |
|
|
|
// The default value for the meteringEnabled property is off (Boolean NO). Before using metering |
|
// for an audio player, you need to enable it by setting this property to YES. |
|
|
|
[_player setMeteringEnabled: YES]; |
|
|
|
// audioAddOnInstance allows us to access self from within the c functions. |
|
|
|
audioAddOnInstance = self; |
|
} |
|
} |
|
return self; |
|
} |
|
|
|
#pragma mark - CodeaAddon Delegate |
|
|
|
// Classes which comply with the <CodeaAddon> Protocol must implement this method |
|
|
|
- (void) codea:(CodeaViewController*)controller didCreateLuaState:(struct lua_State*)L |
|
{ |
|
NSLog(@"AudioAddon Registering Functions"); |
|
|
|
// Register the Audio functions, defined below |
|
|
|
lua_register(L, "playMusic", playMusic); |
|
lua_register(L, "stopMusic", stopMusic); |
|
lua_register(L, "setVolume", setVolume); |
|
lua_register(L, "getVolume", getVolume); |
|
lua_register(L, "peakPowerForChannel", peakPowerForChannel); |
|
lua_register(L, "averagePowerForChannel", averagePowerForChannel); |
|
} |
|
|
|
#pragma mark - Audio Add On Functions and associated Methods |
|
|
|
// Objective C Methods |
|
|
|
- (void)startPlayer |
|
{ |
|
[self.player play]; |
|
} |
|
|
|
- (void)stopPlayer |
|
{ |
|
if ([self.player isPlaying]) |
|
[self.player stop]; |
|
} |
|
|
|
- (void)setPlayerVolume: (int)setting |
|
{ |
|
// The volume property is the playback gain for the AV audio player object, |
|
// it expects a float ranging from 0.0 through 1.0. |
|
// |
|
// Our Codea slider control returns an integer from 0 to 100 so we need to |
|
// convert this to a float in the appropriate range before applying it to |
|
// our player. As a defensive measure we will clamp the result between 0.0 and 1.0. |
|
|
|
float floatSetting = MAX(0.0f, MIN((float)setting / 100.0f, 1.0f)); |
|
|
|
[self.player setVolume: floatSetting]; |
|
} |
|
|
|
- (int)getPlayerVolume |
|
{ |
|
return (self.player.volume * 100.0f); |
|
} |
|
|
|
- (int)getPeakPower: (NSUInteger)channel |
|
{ |
|
if ([self.player isPlaying]) |
|
{ |
|
// Refresh the average and peak power values for all channels of our audio player. |
|
|
|
[self.player updateMeters]; |
|
|
|
// Peak power is a floating-point representation, in decibels, of a given audio channel’s current peak power. |
|
// A return value of 0 dB indicates full scale, or maximum power while a return value of -160 dB indicates |
|
// minimum power (that is, near silence). |
|
// |
|
// If the signal provided to the audio player exceeds ±full scale, then the return value may exceed 0 |
|
// (that is, it may enter the positive range). |
|
// |
|
// Channel numbers are zero-indexed. A monaural signal, or the left channel of a stereo signal, has channel number 0. |
|
|
|
float power = -160.0f; // Initialise to silence |
|
|
|
if (channel <= [self.player numberOfChannels]) |
|
power = [self.player peakPowerForChannel: channel]; |
|
|
|
// Our dial is expecting a value between 0 and 100. |
|
|
|
if (power >= 0) |
|
return 100; |
|
else |
|
{ |
|
power += 160.0f; // power is now a +ve float between 0 and 160 |
|
power = (power / 160.0f) * 100.0f; // change to a percentage |
|
return (int)power; |
|
} |
|
} |
|
else |
|
return 0; |
|
} |
|
|
|
- (int)getAveragePower: (NSUInteger)channel |
|
{ |
|
if ([self.player isPlaying]) |
|
{ |
|
// Refresh the average and peak power values for all channels of our audio player. |
|
|
|
[self.player updateMeters]; |
|
|
|
// A floating-point representation, in decibels, of a given audio channel’s current average power. |
|
// A return value of 0 dB indicates full scale, or maximum power; a return value of -160 dB indicates |
|
// minimum power (that is, near silence). |
|
// |
|
// If the signal provided to the audio player exceeds ±full scale, then the return value may exceed 0 |
|
// (that is, it may enter the positive range). |
|
// |
|
// Channel numbers are zero-indexed. A monaural signal, or the left channel of a stereo signal, has channel number 0. |
|
|
|
float power = -160.0f; // Initialise to silence |
|
|
|
if (channel <= [self.player numberOfChannels]) |
|
power = [self.player averagePowerForChannel: channel]; |
|
|
|
// Our dial is expecting a value between 0 and 100. |
|
|
|
if (power >= 0) |
|
return 100; |
|
else |
|
{ |
|
power += 160.0f; // power is now a +ve float between 0 and 160 |
|
power = (power / 160.0f) * 100.0f; // change to a percentage |
|
return (int)power; |
|
} |
|
} |
|
else |
|
return 0; |
|
} |
|
|
|
// C Functions |
|
// |
|
// Note that the returned value from all exported Lua functions is how many values that function should return in Lua. |
|
// For example, if you return 0 from that function, you are telling Lua that peakPowerForPlayer (for example) returns 0 values. |
|
// If you return 2, you are telling Lua to expect 2 values on the stack when the function returns. |
|
// |
|
// To actually return values, you need to push them onto the Lua stack and then return the number of values you pushed on. |
|
|
|
static int playMusic(struct lua_State *state) |
|
{ |
|
[audioAddOnInstance startPlayer]; |
|
return 0; |
|
} |
|
|
|
static int stopMusic(struct lua_State *state) |
|
{ |
|
[audioAddOnInstance stopPlayer]; |
|
return 0; |
|
} |
|
|
|
static int getVolume(struct lua_State *state) |
|
{ |
|
// Push the integer volume onto the Lua stack |
|
|
|
lua_pushinteger(state, [audioAddOnInstance getPlayerVolume]); |
|
|
|
// Our function returns 1 value = volume |
|
|
|
return 1; |
|
} |
|
|
|
static int setVolume(struct lua_State *state) |
|
{ |
|
[audioAddOnInstance setPlayerVolume: lua_tonumber(state, 1)]; |
|
return 0; |
|
} |
|
|
|
static int peakPowerForChannel(struct lua_State *state) |
|
{ |
|
// Channel numbers are zero-indexed. A monaural signal, |
|
// or the left channel of a stereo signal, has channel number 0. |
|
|
|
NSUInteger channel = lua_tonumber(state, 1); |
|
|
|
// Push the integer power onto the Lua stack |
|
|
|
lua_pushinteger(state, [audioAddOnInstance getPeakPower: channel]); |
|
|
|
// Our function returns 1 value = peak power |
|
|
|
return 1; |
|
} |
|
|
|
static int averagePowerForChannel(struct lua_State *state) |
|
{ |
|
// Channel numbers are zero-indexed. A monaural signal, |
|
// or the left channel of a stereo signal, has channel number 0. |
|
|
|
NSUInteger channel = lua_tonumber(state, 1); |
|
|
|
// Push the integer power onto the Lua stack |
|
|
|
lua_pushinteger(state, [audioAddOnInstance getAveragePower: channel]); |
|
|
|
// Our function returns 1 value = peak power |
|
|
|
return 1; |
|
} |
|
|
|
#pragma mark - AVAudioPlayer Delegate |
|
|
|
// These Audio Player call back methods are not used in this tutorial but provided here |
|
// for information. There are a number of other delegate methods available. Check the |
|
// documentation. |
|
|
|
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag |
|
{ |
|
|
|
} |
|
|
|
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error |
|
{ |
|
NSLog(@"Error decoding audio file: %@", [error localizedDescription]); |
|
} |
|
|
|
-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player |
|
{ |
|
|
|
} |
|
|
|
-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player |
|
{ |
|
|
|
} |
|
|
|
@end |