iPhone - get amplitude of audio at current point

585 views Asked by At

First off: I don't know much about audio processing. I know with Core Audio, you can get the info about a playing track.

My goal is to have a node in SpriteKit with the current amplitude/loudness of the current song from iTunes being its scale (say, scale should be at 1.0 at its loudest and 0.0 at its quietest).

I can do the node programming (setting xScale and yScale to a float amp). My problem is involved with getting amp's value.

Here's my code:

AVAudioPlayer *somePlayer = [AVAudioPlayer new];
__autoreleasing NSError* error;
somePlayer = [somePlayer initWithContentsOfURL:[[[MPMusicPlayerController systemMusicPlayer] nowPlayingItem] valueForProperty:MPMediaItemPropertyAssetURL] error:&error];
somePlayer.currentTime = [[MPMusicPlayerController systemMusicPlayer] currentPlaybackTime];
somePlayer.volume = 0;
[somePlayer play];
[somePlayer setMeteringEnabled:YES];
[somePlayer updateMeters];
NSMutableArray* amps = [NSMutableArray array];
if (error != nil) {
    NSLog(@"error: %@", error.debugDescription);
}
for (int i = 0; i < somePlayer.numberOfChannels; i++) {
    [amps addObject:@([somePlayer averagePowerForChannel:i]);
    NSLog(@"Amplitude: %f db for channel %i", [somePlayer averagePowerForChannel:i], i);
}
float amp = 0;
for (NSNumber *x in amps) {
    amp += [x floatValue];
}
amp /= amps.count;
NSLog(@"amp: %f", amp);
// set xScale and yScale to "amp"
[somePlayer stop];

What I get is a constant output of -120 dB and lag (this is in my update method). Where it would normally be 60 fps, it is 10-11 on my iPhone 6. Any idea how to fix this? I've searched online for a solid hour but got nothing.

1

There are 1 answers

0
dave234 On BEST ANSWER

I'm not sure if I'm interpreting your code correctly, but it looks like you might be instantiating your player every frame with would explain the slowness. You need to set your player up ahead of time. Then on your timer, call updateMeters right before you get averagePowerForChannel. I'm not familiar with spiteKit, so I just used UIKit with viewDidLoad and a displayLink for my timer.

@interface ViewController (){
    AVAudioPlayer *somePlayer;
    UIView *redView;
    UIView *blueView;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSURL *eggman = [[NSBundle mainBundle]URLForResource:@"Eggman" withExtension:@"mp3"];
    somePlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:eggman error:NULL];
    [somePlayer setMeteringEnabled:YES];
    [somePlayer play];


    [self addViews];
    CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animateViews)];
    [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}


-(void)animateViews{

    [somePlayer updateMeters];
    float left_db = [somePlayer averagePowerForChannel:0];
    float right_db;
    if (somePlayer.numberOfChannels > 1) {
        right_db = [somePlayer averagePowerForChannel:1];
    }
    else{
        right_db = left_db;
    }


    float blueScale = db_to_scale(left_db);
    float redScale = db_to_scale(right_db);
    float vert = self.view.bounds.size.height - 80;

    blueView.center = CGPointMake(blueView.center.x, vert - (vert * blueScale));
    redView.center = CGPointMake(redView.center.x, vert - (vert * redScale));

}
float db_to_scale(float decibles){
    return powf(10, (0.05 * decibles));
}
-(void)addViews{
    blueView = [[UIView alloc]initWithFrame:CGRectMake(80, 80, 80, 80)];
    blueView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:blueView];

    redView = [[UIView alloc]initWithFrame:CGRectMake(160, 80, 80, 80)];
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
}