I am trying to play two different files in a queue. One is an intro track and another is a main track.
My Method to setup music playback
-(void)musicPlayBack:(CFStringRef )soundName{
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef;
soundFileURLRef = CFBundleCopyResourceURL(mainBundle
, (CFStringRef) soundName, CFSTR ("mp3"), NULL);
SystemSoundID soundID;
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, FinishedSoundPlayback, NULL);
AudioServicesPlaySystemSound(soundID);
}
-(void)playIntro:(NSString *)introTrackName{
NSArray *introTracks = [NSArray arrayWithObjects:#"heybaby",#"dumbson",#"heybadass",
#"heydarling",#"heyhoney",#"madam",#"sir", nil];
[self musicPlayBack:(__bridge CFStringRef)([introTracks objectAtIndex:2])];
}
-(void)playNotification:(NSString *)notificationTrackName{
[self musicPlayBack:(__bridge CFStringRef)(notificationTrackName)];
}
-(void)notifyWithSound:(int)batteryCase{
NSArray *mainSoundTracks = [NSArray arrayWithObjects:#"ten",#"twenty",#"twentyfive",#"thirty",
#"fourty",#"fifty",#"sixty",#"seventy",#"seventyfive",
#"eigthy",#"ninty",#"hundred",nil];
[self playIntro:#"someRandomIntro"];
while (introFinished==NO) {
if (introFinished==YES) {
break;
}
}
[self playNotification:[mainSoundTracks objectAtIndex:batteryCase]];
}
This is what i have before #implementation
BOOL introFinished = NO;
static void FinishedSoundPlayback(SystemSoundID mySSID, void* data){
introFinished = YES;
AudioServicesRemoveSystemSoundCompletion (mySSID);
NSLog(#"completion Callback");
}
The problem is FinishedSoundPlayback never gets called.
You registered the system sound under the name "soundId" and you expect callback for "mySSID". This suppose to be the same name. BTW I do not recommend this API for playing music because of its limitation.
Related
I am creating MIDI sounds with MusicDeviceMIDIEvent and it's perfectly fine, I use some aupreset files I've created. One for each instrument.
My code is basically the example from apple: LoadPresetDemo. All I use is:
- (void)loadPreset:(id)sender instrumentName:(const char *)instrumentName {
NSString *presetURL1 = [NSString stringWithCString:instrumentName encoding:NSUTF8StringEncoding];
NSString *path = [[NSBundle mainBundle] pathForResource:presetURL1 ofType:#"aupreset"];
NSURL *presetURL = [[NSURL alloc] initFileURLWithPath: path];
if (presetURL) {
NSLog(#"Attempting to load preset '%#'\n", [presetURL description]);
}
else {
NSLog(#"COULD NOT GET PRESET PATH!");
}
[self loadSynthFromPresetURL: presetURL];
}
To load my aupreset file, then:
- (void) noteOn:(id)sender midiNumber:(int)midiNumber {
UInt32 noteNum = midiNumber;
UInt32 onVelocity = 127;
UInt32 noteCommand = kMIDIMessage_NoteOn << 4 | 0;
OSStatus result = noErr;
require_noerr (result = MusicDeviceMIDIEvent (self.samplerUnit, noteCommand, noteNum, onVelocity, 0), logTheError);
logTheError:
if (result != noErr) NSLog (#"Unable to start playing the low note. Error code: %d '%.4s'\n", (int) result, (const char *)&result);
}
to play the note of the previously loaded aupreset, and:
- (void) noteOff:(id)sender midiNumber:(int)midiNumber
when I want it to stop.
Now I would like to play one note of each instrument simultaneously. What is the easiest way of doing this?
1 button, plays 10 sounds?
How can I get a button to play some sounds in on order?
How can I add an extra sound to this action?
-(IBAction)sound1
{
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef;
soundFileURLRef = CFBundleCopyResourceURL(mainBundle, (CFStringRef) #"sound1", CFSTR("wav"), NULL);
UInt32 soundID;
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
AudioServicesPlaySystemSound(soundID);
}
I've had the best luck with AVAudioPlayer - it's a library called AVFoundation you can import through "Build Phases" (first click on the blue xcode project name at top left) and then "Link Binary with Libraries"
Then try this really simple YouTube tutorial to make a button play sound:
http://youtu.be/kCpw6iP90cY
That's the same video I used 2 years ago to create my first sound board. Xcode 5 is a little different but the code will all work.
Ok now you need to create an array of these sounds that will cycle through them. Check out this link from TreeHouse:
https://teamtreehouse.com/forum/creating-an-array-with-mp3-sound-files
If the sounds are name sound0 … soundN, you can just introduce to ivars — one tracks the current index, one defines the number of sounds.
#implementation MyClass {
NSUInteger soundIdx;
NSUInteger soundCount;
}
-(instancetype) init //or any other entry point method like viewDidLoad,....
{
self = [super init];
if (self) {
soundCount = 10;
}
return self;
}
-(IBAction)sound
{
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef;
soundFileURLRef = CFBundleCopyResourceURL(mainBundle, (CFStringRef) [NSString stringWithFormat:#"sound%lu", soundIdx], CFSTR("wav"), NULL);
UInt32 soundID;
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
AudioServicesPlaySystemSound(soundID);
soundIdx = (++soundIdx) % soundCount;
}
#end
If the sound names do not follow any particular naming convention, you could put them in an array
#implementation MyClass {
NSUInteger soundIdx;
NSArray *soundNames;
}
-(instancetype) init //or any other entry point method like viewDidLoad,....
{
self = [super init];
if (self) {
soundNames = #[#"sound1",#"hello", #"ping"];
}
return self;
}
-(IBAction)sound
{
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef;
soundFileURLRef = CFBundleCopyResourceURL(mainBundle, (CFStringRef) soundNames[soundIdx], CFSTR("wav"), NULL);
UInt32 soundID;
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
AudioServicesPlaySystemSound(soundID);
soundIdx = (++soundIdx) % [soundNames count];
}
#end
I want to repeat the sounds that are send to this method?
- (void) playSound:(CFURLRef)soundFileURLRef {
SystemSoundID soundID;
OSStatus errorCode = AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
if (errorCode != 0) {
}else{
AudioServicesPlaySystemSound(soundID);
}
}
I've seen answers on how this is made using numberOfRepeats. But I can't fit it in my code.
Try using AVAudioPlayer:
- (void) playSound:(CFURLRef)soundFileURLRef {
NSError* sndErr;
AVAudioPlayer *player = [[ AVAudioPlayer alloc ] initWithContentsOfURL:(NSURL *)soundFileURLRef error:(&sndErr) ];
// at this point player has a retain count of 1. you should save it
// somewhere like a class member variable so you can stop the playing
// and release the object later. Under ARC, you must have a strong reference
// to the object so that it doesn't get released when this function goes out
// of scope.
if (sndErr != nil) {
player.numberOfLoops = -1; // infinite number of loops. call stop when you want to stop.
[player play];
}
}
I want to play a system sound but I don't hear anything:
- (void) play_system_sound
{
NSLog(#"Play system sound");
SystemSoundID soundID;
NSString *path = [[NSBundle bundleWithIdentifier:#"com.apple.UIKit"] pathForResource:#"Tock" ofType:#"aiff"];
NSLog(#"path = %#", path );
AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:path], &soundID);
AudioServicesPlaySystemSound(soundID);
AudioServicesDisposeSystemSoundID(soundID);
NSLog(#"Play system sound DONE");
}
I don't hear anything. What am I doing wrong?
After AudioServicesPlaySystemSound(soundID) ,don't call AudioServicesDisposeSystemSoundID(soundID) immediately.
Try this :
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle bundleWithIdentifier:#"com.apple.UIKit"] pathForResource:#"Tock" ofType:#"aiff"];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:path], &soundID);
}
-(void)dealloc
{
AudioServicesDisposeSystemSoundID(soundID);
}
- (void)play_system_sound
{
AudioServicesPlaySystemSound(soundID);
}
I don't know if you have solved the problem, but for me, on iOS 7, sometimes the (__bridge CFURLRef) casting won't work, in which case, if you print out the result of (__bridge CFURLRef)[NSURL fileURLWithPath:path], it will be 0x0. This leads to failure of AudioServicesCreateSystemSoundID(). The return value of the function is type of OSStatus. If it fails, the function will return -50, which means one or more parameters are invalid. (If it executes successfully, it returns 0.)
I solved this by using the C style function getting the path of the file:
CFBundleRef mainBundle = CFBundleGetMainBundle ();
CFURLRef soundFileUrl;
soundFileUrl = CFBundleCopyResourceURL(mainBundle, CFSTR("fileName"), CFSTR("wav"), NULL);
AudioServicesCreateSystemSoundID(soundFileUrl, &soundID);
I don't have the "[sender resignFirstResponder]" method called, but my keyboard still closes when the done button is pressed. I need it to stay open, even when I click done. How can I go about doing this?
Here's my action that controls the keyboard:
- (IBAction)returnKeyButton:(id)sender {
BOOL guessCorrect = [gameModel checkGuess:guessTextField.text];
guessTextField.text = #"";
if (guessCorrect) {
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef;
soundFileURLRef = CFBundleCopyResourceURL(mainBundle, (CFStringRef) #"rightAnswer", CFSTR ("mp3"), NULL);
UInt32 soundID;
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
AudioServicesPlaySystemSound(soundID);
if (gameModel.score == 10) {
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef;
soundFileURLRef = CFBundleCopyResourceURL(mainBundle, (CFStringRef) #"win", CFSTR ("mp3"), NULL);
UInt32 soundID;
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
AudioServicesPlaySystemSound(soundID);
[self endGameWithMessage:#"You win!"];
} else {
scrambledWord.text = [gameModel getScrambledWord];
}
}
remainingTime.text = [NSString stringWithFormat:#"%i", gameModel.time];
playerScore.text = [NSString stringWithFormat:#"%i", gameModel.score];
}
First of... to be honest, I have never had the problem you have. I always had to use the resign call to close the keyboard or it would stay open... But, this is what I used for you:
1) Put this in the .h file:
-(IBAction) textFieldDoneEditing : (id) sender;
2) Put this in the .m file:
-(IBAction) textFieldDoneEditing : (id) sender{
[sender resignFirstResponder];
[sender becomeFirstResponder];
}
3) Right click the UITextField in the storyboard and set the Sent Event "Editing Did End" to the textFieldDoneEditing method in the view controller.
Now, whenever the user presses "Return" or "Done", the keyboard will open and close instantaneously. Hope it helps! :)
If it doesn't work, look at my Xcode Project (which does work)... download it here