1

I'm using the EZAudio library for iOS to handle the playback of an audio file and draw its waveform. I'd like to create a a view with the entire waveform (an EZAudioPlotGL view, which is a subclass of UIView) and then save it as a png.

I'm having a couple problems with this:

  1. The temporary audio plot I'm creating to save the snapshot image is drawing to the view, which I don't understand because I never add it as a subview.
  2. The tempPlot is only drawing the top half of the waveform (not "mirrored" as I set it in the code)
  3. The UIImage being saved from the tempPlot is only saving a short portion of the beginning of the waveform.

The problems can be seen in these images:

How the screen should look after (the original audio plot): enter image description here

How the screen does look (showing the tempPlot I don't want to draw to the screen): enter image description here

The saved image I get out that should be a copy of tempPlot: enter image description here

The EZAudio library can be found here: https://github.com/syedhali/EZAudio

And my project can be found here, if you want to see the problem for yourself: https://www.dropbox.com/sh/8ilfaofvaa8aq3p/AADU5rOwqzCtEmJz-ePRXIDZa

I'm not very experienced with OpenGL graphics, so a lot of the work going on inside the EZAudioPlotGL class is a bit over my head.

Here's the relevant code:

ViewController.m:

@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Customizing the audio plot's look
    self.audioPlot.backgroundColor = [UIColor blueColor];
    self.audioPlot.color           = [UIColor whiteColor];
    self.audioPlot.plotType        = EZPlotTypeBuffer;
    self.audioPlot.shouldFill      = YES;
    self.audioPlot.shouldMirror    = YES;

    // Try opening the sample file
    [self openFileWithFilePathURL:[NSURL fileURLWithPath:kAudioFileDefault]];
}
-(void)openFileWithFilePathURL:(NSURL*)filePathURL {
    self.audioFile = [EZAudioFile audioFileWithURL:filePathURL];

    // Plot the whole waveform
    [self.audioFile getWaveformDataWithCompletionBlock:^(float *waveformData, UInt32 length) {
        [self.audioPlot updateBuffer:waveformData withBufferSize:length];
    }];

    //save whole waveform as image
    [self.audioPlot fullWaveformImageForSender:self];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"waveformImage.png"];
    [UIImagePNGRepresentation(self.snapshotImage) writeToFile:filePath atomically:YES];
}
@end

My Category of EZAudioPlotGL:

- (void)fullWaveformImageForSender:(ViewController *)sender{
    EZAudioPlotGL *tempPlot = [[EZAudioPlotGL alloc]initWithFrame:self.frame];

    [tempPlot setPlotType:        EZPlotTypeBuffer];
    [tempPlot setShouldFill:      YES];
    [tempPlot setShouldMirror:    YES];
    [tempPlot setBackgroundColor: [UIColor redColor]];
    [tempPlot setColor:           [UIColor greenColor]];

    //plot full waveform on tempPlot
    [sender.audioFile getWaveformDataWithCompletionBlock:^(float *waveformData, UInt32 length) {
        [tempPlot updateBuffer:waveformData withBufferSize:length];

        //tempPlot.glkVC is a getter for the private EZAudioPlotGLKViewController property in tempPlot (added by me in the EZAudioPlotGL class)
        sender.snapshotImage = [((GLKView *)tempPlot.glkVC.view) snapshot];
    }];
}
Matt Cooper
  • 2,042
  • 27
  • 43
  • In "openFileWithFilePathURL" you should put your call to "fullWaveformImageForSender:" *inside* the completionBlock, because this block is called after the waveform data has been read. As it is, your snapshot image gets written to file before the audioFile has been fully read. – auco May 27 '14 at 01:34
  • Here is answer for it. http://stackoverflow.com/questions/29910476/ezaudio-output-source-is-not-consistant-and-ezaudio-plot-mirror-not-consistant – hardik hadwani Apr 28 '15 at 12:08

1 Answers1

2

drawViewHierarchyInRect only works for capturing CoreGraphics-based view drawing. (CG drawing happens on the CPU and renders into a buffer in main memory, so CG, aka UIGraphics, can just slurp an image out of there.) It won't help you if your view draws its content using OpenGL. (OpenGL drawing happens on the GPU, so you need to use GL to read pixels back from the GPU to main memory before you can build an image out of them.)

It looks like your library does its drawing with an instance of GLKView. (Poking around in the source, EZAudioPlotGL uses EZAudioPlotGLKViewController, which creates its own GLKView.) That class, in turn, has a snapshot method that does all the heavy lifting to get pixels back from the GPU and put them in a UIImage.

rickster
  • 124,678
  • 26
  • 272
  • 326
  • I edited the code in my original post based on your advice, but now I'm experiencing a couple new issues, which I've outlined above. Thanks for the help – Matt Cooper Apr 29 '14 at 15:59
  • I am facing "The tempPlot is only drawing the top half of the waveform (not "mirrored" as I set it in the code)" this issue. do you have solution for this – hardik hadwani Apr 28 '15 at 04:35