Blog

Tag Archives: iPhone

Some updates to TPCircularBuffer

I’ve recently made some updates to TPCircularBuffer (on GitHub), my C circular/ring buffer implementation, which add a memory barrier on read and write, inline the main functions for a potential performance boost, and add support for use within C++ projects.

If you’re using TPCircularBuffer at all, I recommend updating!

Also tagged , , , , | Comments closed

Circular (ring) buffer plus neat virtual memory mapping trick

I’ve just updated my C circular buffer implementation, adopting the trick originally proposed by Philip Howard and adapted to Darwin by Kurt Revis: A virtual copy of the buffer is inserted directly after the end of the buffer, so that you can write past the end of the buffer, but have your writes automatically wrapped around to the start — no need to manually implement buffer wrapping logic.

This dramatically simplifies the use of a circular buffer — you can use chunks of the buffer without any need to worry about where the wrap point is.

See the new implementation, which is thread-safe with one consumer and one producer, with no need for locks, making it perfect for use with high-priority Core Audio threads, on GitHub: TPCircularBuffer.

There’s a basic example of its use over on the original post.

Also tagged , , , | Comments closed

Experiences with some app promotion strategies

Buy my thingIn the dim and distant past, while in a moment of neglecting my PhD to work on the very first version of Loopy (which is now currently one of the most popular music apps on the iPad!), I had grand visions of an almost totally passive income, making apps. I love the creative initial product development process and, with naive optimism, I pictured pumping apps out and then sitting back and watching the money roll on in. Tim Ferriss’s 4-Hour Workweek had me enthusiastically lifestyle-designing and dreaming of all my free moneys.

I bet I’m not the only one, but of course reality struck and we realised that the App Store aint that kind of beast. Like any other product, an app needs to be actively presented to the world on a regular basis, and needs to be nurtured to keep it fresh and relevant.

I say “we” because at this point, my partner Katherine joined me after this particular revelation, and became A Tasty Pixel’s part-time marketing director and PR strategist — it’s taken two of us to keep A Tasty Pixel’s wheels turning smoothly, and we still have a lot to learn.

I thought I’d take a moment to reflect on some of the lessons we’ve learned in the past year, in which we’ve released a relatively successful travel planning and travel assistant app, The Cartographer, a very successful live looping app, Loopy, and its big brother Loopy HD, and tried a bunch of promotion strategies, some successful, some not, and some that haven’t yet run their course. Read More »

Also tagged , , , , , , | Comments closed

Loopy HD is here!

Loopy HD is here!

Loopy hd inset1I’m very pleased to announce that Loopy HD has arrived! A Tasty Pixel HQ has been a-buzz with activity these last few months, and we have a solid iPad live looper with top-notch features to show for it.

Loopy HD takes Loopy, doubles the number of tracks to fill up that big iPad display (6, 9 or 12 tracks), adds a beautiful big control panel with more session controls, and brings an even more polished audio engine and spot-on time accuracy.

We’ve got huge plans for this app and it’s just going to keep getting better. Right around the corner is MIDI support, so you can control the app with a pedal, and sync the tempo with other devices and other apps, over the network or even on the same device. Fun!

App store

If you already own Loopy, Loopy HD will be on sale for $3.99 for a few days, so you don’t have to pay full price. We’ll also be giving away some promo codes from our Facebook page, so join us over there if you’re interested.

We’ve already given away some promo codes on our newsletter, but we do this from time to time, so subscribe if you’d like to nab one the next time.

Loopy has been updated too! Version 2.1 includes all of the improvements to the audio and timing engines from Loopy HD, as well as a smaller version of Loopy HD’s control panel. There’s a number of new features, as well, like count-in muting and unmuting, the ability to turn off track sync, and zip file support.

Thanks, Steve.

Also tagged , | Comments closed

Experiments with precise timing in iOS

iOS is by no means a realtime operating system, but I’m aware that NSTimer and NSObject’s performSelector:withObject:afterDelay: mechanism aren’t particularly accurate, and I was curious to see whether I could do better.

Hands up, backing away

Disclaimer: I am not at all an expert in realtime programming, or Mach, or iOS-device optimisation, so this is pretty much a fumble in the dark. I won’t be at all offended if anyone wishes to shoot me down and offer a more sensible solution — in fact, please do! Until then, watch as I stumble on…

Also note that there are often ways to eliminate the need for precise timing of this nature, by architecting code appropriately — when it comes to audio, for example, CoreAudio provides a very accurate time base in render callbacks. For things like metronomes or audio synthesizers, it’s always better to establish a starting time, and use the difference between the current time and the starting time in order to determine state, rather than using a timer to advance the state. Still, sometimes, you just need a timer…

What the blazes?

So, I’m working on an update to Loopy, which uses a shared clock object to synchronise tracks and a variety of events (like user interface updates or timed track manipulations). A tester noted that the mute/unmute quantisation feature that I’ve recently implemented, which will mute or unmute a loop at its starting point (rather than whenever you tap it), tends to overshoot a little, resulting in a small part of the beginning of the loop being audible.

Of course, there are other solutions to this particular problem (like stopping or starting playback from the audio render callback, and using Core Audio’s timestamps for exact timing), but I use timers in other places outside Core Audio’s domain, which makes Core Audio’s timing mechanism unavailable, and I wanted to see how accurate I could get the timing.

Our friend, mach_wait_until

I read in several places mention of the Mach API utility mach_wait_until (from mach/mach_time.h), which is very low-level and supposedly fairly accurate. So, based on that lead, I put together an Objective-C singleton class that launches a high-priority thread, and uses said thread to schedule events.

An NSArray of events are maintained, and a scheduleAction:target:inTimeInterval: routine creates and adds events to this array, then pokes the thread.

The thread grabs the next event in sequence, then uses mach_wait_until to sleep until the time of the next event arrives, then performs the specified action on the target. It’s kinda a DIY NSRunLoop.

Here’s a comparison between this technique, and just using performSelector:withObject:afterDelay: (which schedules a timer on the NSRunLoop), observed while performing various scheduled events within Loopy running on my iPhone 4 with the debugger, and derived by comparing the time of event execution with the event’s scheduled time:

MechanismAverage discrepancyMinimum discrepancyMaximum discrepancy
NSRunLoop16.9ms0.25ms153.7ms
TPPreciseTimer5.5ms0.033ms72.0ms

That was attempt number 1: This seems to give us about 11.4ms better accuracy on average (three times more accurate).

Not bad, but it turns out mach_wait_until isn’t really that accurate, particularly if there’s a bunch of other stuff going on in other threads.

Spinning, for fun and profit

For my second attempt, the thread performs a mach_wait_until until just before the event is due, then performs a spin lock until the time arrives, using mach_absolute_time to compare the current time with the target time.

This gave further improved results — here’s that table again, but with the new scheme added, with a few different spin lock times:

MechanismAverage discrepancyMinimum discrepancyMaximum discrepancy
NSRunLoop16.9ms0.25ms153.7ms
TPPreciseTimer (original)5.5ms0.033ms72.0ms
TPPreciseTimer (10ms spinlock)6.0ms0.002ms76.5ms
TPPreciseTimer (100ms spinlock)3.7ms0.002ms44.8ms
TPPreciseTimer (200ms spinlock)2.91ms0.002ms74.1ms

It appears that the more stuff there is going on in other threads, the more likely the mach_absolute_time call is to overshoot. So, the more time spent in the spin lock, the more leeway mach_absolute_time has to wait too long. Of course, that’s at the cost of making the CPU twiddle its thumbs for the duration.

Better than a punch in the knee

The results weren’t quite as fantastic as I’d hoped — still within the same order of magnitude, that’s for sure — but the average case for the 200ms spinlock approach is 14ms, or 5.8 times, more accurate than the traditional approach, and the minimum case is dramatically better.

You know, I think if I was aware of the results in advance, I might not bother, but I’ll stick with my hard-won 14ms now that I’m here (that’s 617 audio samples, I’ll have you know).

If anyone’s curious about the implementation (or wants to take a stab at doing better), here it is, along with a wildly simplistic commandline test app: TPPreciseTimer.zip

Now to get back to some real work.

Addendum: GCD follow-up

Chris in the comments below suggested trying a GCD-based approach, using dispatch_after. Curious, I rigged it up, and these are the stats, collected the same way as above, added to the prior table:

MechanismAverage discrepancyMinimum discrepancyMaximum discrepancy
NSRunLoop16.9ms0.25ms153.7ms
TPPreciseTimer (original)5.5ms0.033ms72.0ms
TPPreciseTimer (10ms spinlock)6.0ms0.002ms76.5ms
TPPreciseTimer (100ms spinlock)3.7ms0.002ms44.8ms
TPPreciseTimer (200ms spinlock)2.91ms0.002ms74.1ms
dispatch_after (main queue)14.8ms0.16ms161.2ms
dispatch_after (dedicated queue)19.2ms0.1ms174.9ms
dispatch_after (dedicated queue + 100ms spinlock)22.4ms0.002ms306.8ms

So, they appear pretty much the same as the NSRunLoop stats.

Also tagged , , | Comments closed

99¢ Sale

The Cartographer iPhone App Google Maps The Cartographer is on sale for a limited time for 75% off its regular price. 99¢ is all it takes to travel the world with personalized, annotated, customized Google maps! Go forth and be frugally itinerant!

Also tagged , , , | Comments closed

Seamlessly manage portrait and landscape view controller layouts

A headache-inducing scenario: I’m working on a view controller, and I realise that in order to support landscape and portrait modes, I’m going to need to provide two different layouts.

So, I create two different views within the nib, one portrait, one landscape, each with the same view hierarchy, but with a different layout.

When the orientation changes, I set self.view to the appropriate view. I initialise both views on load, and keep both of them synced to properly reflect the app’s state — basically, I’m double-handling everything, which bloats my code and increases the chance I’ll make a mistake.

So, here’s an easier way: Rather than maintaining two separate view hierarchies and switching between them when the orientation changes, why not just change the layout of one single view hierarchy? The only changes between the portrait and landscape views are layout changes, so if we can extract just the layout information from each view, then we don’t have to worry about maintaining both view hierarchies.

Basically, we’re talking about using each view version as a layout template only.

That’s what TPMultiLayoutViewController class does. It’s a drop-in UIViewController subclass that automatically manages switching between different view layouts for portrait and landscape orientations, without the need to maintain view state across two different view hierarchies.

It works by defining portraitView and landscapeView outlets which it traverses upon loading the nib. It matches each subview element to its counterpart in the other layout (based on tag, target/action, title, etc.), and stores just the layout attributes of each element.

Then, when the orientation changes, the view hierarchy is traversed and these layouts are applied to each subview.

To use it,

  1. Set the superclass for your view controller to TPMultiLayoutViewController.
  2. In Interface Builder, create two different views: one for portrait orientation, and one for landscape orientation.
  3. Attach your portrait orientation root view to the “portraitView” outlet, and the landscape orientation root view to the “landscapeView” outlet.
  4. Attach one of the views (whichever you prefer) to the “view” outlet, and connect any actions and outlets from that view.

Grab it from the TPMultiLayoutViewController GitHub repository, and let me know what you think.

Also tagged , , , | Comments closed

Loopy 2.0.1 coming soon, with noise gate/expander, more

Loopy 2.0.1 Screenshots We just submitted Loopy 2.0.1 to the App Store!

Update: It’s now available!

The new version includes:

  • Full-featured noise gate/expander, with auto-calibration
  • Bigger panel and clock controls
  • Document support: Import loops from Mail and other apps
  • Play/pause button on main screen (great for breaks)
  • Option to add track to Loopy’s SoundCloud group when publishing
  • Many more improvements and bug-fixes

Expect it to land in a week or so. Now, off to work on the next version..

Also tagged , | Comments closed