Background audio in 1.4?
The specs say that background audio is supported in version 1.4, but I can't seem to find how to implement it.
There is a new example window in the new 1.4 kitchen sink, but it doesn't actually play in the background, as it stops when you close the application.
So, does anyone know how to implement this new feature? If there is a new version of Kitchen Sink that has that feature working, or other example code of it working, that would be mighty helpful.
Thanks,
Peter Janett
UPDATE - I have been able to make this work, see the second answer below!
13 Answers
-
Accepted Answer
I posted this question 2 months ago, and today, after much trial and error, and help from this forum (which for this issue was more helpful than paid support), I have found the answers.
So, here's how to do streaming background audio:
1) Include the following (I put it in both app.js and my music window code):
Ti.Media.defaultAudioSessionMode = Ti.Media.AUDIO_SESSION_MODE_PLAYBACK;
2) Use Ti.Media.createAudioPlayer(); as this is the function that actually streams your audio.
3) Edit your info.plist file:
a) Open "YOUR APP FOLDER"/Build/Iphone (You should see BOTH
Info.plist AND Info.plist.template files in this folder.)b) Copy Info.plist into "YOUR APP FOLDER". I did this by right clicking
on Info.plist, choosing "Duplicate", which created "Info copy.plist".
Then drag "Info copy.plist" up 3 directory levels into "YOUR APP
FOLDER". Finally, rename "Info copy.plist" to "Info.plist".c) Open "YOUR APP FOLDER"/Info.plist with a text editor. (I've read
that the plist editor that may be the default program to open the file
will not edit the file correct.)d) Add these lines into "YOUR APP FOLDER"/Info.plist:
<key>UIBackgroundModes</key> <array> <string>audio</string> </array>
4) Test on an iPhone 3GS or an iPhone 4. (The simulator simply won't work, you must test on a real device.)
A few notes, to try to make this as clear as possible. "YOUR APP FOLDER" represents the folder that is the same name as your Titanium Project. (It's the folder that contains the Resources folder where you edit all your JavaScript files.)
Also note that the "Audio Controls", which you see when you double click the home button, and to the left of the running apps on iOS 4, DO NOT WORK with this setup. We'll hope that is fixed in the next version, or that someone here can figure out how to enable those with streaming audio…
I also included:
Ti.App.idleTimerDisabled = true;
In both my app.js and my JS file for the streaming window, which should prevent the device from going to sleep. I haven't yet experimented to see if I want or need to keep this code in the files.
I know this is wordy, but I wanted to try to spell it all out in detail, to save others from all the trial and error.
Special thanks to Eric Telford, who's app showed me that background audio was possible, after I had been told it was not. Eric also shared how easy it really is, and provided the above info showing it was possible.
http://developer.appcelerator.com/question/58471/app-approved—thank-you-to-appceleratorI hope this helps!
Peter Janett
-
Here are the changes that need made to accommodate Background Audio Streaming controls:
MediaModule.h - add the following properties:
@property (nonatomic,readonly) NSNumber *REMOTE_CONTROL_PLAY; @property (nonatomic,readonly) NSNumber *REMOTE_CONTROL_PAUSE; @property (nonatomic,readonly) NSNumber *REMOTE_CONTROL_STOP; @property (nonatomic,readonly) NSNumber *REMOTE_CONTROL_PLAY_PAUSE; @property (nonatomic,readonly) NSNumber *REMOTE_CONTROL_NEXT; @property (nonatomic,readonly) NSNumber *REMOTE_CONTROL_PREV; @property (nonatomic,readonly) NSNumber *REMOTE_CONTROL_START_SEEK_BACK; @property (nonatomic,readonly) NSNumber *REMOTE_CONTROL_END_SEEK_BACK; @property (nonatomic,readonly) NSNumber *REMOTE_CONTROL_START_SEEK_FORWARD; @property (nonatomic,readonly) NSNumber *REMOTE_CONTROL_END_SEEK_FORWARD;
MediaModule.m - create system properties using the following:
MAKE_SYSTEM_PROP(REMOTE_CONTROL_PLAY,UIEventSubtypeRemoteControlPlay); MAKE_SYSTEM_PROP(REMOTE_CONTROL_PAUSE,UIEventSubtypeRemoteControlPause); MAKE_SYSTEM_PROP(REMOTE_CONTROL_STOP,UIEventSubtypeRemoteControlStop); MAKE_SYSTEM_PROP(REMOTE_CONTROL_PLAY_PAUSE,UIEventSubtypeRemoteControlTogglePlayPause); MAKE_SYSTEM_PROP(REMOTE_CONTROL_NEXT,UIEventSubtypeRemoteControlNextTrack); MAKE_SYSTEM_PROP(REMOTE_CONTROL_PREV,UIEventSubtypeRemoteControlPreviousTrack); MAKE_SYSTEM_PROP(REMOTE_CONTROL_START_SEEK_BACK,UIEventSubtypeRemoteControlBeginSeekingBackward); MAKE_SYSTEM_PROP(REMOTE_CONTROL_END_SEEK_BACK,UIEventSubtypeRemoteControlEndSeekingBackward); MAKE_SYSTEM_PROP(REMOTE_CONTROL_START_SEEK_FORWARD,UIEventSubtypeRemoteControlBeginSeekingForward); MAKE_SYSTEM_PROP(REMOTE_CONTROL_END_SEEK_FORWARD,UIEventSubtypeRemoteControlEndSeekingForward);
TiMediaAudioPlayerProxy.h - add a private fireRemoteControlEvent variable:
BOOL fireRemoteControlEvents;
TiMediaAudioPlayerProxy.m - make changes/create to the following functions:
-(void)_initWithProperties:(NSDictionary *)properties { volume = [TiUtils doubleValue:@"volume" properties:properties def:1.0]; url = [[TiUtils toURL:[properties objectForKey:@"url"] proxy:self] retain]; int initialMode = [TiUtils intValue:@"audioSessionMode" properties:properties def:0]; if (initialMode) { [self setAudioSessionMode:[NSNumber numberWithInt:initialMode]]; } // default handlePlayRemoteControls to true bool handlePlayRemoteControls = [TiUtils boolValue:@"handlePlayRemoteControls" properties:properties def:YES]; [self setValue:NUMBOOL(handlePlayRemoteControls) forKey:@"handlePlayRemoteControls"]; WARN_IF_BACKGROUND_THREAD_OBJ; //NSNotificationCenter is not threadsafe! [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(remoteControlEvent:) name:kTiRemoteControlNotification object:nil]; } -(void)_listenerAdded:(NSString *)type count:(int)count { if (count == 1 && [type isEqualToString:@"progress"]) { progress = YES; } if (count == 1 && [type isEqualToString:@"remoteControl"]) { fireRemoteControlEvents = YES; } } -(void)_listenerRemoved:(NSString *)type count:(int)count { if (count == 0 && [type isEqualToString:@"progress"]) { progress = NO; } if (count == 0 && [type isEqualToString:@"remoteControl"]) { fireRemoteControlEvents = NO; } } - (void)remoteControlEvent:(NSNotification*)note { UIEvent *uiEvent = [[note userInfo] objectForKey:@"event"]; if (fireRemoteControlEvents) { NSDictionary *event = [NSDictionary dictionaryWithObject:NUMINT(uiEvent.subtype) forKey:@"controlType"]; [self fireEvent:@"remoteControl" withObject:event]; } if (![TiUtils boolValue:[self valueForKey:@"handlePlayRemoteControls"]]) { return; } switch(uiEvent.subtype) { case UIEventSubtypeRemoteControlTogglePlayPause: { if ([player isPaused]) { [self start:nil]; } else { [self pause:nil]; } break; } case UIEventSubtypeRemoteControlPause: { [self pause:nil]; break; } case UIEventSubtypeRemoteControlStop: { [self stop:nil]; break; } case UIEventSubtypeRemoteControlPlay: { [self start:nil]; break; } default: break; } }
Then in your Titanium Mobile application you can do the following:
audioPlayer.addEventListener('remoteControl',function(e) { var handledString = ''; // if we set the 'handlePlayRemoteControls' property of our Ti.Media.AudioPlayer to false, // then we will need to handle all remote control events // otherwise: play, pause, stop, and play / pause toggle will be handled for us if (audioPlayer.handlePlayRemoteControls) { handledString = ' - will be handled by our Ti.Media.AudioPlayer'; } switch(e.controlType) { case Ti.Media.REMOTE_CONTROL_PLAY: Ti.API.info('remote control - play' + handledString); break; case Ti.Media.REMOTE_CONTROL_PAUSE: Ti.API.info('remote control - pause' + handledString); break; case Ti.Media.REMOTE_CONTROL_STOP: Ti.API.info('remote control - stop' + handledString); break; case Ti.Media.REMOTE_CONTROL_PLAY_PAUSE: Ti.API.info('remote control - play / pause toggle' + handledString); break; case Ti.Media.REMOTE_CONTROL_NEXT: Ti.API.info('remote control - next track'); break; case Ti.Media.REMOTE_CONTROL_PREV: Ti.API.info('remote control - prev track'); break; case Ti.Media.REMOTE_CONTROL_START_SEEK_BACK: Ti.API.info('remote control - seek back - start'); break; case Ti.Media.REMOTE_CONTROL_END_SEEK_BACK: Ti.API.info('remote control - seek back - end'); break; case Ti.Media.REMOTE_CONTROL_START_SEEK_FORWARD: Ti.API.info('remote control - seek forward - start'); break; case Ti.Media.REMOTE_CONTROL_END_SEEK_FORWARD: Ti.API.info('remote control - seek forward - end'); break; } });
NOTE: This code came from Instant Automatic's titanium_mobile branch https://github.com/instantautomatic/titanium_mobile
-
I was talking about how background audio works if, for example, one wants to play a mp3 file. I had to struggle with guys at Apple and understood that it is not an easy task as one may think.
I hope Titanium will support this, but I suggest to give a chance to xCode by now to get these results. As I pointed out, the background audio implies some modifications in plist and AppDelegate. The biggest problem was however to handle the new lifecycle methods implied with background. For example, audio can quite easily play in background but forget that iPhone handles it if you receive a phone call. You have to invoke a delegate method of AVAudioPlayer framework to do this. So, it's not so easy as one may think.
The biggest obstacle was to have to do test renting an iPhone 3GS, because iPhone 3G is not supporting anything in background and simulators don't support audio while multitasking (Apple confirmed it).
Another problem is that customers of applications seem to be very unhappy, claiming to have not a multitasking audio solution, when developers had months to update apps (when SDK 4 was in beta). But they are not completely right. being everything under NDA, you could even not talk about it in forums, so that only information could come from a very uncompleted documentation from Apple pr from support, which one pays.
So, I suggest to use xCode by now for this. This stated, Titanium rocks :)
-
I want to know this, too. Please don't tell me you can only play iPod songs in the background, I want to be able to stream audio in the background. Thanks.
-
I don't know the solution, but I suggest to consider that:
- background audio is not possible on iOS4 if the iPhone is a 3G one;
- the simulator can NOT play background audio.
To test this you need to:
- build against SDK 4;
- test with a real iPhone 3GS or 4
p.s. do NOT test with iPad, because SDK 4 does not exist, it will be available in September…
I suggest to read my next post as well about confusion with SDK.
Hope it helps…
Fabio -
Hi,
the answer , is "YES YOU CANT" :(
The background logic functionality and the background engine will be available in the next release, titanium sdk 1.5Background audio has to be intended from ipod playlist integration and not streaming.
https://developer.appcelerator.com/apidoc/mobile/1.4/changelog.html
Hope some guys at appcell tell me that im wrong too.
Andrea -
Aww, come on Appcelerator. Why don't you guys completely support background radio streaming.. My users are eagerly waiting for it!
-
BUMP
-
I signed up for pro support and asked that this featured be made a priority to be added into Titanium mobile.
https://appcelerator.lighthouseapp.com/projects/32238/tickets/1671-add-streaming-audio-support-for-background-audio-ios-4
Peter Janett
http://www.NewMediaOne.net
-
My app has links to MP3 that are on my server. I was about to implement these tips and had just added the UIBackgroundModes node in Info.plist and got sidetracked. Turns out just adding that line made background audio work for me with the default QuickTime player. The controller (pause play, etc) in the multitasking dock and on the lock screen also work. Boy. That was easy ;)
Not sure if you all were trying to do more than that but just wanted to share my experience.
-
thank you Peter. Any update on your app? is it already working with the "audio controls" outside app? and earphone remote maybe?
-
worked great for me, thanks for the info!
-
25