WaveForm on IOS WaveForm on IOS ios ios

WaveForm on IOS


Thank all.

I found this example here: Drawing waveform with AVAssetReader , changed it and developed a new class based on.

This class returns UIImageView.

//.h file#import <UIKit/UIKit.h>@interface WaveformImageVew : UIImageView{}-(id)initWithUrl:(NSURL*)url;- (NSData *) renderPNGAudioPictogramLogForAssett:(AVURLAsset *)songAsset;@end//.m file#import "WaveformImageVew.h"#define absX(x) (x<0?0-x:x)#define minMaxX(x,mn,mx) (x<=mn?mn:(x>=mx?mx:x))#define noiseFloor (-50.0)#define decibel(amplitude) (20.0 * log10(absX(amplitude)/32767.0))#define imgExt @"png"#define imageToData(x) UIImagePNGRepresentation(x)@implementation WaveformImageVew-(id)initWithUrl:(NSURL*)url{    if(self = [super init]){        AVURLAsset * urlA = [AVURLAsset URLAssetWithURL:url options:nil];        [self setImage:[UIImage imageWithData:[self renderPNGAudioPictogramLogForAssett:urlA]]];    }    return self;}-(UIImage *) audioImageLogGraph:(Float32 *) samples                   normalizeMax:(Float32) normalizeMax                    sampleCount:(NSInteger) sampleCount                    channelCount:(NSInteger) channelCount                    imageHeight:(float) imageHeight {    CGSize imageSize = CGSizeMake(sampleCount, imageHeight);    UIGraphicsBeginImageContext(imageSize);    CGContextRef context = UIGraphicsGetCurrentContext();    CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);    CGContextSetAlpha(context,1.0);    CGRect rect;    rect.size = imageSize;    rect.origin.x = 0;    rect.origin.y = 0;    CGColorRef leftcolor = [[UIColor whiteColor] CGColor];    CGColorRef rightcolor = [[UIColor redColor] CGColor];    CGContextFillRect(context, rect);    CGContextSetLineWidth(context, 1.0);    float halfGraphHeight = (imageHeight / 2) / (float) channelCount ;    float centerLeft = halfGraphHeight;    float centerRight = (halfGraphHeight*3) ;     float sampleAdjustmentFactor = (imageHeight/ (float) channelCount) / (normalizeMax - noiseFloor) / 2;    for (NSInteger intSample = 0 ; intSample < sampleCount ; intSample ++ ) {        Float32 left = *samples++;        float pixels = (left - noiseFloor) * sampleAdjustmentFactor;        CGContextMoveToPoint(context, intSample, centerLeft-pixels);        CGContextAddLineToPoint(context, intSample, centerLeft+pixels);        CGContextSetStrokeColorWithColor(context, leftcolor);        CGContextStrokePath(context);        if (channelCount==2) {            Float32 right = *samples++;            float pixels = (right - noiseFloor) * sampleAdjustmentFactor;            CGContextMoveToPoint(context, intSample, centerRight - pixels);            CGContextAddLineToPoint(context, intSample, centerRight + pixels);            CGContextSetStrokeColorWithColor(context, rightcolor);            CGContextStrokePath(context);         }    }    // Create new image    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    // Tidy up    UIGraphicsEndImageContext();       return newImage;}- (NSData *) renderPNGAudioPictogramLogForAssett:(AVURLAsset *)songAsset {    NSError * error = nil;        AVAssetReader * reader = [[AVAssetReader alloc] initWithAsset:songAsset error:&error];    AVAssetTrack * songTrack = [songAsset.tracks objectAtIndex:0];    NSDictionary* outputSettingsDict = [[NSDictionary alloc] initWithObjectsAndKeys:                                        [NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey,                                        //     [NSNumber numberWithInt:44100.0],AVSampleRateKey, /*Not Supported*/                                        //     [NSNumber numberWithInt: 2],AVNumberOfChannelsKey,    /*Not Supported*/                                        [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,                                        [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,                                        [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,                                        [NSNumber numberWithBool:NO],AVLinearPCMIsNonInterleaved,                                        nil];    AVAssetReaderTrackOutput* output = [[AVAssetReaderTrackOutput alloc] initWithTrack:songTrack outputSettings:outputSettingsDict];    [reader addOutput:output];    [output release];    UInt32 sampleRate,channelCount;    NSArray* formatDesc = songTrack.formatDescriptions;    for(unsigned int i = 0; i < [formatDesc count]; ++i) {        CMAudioFormatDescriptionRef item = (CMAudioFormatDescriptionRef)[formatDesc objectAtIndex:i];        const AudioStreamBasicDescription* fmtDesc = CMAudioFormatDescriptionGetStreamBasicDescription (item);        if(fmtDesc ) {            sampleRate = fmtDesc->mSampleRate;            channelCount = fmtDesc->mChannelsPerFrame;            //    NSLog(@"channels:%u, bytes/packet: %u, sampleRate %f",fmtDesc->mChannelsPerFrame, fmtDesc->mBytesPerPacket,fmtDesc->mSampleRate);        }    }    UInt32 bytesPerSample = 2 * channelCount;    Float32 normalizeMax = noiseFloor;    NSLog(@"normalizeMax = %f",normalizeMax);    NSMutableData * fullSongData = [[NSMutableData alloc] init];    [reader startReading];        UInt64 totalBytes = 0;     Float64 totalLeft = 0;    Float64 totalRight = 0;    Float32 sampleTally = 0;    NSInteger samplesPerPixel = sampleRate / 50;        while (reader.status == AVAssetReaderStatusReading){        AVAssetReaderTrackOutput * trackOutput = (AVAssetReaderTrackOutput *)[reader.outputs objectAtIndex:0];        CMSampleBufferRef sampleBufferRef = [trackOutput copyNextSampleBuffer];        if (sampleBufferRef){            CMBlockBufferRef blockBufferRef = CMSampleBufferGetDataBuffer(sampleBufferRef);            size_t length = CMBlockBufferGetDataLength(blockBufferRef);            totalBytes += length;            NSAutoreleasePool *wader = [[NSAutoreleasePool alloc] init];            NSMutableData * data = [NSMutableData dataWithLength:length];            CMBlockBufferCopyDataBytes(blockBufferRef, 0, length, data.mutableBytes);            SInt16 * samples = (SInt16 *) data.mutableBytes;            int sampleCount = length / bytesPerSample;            for (int i = 0; i < sampleCount ; i ++) {                Float32 left = (Float32) *samples++;                left = decibel(left);                left = minMaxX(left,noiseFloor,0);                totalLeft  += left;                Float32 right;                if (channelCount==2) {                    right = (Float32) *samples++;                    right = decibel(right);                    right = minMaxX(right,noiseFloor,0);                    totalRight += right;                }                sampleTally++;                if (sampleTally > samplesPerPixel) {                    left  = totalLeft / sampleTally;                     if (left > normalizeMax) {                        normalizeMax = left;                    }                                        // NSLog(@"left average = %f, normalizeMax = %f",left,normalizeMax);                                        [fullSongData appendBytes:&left length:sizeof(left)];                    if (channelCount==2) {                        right = totalRight / sampleTally;                         if (right > normalizeMax) {                            normalizeMax = right;                        }                                                [fullSongData appendBytes:&right length:sizeof(right)];                    }                    totalLeft   = 0;                    totalRight  = 0;                    sampleTally = 0;                }            }                                    [wader drain];                        CMSampleBufferInvalidate(sampleBufferRef);            CFRelease(sampleBufferRef);        }    }        NSData * finalData = nil;    if (reader.status == AVAssetReaderStatusFailed || reader.status == AVAssetReaderStatusUnknown){        // Something went wrong. Handle it.    }    if (reader.status == AVAssetReaderStatusCompleted){        // You're done. It worked.        NSLog(@"rendering output graphics using normalizeMax %f",normalizeMax);        UIImage *test = [self audioImageLogGraph:(Float32 *) fullSongData.bytes                                     normalizeMax:normalizeMax                                      sampleCount:fullSongData.length / (sizeof(Float32) * 2)                                     channelCount:2                                     imageHeight:100];        finalData = imageToData(test);    }            [fullSongData release];    [reader release];    return finalData;}@end


Been reading your question and created a control for this. Looks like this:

enter image description here

Code here:

https://github.com/fulldecent/FDWaveformView

Discussion here:

https://www.cocoacontrols.com/controls/fdwaveformview

UPDATE 2015-01-29: This project is going strong and making consistent releases. Thanks for SO for all the exposure!


I can give you reference of the one that I have implemented in my application. It was apple's example. Here is the example of AurioTouch which analyzes 3 types of sound audio. Apple has still not provided to analyse directly the audio waves... so this example also uses the Mic to analyse the sound...

Amongst 3 I have used only Oscilloscope for analysing the amplitude effect. I have to change that code drastically to match my requirement, so best of luck if you are going to use...

You can also see one more example using such amplitude : SpeakHere of Apple