Question

I am trying to make an application that use iBeacons to trigger audio when within a certain proximity. In this case it is when in the immediate proximity of the beacon. I have written an if statement that does this the only problem is that the audio file keeps restarting for as long as the device is in that proximity. I want it to trigger only once, play and finish. Not keep triggering over and over again.

The audio to play is defined here.

-(void)playSound
{
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/05 Rooster.m4a", [[NSBundle mainBundle] resourcePath]]];

NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.numberOfLoops = 1;
audioPlayer.currentTime = 0;
audioPlayer.volume = 100.0;

[audioPlayer play];
}

The audio is later called in the if statement

-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
beacon = [beacons lastObject];

self.beaconFoundLabel.text = @"In Range";
self.proximityUUIDLabel.text = beacon.proximityUUID.UUIDString;
self.majorLabel.text = [NSString stringWithFormat:@"%@", beacon.major];
self.minorLabel.text = [NSString stringWithFormat:@"%@", beacon.minor];
self.accuracyLabel.text = [NSString stringWithFormat:@"%f", beacon.accuracy];

if (beacon.proximity == CLProximityUnknown) {
    self.distanceLabel.text = @"Unknown Proximity";
}

else if (beacon.proximity == CLProximityImmediate)
{
    self.distanceLabel.text = @"Immediate";

    self.view.backgroundColor = [UIColor greenColor];
    _portrait.hidden = YES;
    _portraitText.hidden = YES;
    _landscape.hidden = NO;
    _landscapeText.hidden = NO;

    _artworkTitle.text = @"Impression, Sunrise (Impression, soleil levant), 1872";


    UIImage *photo = [UIImage imageNamed: @"Sunrise.png"];
    [_landscape setImage:photo];
    _landscapeText.text = @"Dated 1872, its subject is the harbour of Le Havre in France, using very loose brush strokes that suggest rather than delineate it. Monet explained the title later: \n\nLandscape is nothing but an impression, and an instantaneous one, hence this label that was given us, by the way because of me. I had sent a thing done in Le Havre, from my window, sun in the mist and a few masts of boats sticking up in the foreground.\n\n They asked me for a title for the catalogue, it couldn't really be taken for a view of Le Havre, and I said: 'Put Impression.'";

    [self playSound];


}
Was it helpful?

Solution 2

The other poster's code would let you avoid triggering the sound from playing the same sound multiple times at once. I gather that instead you only want the sound to be played once when you reach a given proximity.

You can adapt Armand's code to do that. Rename the boolean to be "hasPlayed"

Then make your didRange call look something like this:

void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region 
{
//Do whatever else you need to do to handle the ranging call
  if (!self.hasPlayed)
  {
    [self playSound];
    self.hasPlayed = true;
  }
}

Get rid of the custom code in audioPlayerDidFinishPlaying:successfully: that clears the flag.

If you want to keep track of multiple beacons, and want to play your sound when the user enters the proximity of a different beacon, it's a little more complicated.

If you use the same UUID but a different major value, say, then instead of a boolean, save a unique id of some sort (like the major value) for the last beacon who's sound you've played. Let's call the property lastBeaconRanged. Then when you range a beacon, see if the ranged beacon's ID matches the one who's sound you played last. If not, play the sound and save this beacon's ID into your lastBeaconRanged property.

OTHER TIPS

Define a property like Bool isPlaying, and set it to false in viewDidLoad

implement (this is call when finished playing current song) :

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
        self.isPlaying = false;
}

Don't manage iBeacon change while playing, in

-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
  if (self.isPlaying) return;

...

  [self playSound];
  self.isPlaying = true;
}

Hop this can help

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top