Last active
December 16, 2015 04:29
-
-
Save reefwing/5377746 to your computer and use it in GitHub Desktop.
Tutorial 28 - Codea Audio Player Add On (Enhanced)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// AppDelegate.h | |
// LunarLander HD | |
// | |
// Created by Reefwing Software on Saturday, 13 April 2013 | |
// Copyright (c) Reefwing Software. All rights reserved. | |
// | |
#import <UIKit/UIKit.h> | |
#import "AudioAddon.h" | |
@class CodeaViewController; | |
@interface AppDelegate : UIResponder <UIApplicationDelegate> | |
@property (strong, nonatomic) AudioAddOn *audioAddOn; | |
@property (strong, nonatomic) UIWindow *window; | |
@property (strong, nonatomic) CodeaViewController *viewController; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// AppDelegate.mm | |
// LunarLander HD | |
// | |
// Created by Reefwing Software on Saturday, 13 April 2013 | |
// Copyright (c) Reefwing Software. All rights reserved. | |
// | |
#import "AppDelegate.h" | |
#import "CodeaViewController.h" | |
@implementation AppDelegate | |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions | |
{ | |
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; | |
self.viewController = [[CodeaViewController alloc] init]; | |
// Create and add our AudioAddOn to Codea | |
self.audioAddOn = [[AudioAddOn alloc] init]; | |
[self.viewController registerAddon: self.audioAddOn]; | |
NSString* projectPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"LunarLanderHD.codea"]; | |
[self.viewController loadProjectAtPath:projectPath]; | |
self.window.rootViewController = self.viewController; | |
[self.window makeKeyAndVisible]; | |
return YES; | |
} | |
- (void)applicationWillResignActive:(UIApplication *)application | |
{ | |
} | |
- (void)applicationDidEnterBackground:(UIApplication *)application | |
{ | |
} | |
- (void)applicationWillEnterForeground:(UIApplication *)application | |
{ | |
} | |
- (void)applicationDidBecomeActive:(UIApplication *)application | |
{ | |
} | |
- (void)applicationWillTerminate:(UIApplication *)application | |
{ | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// AudioAddOn.h | |
// 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/14) | |
#import <Foundation/Foundation.h> | |
#import <AVFoundation/AVFoundation.h> | |
#import "CodeaAddon.h" | |
id audioAddOnInstance; | |
// This class conforms to the CodeaAddon & AVAudioPlayerDelegate Protocols | |
@interface AudioAddOn : NSObject<CodeaAddon, AVAudioPlayerDelegate> | |
@property (strong, nonatomic) AVAudioPlayer *player; | |
// Forward declare our Lua Audio functions. These are static to confine their scope | |
// to this file. By default c functions are global. | |
static int playMusic(struct lua_State *state); | |
static int stopMusic(struct lua_State *state); | |
static int getVolume(struct lua_State *state); | |
static int setVolume(struct lua_State *state); | |
static int peakPowerForChannel(struct lua_State *state); | |
static int averagePowerForChannel(struct lua_State *state); | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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/14) | |
#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); | |
} | |
// Optional method | |
- (void) codeaWillDrawFrame:(CodeaViewController*)controller withDelta:(CGFloat)deltaTime | |
{ | |
} | |
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- AudioDemo | |
-- | |
-- Uses Cider Controls v1.6 from @Mark and @aciolino | |
-- make sure you add this as a Dependency | |
-- | |
-- Reefwing Software (c) 2013 | |
-- www.reefwing.com.au | |
function setup() | |
displayMode(FULLSCREEN) | |
setInstructionLimit(0) | |
ctlFrame = Control("Audio Player", 20, HEIGHT - 600, 450, HEIGHT - 20) | |
ctlFrame.background = color(181, 141, 203, 255) | |
ctlFrame.textAlign = CENTER | |
ctlFrame.fontSize = 24 | |
-- Initialise the Cider Controls | |
playBtn = TextButton("Play", 70, 480, 225, 520) | |
stopBtn = TextButton("Stop", 245, 480, 400, 520) | |
sldVolume = Slider("Volume Control", 70, HEIGHT - 450, 400, HEIGHT - 420, 0, 100, 50) | |
-- Initialise the Cider Volume Indicators | |
dial = Dial("Left", 70, 780, 220, 930, 0, 100, 0) | |
doughnut = Doughnut("Right", 250, 780, 400, 930, 0, 100, sldVolume.val) | |
doughnut.intervals = 25 | |
doughnut.warm = 9 | |
doughnut.hot = 13 | |
end | |
function draw() | |
-- This sets a dark background color | |
background(178, 173, 173, 255) | |
-- Draw the Cider Controls | |
ctlFrame:draw() | |
playBtn:draw() | |
stopBtn:draw() | |
sldVolume:draw() | |
dial:draw() | |
doughnut:draw() | |
-- Update the dB Meter Dials | |
-- | |
-- The iPad should have 2 channels, left = 0 and right = 1 | |
dial.val = averagePowerForChannel(0) or 0 | |
doughnut.val = averagePowerForChannel(1) or 0 | |
end | |
function touched(touch) | |
if sldVolume: touched(touch) then | |
-- call AudioAddOn function | |
setVolume(sldVolume.val) | |
end | |
if playBtn:touched(touch) then | |
-- call AudioAddOn function | |
playMusic() | |
end | |
if stopBtn:touched(touch) then | |
-- call AudioAddOn function | |
stopMusic() | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment