Skip to content

Instantly share code, notes, and snippets.

@reefwing
Last active December 16, 2015 04:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save reefwing/5377746 to your computer and use it in GitHub Desktop.
Save reefwing/5377746 to your computer and use it in GitHub Desktop.
Tutorial 28 - Codea Audio Player Add On (Enhanced)
//
// 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
//
// 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
//
// 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
//
// 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
-- 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