Monthly Archives: July 2010

Editing 100’s of sound files

I’ve mentioned in a previous post that my latest app has 100’s of images. Well, it also needs almost the same number of sound files. And, as an independent developer, I don’t have the resources to get the sounds professionally made. So my friend and co-designer coached his girlfriend through reading all the words. She did an excellent job. But we could not figure out how to record them without extra silence at the end of each file. The extra silence caused strange delays in the app, so it had to be fixed. And it had to happen in a batch. Because I’m not doing 500+ files by hand.

Normally, when I need sound editing, I turn to Audacity. Thankfully, they added .m4a support just before I needed it. So I was able to take the Garage Band files, and use Audacity’s truncate silence effect. But repeating an effect over several files was a new trick for me. It turns out Audacity has a feature known as an Chain, which can be used on a group of files. Using “Edit Chains”, I was able to create a chain that removed silence, and removed an unneeded stereo channel at the same time:

Edit Chain showing remove silence.

But can you see the problem? Audacity does not support exporting as .m4a in a chain, so I had to export them all as .wav’s. This makes them too large, and hurts my chances for keeping the app under the 3G limit of 20M. So I needed another tool, preferably a free one.

If you have a UNIX background like I do, afconvert is the perfect tool. It’s a command line utility, included for free on a Mac, that converts sound files to any format with tons of options. I just needed it to convert back to a proper m4a. It turns out, I needed 2 options to do it, -f m4af to set the file format, and -d aac to set the correct data format inside the file. So I wrote the following script to convert all the files in a directory:

#!/bin/bash
find . -name '* *.wav' | while read i
do
 afconvert -f m4af -d aac "$i"
done

If you are not familiar with BASH, the first line finds all the files with a space in the name, and .wav at the end. The filenames are then read into a loop variable, i, which I then use to run afconvert on the proper files. Don’t ask me why all the sounds had a space in the filename, they just came to me that way.

With these tools, I was able to remove silence from over 500 sounds in a very short time. While I doubt you’ll ever face exactly this problem, maybe one of these tools will help in your project.

Advertisements

Leave a comment

Filed under Audio

Undocumented Annoyances when moving code to iOS 4

Maybe it was the fact that AT&T messed up my upgrade date, so I don’t still have my iPhone4. But I think it’s the fact that I lost valuable time tracking down these issues when I moved my projects to the iOS 4.0 SDK. These 4 issues are undocumented changes that came up by simply moving a project running under 3.1.3 to be built under the 4.0 SDK.

1. The definition of shadows in Quartz has changed. They used to be drawn based on the direction of the light casting the shadow. Now, they are based on the direction of the shadow itself. There’s a post on the Apple forums claiming it is by design, and actually changed in version 3.2. So my nice custom drawn buttons all look wrong. And worst of all, the only way to support 3.1 is to detect the version number and change the sign of the y parameter. After all the forum posts where we were told to detect features, and not the version numbers. Grumble, grumble.

2. Using a custom UIButton in tableFooterView is displaying too large. I have an image that is 297 pixels wide, and I use it to make a custom button 297 points wide. And 4.0 on both the 320×480 simulator and the 3GS draw it much larger, so it can’t be a scale issue. I can’t tell how big, because it just goes off the screen. This is the most aggravating personally, because I still have no reason why this happened, and no one answered about it on the forums. After wasting time trying to fix it, I moved the button to the sectionFooterView, where it renders perfectly. It’s a good thing I don’t know any Apple engineers, I would have yelled at them about how much time this wasted.

3. Calling dismissModalViewControllerAnimated when there are multiple view controllers to be dismissed is suddenly calling “viewDidAppear” on all the controllers being dismissed. This would probably not be apparent, except that one of my view controller plays a sound everytime it appears. Now the sound is being played when it is being dismissed. This behavior is bizarre, and disconcerting because there does not seem to be another way to detect when a view controllers view is displayed. And, again, no answer on the official forums. I’m having to add a goofy BOOL property to the view controller that is only set when the controller is created.

4. touchEnded is being called differently. This one is actually working out to be a feature, but I can’t find it documented. Basically, under 3.x, if you press inside a view, and release your finger outside of that view, touchesEnded is never called. Under 4.0 it is. The reason this is a feature is that one of my apps is designed to be used by children who might not have the motor control to press and release inside the same view.  So under 4.0 they can use my control, whereas the 3.x version needed to be fixed. By duplicating the same code in touchesEnded and touchesCancelled, it looks like things are working correctly.

I know this is not an exhaustive list, and other people are having worse issues, like crashes and major visual issues, moving to 4.0. But even these 4 were enough for me to curse the new SDK. Of course, when my iPhone4 gets here, I’m sure I’ll get over it.

1 Comment

Filed under iOS4

Alternatives for Supporting All Resolutions in UIKit Apps

I’ve been working hard to get an iPhone/iPod version of my iPad app out. When my business partner got his new iPhone4, we were happy to see that the techniques I used to re-use graphics between the iPad and iPhone worked so well that the app even looks good on the Retina Display.

First off, I understand that the official directions to support high resolutions will work for most people. But my application has currently over 500 images in it, and a few hundred more are being added. So to make a @2x version of each image just seems out of the question for me. So I had used 2 other techniques that I originally developed to share images betwen the iPhone and iPad version that worked with no extra effort on the iPhone 4.

Method 1: Draw scaled down bitmap images using Quartz.

I actually went with Quartz because I needed to draw shadows, which is a subject I’ll return to later. But it turned out that CGContextDrawImage will scale the image into the frame perfectly on all devices. If the image has enough resolution, it even looks sharp on the iPhone4. Since my images all were originally from the iPad, they looked perfectly sharp. I don’t know if everyone’s images will scale so well, but my experience says it’s worth a try.

The problem with using Quartz is that the coordinate system is not the same as UIView. But a couple of transformations, and everything looks fine. I made my own subview of UIView, and drawRect looked something like this:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0 , -1.0);

    CGContextDrawImage(context, self.frame, self.theImage.CGImage);
}

Method 2: Use PDF for Vector Graphics

This turns out to be very powerful and useful. PDF was actually one of the source formats for much of the licensed graphics we used. And since Adobe Illustrator can output PDF as well, it was very easy to make changes or new images. And I was able to use Automator on the Mac to easily use the reduce file size Quartz filter to get the overall download size to under 20 Meg.

PDFs are a little confusing to use, so I also made a UIView subclass to display them. I’m loading them from a file URL in the drawRect code below, and rescaling them proportionally by hand. The code below is reloading the PDF every time it draws. With 7 PDFs on the screen at once, it has a slight pause, otherwise it was fast enough that I never felt like I needed to make it faster. You could certainly preload them, or even draw them into a bitmap if you need to redraw them frequently.

- (void)drawRect:(CGRect)rect {
    if (self.pdfURL != nil) {
        pdfDoc = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
        page = CGPDFDocumentGetPage (pdfDoc, 1);

        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
        CGRect boxRect = CGPDFPageGetBoxRect (page, kCGPDFCropBox);

        float xRatio = self.bounds.size.width/boxRect.size.width;
        float yRatio = self.bounds.size.height/boxRect.size.height;
        float ratio = xRatio < yRatio? xRatio : yRatio;
        CGContextTranslateCTM(context, 0.0, -(self.bounds.size.height-boxRect.size.height*ratio)/2);
        CGContextScaleCTM(context, ratio , -ratio);

        CGContextSaveGState (context);

        CGContextClipToRect (context,boxRect);
        CGContextDrawPDFPage (context, page);
        CGContextRestoreGState (context);
        CGPDFDocumentRelease(pdfDoc);
    }
}

If app size is looking like it might be an issue that makes it hard to support all the different devices, consider if one of these techniques will help.

Leave a comment

Filed under Graphics