Tutorial details

Tutorial 28 - Codea v1.5.2: Objective C Add On, Audio | App Code for Sale | Preview

Tutorial 28 - Codea v1.5.2: Objective C Add On, Audio | iOS Tutorial

How to play an mp3 file from within Codea

Overview PAGE TOP

mukul.png

28.1 Overview PAGE TOP

CodeaAddon is an experimental protocol for adding native extensions to exported Codea projects. You must deal with Lua directly to register your functions and globals. This protocol should be considered alpha in version 1.5.2 and is subject to change in future Codea releases. If the protocol changes we will update the tutorial to reflect this.

As our first example we will demonstrate how to play an mp3 file from within Codea. iOS provides a number of mechanisms for implementing audio playback. The easiest technique from the perspective of the application developer is to use the AVAudioPlayer class which is part of the AV Foundation Framework. Apple recommends that you use this class for audio playback unless you are playing audio captured from a network stream or require very low I/O latency.

Upon instantiating an AVAudioPlayer class, the playback of audio may be controlled and monitored via the methods and properties available. Play, pause and stop methods may be used to control playback and the volume property may be used to adjust the volume level. The playing property may be used to determine whether or not the AVAudioPlayer object is currently playing audio. Using an audio player you can:

  • Play sounds of any duration
  • Play sounds from files or memory buffers
  • Loop sounds
  • Play multiple sounds simultaneously, one sound per audio player, with precise synchronisation
  • Control relative playback level, stereo positioning, and playback rate for each sound you are playing
  • Seek to a particular point in a sound file, which supports such application features as fast forward and rewind
  • Obtain data you can use for playback-level metering

The AV Foundation framework supports the playback of a variety of different audio formats and codecs including both software and hardware based decoding. Codecs and formats currently supported are as follows:

  • AAC (MPEG-4 Advanced Audio Coding)
  • ALAC (Apple Lossless)
  • AMR (Adaptive Multi-rate)
  • HE-AAC (MPEG-4 High Efficiency AAC)
  • iLBC (internet Low Bit Rate Codec)
  • Linear PCM (uncompressed, linear pulse code modulation)
  • MP3 (MPEG-1 audio layer 3)
  • µ-law and a-law

gay.png
Figure 1. Adding the AVFoundation Framework

You can optionally implement a delegate to handle interruptions (such as an incoming phone call), or if required update the user interface when a sound has finished playing.

28.2 Add the AVFoundation Framework to your App PAGE TOP

Since the AVAudioPlayer class is part of the AV Foundation framework, it will be necessary to add this framework to the project. Fire up Xcode and load the exported version of your Codea application (See Tutorial 27). Click on the imported project file at the top left of the project navigator then in the Build Phases tab, scroll down to the link binary with libraries area and select the drop down arrow. Click on the "+" button below your existing frameworks to add a new framework. Find AVFoundation framework and click on "Add" (Figure 1).

lagam.png
Figure 2. MP3 File added to the Resource Group

28.3 Add the MP3 file to your Project PAGE TOP

Any mp3 files that you want to play in your app need to be included in your project bundle. Adding these is simple. For our tutorial we generated a simple loop using Garage Band and then exported it to an mp3 file. Locate your mp3 and drag it from Finder to the Resources Group (folder) in the the Project Navigator of Xcode (Figure 2). We added a sub folder called Music (right click on the Resources Group and add a new Group) to contain the mp3 file but that is optional.

bri.png
Figure 3. Adding a new Objective-C class.

28.4 Create our Custom Audio Add On Class PAGE TOP

To create our new AudioAddOn class, right click on the Addons Group and select New File... from the pop up menu. Click on Cocoa Touch under iOS and make sure that Objective-C class has been selected (Figure 3), then click on Next.

Select Subclass of type NSObject and call the Class AudioAddOn. The two check boxes (Targeted for iPad and With XIB for user interface) should be greyed out. Click on Next, and then click on Create. Two new files will be added to the Addons Group, AudioAddOn.h and AudioAddOn.m.

The header file for our AudioAddOn class is mostly straight forward. We import the AVFoundation framework which we added above and the CodeaAddon class. AudioAddOn conforms to both the CodeaAddon and AVAudioPlayerDelegate protocols. We define a variable called audioAdOnInstance, which allows us to access Objective C methods from within our C functions playMusic() and stopMusic().

In the initialisation of this class we create our audio player object and assign our mp3 file to it. We also pre-buffer the mp3 file and set the number of loops to be infinite (by assigning a negative integer). At the end we assign audioAdOnInstance to self.

Some of the most used AVAudioPlayer delegate methods have been included, but we don't use them in this tutorial. The code is shown at the end of the tutorial.

28.5 Modifications to the Application Delegate PAGE TOP

There are only minor changes to the Application Delegate files. We import our add on in the header file and create an instance variable which points to it (called audioAddOn).

In AppDelegate.mm we instantiate our add on class and then register the add on so we can use it in the Lua code.

To play your mp3 file from within your Lua code just call playMusic() and to stop it, call stopMusic(). Too easy!

1   //
2   //  AppDelegate.h
3   //  LunarLander HD
4   //
5   //  Created by Reefwing Software on Saturday, 13 April 2013
6   //  Copyright (c) Reefwing Software. All rights reserved.
7   //
8   
9   #import <UIKit/UIKit.h>
10  #import "AudioAddon.h"
11  
12  @class CodeaViewController;
13  
14  @interface AppDelegate : UIResponder <UIApplicationDelegate>
15  
16  @property (strong, nonatomic) AudioAddOn *audioAddOn;
17  @property (strong, nonatomic) UIWindow *window;
18  @property (strong, nonatomic) CodeaViewController *viewController;
19  
20  @end

1   //
2   //  AppDelegate.mm
3   //  LunarLander HD
4   //
5   //  Created by Reefwing Software on Saturday, 13 April 2013
6   //  Copyright (c) Reefwing Software. All rights reserved.
7   //
8   
9   #import "AppDelegate.h"
10  #import "CodeaViewController.h"
11  
12  @implementation AppDelegate
13  
14  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
15  {
16      self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
17      self.viewController = [[CodeaViewController alloc] init];
18      
19      //  Create and add our AudioAddOn to Codea
20      
21      self.audioAddOn = [[AudioAddOn alloc] init];
22      [self.viewController registerAddon: self.audioAddOn];
23      
24      NSString* projectPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"LunarLanderHD.codea"];
25      
26      [self.viewController loadProjectAtPath:projectPath];
27      
28      self.window.rootViewController = self.viewController;
29      [self.window makeKeyAndVisible];
30      
31      return YES;
32  }
33  
34  - (void)applicationWillResignActive:(UIApplication *)application
35  {
36  }
37  
38  - (void)applicationDidEnterBackground:(UIApplication *)application
39  {
40  }
41  
42  - (void)applicationWillEnterForeground:(UIApplication *)application
43  {
44  }
45  
46  - (void)applicationDidBecomeActive:(UIApplication *)application
47  {
48  }
49  
50  - (void)applicationWillTerminate:(UIApplication *)application
51  {
52  }
53  
54  @end

1   //
2   //  AudioAddOn.h
3   //  AudioDemo
4   //
5   //  Created by David Such on 13/04/13.
6   //  Copyright (c) 2013 Reefwing Software. All rights reserved.
7   //
8   //  Version:    1.0 - Original (13/04/13)
9   //              1.1 - Volume control & monitoring added, metering enabled (14/04/14)
10  
11  #import <Foundation/Foundation.h>
12  #import <AVFoundation/AVFoundation.h>
13  #import "CodeaAddon.h"
14  
15  id audioAddOnInstance;
16  
17  //  This class conforms to the CodeaAddon & AVAudioPlayerDelegate Protocols 
18  
19  @interface AudioAddOn : NSObject<CodeaAddon, AVAudioPlayerDelegate>
20  
21  @property (strong, nonatomic) AVAudioPlayer *player;
22  
23  //  Forward declare our Lua Audio functions. These are static to confine their scope
24  //  to this file. By default c functions are global.
25  
26  static int playMusic(struct lua_State *state);
27  static int stopMusic(struct lua_State *state);
28  
29  static int getVolume(struct lua_State *state);
30  static int setVolume(struct lua_State *state);
31  
32  static int peakPowerForChannel(struct lua_State *state);
33  static int averagePowerForChannel(struct lua_State *state);
34  
35  @end

1   //
2   //  AudioAddOn.m
3   //  AudioDemo
4   //
5   //  Created by David Such on 13/04/13.
6   //  Copyright (c) 2013 Reefwing Software. All rights reserved.
7   //
8   //  Version:    1.0 - Original (13/04/13)
9   //              1.1 - Volume control & monitoring added, metering enabled (14/04/14)
10  
11  #import "AudioAddOn.h"
12  #import "lua.h"
13  
14  @implementation AudioAddOn
15  
16  #pragma mark - Initialisation
17  
18  - (id)init
19  {
20      self = [super init];
21      if (self)
22      {
23          //  Initialise the Audio Player with your mp3 file.
24          
25          NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
26                                               pathForResource:@"LunarLander"
27                                               ofType:@"mp3"]];
28          
29          NSError *error;
30          
31          _player = [[AVAudioPlayer alloc] initWithContentsOfURL: url error: &error];
32          
33          if (error)
34              NSLog(@"Error initialiasing Audio Add On: %@", [error localizedDescription]);
35          else
36          {
37              // Player initialised, assign delegate to this class
38              
39              [_player setDelegate: self];
40              
41              //  Calling prepareToPlay preloads buffers and acquires the audio hardware needed for playback,
42              //  which minimizes the lag between calling the play method and the start of sound output.
43              
44              [_player prepareToPlay];
45              
46              //  A value of 0, which is the default, means to play the sound once. Set a positive integer
47              //  value to specify the number of times to return to the start and play again. For example,
48              //  specifying a value of 1 results in a total of two plays of the sound. Set any negative
49              //  integer value to loop the sound indefinitely until you call the stop method.
50              
51              [_player setNumberOfLoops: -1];
52              
53              //  The default value for the meteringEnabled property is off (Boolean NO). Before using metering
54              //  for an audio player, you need to enable it by setting this property to YES.
55              
56              [_player setMeteringEnabled: YES];
57              
58              //  audioAddOnInstance allows us to access self from within the c functions.
59              
60              audioAddOnInstance = self;
61          }
62      }
63      return self;
64  }
65  
66  #pragma mark - CodeaAddon Delegate
67  
68  //  Classes which comply with the <CodeaAddon> Protocol must implement this method
69  
70  - (void) codea:(CodeaViewController*)controller didCreateLuaState:(struct lua_State*)L
71  {
72      NSLog(@"AudioAddon Registering Functions");
73      
74      //  Register the Audio functions, defined below
75      
76      lua_register(L, "playMusic", playMusic);
77      lua_register(L, "stopMusic", stopMusic);
78      lua_register(L, "setVolume", setVolume);
79      lua_register(L, "getVolume", getVolume);
80      lua_register(L, "peakPowerForChannel", peakPowerForChannel);
81      lua_register(L, "averagePowerForChannel", averagePowerForChannel);
82  }
83  
84  //  Optional method
85  
86  - (void) codeaWillDrawFrame:(CodeaViewController*)controller withDelta:(CGFloat)deltaTime
87  {
88      
89  }
90  
91  #pragma mark - Audio Add On Functions and associated Methods
92  
93  //  Objective C Methods
94  
95  - (void)startPlayer
96  {
97      [self.player play];
98  }
99  
100 - (void)stopPlayer
101 {
102     if ([self.player isPlaying])
103         [self.player stop];
104 }
105 
106 - (void)setPlayerVolume: (int)setting
107 {
108     // The volume property is the playback gain for the AV audio player object,
109     // it expects a float ranging from 0.0 through 1.0.
110     //
111     // Our Codea slider control returns an integer from 0 to 100 so we need to
112     // convert this to a float in the appropriate range before applying it to
113     // our player. As a defensive measure we will clamp the result between 0.0 and 1.0.
114     
115     float floatSetting = MAX(0.0f, MIN((float)setting / 100.0f, 1.0f));
116     
117     [self.player setVolume: floatSetting];
118 }
119 
120 - (int)getPlayerVolume
121 {
122     return (self.player.volume * 100.0f);
123 }
124 
125 - (int)getPeakPower: (NSUInteger)channel
126 {
127     if ([self.player isPlaying])
128     {
129         //  Refresh the average and peak power values for all channels of our audio player.
130         
131         [self.player updateMeters];
132         
133         //  Peak power is a floating-point representation, in decibels, of a given audio channel’s current peak power.
134         //  A return value of 0 dB indicates full scale, or maximum power while a return value of -160 dB indicates
135         //  minimum power (that is, near silence).
136         //
137         //  If the signal provided to the audio player exceeds ±full scale, then the return value may exceed 0
138         //  (that is, it may enter the positive range).
139         //
140         //  Channel numbers are zero-indexed. A monaural signal, or the left channel of a stereo signal, has channel number 0.
141         
142         float power = -160.0f;  // Initialise to silence
143         
144         if (channel <= [self.player numberOfChannels])
145             power = [self.player peakPowerForChannel: channel];
146         
147         //  Our dial is expecting a value between 0 and 100.
148         
149         if (power >= 0)
150             return 100;
151         else
152         {
153             power += 160.0f;                        //  power is now a +ve float between 0 and 160
154             power = (power / 160.0f) * 100.0f;      //  change to a percentage
155             return (int)power;
156         }
157     }
158     else
159         return 0;
160 }
161 
162 - (int)getAveragePower: (NSUInteger)channel
163 {
164     if ([self.player isPlaying])
165     {
166         //  Refresh the average and peak power values for all channels of our audio player.
167         
168         [self.player updateMeters];
169         
170         //  A floating-point representation, in decibels, of a given audio channel’s current average power.
171         //  A return value of 0 dB indicates full scale, or maximum power; a return value of -160 dB indicates
172         //  minimum power (that is, near silence).
173         //
174         //  If the signal provided to the audio player exceeds ±full scale, then the return value may exceed 0
175         //  (that is, it may enter the positive range).
176         //
177         //  Channel numbers are zero-indexed. A monaural signal, or the left channel of a stereo signal, has channel number 0.
178         
179         float power = -160.0f;  // Initialise to silence
180         
181         if (channel <= [self.player numberOfChannels])
182             power = [self.player averagePowerForChannel: channel];
183         
184         //  Our dial is expecting a value between 0 and 100.
185         
186         if (power >= 0)
187             return 100;
188         else
189         {
190             power += 160.0f;                        //  power is now a +ve float between 0 and 160
191             power = (power / 160.0f) * 100.0f;      //  change to a percentage
192             return (int)power;
193         }
194     }
195     else
196         return 0;
197 }
198 
199 //  C Functions
200 //
201 //  Note that the returned value from all exported Lua functions is how many values that function should return in Lua.
202 //  For example, if you return 0 from that function, you are telling Lua that peakPowerForPlayer (for example) returns 0 values.
203 //  If you return 2, you are telling Lua to expect 2 values on the stack when the function returns.
204 //
205 //  To actually return values, you need to push them onto the Lua stack and then return the number of values you pushed on.
206 
207 static int playMusic(struct lua_State *state)
208 {
209     [audioAddOnInstance startPlayer];
210     return 0;
211 }
212 
213 static int stopMusic(struct lua_State *state)
214 {
215     [audioAddOnInstance stopPlayer];
216     return 0;
217 }
218 
219 static int getVolume(struct lua_State *state)
220 {
221     //  Push the integer volume onto the Lua stack
222     
223     lua_pushinteger(state, [audioAddOnInstance getPlayerVolume]);
224     
225     //  Our function returns 1 value = volume
226     
227     return 1;
228 }
229 
230 static int setVolume(struct lua_State *state)
231 {
232     [audioAddOnInstance setPlayerVolume: lua_tonumber(state, 1)];
233     return 0;
234 }
235 
236 static int peakPowerForChannel(struct lua_State *state)
237 {
238     //  Channel numbers are zero-indexed. A monaural signal,
239     //  or the left channel of a stereo signal, has channel number 0.
240     
241     NSUInteger channel = lua_tonumber(state, 1);
242     
243     //  Push the integer power onto the Lua stack
244     
245     lua_pushinteger(state, [audioAddOnInstance getPeakPower: channel]);
246     
247     //  Our function returns 1 value = peak power
248     
249     return 1; 
250 }
251 
252 static int averagePowerForChannel(struct lua_State *state)
253 {
254     //  Channel numbers are zero-indexed. A monaural signal,
255     //  or the left channel of a stereo signal, has channel number 0.
256     
257     NSUInteger channel = lua_tonumber(state, 1);
258     
259     //  Push the integer power onto the Lua stack
260     
261     lua_pushinteger(state, [audioAddOnInstance getAveragePower: channel]);
262     
263     //  Our function returns 1 value = peak power
264     
265     return 1;
266 }
267 
268 #pragma mark - AVAudioPlayer Delegate
269 
270 //  These Audio Player call back methods are not used in this tutorial but provided here
271 //  for information. There are a number of other delegate methods available. Check the
272 //  documentation.
273 
274 - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
275 {
276     
277 }
278 
279 - (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error
280 {
281     NSLog(@"Error decoding audio file: %@", [error localizedDescription]);
282 }
283 
284 -(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
285 {
286     
287 }
288 
289 -(void)audioPlayerEndInterruption:(AVAudioPlayer *)player
290 {
291     
292 }
293 
294 @end

1   -- AudioDemo
2   --
3   -- Uses Cider Controls v1.6 from @Mark and @aciolino
4   -- make sure you add this as a Dependency
5   --
6   -- Reefwing Software (c) 2013
7   -- www.reefwing.com.au
8   
9   
10  function setup()
11      
12      displayMode(FULLSCREEN)
13      setInstructionLimit(0)
14      ctlFrame = Control("Audio Player", 20, HEIGHT - 600, 450, HEIGHT - 20)
15      ctlFrame.background = color(181, 141, 203, 255)
16      ctlFrame.textAlign = CENTER
17      ctlFrame.fontSize = 24
18      
19      -- Initialise the Cider Controls
20      
21      playBtn = TextButton("Play", 70, 480, 225, 520)
22      stopBtn = TextButton("Stop", 245, 480, 400, 520)
23      sldVolume = Slider("Volume Control", 70, HEIGHT - 450, 400, HEIGHT - 420, 0, 100, 50)
24      
25      -- Initialise the Cider Volume Indicators
26      
27      dial = Dial("Left", 70, 780, 220, 930, 0, 100, 0)
28      doughnut = Doughnut("Right", 250, 780, 400, 930, 0, 100, sldVolume.val)
29      doughnut.intervals = 25
30      doughnut.warm = 9
31      doughnut.hot = 13
32      
33  end
34  
35  function draw()
36      
37      -- This sets a dark background color 
38      
39      background(178, 173, 173, 255)
40  
41      -- Draw the Cider Controls
42      
43      ctlFrame:draw()
44      playBtn:draw()
45      stopBtn:draw()
46      sldVolume:draw()
47      dial:draw()
48      doughnut:draw()
49  
50      -- Update the dB Meter Dials
51      --
52      -- The iPad should have 2 channels, left = 0 and right = 1
53  
54      dial.val = averagePowerForChannel(0) or 0
55      doughnut.val = averagePowerForChannel(1) or 0
56  
57  end
58  
59  function touched(touch)
60      
61      if sldVolume: touched(touch) then
62          -- call AudioAddOn function
63          setVolume(sldVolume.val)
64      end
65      
66      if playBtn:touched(touch) then
67          -- call AudioAddOn function
68          playMusic()
69      end
70      
71      if stopBtn:touched(touch) then
72          -- call AudioAddOn function
73          stopMusic()
74      end
75      
76  end

Reference PAGE TOP

http://codeatuts.blogspot.in/2013/04/tutorial-28-codea-v152-objective-c-add.html

0 Comments Leave a comment

Please login in order to leave a comment.

Newest first
!

Sign-in to your Chupamobile Account.

The Easiest way to Launch your next App or Game.

Join Chupamobile and get instant access to thousands of ready made App and Game Templates.

Creating an account means you’re okay with Chupamobile’s Terms of Service and Privacy Policy.