Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIImageView animation based on volume level feedback

I have a requirement to create a audio level visualizer with a custom pattern. I have the image set as png's. My current approach is like this

1) Get the audio level of the microphone

2) Load the relevant image to the UIImageView based on the volume level.

// audio level timer
self.levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.001 target: self selector: @selector(levelTimerCallback:) userInfo: nil repeats: YES];

ImageView

- (void)levelTimerCallback:(NSTimer *)timer {
[self.audioRecorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [self.audioRecorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;

//NSLog(@"Average input: %f Peak input: %f Low pass results: %f", [self.audioRecorder averagePowerForChannel:0], [self.audioRecorder peakPowerForChannel:0], lowPassResults);

if (lowPassResults > 0.0 && lowPassResults <= 0.05){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim1"];
}
if (lowPassResults > 0.06 && lowPassResults <= 0.10){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim2"];
}
if (lowPassResults > 0.11 && lowPassResults <= 0.15){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim3"];
}
if (lowPassResults > 0.16 && lowPassResults <= 0.20){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim4"];
}
if (lowPassResults > 0.21 && lowPassResults <= 0.25){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim5"];
}
if (lowPassResults > 0.26 && lowPassResults <= 0.30){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim6"];
}
if (lowPassResults > 0.31 && lowPassResults <= 0.35){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim7"];
}
if (lowPassResults > 0.36 && lowPassResults <= 0.40){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim8"];
}
if (lowPassResults > 0.41 && lowPassResults <= 0.45){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim9"];
}
if (lowPassResults > 0.46 && lowPassResults <= 0.50){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim10"];
}
if (lowPassResults > 0.51 && lowPassResults <= 0.55){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim11"];
}
if (lowPassResults > 0.56 && lowPassResults <= 0.60){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim12"];
}
if (lowPassResults > 0.61 && lowPassResults <= 0.65){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim13"];
}
if (lowPassResults > 0.66 && lowPassResults <= 0.70){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim14"];
}
if (lowPassResults > 0.71 && lowPassResults <= 0.75){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim15"];
}
if (lowPassResults > 0.76 && lowPassResults <= 0.80){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim16"];
}
if (lowPassResults > 0.81 && lowPassResults <= 0.85){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim17"];
}
if (lowPassResults > 0.86 && lowPassResults <= 0.90){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim18"];
}
if (lowPassResults > 0.86){
    imgViewRecordAnimation.image = nil;
    imgViewRecordAnimation.image = [UIImage imageNamed:@"anim19"];
}

}

But the output is not smooth as a real visualizer animation. What should be the best approach? Share there's any better ways to do this.

Couple of images

enter image description here

enter image description here

enter image description here

like image 634
smartsanja Avatar asked Jun 16 '15 10:06

smartsanja


2 Answers

If you feel the image changes is looking jerky, you better apply animations on them. By the way, you do not need to set nil to imgViewRecordAnimation.image, as you are setting proper images after that all the times, which will introduce unnecessary delay. You can also make all the if statements to else if, otherwise each time all the if statements get executed.

Coming to the solution, you can simply follow the link that @MileAtNobel posted. Or if you do not want to do much code changes, you can apply simple animation on backgroundColor property. You can remove the timer also, check the below code and see if the code is working good. I also tried to optimize the code with close resemblance, I hope this improves the performance.

BOOL flagToContinue ; //assign NO to this flag to stop updating the images

-(void)levelTimerCallback
{
    NSLog(@"Volume Logic Begins") ;//Volume level logic begins
    [self.audioRecorder updateMeters];
    const double ALPHA = 0.05;
    double peakPowerForChannel = pow(10, (0.05 * [self.audioRecorder peakPowerForChannel:0]));
    lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults ;
    NSLog(@"Volume Logic ends") ;//Volume level logic ends

    [UIView animateWithDuration:0.001f
              delay:0.0f
            options: UIViewAnimationOptionCurveLinear
         animations:^{
           mgViewRecordAnimation.backgroundColor = [UIColor colorWithPatternImage:[NSString stringWithFormat:@"anim%d", (int)ceil(lowPassResults * 20)]];
         }
         completion:^(BOOL finished) {
           if(finished && flagToContinue) [self levelTimerCallback];
    }];
}
like image 187
Trident Avatar answered Oct 09 '22 09:10

Trident


Ok, I will not give you a direct solution, but will try to suggest a solution, which you will have to try. So, instead of changing the image of the imageView, write a custom control. Create a subclass of a view. Override the drawrect method. In that, either you can draw the required pattern depending upon the control value or you draw the image itself. But the first approach is recommended. Custom drawing is smooth as compared to changing the image. Please try it out and let me know it this works.

like image 35
shoan Avatar answered Oct 09 '22 09:10

shoan