<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>philippseifried.com</title>
	<atom:link href="http://philippseifried.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://philippseifried.com/blog</link>
	<description>procedural art &#38; artistic procedures</description>
	<lastBuildDate>Thu, 10 May 2012 18:15:02 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Lessons learned on the App Store – part 3</title>
		<link>http://philippseifried.com/blog/2012/05/10/lessons-learned-on-the-app-store-part-3/</link>
		<comments>http://philippseifried.com/blog/2012/05/10/lessons-learned-on-the-app-store-part-3/#comments</comments>
		<pubDate>Thu, 10 May 2012 18:12:57 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Business]]></category>
		<category><![CDATA[iOS]]></category>

		<guid isPermaLink="false">http://philippseifried.com/blog/?p=438</guid>
		<description><![CDATA[<p>Part three of my series on various things we learned about selling on the iOS App Store. (<a title="Lessons learned on the App Store – part 1" href="http://philippseifried.com/blog/2012/05/01/lessons-learned-on-the-app-store-part-1/">Part 1</a>, <a title="Lessons learned on the App Store – part 2" href="http://philippseifried.com/blog/2012/05/04/lessons-learned-on-the-app-store-part-2/">part 2</a>)</p> Don&#8217;t pay for AdMob clicks! <p>We&#8217;ve tried buying ads on AdMob for all three [...]]]></description>
			<content:encoded><![CDATA[<p>Part three of my series on various things we learned about selling on the iOS App Store. (<a title="Lessons learned on the App Store – part 1" href="http://philippseifried.com/blog/2012/05/01/lessons-learned-on-the-app-store-part-1/">Part 1</a>, <a title="Lessons learned on the App Store – part 2" href="http://philippseifried.com/blog/2012/05/04/lessons-learned-on-the-app-store-part-2/">part 2</a>)</p>
<h2>Don&#8217;t pay for AdMob clicks!</h2>
<p>We&#8217;ve tried buying ads on AdMob for all three of our games, and, like just about any other account on the web that we&#8217;ve read said, the results were abysmal. It wasn&#8217;t for lack of trying to be smart, either:</p>
<p><span id="more-438"></span></p>
<p>We restricted the geographic locations to US/Canada/UK/Australia (because why pay for Chinese clicks on an English ad), we restricted the target group to what we thought were our core demographics, and we experimented with different ad texts (we figured if we could get only geeks to click on our &#8220;<a title="CellShades on iPhone &amp; iPad" href="http://www.bobblebrook.com/cellshades">CellShades</a>&#8221; ad and scare everyone else off, our conversion rate per click would be so much higher). All to no avail. Our click-through rate was always in the low to mid 0.x%, but our conversion rate after the click-through was so low that for every $50 we spent, the bump in downloads was negligible (as in practically not measurable), even with our free app &#8220;<a title="CellShades on iPhone and iPad" href="http://www.bobblebrook.com/cellshades">CellShades</a>&#8220;.</p>
<p>My conclusion is that AdMob may have it&#8217;s place if you have a huge marketing budget and you need to generate just enough extra sales to make it from position 26 to 25 in the charts. As you can generate a lot of impressions for little money, it may also be worthwhile to use AdMob to A/B test your app&#8217;s name and icon. I&#8217;ll share our experience with that in another post. But for an indie dev on a budget, buying ads to sell your game is a complete waste of money.</p>
<h2>Setting your app free for a day will result in massive downloads</h2>
<p>When you set a paid app to free, a slew of app shopping sites will automatically pick up on the fact and spread the news over the web and social media sites. The effect can be in the tens of thousands of additional downloads over the next couple of days, with download numbers quickly tapering back to normal free app levels over the course of a week or so.</p>
<p>The effect is repeatable, but if the intervals between repetitions aren&#8217;t very long (several months to half a year, it seems to me), its intensity will gradually fade away. Repeating the experiment after two weeks, for instance, probably won&#8217;t produce a significant amount of downloads. Also keep in mind that some sites will offer a complete history of your app&#8217;s pricing, so if you do this frequently, potential customers might pick up on it.</p>
<p>Don&#8217;t expect the additional downloads to significantly increase sales after you switch the app back to paid, though. The people who visit automated price drop reporting sites don&#8217;t seem to be particularly willing to pay for apps they see there that have reverted back to full price.<br />
Also, as the downloads you get are very untargeted, a lot of developers have reported that the users who download your app when it&#8217;s free are statistically more likely to leave negative reviews in the App Store (something we have not personally experienced).</p>
<p>So why do it? In-app purchases are one good reason. Another one would be that if you have the kind of app that people like to show off to their friends, the amount of additional word of mouth might well make it worth it.</p>
<h2>Read up on App Store SEO!</h2>
<p>Users searching the App Store can contribute a significant portion of revenue over your app&#8217;s lifetime. Whether or not your app is among the results for any particular search depends on your title, the keywords you specify in iTunes Connect and the names of any in app purchases. Interestingly, your app&#8217;s description text does not contribute to its search engine placement.</p>
<p>Apple only gives you 100 bytes for your app&#8217;s keywords and you can only update them with new versions of your binary. Make the most of them! People have experimented with different keywords and shared lessons about SEO on the App Store, so read their accounts (here&#8217;s an interesting four-parter: <a href="http://blog.dacaposoft.com/seo-optimizing-app-keywords-on-itunes-app-store/">part 1</a>, <a href="http://blog.dacaposoft.com/httpblog-dacaposoft-comseo-optimizing-app-keywords-on-itunes-app-store-part2/">part 2</a>, <a href="http://blog.dacaposoft.com/seo-optimizing-your-app-for-itunes-what-doesnt-work/">part 3</a>, <a href="http://blog.dacaposoft.com/%E2%80%9Cseo-optimizing%E2%80%9D-your-app-for-itunes-%E2%80%93-part-4/">part 4</a>)!</p>
<p>Use commas (instead of commas and spaces) to separate keywords to save room. All other things equal, shorter keywords are better than longer ones. Use small updates to your app to optimize your keywords and periodically check where you rank for any of your search terms. Use services like <a href="http://www.appcod.es/appsearch/">http://www.appcod.es/appsearch/</a> to see what keywords your competitors might be using.</p>
<p>It&#8217;s also worth pointing out that your keyword ranking depends on factors such as how many downloads you&#8217;ve recently had (and your app&#8217;s ratings), so if you&#8217;ve previously optimized out a popular keyword because your rank was too low, and your app picks up steam, you may want to reconsider.</p>
<h2>User behavior tracking is easy!</h2>
<p>There are plenty of services on the web that let you add tracking code to your iOS application and give you statistics on how users interact with it. If you&#8217;re writing a game, how many people get through the first level after the tutorial? Is there a particular puzzle at which people tend to give up? If you change the upsell screen in your lite version, or the description of your in-app purchase, will more or less users make the purchase?</p>
<p>When we started out, we refrained from tracking user behavior, because it seemed like a lot of work. As it turns out, you can sign up for a free service and add the relevant functionality in an hour or two. We&#8217;ve been using the free service of <a href="https://playtomic.com/">Playtomic</a> ourselves, a cross-platform API geared towards games, which has the nice benefit of also being compatible with C++, Flash and Android applications. There are other services as well, but I haven&#8217;t yet tried any of them.</p>
<p>Playtomic is by no means perfect: you have to resort to creatively naming your tracking events in order to do A/B testing (i.e. showing different portions of your users different versions of the same thing – such as an upsell screen –, and measuring the difference in reaction), and I really miss the ability to assign attributes to users and track them separately based on them (for instance, I&#8217;d like to track the behavior of pirates, new users, power users and users that pay for an iAP separately). The service has also had some downtime and a few instances of quirky reporting in the three months that we&#8217;ve been using it, but I&#8217;d chalk that up to it being relatively young.</p>
<p>On the upside, the analytics are updated on a nearly real-time basis, the API is dead simple to add, and you can&#8217;t beat the price of free.</p>
]]></content:encoded>
			<wfw:commentRss>http://philippseifried.com/blog/2012/05/10/lessons-learned-on-the-app-store-part-3/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Lessons learned on the App Store – part 2</title>
		<link>http://philippseifried.com/blog/2012/05/04/lessons-learned-on-the-app-store-part-2/</link>
		<comments>http://philippseifried.com/blog/2012/05/04/lessons-learned-on-the-app-store-part-2/#comments</comments>
		<pubDate>Fri, 04 May 2012 16:52:31 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Business]]></category>
		<category><![CDATA[iOS]]></category>

		<guid isPermaLink="false">http://philippseifried.com/blog/?p=431</guid>
		<description><![CDATA[<p>In part two of my series on lessons we learned about selling on the iOS App Store, I&#8217;ll focus on our experience with review sites. Again, this is not an exhaustive list of all the stuff you need to know, but rather a collection of things that eluded us when we published our first couple [...]]]></description>
			<content:encoded><![CDATA[<p>In part two of my series on lessons we learned about selling on the iOS App Store, I&#8217;ll focus on our experience with review sites. Again, this is not an exhaustive list of all the stuff you need to know, but rather a collection of things that eluded us when we published our first couple of apps. <a title="Lessons learned on the iOS App Store, part 1" href="http://philippseifried.com/blog/2012/05/01/lessons-learned-on-the-app-store-part-1/">Part 1 is here</a>, in case you missed it. <a title="Lessons learned on the App Store – part 3" href="http://philippseifried.com/blog/2012/05/10/lessons-learned-on-the-app-store-part-3/">Part 3 is here.</a></p>
<p><span id="more-431"></span></p>
<h2>Contact review sites before your app launches!</h2>
<p>If you&#8217;re looking for reviews of your app or game, don&#8217;t wait until it&#8217;s out before you contact review sites. Major review sites receive a few hundred inquiries a week, and asking them to review an app that&#8217;s been out for weeks doesn&#8217;t turn the odds in your favor (case in point: we contacted review sites for our first game <a title="Drifts on iPhone and iPad" href="http://www.bobblebrook.com/ios/drifts">&#8220;Drifts&#8221;</a> a couple of months after it launched and received zero coverage).</p>
<p>For a game, you ideally want to build up buzz over time, with sites picking up early trailers or gameplay videos months before the game is finished. But if that&#8217;s not an option – for instance, because your total development time is short, your game is too small to build up anticipation, or you&#8217;re working on an app with a novel idea that would be easy to clone – providing advance copies to review sites will provide the site with potentially more interesting news and your app with launch day coverage.</p>
<p>How to approach review sites would merit an article on its own, but if you&#8217;re asking a reviewer to take a look at an advance copy of your app, it&#8217;s a good idea to link them to screenshots and a trailer or gameplay video, so they can gauge whether your product is worth their time. I&#8217;d recommend researching press kits and putting a proper one together as well.</p>
<p>There are three ways of handing out advance copies:</p>
<p>The first way is creating an ad hoc build. For this, you would need the device ID of a reviewer&#8217;s iPhone or iPad. The procedure requires some back and forth and may be a little tedious, so don&#8217;t expect every reviewer to jump at the chance of doing it this way (although some sites do actively advertise that they&#8217;re willing to look at ad hoc builds). Also, with a regular developer account, you only get a total of 100 devices per year between which you can test all of your apps – these include your own devices, those of any clients you may be working for, and anybody else you give ad hoc builds to, so you might refrain from offering ad hoc builds willy-nilly.</p>
<p>The advantage of ad hoc builds is that they run natively on a reviewer&#8217;s device, and you can theoretically start handing them out as soon as you start development.</p>
<p>The second way is using a service such as <a title="TestFlight" href="https://testflightapp.com/">TestFlight</a>. You send your builds to the service <del>(using their UDIDs), and they provide an interactive streaming video of the application to your reviewers</del>. I&#8217;ve never used TestFlight<del>, and I&#8217;d expect it to have the drawbacks that streaming video has, namely that it might not be responsive enough for a 60fps game; nevertheless,</del> it&#8217;s probably the most hassle free way of giving a reviewer a glimpse of your application in its early stages. (<strong>C0rrection:</strong> I was wrong about TestFlight streaming videos to testers. They actually distribute your build)</p>
<p>The third way is to use promo codes. For each version of your app, Apple provides you with 50 promo codes which can be redeemed via iTunes. It&#8217;s not at all obvious, but you can use these codes as soon as your application has passed review, even if it&#8217;s not yet available in the App Store. If you set your release date way after Apple&#8217;s approval date, you can send out codes to review sites as soon as your app has been approved. This also works for free apps.<br />
The advantage here is that from a reviewer&#8217;s perspective, this is the way review copies are usually handed out, so there is no extra hassle for them. The disadvantage is that you can only do this once your game is complete and ready to ship, and you&#8217;ll have to push your release date out accordingly.</p>
<h2>Offer giveaways to review sites!</h2>
<p>A lot of review sites do periodic giveaways of promo codes for their readers. If you have the codes to spare, offering a giveaway to a review site might be the way to sway them towards covering your game. And if they&#8217;ve already reviewed your game in the past, giveaways could help get your game repeat coverage.</p>
<p>Depending on the site, expect to give away somewhere between 2 and 10 codes. It&#8217;s a good idea to ask sites how many they&#8217;d usually want to give away – most answers you get will be towards the lower end of the spectrum. The 50 promo codes you get from Apple will be gone quickly, so if someone asks for more than 5 codes, compare their Alexa ranking to review sites of known size or research their Twitter or Facebook followings to make sure your codes are well spent.</p>
<p>Depending on your marketing budget (and the price of your app), there&#8217;s also the option of gifting yourself your app to produce additional promo codes, if you don&#8217;t want to turn anybody down.</p>
<h2>You can re-use unused review site promo codes!</h2>
<p>Review sites are busy, and they get a lot of requests, so you want to make everything as easy for them as possible. Putting a promo code or two (we use two for major sites or sites that explicitly ask for it) in your e-mail will remove one barrier between a reviewer and your game.</p>
<p>On the other hand, a good portion of your potential reviewers won&#8217;t even use their codes, and 50 codes are gone quickly. Rather than let them go to waste, you can use this little trick to test which codes have not been used: <a title="Testing app store codes" href="http://davidbarnard.com/post/10980474471/testing-app-store-promo-codes?bf77bd08">http://davidbarnard.com/post/10980474471/testing-app-store-promo-codes</a></p>
<p>Two things to note:<br />
First, I&#8217;ve heard reports of this method not being 100% accurate, so I wouldn&#8217;t give away codes that it marks as free to other reviewers or in giveaways that single out a winner. Instead, post them on internet forums or your facebook page or somewhere else where it doesn&#8217;t matter as much if one of the codes turns out to be already used after all!<br />
Second, promo codes are valid for four weeks after you create them, and review sites often take some time before they get to your game. We&#8217;ve had a review site cover <a title="&quot;Coign of Vantage&quot; for iPhone and iPad" href="http://www.bobblebrook.com/ios/coign-of-vantage">&#8220;Coign of Vantage&#8221;</a> out of the blue, almost three weeks after we sent them their codes. Keep track of which codes you send to which review sites, and check for unused codes three weeks or so after they were created. We also make a point not to re-use codes we&#8217;ve sent to the biggest sites even after three weeks, just in case.</p>
<h2>Don&#8217;t expect reviews to be your key to success</h2>
<p>One thing that surprised me is how little review sites actually do in terms of sales boosts. <a title="&quot;Coign of Vantage&quot; for iPhone and iPad" href="http://www.bobblebrook.com/ios/coign-of-vantage">&#8220;Coign of Vantage&#8221;</a> was reviewed by a bunch of sites, some of them very big, and all of them saying good things about our game (never less than 4 out of 5 stars). We had an Italian iPhone site review the game, which had an Alexa ranking of 12000 and around 300 in Italy. During the next few days we sold one copy in Italy. A Chinese news site with a similar worldwide traffic rank and a 2500 ranking in China had an article about the game, which caused 0 Chinese sales on that day.</p>
<p>The same goes for some of the major US review sites (I&#8217;ve heard that <a title="TouchArcade" href="http://toucharcade.com/">TouchArcade</a> is an exception to that, but alas, we have not been reviewed by it). Some of them provided minor boosts, but if you&#8217;re thinking that review sites by themselves will significantly contribute to your app&#8217;s sales, you will almost certainly be disappointed.</p>
<p>As with almost everything related to iOS marketing, I&#8217;ve come to think of review sites as multipliers that work in conjunction with everything else you do to promote your app. They help build links to your app. They get your screenshots and icon in front of consumers (which might just need multiple impressions before they give your app a try). They come up when potential users google your app&#8217;s name. They provide awesome quotes you can use in your marketing texts (which may sway potential buyers who never even saw the actual review). They probably increase the likelyhood that Apple picks your game for the new and noteworthy list. They help shape the perception of the few readers that buy your game, so they in turn may leave positive user reviews in the App Store. And articles on review sites increase the likelyhood that other review sites pick up your game or app.</p>
<p>Also, they can be great for bragging rights.</p>
<p>A portion of this applies to paid review sites. Quite a few of the sites you contact will write back and ask you to pay anywhere between 20 and 100 USD for a guaranteed &#8220;expedited&#8221; review. If you use these, you&#8217;re pretty much guaranteed to not break even on your investment, in terms of additional sales. They might however help get the ball rolling, if your app received no other coverage.</p>
<h2>Pay for press release distribution</h2>
<p>In addition to contacting review sites in advance, it pays off to do a proper press release on launch day and pay a distribution service to disseminate it. Not only will your release reach the inboxes of reporters at large blogs and magazines that you may not be able to hunt down on your own (and those reporters have specifically opted in to the press release service, whereas they may be less inclined to read your direct and unsolicited e-mails), but your release will also be picked up automatically by a lot of aggregator sites on the web, which will provide backlinks to your app. The SEO value of these alone probably exceeds the 20$ a service like <a href="http://prmac.com/">prmac.com</a> takes.</p>
<p>Again, how to write a good press release is way beyond the scope of this article. Research it! Spend some time looking for iOS specific advice on forums, and spend some time reading actual press releases of apps similar to yours (you&#8217;ll find them on press release sites).</p>
<p>One word of warning: prmac.com actually changed the text of our press release before sending it out, without consulting or even notifying us first. They removed some links to additional material and changed some of the wording in our first paragraph. The changes didn&#8217;t really affect the quality or value of the release, so we might go with them again, but if you consider that behavior inappropriate, I suggest you research other services.</p>
]]></content:encoded>
			<wfw:commentRss>http://philippseifried.com/blog/2012/05/04/lessons-learned-on-the-app-store-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lessons learned on the App Store – part 1</title>
		<link>http://philippseifried.com/blog/2012/05/01/lessons-learned-on-the-app-store-part-1/</link>
		<comments>http://philippseifried.com/blog/2012/05/01/lessons-learned-on-the-app-store-part-1/#comments</comments>
		<pubDate>Tue, 01 May 2012 13:18:51 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Business]]></category>
		<category><![CDATA[iOS]]></category>

		<guid isPermaLink="false">http://philippseifried.com/blog/?p=419</guid>
		<description><![CDATA[<p>We&#8217;ve been releasing apps on the iOS App Store for about a year now and have three apps in the store (our games <a title="Drifts for iPhone &#38; iPad" href="http://bobblebrook.com/ios/drifts">“Drifts”</a> and <a title="&#34;Coign of Vantage&#34; for iPhone &#38; iPad" href="http://bobblebrook.com/ios/coign-of-vantage">“Coign of Vantage”</a>, and a sandbox game / interactive toy named <a title="&#34;CellShades&#34; for iPhone &#38; [...]]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve been releasing apps on the iOS App Store for about a year now and have three apps in the store (our games <a title="Drifts for iPhone &amp; iPad" href="http://bobblebrook.com/ios/drifts">“Drifts”</a> and <a title="&quot;Coign of Vantage&quot; for iPhone &amp; iPad" href="http://bobblebrook.com/ios/coign-of-vantage">“Coign of Vantage”</a>, and a sandbox game / interactive toy named <a title="&quot;CellShades&quot; for iPhone &amp; iPad" href="http://www.bobblebrook.com/cellshades/">“CellShades”</a>). Although we&#8217;ve tried our best to do our homework and read up on how to do business on the App Store before our first release, there are numerous things that eluded us when we started out.</p>
<p>In this series of posts, I&#8217;ll try to summarize some of the things we learned over the year. This is by no means a comprehensive list – just a series of items that I would have liked to read about when we worked on our first app. <a title="Lessons learned on the App Store – part 2" href="http://philippseifried.com/blog/2012/05/04/lessons-learned-on-the-app-store-part-2/">Part 2 is here</a>, and <a title="Lessons learned on the App Store – part 3" href="http://philippseifried.com/blog/2012/05/10/lessons-learned-on-the-app-store-part-3/">part 3 here</a>!</p>
<p><span id="more-419"></span></p>
<h2>Be wary of outdated information</h2>
<p>The App Store is an environment that is ever changing, in terms of the competition, in terms of what users like in an app, and in terms of the inner workings of the store and the rules set forth by Apple.</p>
<p>While the early days of iPhone development yielded story after story of lone indie devs taking the charts by storm and then using the momentum of high ranks to stay there, these days dozens of big fish wield enormous marketing budgets to fight for the coveted top 25 spots, and surprise hits are considerably less common. While players might still find the idea of collecting coins to buy little hats for their avatars fun now, they may just as well be tired of it in another six months. And while giving out promo codes in exchange for user reviews or making your game free for a day to rise up the charts used to work great a couple of years ago, Apple has changed the way the App Store works to remove these ways of gaming the system.</p>
<p>The bottom line is, you should take any information about what works well on the App Store that is older than six months with a large grain of salt; and much more so, if the information could be considered to be exploiting loopholes in the store. Any advice older than a year is probably not worth taking. To save you from scrolling up: this article was written in May 2012.</p>
<p>I&#8217;d also suggest considering possible future developments before resorting to black hat techniques such as buying fake user reviews. Apple has explicitly stated that such behavior may be grounds for terminating your account, and just because they aren&#8217;t cracking down on the cheaters now doesn&#8217;t mean they can&#8217;t or won&#8217;t.</p>
<h2>If you pick a name for your app, you have 180 days to finish it.</h2>
<p>This came as quite a surprise for us (guess whether it was a pleasant one!):</p>
<p>From the day you create your app in iTunes Connect, you have 180 days to upload a binary. After 150 days or so, you&#8217;ll get a few reminder e-mails telling you that if you don&#8217;t finish your app, the name will be available for another developer to use. What they don&#8217;t tell you, though, is that you won&#8217;t be able to re-register the name yourself (as opposed to an expired domain name, which becomes available to the world, <em>including</em> you).</p>
<p>Since you need to create and name your app in iTunes Connect in order to add things like Game Center support or iAPs, this is something to really look out for if your project is big or you&#8217;re doing it on the backburner.</p>
<div class="mceTemp">
<dl id="attachment_422" class="wp-caption alignright" style="width: 410px;">
<dt class="wp-caption-dt"><a href="http://itunes.apple.com/us/app/coign-of-vantage/id448999277"><img class="size-full wp-image-422 " title="Coign of Vantage on iPhone &amp; iPad" src="http://philippseifried.com/blog/wp-content/uploads/2012/05/COV_screenshot_small.jpg" alt="Coign of Vantage on iPhone &amp; iPad" width="400" height="300" /></a><a href="http://itunes.apple.com/us/app/coign-of-vantage/id448999277">Coign of Vantage (iPhone &amp; iPad)</a></dt>
</dl>
</div>
<p>We&#8217;ve run into this with our own game <a title="&quot;Coign of Vantage&quot; for iPhone &amp; iPad" href="http://www.bobblebrook.com/ios/coign-of-vantage">“Coign of Vantage”</a>. To be fair, we sent Apple an e-mail stating our case at the time, and we did receive a 120 day extension. But they took more than a month to reply, and just because they resolved our issue doesn&#8217;t mean they will do the same for you.</p>
<p>If your project could take more than half a year to complete, it might make sense to start it under a different name, which you can delete from iTunes Connect later on. (Disclaimer: I haven&#8217;t checked whether this is against any of Apple&#8217;s rules)</p>
<p><strong>Update:</strong> Another way that appears to still work is to upload a dummy binary when the deadline arrives and then self-reject it.</p>
<h2>Don&#8217;t mess up your placement in the “New Releases” list</h2>
<p>While its importance has declined a lot in the past couple of years, the “new releases” list in the App Store gives you a few guaranteed sales and some muchly needed initial exposure. Since you&#8217;ll need all the momentum you can get, make sure your app doesn&#8217;t hit one of the few remaining obstacles that mess up its placement on the list:</p>
<p>If your app is approved and released after the release date you specify, it enters the “new releases” list a few pages back (which means nobody will find it), so when submitting your app for review, pick a release date a few months in the future. Only once the app has been approved should you set the date back to the actual date you want to release it. Set its release to at least one full day after the current date.</p>
<p>I&#8217;ve also heard that before your app is released, you can only change the release date once without messing up your “new releases” placement. I have no idea whether that&#8217;s true, but I&#8217;d rather play it safe. Your app will only be placed in the “new releases” list of the primary category you choose. The secondary category will be ignored.</p>
<p>The “new releases” list is sorted by alphabet by day, so all other things equal, apps that start with letters A to C will have a slight edge over other apps which may appear on the second or third page.</p>
<h2>Submit your first update on the day you launch</h2>
<p>Whether you&#8217;ll spend them on review sites, giveaways, or your twitter followers, during launch week you&#8217;ll quickly run out of the 50 promo codes Apple provides for your app. As you get a fresh batch of codes with every update and an update normally takes about a week to pass Apple&#8217;s review, you&#8217;ll want your first update to go through as quickly as possible. Keep a minor feature or a couple of levels out of the initial release, and submit your version 1.1 the day your app hits the App Store.</p>
<p>If your release date is after the approval date, it might even be worth submitting your first version before the app goes live, although I don&#8217;t know if that behavior could raise any red flags at Apple.</p>
<p><strong>Update:</strong> You can now also buy promo codes for your own game as gifts (effectively at 30% the price of your app, as you earn 70% back for the sales), and hand them out. These come with the advantage that people who use them can leave a review on the App Store, which you can&#8217;t do with regular promo codes.</p>
<h2>Expedited Apple review requests</h2>
<p>Any update you submit to Apple will go through the same review process as a new app, which is to say, it will take about a week to go through, provided everything checks out okay. If you discover a critical bug on launch day, a week could be what makes or breaks your release buzz. For such situations, Apple gives you the option of requesting an expedited review at this URL: <a title="Expedited App Store reviews" href="https://developer.apple.com/appstore/contact/appreviewteam/index.html">https://developer.apple.com/appstore/contact/appreviewteam/index.html</a></p>
<p>Expedited reviews are granted on a limited basis, so be prepared to state a good reason why your update should get preferential treatment. Also, I wouldn&#8217;t be surprised if crying wolf too often could get your account flagged.</p>
<h2>Use a third-party tool to generate sales reports</h2>
<p>Let&#8217;s face it, as far as monitoring your sales and download data is concerned, iTunes Connect is rubbish. You can either look at graphs for the downloads, sales, iAPs and updates of all your apps lumped together, or you can download an Excel sheet with raw data. You should also be aware that iTunes Connect only keeps track of your sales during the past couple of months. If you want to look at sales stats from last year, you&#8217;re out of luck.</p>
<p>There are numerous free services around that provide much better data, <a title="AppAnnie" href="http://www.appannie.com/">AppAnnie</a> being the one we are using. The way this works is you hand AppAnnie your iTunes Connect credentials, and once a day the service pulls the sales data from there, combining it with data they gather from the web.</p>
<p>You can then get a daily report of your exact revenue (from all App Stores combined) and downloads separated by app, your ranking in the various stores, any new reviews of your apps, and other things. You can also check out sales graphs that go back for as long as AppAnnie had access to your iTunes Connect account.</p>
]]></content:encoded>
			<wfw:commentRss>http://philippseifried.com/blog/2012/05/01/lessons-learned-on-the-app-store-part-1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Two ways your iOS app can miss the App Store&#8217;s “New Releases” List in 2012…</title>
		<link>http://philippseifried.com/blog/2012/03/01/app-store-woes-2012/</link>
		<comments>http://philippseifried.com/blog/2012/03/01/app-store-woes-2012/#comments</comments>
		<pubDate>Thu, 01 Mar 2012 14:00:51 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Business]]></category>
		<category><![CDATA[iOS]]></category>

		<guid isPermaLink="false">http://philippseifried.com/blog/?p=407</guid>
		<description><![CDATA[<p>If you&#8217;ve released an iOS app, you&#8217;ve probably carefully read the stories of developers who were burned by getting the wrong release date in the App Store and subsequently missing out on the new releases list. As we painfully learned with <a title="CellShades" href="http://www.bobblebrook.com/cellshades/">CellShades</a>, there are still pitfalls to look out for in 2012. I&#8217;ll [...]]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;ve released an iOS app, you&#8217;ve probably carefully read the stories of developers who were burned by getting the wrong release date in the App Store and subsequently missing out on the new releases list. As we painfully learned with <a title="CellShades" href="http://www.bobblebrook.com/cellshades/">CellShades</a>, there are still pitfalls to look out for in 2012. I&#8217;ll get to these in a moment. First I&#8217;ll describe what used to be the problem:</p>
<p>When you submit an app for approval, you pick an availability date in iTunes Connect, at which you want the app to be released. If the availability date is after Apple approves your app, the app will be released at the availability date. If the approval process takes longer and the availability date is passed, the app will be released once Apple has approved it.</p>
<p>New apps that arrive in the app store are listed in the &#8220;new releases&#8221; section, by the day they were released (and then alphabetically. <strong>PROTIP:</strong> your app has a better chance to be seen if its name starts with an A or B!). Being listed there means that a lot of users get a chance to discover your app, which can be critical in building up the momentum a new app needs in order to get picked up by review sites or top lists in the App Store.</p>
<p><span id="more-407"></span></p>
<p>Now, there used to be a major pitfall associated with setting the availability date – Apple&#8217;s way of determining the release date by which your app would be sorted in the &#8220;new releases&#8221; list used to be as follows:</p>
<blockquote><p>The “Released” date is currently determined by the date of the app’s approval by Apple and the date that the developer has listed for the app’s availability within iTunes Connect, whichever is earlier.</p></blockquote>
<p>This means that if you set your app&#8217;s release date to January 20th, and the app was approved on January 10th, then the app would become available in the app store on Jan 20th, but its listing among the new releases would be at the same position as if it had been released on January 10th (i.e. so many pages back that nobody would ever find it there). Conversely, if the app was set to be released on January 10th but the approval process took until the 20th, the app would also enter the store on the 20th, but its release date (and rank in the new releases list) would be set to January 10th!</p>
<p>The solution to this used to be to set your release date way in the future when submitting to Apple&#8217;s approval, and then the moment you received the notification that your app was approved, you&#8217;d set the release date to today. Your app would then appear in the App Store, with the correct position in the &#8220;what&#8217;s new&#8221; list.</p>
<p>Apparently, Apple fixed all of this somewhere in the last year or two. Now, when you release an app, you just pick a release date in the future, and once Apple approves your app, you either leave that date alone or set it to any other date after the approval date (<strong>Disclaimer:</strong> this is what I gather from our own observations and various accounts on the web – I don&#8217;t accept responsibility if it doesn&#8217;t work as expected).</p>
<p>Still, there are ways that you can mess up your placement in the new releases list. Read on for how we managed to do it…</p>
<p>&nbsp;</p>
<h2>CellShades</h2>
<p>CellShades is a little experiment that lets you spill virtual liquid and then watch cells spawn, consume the liquid and build clusters, with a wide variety of pretty rad dynamics (that the user can explore by changing the simulation&#8217;s underlying parameters).</p>
<p><iframe src="http://www.youtube.com/embed/EXmYqmBiGB0" frameborder="0" width="560" height="315"></iframe></p>
<p>If you want more information on the app or give it a try, the App Store link is <a href="http://itunes.apple.com/us/app/cellshades/id498962270">here</a>, the app&#8217;s microsite with screenshots is <a href="http://www.bobblebrook.com/cellshades/">here</a>, and I&#8217;ve written a blog post with a little bit of background info about the technology and libraries used <a href="http://philippseifried.com/blog/2012/02/28/cellshades/">here</a>.</p>
<p>I think it&#8217;s important to point out that we didn&#8217;t really know which app category to put CellShades in. It&#8217;s not quite a game, it&#8217;s not quite educational, maybe it would fit in the entertainment category, but that place is such a horrible mess of flash light apps, horoscopes and other nasties that we weren&#8217;t keen on competing in that neighborhood (although in hindsight, I wouldn&#8217;t be surprised if that one had one of the highest amounts of users actively browsing its new releases list&#8230;).</p>
<p>To pick a category, we looked at where simulations of Conway&#8217;s Game of Life would position themselves. As it turns out, quite a lot of them placed themselves in the education category, and quite a lot in the games category as well, so we picked these two (games being the secondary category, a mistake I&#8217;ll get to in a moment).</p>
<p>&nbsp;</p>
<h2>What happened</h2>
<p>CellShades was approved on February 15th. We wanted to release the app as soon as we had all marketing materials ready, and we had originally picked a late March date, so we could set it back once we were ready to release.</p>
<p>On the 22nd of February, we decided to launch and set the app&#8217;s availability date to the same day. It appeared in the app store maybe an hour or so later. It did not appear in any &#8220;new releases&#8221; list that day, neither in &#8220;education&#8221; nor in &#8220;games&#8221;.</p>
<p>On the 23rd of February, CellShades appeared in the &#8220;new releases&#8221; list in the education category. It appeared a couple of pages back, however, ranked by its release date which was given as February 22nd. It also never appeared in the games category.</p>
<p>I&#8217;m pretty sure we hardly had a single user discover us from the &#8220;What&#8217;s new&#8221; placement. I&#8217;m not able to put a number on the amount of users we missed out on, but I&#8217;m pretty sure it was substantial.</p>
<p>I should point out that the following is just my conjecture. It fits what happened, but the only data I have is from a single app, so if you have a different theory or more information, please leave a comment. With that out of the way, here&#8217;s the sense I make from all of this:</p>
<p>1.) The &#8220;new releases&#8221; list is only updated once a day, but your app&#8217;s placement in it still depends on when the app actually came out. Resist the temptation to release your app sometime during the day, because it will only appear in the new releases list of the next day, but ranked behind any other release that comes out then. Instead, leave your release date at any future date you like and wait for the app to roll out automatically.</p>
<p>2.) Your app will only appear in the &#8220;new releases&#8221; list of its <em>primary</em> category. This means that if your app is a fit for both &#8220;games&#8221; and &#8220;education&#8221;, make &#8220;games&#8221; the primary category, as more people are likely to browse its new releases list!</p>
]]></content:encoded>
			<wfw:commentRss>http://philippseifried.com/blog/2012/03/01/app-store-woes-2012/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>CellShades for iPhone/iPad</title>
		<link>http://philippseifried.com/blog/2012/02/28/cellshades/</link>
		<comments>http://philippseifried.com/blog/2012/02/28/cellshades/#comments</comments>
		<pubDate>Tue, 28 Feb 2012 17:33:00 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Apps]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Toys]]></category>

		<guid isPermaLink="false">http://philippseifried.com/blog/?p=397</guid>
		<description><![CDATA[<p><br /> Last week, we released a new iPhone/iPad app named <a title="CellShades" href="http://www.bobblebrook.com/cellshades/">CellShades</a>.</p> <p>CellShades is an experimental little app, similar to &#8220;<a title="Wikipedia on CAs" href="http://en.wikipedia.org/wiki/Cellular_automata">Cellular Automata</a>&#8220;, in which you spill liquid which provides energy for cells to spawn and harvest. These cells follow a handful of very simple rules (such as that they will always [...]]]></description>
			<content:encoded><![CDATA[<p><iframe src="http://www.youtube.com/embed/EXmYqmBiGB0" frameborder="0" width="560" height="315"></iframe><br />
Last week, we released a new iPhone/iPad app named <a title="CellShades" href="http://www.bobblebrook.com/cellshades/">CellShades</a>.</p>
<p>CellShades is an experimental little app, similar to &#8220;<a title="Wikipedia on CAs" href="http://en.wikipedia.org/wiki/Cellular_automata">Cellular Automata</a>&#8220;, in which you spill liquid which provides energy for cells to spawn and harvest. These cells follow a handful of very simple rules (such as that they will always move towards adjacent positions which they perceive as more energetic, or that they will perish and dissolve into liquid if they can&#8217;t keep up their energy levels) to produce a wide variety of different behaviors. The examples that ship with the app range from crashing waves to flowering structures to larger microorganisms crawling across the screen, giving a taste of the huge spectrum of dynamics possible with the “simulation”.</p>
<p>The app comes with 9 free preset behaviors. A one Dollar (0.79€, etc.) in-App purchase unlocks the ability to manipulate the simulation&#8217;s underlying parameters and save your own behaviors.</p>
<h2>Technology and libraries</h2>
<p>We wrote CellShades in Objective-C, with the main simulation parts being mostly C. I started out writing the app in <a title="Cocos2D" href="http://www.cocos2d-iphone.org/">cocos2d</a>, mainly because it provided a very quick way of setting up everything so that the app could draw and update its simulation and respond to user input. The main GUI of the app consists of UIKit elements residing on top of the cocos2d surface.</p>
<p><span id="more-397"></span>As a relative beginner to Objective-C, I was a little wary of the cocos2d/UIKit mix and what pitfalls it might harbor, but I have to say that everything went very smoothly and I wouldn&#8217;t hesitate to go that route again (a good starting tutorial is <a title="How to integrate cocos2d with UIKit" href="http://www.raywenderlich.com/4817/how-to-integrate-cocos2d-and-uikit">here</a>).</p>
<p>We have some sharing features in CellShades which were implemented using the <a title="ShareKit" href="http://getsharekit.com/">ShareKit</a> library and which enable users to put screenshots into their photo album or share them via Facebook, Twitter or Tumblr. That took a little longer than expected because the library wouldn&#8217;t quite work out of the box – specifically, I recall hunting down a couple of things to change for Twitter compatibility on the forums. We also didn&#8217;t like ShareKit&#8217;s default way of presenting an ActionSheet, complete with a ShareKit settings button, so a little more work was spent replacing that with our own GUI which would then create and share items manually. But all in all, these problems were pretty minor and the end result is a very re-useable bit of code, so no complaints there.</p>
<p>We also used <a title="Playtomic analytics API" href="http://playtomic.com/">Playtomic</a>&#8216;s analytics API for tracking, gathering information such as how often the app is launched before the in-app purchase is made or how often the info-screens are opened. As this is the first time we&#8217;re using it and the app has just been released, the jury on that is still out, and I&#8217;ll reserve a detailed account of our experience with Playtomic for a future post.</p>
<p>&nbsp;</p>
<p><a href="http://itunes.apple.com/us/app/cellshades/id498962270"><img title="CellShades Screenshot" src="http://philippseifried.com/blog/wp-content/uploads/2012/02/cellshades-screenshot.jpg" alt="CellShades Screenshot" width="450" height="300" /></a></p>
<p>Finally, if you&#8217;d like to see the app in action, <a href="http://itunes.apple.com/us/app/cellshades/id498962270">click here</a> for the App Store link!</p>
]]></content:encoded>
			<wfw:commentRss>http://philippseifried.com/blog/2012/02/28/cellshades/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Realtime audio processing in Flash, part 5: Interpolation and pitching</title>
		<link>http://philippseifried.com/blog/2011/12/27/dynamic-audio-in-as3-part-5-interpolation-and-pitching/</link>
		<comments>http://philippseifried.com/blog/2011/12/27/dynamic-audio-in-as3-part-5-interpolation-and-pitching/#comments</comments>
		<pubDate>Tue, 27 Dec 2011 10:28:12 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[AS3]]></category>
		<category><![CDATA[Audio]]></category>
		<category><![CDATA[Tutorials]]></category>

		<guid isPermaLink="false">http://philippseifried.com/blog/?p=369</guid>
		<description><![CDATA[<p>[Update: Iain Peet pointed out that the definitions of “aliasing” and “anti-aliasing” in audio are more narrow than I thought when I wrote this post. Specifically, aliasing in audio refers to the artifacts you get when you shift or create frequencies beyond half the sample rate, and anti-aliasing refers to low-pass filtering audio signals in [...]]]></description>
			<content:encoded><![CDATA[<p><em>[Update: Iain Peet pointed out that the definitions of “aliasing” and “anti-aliasing” in audio are more narrow than I thought when I wrote this post. Specifically, aliasing in audio refers to the artifacts you get when you shift or create frequencies beyond half the sample rate, and anti-aliasing refers to low-pass filtering audio signals in order to remove these artifacts. Both of these play no part in this post, and I have updated it to remove the terms.]</em></p>
<p>In this fifth installment of my series on dynamic audio programming in AS3, I want to take a quick look at the artifacts we introduce when we process audio signals without interpolation. We&#8217;ve already had a brief encounter with these, when we implemented a basic flanger effect back in <a title="Dynamic audio in Flash, part 3" href="http://philippseifried.com/blog/2011/10/20/dynamic-audio-in-as3-part-3-robot-voice/">part 3</a>, which turned out to have a somewhat dirty, distorted sound to it. In this article, we&#8217;ll take a closer look at where this dirt came from, by looking at a naïve implementation of pitch shifting.</p>
<p>&nbsp;</p>
<h2>Pitch-shifting, and how not to do it</h2>
<p>To be clear, for the purposes of this article, “pitch shifting” is what happens when you slow a recorded sound down (the pitch lowers, by an octave whenever the speed is halved) or speed it up (the pitch rises, by an octave whenever the speed is doubled). Pitch-shifting while preserving the original duration of a sound is an entirely different story, and a good deal more involved.</p>
<p>With that out of the way, let&#8217;s look at what a basic implementation of the effect might look and sound like:</p>
<p><span id="more-369"></span></p>
<p>The following class stores a Vector.&lt;Number&gt; of mono sample data for a sound and writes a pitched version of that sound to an output Vector of arbitrary length whenever you call its <em>process()</em> method. It loops its assigned sample seamlessly (placing multiple copies into the output Vector if necessary), so it can be called whenever a dynamic sound needs more data:</p>
<p><p>
								<pre class="Plum_Code_Box"><code class="javascript">package
{
  public class PitchShifter
  {
    protected var source:Vector.&lt;Number&gt;;
    protected var currentIndex:Number = 0;
    public var pitch:Number = 1;
    
    public function PitchShifter(monoSample:Vector.&lt;Number&gt;)
    {
      source = monoSample;
    }
    
    public function setSource(monoSample:Vector.&lt;Number&gt;):void
    {
      source = monoSample;
      currentIndex = 0;
    }
    
    public function process(monoDestination:Vector.&lt;Number&gt;):void 
    {
      var numSamples:int = monoDestination.length;
      var sourceLength:int = source.length;
      
      for (var i:int = 0; i&lt;numSamples; i++) 
      {
        currentIndex += pitch;
        if (currentIndex &gt;= sourceLength) currentIndex -= sourceLength;
        monoDestination[i] = source[int(currentIndex)];
      }
    }
  }
}</code>
									</pre>
							</p></p>
<p><em>(Hint: if you&#8217;re trying this out and compiling from the Flash IDE, make sure you set your sound compression settings to a high bit rate!)</em></p>
<p>This implementation is pretty straightforward: for each sample in the output array, the <em>currentIndex</em> member (which is a floating point Number, not an int) is incremented a little – by 1, if the effect is set to original playback speed, 0.5 if it&#8217;s slowed down by half, 2.0 if its speed is doubled. The <em>currentIndex</em> is then cast to an int, so each output sample index corresponds to an integer index into the source sound&#8217;s data.</p>
<p>The following example shows this code in action, with a knob that lets you change the playback speed and three different sound samples to try out:</p>

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
			id="fm_SimplePitcher_1916924176"
			class="flashmovie"
			width="450"
			height="150">
	<param name="movie" value="http://www.philippseifried.com/blog/files/audio-5/SimplePitcher.swf" />
	<!--[if !IE]>-->
	<object	type="application/x-shockwave-flash"
			data="http://www.philippseifried.com/blog/files/audio-5/SimplePitcher.swf"
			name="fm_SimplePitcher_1916924176"
			width="450"
			height="150">
	<!--<![endif]-->
		
<p><a href="http://adobe.com/go/getflashplayer"><img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" /></a></p>

	<!--[if !IE]>-->
	</object>
	<!--<![endif]-->
</object>
<p>Obviously something weird is happening to the sound, when it&#8217;s pitched in this manner. When pitching down, the sound does get lower, but it also gets a ringing quality that wasn&#8217;t there before. When pitching up, things sound better, as long as the pitch factor is an integer multiple of 1. Any value in between will result in the same sort of distortion.</p>
<p>To better understand the root of the problem, let&#8217;s look at what should happen when you pitch down a basic sine wave, and what actually happens in our implementation.</p>
<div id="attachment_371" class="wp-caption aligncenter" style="width: 512px"><a href="http://philippseifried.com/blog/wp-content/uploads/2011/12/badPitching.png"><img class="size-full wp-image-371" title="Sine wave scaled up without antialiasing" src="http://philippseifried.com/blog/wp-content/uploads/2011/12/badPitching.png" alt="Sine wave scaled up without antialiasing" width="502" height="301" /></a><p class="wp-caption-text">A sine wave scaled up without interpolation. Left: the original wave. Center: what the stretched wave should look like. Right: what the stretched wave actually looks like in our simple implementation.</p></div>
<p>The center part of this illustration shows how a sine wave should look after being stretched out to half its frequency. The right part shows what happens when we stretch samples with the naïve implementation given above: if the samples are pitched down by an octave (i.e. 1/2 of their original frequency), the output is simply two identical samples in a row for every input sample.</p>
<p>What should be a smooth wave becomes a collection of jagged edges, and wherever these edges appear, distortion is added to the frequency spectrum. This gets worse when using this method to stretch by non-integer multiples – since the index into the sample array is always an integer, one sample of the original wave may be stretched out to three samples, the next to four, the next to three again, and so on!</p>
<p>The same thing happens when pitching up by a non-integer multiple, instead of down: if you&#8217;re pitching to 2.5 of the original speed, one input sample is skipped for each output sample half of the time, and two input samples per output sample the other half. Again, these alternations introduce unwanted additional frequencies.</p>
<p>The following two illustrations show spectrograms of these artifacts. The first image shows the frequency distributions of a guitar chord when pitched up or down. The second image shows pitched sine waves.</p>
<div id="attachment_372" class="wp-caption aligncenter" style="width: 810px"><a href="http://philippseifried.com/blog/wp-content/uploads/2011/12/badAntialiasingGuitar.jpg"><img class="size-full wp-image-372" title="pitched guitar sample without anti-aliasing" src="http://philippseifried.com/blog/wp-content/uploads/2011/12/badAntialiasingGuitar.jpg" alt="pitched guitar sample without anti-aliasing" width="800" height="490" /></a><p class="wp-caption-text">Spectrogram of a guitar chord, pitched up and down to various speeds, without interpolation. Stretching to any speed other than an integer multiple creates copies across the frequency spectrum. Note that the zoom factors of the different samples along the time axis are not consistent (e.g. the 0.5x sample should actually be twice the length of the 1x version).</p></div>
<div id="attachment_375" class="wp-caption aligncenter" style="width: 664px"><a href="http://philippseifried.com/blog/wp-content/uploads/2011/12/badAntialiasingSine.jpg"><img class="size-full wp-image-375" title="pitched sine wave without anti-aliasing" src="http://philippseifried.com/blog/wp-content/uploads/2011/12/badAntialiasingSine.jpg" alt="pitched sine wave without anti-aliasing" width="654" height="230" /></a><p class="wp-caption-text">Spectrogram of a sine wave stretched to various speeds without interpolation. Again, the zoom factors of the samples along the time axis are not consistent.</p></div>
<p>The sine wave illustrates the effects particularly well: when pitched up by an integer (2x, 4x), the spectral shape of the sound remains more or less intact. Pitching down by an integer (0.5x, 0.25x) introduces copies of the shape at different frequencies. Pitching up or down by a non-integer value creates a lot more of these copies, making the distortion of the sound even worse.</p>
<p>&nbsp;</p>
<h2>Interpolation</h2>
<p>The solution to avoid artifacts when pitching a piece of audio down is the same as the solution to avoid jagged edges when scaling up a bitmap image: interpolation.</p>
<p>Remember that in our basic implementation we used a floating point index into the sample data. In order to get each sample for our output Vector, we incremented this index a little (depending on playback speed) and then cast it to an int before fetching the corresponding source sample. Even though for any non-integer playback speed the index should usually fall between two samples, our implementation rounded the index to the next smallest integer, discarding any sub-sample information and thereby introducing artifacts.</p>
<p>Adding simple linear interpolation improves things drastically:</p>
<p>Suppose that <em>i</em> is the current floating point index into the samples Vector. <em>iint = (int) i</em> is then the next smallest integer (the same as <em>Math.floor(i)</em>). The resulting output sample is then:</p>
<p><em>var f:Number = i-iint; // f goes from 0&#8230;1</em><br />
<em>var result:Number = samples[iint]*f + samples[iint+1]*(1-f); </em></p>
<p>Applying that to our implementation results in the following code sample:</p>
<p><p>
								<pre class="Plum_Code_Box"><code class="javascript">package
{
  public class AntialiasedPitchShifter extends PitchShifter
  {
    public function AntialiasedPitchShifter(monoSample:Vector.&lt;Number&gt;)
    {
      super(monoSample);
    }

    override public function process(monoDestination:Vector.&lt;Number&gt;):void 
    {
      var numSamples:int = monoDestination.length;
      // NOTE: we're reducing the source's length by one here, which effectively 
      // removes the last sample, but allows us to disregard the edge case of 
      // currentIndex falling between the last and first sample at the end
      // of a loop.
      var sourceLength:int = source.length-1;
      
      for (var i:int = 0; i&lt;numSamples; i++) 
      {
        currentIndex += pitch;
        if (currentIndex &gt;= sourceLength) currentIndex -= sourceLength;
        
        var index1:int = int(currentIndex);
        var index2:int = index1+1;
        var index2Factor:Number = currentIndex-index1;
        var index1Factor:Number = 1-index2Factor;
        
        monoDestination[i] = source[index1]*index1Factor + source[index2]*index2Factor;
      }
    }
    
  }
}</code>
									</pre>
							</p></p>
<p>The following spectrogram shows how much of an improvement linear interpolation gives us over simple nearest-neighbor sampling:</p>
<div id="attachment_376" class="wp-caption aligncenter" style="width: 810px"><a href="http://philippseifried.com/blog/wp-content/uploads/2011/12/linearInterpolationGuitar.jpg"><img class="size-full wp-image-376" title="pitched guitar sample with anti-aliasing" src="http://philippseifried.com/blog/wp-content/uploads/2011/12/linearInterpolationGuitar.jpg" alt="pitched guitar sample with anti-aliasing" width="800" height="490" /></a><p class="wp-caption-text">Spectrogram of a guitar chord, pitched up and down with linear interpolation. Note that the spectral &quot;echoes&quot; are still present, but much fainter! Again, the zoom factors of the samples along the time axis are not consistent.</p></div>
<div id="attachment_377" class="wp-caption aligncenter" style="width: 664px"><a href="http://philippseifried.com/blog/wp-content/uploads/2011/12/linearInterpolationSine.jpg"><img class="size-full wp-image-377" title="pitched sine wave with anti-aliasing" src="http://philippseifried.com/blog/wp-content/uploads/2011/12/linearInterpolationSine.jpg" alt="pitched sine wave with anti-aliasing" width="654" height="230" /></a><p class="wp-caption-text">Spectrogram of a sine wave pitched up and down with linear interpolation. (Again, the scaling of the different copies along the time axis is not consistent.)</p></div>
<p>The copies of the original sound across the frequency spectrum are substantially fainter, both when pitching down and when pitching up. To be sure, there are situations where this type of interpolation isn&#8217;t enough: If you play back the source at several times its original speed, high frequency content – such as very short spikes – can disappear (if one sample index is before a spike and the next sample index after it) or be substantially altered. In this case, each output sample should be a weighted average of all input samples between one index <em>i</em> and the next.</p>
<p>In the same vein, when you pitch down with linear interpolation, the stretched waveform is simply approximated with straight lines connecting the original wave&#8217;s samples. Using a better approximation method such as cubic interpolation would reduce unwanted artifacts even more, at the cost of a higher performance penalty.</p>
<p>For most purposes however, linear interpolation produces good results, and it&#8217;s fast enough to be easily done in realtime in AS3.</p>
<p>The following example shows the principle in action, using the same sounds as the distorted version from the beginning of this article.</p>

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
			id="fm_AntialiasedPitcher_1764770127"
			class="flashmovie"
			width="450"
			height="150">
	<param name="movie" value="http://www.philippseifried.com/blog/files/audio-5/AntialiasedPitcher.swf" />
	<!--[if !IE]>-->
	<object	type="application/x-shockwave-flash"
			data="http://www.philippseifried.com/blog/files/audio-5/AntialiasedPitcher.swf"
			name="fm_AntialiasedPitcher_1764770127"
			width="450"
			height="150">
	<!--<![endif]-->
		
<p><a href="http://adobe.com/go/getflashplayer"><img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" /></a></p>

	<!--[if !IE]>-->
	</object>
	<!--<![endif]-->
</object>
]]></content:encoded>
			<wfw:commentRss>http://philippseifried.com/blog/2011/12/27/dynamic-audio-in-as3-part-5-interpolation-and-pitching/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Realtime audio processing, part 4: Comb filters, Flangers and Chorus effects – a bit of theory</title>
		<link>http://philippseifried.com/blog/2011/11/01/dynamic-audio-4-comb-filters-flangers-and-chorus-effects/</link>
		<comments>http://philippseifried.com/blog/2011/11/01/dynamic-audio-4-comb-filters-flangers-and-chorus-effects/#comments</comments>
		<pubDate>Tue, 01 Nov 2011 17:23:42 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Audio]]></category>
		<category><![CDATA[Tutorials]]></category>

		<guid isPermaLink="false">http://philippseifried.com/blog/?p=283</guid>
		<description><![CDATA[<p>In <a title="Realtime audio processing in AS3, part 3" href="http://philippseifried.com/blog/2011/10/20/dynamic-audio-in-as3-part-3-robot-voice/">part 3</a>, we had a first look at creating audio effects in AS3 by processing microphone input with robot voice effects. One of the things we did was to create a so-called comb filter by adding a delayed version of the original signal to itself. We [...]]]></description>
			<content:encoded><![CDATA[<p>In <a title="Realtime audio processing in AS3, part 3" href="http://philippseifried.com/blog/2011/10/20/dynamic-audio-in-as3-part-3-robot-voice/">part 3</a>, we had a first look at creating audio effects in AS3 by processing microphone input with robot voice effects. One of the things we did was to create a so-called comb filter by adding a delayed version of the original signal to itself. We then created a flanging effect by oscillating the delay&#8217;s offset.</p>
<p><em>Audio sample: 2 drum samples, one dry, one processed with a flanger.</em></p>
<p>Given that flangers are one of the staples of audio effects (especially when applied to electric guitars), you may have been wondering why exactly this worked the way it did. In this article we&#8217;ll figure this out.</p>
<p>This gives us an opportunity to review a bit of audio theory, independent of AS3. Next time, we&#8217;ll redo our quick and dirty flanger implementation and add antialiasing, but today&#8217;s article will be free of code – you&#8217;ll just need a little high school math.</p>
<p>&nbsp;</p>
<p><span id="more-283"></span></p>
<h2>Audio fundamentals</h2>
<p>There are a handful of basic audio concepts that we&#8217;ll need to know in order to understand flanging. Chances are that you&#8217;re already familiar with these, and any of them could be the focus of a detailed discussion in itself – but for our purposes the following summaries should hopefully get you up to speed:</p>
<p><strong>1.)</strong> The most basic type of sound, a periodic <em>sine wave</em>, is completely determined by three parameters: its <em>amplitude</em>, its <em>frequency</em> and its <em>phase</em>.</p>
<p style="text-align: center;">
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
			id="fm_sine_wave_262532046"
			class="flashmovie"
			width="400"
			height="300">
	<param name="movie" value="http://www.philippseifried.com/blog/files/audio-4/sine_wave.swf" />
	<!--[if !IE]>-->
	<object	type="application/x-shockwave-flash"
			data="http://www.philippseifried.com/blog/files/audio-4/sine_wave.swf"
			name="fm_sine_wave_262532046"
			width="400"
			height="300">
	<!--<![endif]-->
		
<p style="text-align: center;"><a href="http://adobe.com/go/getflashplayer"><img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" /></a></p>
<p style="text-align: center;">
	<!--[if !IE]>-->
	</object>
	<!--<![endif]-->
</object>
<p>The <em>amplitude</em> is the height of the peak of the wave. This is related to the sound&#8217;s perceived loudness, but not quite the same thing (for example, the human ear places different emphasis on different frequencies).</p>
<p>The <em>frequency </em>is a measure of how often the wave repeats in a given timespan. If the timespan is a second, the unit of frequency is a <em>Hertz</em> (or Hz). The frequency of a sound wave determines its pitch.</p>
<p>The <em>phase</em> is the position within the wave&#8217;s cycle at a specific point in time (e.g. at the start of the recording). As a sine wave repeats itself every 2*PI radians, you can think of a phase of 2*PI as the same position in the wave as a phase of 0, 4*PI, 6*PI, and so on. You can think of changing the phase of a sine wave as shifting the whole wave to the left or right.</p>
<p>The value of a sine wave at any given time is <em>y(time) = sin( 2*PI*frequency*time + phase )*amplitude</em>.<br />
This should be pretty straightforward: multiplying the frequency by two makes the whole wave repeat twice as fast. Multiplying the amplitude by two doubles the height of the wave&#8217;s peaks. Changing the phase by adding or subtracting an offset shifts the wave left or right. The amount of shifting for any given offset is dependent on the frequency, i.e. at higher frequencies, the same offset will shift the wave by a shorter distance in time.</p>
<p><strong>2.)</strong> <em>Any</em> sound of finite length can be seen as a sum of basic sine waves. Take any periodic waveform at all: you can decompose it into a series of sines, each with its own frequency, phase and amplitude.</p>
<p>This is the basis of the Fourier transform. The Fourier transform takes a signal that is in the time domain (i.e. a sound wave) and decomposes it into a series of sine and cosine waves of increasing frequency (note that a cosine is just a sine wave with a phase shift of PI/2). For each sine and cosine, it gives us the appropriate amplitude and phase. Add these waves together, and you get back the original sound wave.</p>
<p>Your favorite audio editing software probably has two ways of displaying sound data: a waveform view and a spectral view. This spectral view (called a &#8220;<em>spectrogram</em>&#8220;), which shows you the intensity of the sound at different frequencies, is the result of applying the Fourier transform to successive slices of the audio material.</p>
<div id="attachment_286" class="wp-caption aligncenter" style="width: 583px"><a href="http://philippseifried.com/blog/wp-content/uploads/2011/10/audition_spectral.jpg"><img class="size-full wp-image-286" title="Spectral view" src="http://philippseifried.com/blog/wp-content/uploads/2011/10/audition_spectral.jpg" alt="" width="573" height="375" /></a><p class="wp-caption-text">Waveform and spectrogram of a voice recording in Adobe Audition. Warmer colors indicate more intensity in the corresponding frequencies.</p></div>
<p><strong>3.)</strong> Suppose you play two sine waves of the same frequency at once: what you get is constructive or destructive interference. Think of the values of the sines as going from -1 to 1, and think of two sines that are played at once as being added together: If the phases of the sines (that is: the position in their cycle that they are in) are identical, the peaks of the sines are added, resulting in a total output that goes from -2 to +2.<br />
If the phases are opposite to each other (which is to say they are PI radians apart), the peaks and troughs of the two waves cancel each other out (because when one is at peak +1, the other is at -1 and vice versa), producing a total amplitude of zero. Anything in between and you get a result that&#8217;s somewhere in between. If the frequencies are not exactly the same, you get an effect called “beating”, where constructive and destructive interference are alternating.</p>
<p style="text-align: center;">
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
			id="fm_interference_1694948146"
			class="flashmovie"
			width="400"
			height="300">
	<param name="movie" value="http://www.philippseifried.com/blog/files/audio-4/interference.swf" />
	<!--[if !IE]>-->
	<object	type="application/x-shockwave-flash"
			data="http://www.philippseifried.com/blog/files/audio-4/interference.swf"
			name="fm_interference_1694948146"
			width="400"
			height="300">
	<!--<![endif]-->
		
<p style="text-align: center;"><a href="http://adobe.com/go/getflashplayer"><img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" /></a></p>
<p style="text-align: center;">
	<!--[if !IE]>-->
	</object>
	<!--<![endif]-->
</object>
<p><strong>4.)</strong> Finally, it&#8217;s worth pointing out that humans are good at hearing the frequency of a sound wave, but very bad at distinguishing phase. To illustrate this, think of a piano note. Now think of the same piano note again, but played at a starting position offset by maybe a millisecond. All this offset is, is a phase change (and a big one!) – and you wouldn&#8217;t hear it at all.</p>
<p>&nbsp;</p>
<h2>So what&#8217;s a comb filter?</h2>
<p><em>Audio sample: 2 drum samples, one dry, one processed with a comb filter.</em></p>
<p>In last week&#8217;s article we discovered that adding a delayed version of a signal back to itself results in a metallic sounding effect. In audio processing, this effect is called a &#8220;<em>comb filter</em>&#8220;, the name referring to its characteristic frequency response.</p>
<p>You can think of the frequency response of a filter as a measure of how the filter will attenuate or amplify a signal&#8217;s parts at different frequencies.<br />
For instance, a low pass filter will gradually reduce the power of the source signal&#8217;s components above the cutoff frequency:</p>
<div id="attachment_290" class="wp-caption aligncenter" style="width: 546px"><a href="http://philippseifried.com/blog/wp-content/uploads/2011/10/freq_response_low_pass1.jpg"><img class="size-full wp-image-290" title="Frequency response of a low pass filter" src="http://philippseifried.com/blog/wp-content/uploads/2011/10/freq_response_low_pass1.jpg" alt="" width="536" height="444" /></a><p class="wp-caption-text">Frequency response of a low pass filter and its effect on white noise (below).</p></div>
<p>The frequency response of a comb filter is characterized by a series of regularly spaced spikes, which look a bit like the teeth of a comb. The distance between these spikes depends on the length of the delay. Shorter delay times cause the spikes to be spaced farther apart.</p>
<div id="attachment_322" class="wp-caption aligncenter" style="width: 310px"><a href="http://philippseifried.com/blog/wp-content/uploads/2011/10/combfirresp.gif"><img class="size-full wp-image-322" title="Comb filter frequency response" src="http://philippseifried.com/blog/wp-content/uploads/2011/10/combfirresp.gif" alt="" width="300" height="260" /></a><p class="wp-caption-text">Frequency response of a comb filter (image by Davide Rocchesso (CC BY 1.0), source: http://cnx.org/content/m11657/latest/#fig4)</p></div>
<div id="attachment_291" class="wp-caption alignright" style="width: 183px"><a href="http://philippseifried.com/blog/wp-content/uploads/2011/10/reason_comb_filter.jpg"><img class="size-full wp-image-291" title="Comb filter in Reason" src="http://philippseifried.com/blog/wp-content/uploads/2011/10/reason_comb_filter.jpg" alt="" width="173" height="137" /></a><p class="wp-caption-text">The comb filter in Reason&#39;s Maelström synth lets you select between additive and subtractive filtering</p></div>
<p>A comb filter can be created by taking the delayed version of the signal and <em>adding</em> it to the original, or it can be created by <em>subtracting</em> it from the original (or adding the inverse of the original, which is the same thing). The only difference between the two is that the additive comb filter&#8217;s peaks are placed at the frequencies of the subtractive comb filter&#8217;s troughs and vice versa.</p>
<p>So why does a mere addition of the delayed signal result in such an interesting frequency response and, by extension, such an interesting sounding effect?</p>
<div id="attachment_296" class="wp-caption aligncenter" style="width: 576px"><a href="http://philippseifried.com/blog/wp-content/uploads/2011/10/comb_filter_spectral.jpg"><img class="size-full wp-image-296" title="Comb filter spectral views" src="http://philippseifried.com/blog/wp-content/uploads/2011/10/comb_filter_spectral.jpg" alt="" width="566" height="333" /></a><p class="wp-caption-text">Spectrograms of a comb filter applied to a voice sample (top) and noise (bottom). Note that the &quot;teeth&quot; of the comb are actually regularly spaced! The graph shows frequencies logarithmically, making it look as if the distance between spikes decreases at higher frequencies.</p></div>
<h2>&#8230;and why does it work?</h2>
<p>To answer this question, let&#8217;s look at what happens to sine waves of different frequencies when we delay them by the same amount of time:</p>
<p>Again, the formula for a sine wave is <em>y(time) = sin( 2*PI*frequency*time + phase )*amplitude</em>.</p>
<p>To make things easier, let&#8217;s assume a constant amplitude of 1, and discard the amplitude scalar at the end of the equation for the rest of this discussion. Let&#8217;s also assume that the unit of time is seconds. If we set the frequency of the wave to 1.0, then the sine wave will repeat every second, that is to say, at the end of each second the term <em>2*PI*frequency*time + phase </em>will be equal to an integer multiple of 2*PI, plus the original phase of the wave.</p>
<p>Now suppose that we subtract a constant offset from the <em>time</em> variable, as would be the case if we sent this waveform through a delay line: The term inside the sine operation now becomes <em>2*PI*frequency*(time-offset) + phase</em>.</p>
<p>With a bit of basic algebra, we can restate this as <em>2*PI*frequency*time + (phase &#8211; offset*2*PI*frequency)</em>.</p>
<p>Think about this for a second: What this means is that subtracting a constant time offset from a sine wave (i.e. adding a delay) is the same as shifting the wave&#8217;s phase by a value depending on both this offset and the wave&#8217;s frequency!</p>
<p>Take a wave of frequency 1.0: its value at any given point in time is <em>sin(2*PI*1*time + phase)</em>. Now delay the wave by 0.5 seconds: its value is now <em>sin(2*PI*1*time + phase – 0.5*2*PI*1)</em>, which is equal to <em>sin(2*PI*time + phase &#8211; PI)</em> or a phase shift of -PI radians.</p>
<p>Now take a wave of frequency 2.0 and delay it by the same amount of time, 0.5 seconds: its value is now <em>sin(2*PI*2*time + phase – 0.5*2*PI*2)</em>, which is equal to <em>sin(2*PI*2*time + phase – PI*2)</em> or a phase shift of -PI*2 radians.</p>
<p>The thing to note here is that adding a sine wave to a phase shifted copy of itself will result in constructive interference, if the two waves are 2*PI radians apart (the resulting wave will then go from -2 to 2, instead of -1 to 1), and complete annihilation if the two waves are PI radians apart.</p>
<p>With that in mind, let&#8217;s look at a simple table of how much phase shift is introduced if a wave of a given frequency is delayed by 0.5 seconds:</p>
<p>&nbsp;</p>
<table style="text-align: left;">
<tbody>
<tr>
<td style="text-align: left;"><strong>frequency:</strong></td>
<td style="text-align: left;"><strong>0.5 seconds delay:</strong></td>
<td style="text-align: left;"><strong>resulting phase shift in radians:</strong></td>
</tr>
<tr>
<td style="text-align: left;">0.5</td>
<td style="text-align: left;">sin(2*PI*0.5*time + phase – 0.5*2*PI*0.5)</td>
<td style="text-align: left;">-PI*0.5</td>
</tr>
<tr>
<td style="text-align: left;">1.0</td>
<td style="text-align: left;">sin(2*PI*1*time + phase – 0.5*2*PI*1)</td>
<td style="text-align: left;">-PI</td>
</tr>
<tr>
<td style="text-align: left;">2.0</td>
<td style="text-align: left;">sin(2*PI*2*time + phase – 0.5*2*PI*2)</td>
<td style="text-align: left;">-PI*2 = 0 <em>(-&gt; sin(2*PI) == sin(0))</em></td>
</tr>
<tr>
<td style="text-align: left;">3.0</td>
<td style="text-align: left;">sin(2*PI*3*time + phase – 0.5*2*PI*3)</td>
<td style="text-align: left;">-PI*3 = -PI</td>
</tr>
<tr>
<td style="text-align: left;">4.0</td>
<td style="text-align: left;">sin(2*PI*4*time + phase – 0.5*2*PI*4)</td>
<td style="text-align: left;">-PI*4 = 0</td>
</tr>
<tr>
<td style="text-align: left;">5.0</td>
<td style="text-align: left;">sin(2*PI*5*time + phase – 0.5*2*PI*5)</td>
<td style="text-align: left;">-PI*5 = -PI</td>
</tr>
</tbody>
</table>
<p>[…]</p>
<p>What this table shows is that the same time delay of 0.5 seconds causes phase shifts in sine waves, with the intensity of the shifts periodically varying with increasing frequency. Any sine wave of an integer odd frequency will get shifted by -PI, any sine wave of even frequency will get shifted by a multiple of 2*PI, which is the same as no shift at all. Any frequencies in between will be phase shifted by an amount between 0 and -PI.</p>
<p>Since we know that phase shifted sine waves either cancel each other out or amplify one another, and we also know that any complex sound can be seen as composed of basic sines, it should now become clear why adding (or subtracting) a delayed version of any waveform to itself results in a comb filter:</p>
<p>The waveform&#8217;s constituent sine and cosine waves are phase-shifted by the delay, and the amount of shifting depends on the waves&#8217; individual frequencies, so some frequencies in the original signal will be wiped out, while other frequencies will be amplified. To see how this is dependent on the duration of the delay, simply take a look at the above table and check what happens if you substitute 0.1 or 2.0 for the 0.5 second delay we have been working with so far.</p>
<p>&nbsp;</p>
<h2><em>Flangers and chorus effects</em></h2>
<p>The spacing of the spikes in a comb filter&#8217;s frequency response depends on the duration of the delay that is used to create the filter. A flanger simply varies the duration of the delay over time, typically with a low frequency sine or triangle wave. This results in the peaks and troughs of the filter wandering across the spectrum, such as in the following illustration:</p>
<div id="attachment_284" class="wp-caption aligncenter" style="width: 580px"><a href="http://philippseifried.com/blog/wp-content/uploads/2011/10/spectrum_flanger.jpg"><img class="size-full wp-image-284" title="Spectral view of noise after applying a flanger" src="http://philippseifried.com/blog/wp-content/uploads/2011/10/spectrum_flanger.jpg" alt="" width="570" height="164" /></a><p class="wp-caption-text">Spectrogram of noise after applying a flanger - you can see the teeth of a comb filter oscillate back and forth.</p></div>
<p>This is your basic flanger: a single delayed version of the signal, with the delay offset being manipulated by a low frequency oscillator (<em>LFO</em>). Your favorite audio software&#8217;s flanger implementation usually offers control over the intensity of the effect (i.e. a scalar with which the delayed copy of the signal is multiplied before being added back), the duration of the delay as unmodified by the LFO, and the frequency and depth of the LFO.</p>
<p>More advanced flanger implementations may also let you choose between positive and negative comb filters, they may let you feed the modified signal back into the delay line (with a parameter controlling the amount of feedback), or they may let you filter the delayed signal (applying low pass or high pass filters, for example), so as to only allow a certain frequency range to be affected by the flanger effect.</p>
<p>Another neat trick that is easy to implement is to perform stereo flanging, by having the phase of the LFO used to flange the left channel be opposite (i.e. PI radians apart) from the right channel&#8217;s.</p>
<p>Chorus effects are basically the same as flangers, but with a much longer delay.</p>
<p><em>Audio sample: 2 guitar samples, one dry, one processed with a chorus effect.</em></p>
<p>The idea behind a chorus effect is that having a second version (or several versions) of the same sound, which is slightly out of tune, will provide a richer timbre. Well, what happens when you take a delayed copy of a sound and have the delay length oscillate between two values? That copy speeds up and down periodically, which means that its pitch oscillates as in a vibrato effect! Add that version back to the original, and you get an effect not unlike two voices singing the same track in unison – the delay length just has to be long enough so that there are no noticeable flanging effects.</p>
<p>The chorus effect can be heard on a lot of vocals and guitars throughout the ages (I think the 80s were particularly fond of the effect). Just as with flangers, your typical chorus lets you set the depth of the delay, the depth and frequency of the vibrato effect, and the dry/wet mix of the signal. Advanced implementations will also let you set the polarity (adding or subtracting the original signal) of the effect and possibly let you filter the delayed portion of the signal so as to exclude parts of the frequency spectrum. Again, as with flangers, it&#8217;s easy to implement a chorus effect in a manner so that one stereo channel can receive a different delay offset than the other.</p>
]]></content:encoded>
			<wfw:commentRss>http://philippseifried.com/blog/2011/11/01/dynamic-audio-4-comb-filters-flangers-and-chorus-effects/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
<enclosure url="http://philippseifried.com/blog/wp-content/uploads/2011/11/sample_flanger.mp3" length="169052" type="audio/mpeg" />
<enclosure url="http://philippseifried.com/blog/wp-content/uploads/2011/11/sample_comb.mp3" length="169052" type="audio/mpeg" />
<enclosure url="http://philippseifried.com/blog/wp-content/uploads/2011/11/sample_chorus.mp3" length="240416" type="audio/mpeg" />
		</item>
		<item>
		<title>Realtime audio processing in Flash, part 3: Crush all humans!</title>
		<link>http://philippseifried.com/blog/2011/10/20/dynamic-audio-in-as3-part-3-robot-voice/</link>
		<comments>http://philippseifried.com/blog/2011/10/20/dynamic-audio-in-as3-part-3-robot-voice/#comments</comments>
		<pubDate>Thu, 20 Oct 2011 20:31:24 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[AS3]]></category>
		<category><![CDATA[Audio]]></category>
		<category><![CDATA[Tutorials]]></category>

		<guid isPermaLink="false">http://philippseifried.com/blog/?p=257</guid>
		<description><![CDATA[<p>In the first two tutorials of this series on dynamic audio in AS3, we&#8217;ve covered pretty much everything that Flash&#8217;s realtime sound API offers us. Let&#8217;s put all of it to use and benefit humankind by building a little app that will turn your voice into a horrible robot!</p> <p>Basically, what we&#8217;re going to do [...]]]></description>
			<content:encoded><![CDATA[<p>In the first two tutorials of this series on dynamic audio in AS3, we&#8217;ve covered pretty much everything that Flash&#8217;s realtime sound API offers us. Let&#8217;s put all of it to use and benefit humankind by building a little app that will turn your voice into a <em>horrible robot</em>!</p>
<div id="attachment_267" class="wp-caption aligncenter" style="width: 325px"><a href="http://philippseifried.com/blog/wp-content/uploads/2011/10/robot.gif"><img class="size-full wp-image-267" title="Red Robot" src="http://philippseifried.com/blog/wp-content/uploads/2011/10/robot.gif" alt="" width="315" height="261" /></a><p class="wp-caption-text">Pictured: Code sample 5. (image source: http://www.dieselsweeties.com)</p></div>
<p>Basically, what we&#8217;re going to do is take input from the microphone in a stream of sound samples, try to come up with something interesting to do with the samples, and then send the samples on to the sound card&#8217;s output.</p>
<p>Rather than building the app so that the user gets record and play buttons with which they can record a take and play back the processed version, we&#8217;ll build a real-time effect and process and play back the sound as it comes in. There are two reasons I want to go this route:</p>
<p>First, there&#8217;s less chrome involved that doesn&#8217;t add to the subject (GUI, application state and such).</p>
<p>Second, real-time processing is actually harder and more interesting than processing pre-recorded audio, because it poses the question how we can make (reasonably) sure that we have received enough input from the mic whenever we&#8217;re asked to fill a new output buffer. I&#8217;ll go over the intricacies of that in the next two sections – if you want to get right to the part where we do nasty things to your vowels, feel free to skip these and come back to them later.</p>
<p><span id="more-257"></span></p>
<p>&nbsp;</p>
<h2>Buffer syncing woes, in theory&#8230;</h2>
<p>We have a microphone that periodically provides input to a buffer, and sound output which periodically asks us to fill a buffer with new samples. Let&#8217;s take a look at why syncing the two isn&#8217;t that trivial:</p>
<p>Suppose that, for argument&#8217;s sake, your output buffer is 3500 samples large, and the microphone dispatches a new SampleDataEvent whenever it has 3000 new samples available for you. If you start both the output sound instance and the microphone input at the same time, what&#8217;s going to happen?</p>
<p>At t=0 (the time unit here is single samples), you&#8217;re asked to create a 3500 sample output buffer, but you have collected no input yet! So, let&#8217;s say you fill the output buffer with zeros.</p>
<p>At t=3000, 3000 samples come in from the microphone, so you process and store these to use later. At t=3500, the sound card needs 3500 new samples, but you have only collected 3000 from the input so far! Do you wait until the next output cycle (introducing a further 3500 sample lag!), or do you send 500 zero-samples followed by 3000 processed input samples to the output buffer and continue from there?</p>
<p>Let&#8217;s say you do the latter: At t=3500, your input buffer is now of size 0. At t=6000, 3000 new samples come in from the input, so you add them to your input buffer (which is now 3K). However, at t=7000, the output buffer is empty again, and you&#8217;ll need 3500 input samples to fill it, but you only have 3000 available. If you pad the rest with zeros, you&#8217;ve introduced stuttering. What&#8217;s worse, the math actually works out so that the stuttering will reoccur periodically (if you go the route of adding to the output whatever number of samples you have at the time and padding the rest with zeros): at t=7000, your input is at 0 again. T=9000, 3K samples come from the input, but at t=10500, 3.5K samples are needed again for the output and you only have 3K&#8230;</p>
<p>The above is what happens when we start both mic input and sound output at the same time, and the output buffer is larger than the input buffer. Let&#8217;s look at a case where both are exactly the same size, say 3K:</p>
<p>At t=0, the output buffer needs 3K samples, but you don&#8217;t have any yet. At t=3K, 3K samples come in from the mic input, and the output buffer needs 3K new samples. So you process the input, send it to the output, and your input storage remains at 0. At t=6K, the same thing happens again, and 3K samples come in just before the output buffer needs 3K new output samples, again setting the input buffer to size 0.</p>
<p>The “<em>just before</em>” in the last sentence means trouble! What happens, if, by whatever operating system hiccup or timer inconsistency or hardware inconsistency, the SampleDataEvent that asks for new output suddenly gets called before the SampleDataEvent that provides new input? You get a 3000 sample pause in your output!</p>
<p>Finally, let&#8217;s look at a case where the output buffer is smaller than the input buffer size. Let&#8217;s say the microphone data comes in every 3500 samples, and your output buffer is 3000 samples in size (and you&#8217;re starting both at the same time).</p>
<p>At t=0, you&#8217;re expected to send 3000 samples to the output, but you haven&#8217;t received any from the mic, so you send 0s. At t=3K, the same thing happens again. At t=3.5K, 3.5K samples come in from the mic, so you process and store them. At t=6K, you need to send 3000 samples to the output, so you take them from your input storage, leaving 500 more samples in store. At t=7K, the mic sends you a further 3.5K samples, so your input buffer is now 4K samples big. At t=9K, the output requests 3K, leaving 1K in your input buffer.</p>
<p>I&#8217;ll cut this a little short and give you a table of what happens to the input buffer&#8217;s size at subsequent points in time.</p>
<p>t=10.5K → 4.5K (+3.5K came from input)<br />
t=12K → 1.5K (-3K went to output)<br />
t=14K → 5K (in)<br />
t=15K → 2K (out)<br />
t=17.5K → 5.5K (in)<br />
t=18K → 2.5K (out)<br />
The interesting thing actually happens at t=21K. In theory, at this point, you should both receive 3.5K of input samples and supply 3K of output samples! The problem is that, if you&#8217;re asked to produce the output before the input arrives (and there&#8217;s really no guarantee which will happen first), you are 500 samples short and you&#8217;ll have a gap in your playback!</p>
<p>&nbsp;</p>
<h2>… and in practice!</h2>
<p>Clearly, if we&#8217;re transferring mic input to audio output, we need to accumulate some input and buffer it before we start the output. How much? To be frank, I really don&#8217;t know.</p>
<p>For starters, how much sample data will we get from the microphone at once? The answer is, that&#8217;s actually inconsistent! On my Mac, I&#8217;m getting new mic data every 1024 samples, but <em>just occasionally</em> the mic skips a SampleDataEvent entirely, and the next one comes with 2048. I&#8217;ve even seen hiccups where I get 4096 samples at once, although to be fair, I&#8217;ve only seen these near the application&#8217;s initialization.</p>
<p>Also, I have found no information on whether the 1024 sample interval is a target for all platforms, or whether the input intervals are different depending on your operating system, or even your sound card. If that information isn&#8217;t explicitly stated anywhere, it&#8217;s probably a good idea to treat it as undefined, and therefore subject to change.</p>
<p>So how do we proceed? I can think of two strategies:</p>
<p>a.) Buffer up an empirically determined amount of input samples before even starting the output, erring on the side of caution. The amount you want to pre-buffer is at least the size of one complete output buffer plus one complete input. Suppose your output buffer is 2048 samples and new data comes in every 1024 samples – you&#8217;d buffer up 3072 samples before starting playback. Since we already know that occasionally new mic input only comes in every 2048 samples, let&#8217;s be safe and make the buffer at least 4096. To be sure, you&#8217;re adding 4096/44100 = 92ms of latency between when a sample comes in from the mic&#8217;s SampleDataEvent and when it goes into the output sample data (and that&#8217;s on top of all the other latencies, including another guest to the party, which is your sound card&#8217;s input lag), but at least you&#8217;re fairly safe as far as buffer underflows are concerned.</p>
<p>b.) Start with a small safety buffer and monitor for underflows (i.e. instances where you have no input data left so you need to pad with zeros). Whenever an underflow occurs, increase the safety buffer&#8217;s maximum size and buffer until it is full again before continuing playback. This will be sure to have stutters in the first few seconds or so, but it should converge on the minimum “safe” latency (where “safe” means safe until something that holds up the input happens that didn&#8217;t happen before).</p>
<p>Those are the two avenues I&#8217;ve come up with. If you have a different suggestion, please leave a comment!</p>
<p>Personally, I&#8217;d argue that route a.) is almost always preferable. It works, it&#8217;s easier to implement, and if your objective is low latency input processing in Flash, then I&#8217;m afraid you&#8217;ve already lost. We might as well make the application perform with as few gaps as we can.</p>
<p>&nbsp;</p>
<h2>Enough already, let&#8217;s make some noise!</h2>
<p>The following code sample sets up input and output SampleDataEvent handlers and pre-buffers at least the amount of samples specified in the MIN_SAFETY_BUFFER constant (more if MIN_SAFETY_BUFFER is not an integer multiple of the microphone&#8217;s input buffer size). In order to make this a complete application, it would be a good idea to handle microphone status events as well, so as to deal with users that don&#8217;t allow you to gather microphone input, but we&#8217;ll leave that out for now, to keep the example short and simple.</p>
<p>You can actually put the code on the timeline in Flash! Make sure you put on your headphones so you don&#8217;t get feedback.</p>
<p><p>
								<pre class="Plum_Code_Box"><code class="javascript">import flash.media.Microphone;
import flash.events.SampleDataEvent;
import flash.media.Sound;

/*
Example 1:
Microphone input to audio output
*/

const BUFFER_SIZE:int = 2048; // output buffer size
const MIN_SAFETY_BUFFER:int = 1024; // minimum collected input before output starts

var outputActive:Boolean = false; // will be set to true once MIN_SAFETY_BUFFER samples have been collected.

var mic:Microphone = Microphone.getMicrophone();
mic.rate = 44;
mic.setSilenceLevel(0); // you need to set this, or else the mic will stop sending data when it detects prolonged silence!
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);

var inputBuffer:Vector.&lt;Number&gt; = new Vector.&lt;Number&gt;(); // buffer in which we'll store input data

var playbackSound:Sound = new Sound();
playbackSound.addEventListener(SampleDataEvent.SAMPLE_DATA, soundSampleDataHandler);
playbackSound.play();


function micSampleDataHandler(event:SampleDataEvent):void 
{
  while(event.data.bytesAvailable)
  {
    var sample:Number = event.data.readFloat(); // microphone input is mono! 
    inputBuffer.push(sample);
  }
  if (!outputActive &amp;&amp; inputBuffer.length &gt;= MIN_SAFETY_BUFFER)
  {
    trace(&quot;starting playback!&quot;);
    outputActive = true;
  }
}

function soundSampleDataHandler(event:SampleDataEvent):void
{
  var outputBuffer:Vector.&lt;Number&gt;;
  // move samples from input to output buffer:
  if (outputActive)
  {
    // if playback is enabled, take BUFFER_SIZE number of samples from the input buffer...
    outputBuffer = inputBuffer.splice(0, BUFFER_SIZE);
    if (outputBuffer.length &lt; BUFFER_SIZE)
    {
      trace(&quot;buffer underflow!&quot;);
      while (outputBuffer.length &lt; BUFFER_SIZE) outputBuffer.push(0);
    }
  } else
  {
    // ... otherwise create an empty output buffer of the right size
    outputBuffer = new Vector.&lt;Number&gt;(BUFFER_SIZE);
  }
  
  // process samples and add them to the SampleDataEvent's data.
  for (var i:int=0; i&lt;BUFFER_SIZE; i++)
  {
    var currentSample:Number = outputBuffer[i];
    // do something interesting with the sample here!

    event.data.writeFloat(currentSample); // left channel
    event.data.writeFloat(currentSample); // right channel
  }
}
</code>
									</pre>
							</p></p>
<p>Right at the bottom of the code, there&#8217;s a line where the currentSample variable is set, just before it is added to the ByteArray that is sent to the sound card: <em>var currentSample:Number = outputBuffer[i];</em></p>
<p>Here would be the best place to do something interesting to each sample.</p>
<p>What should we do? It&#8217;s up to you to experiment! Here are a few ideas to get you started:</p>
<p>1.) Add a very short delay (a few milliseconds) with feedback! The result is basically a comb filter with a very metallic sound. One way to implement this is to have another Vector.&lt;Number&gt;, and use it as a queue. For each sample, if the queue has reached the length necessary to begin the delay effect, take the first element off the queue, multiply it with the feedback factor and add it to the current sample. Finish processing the current sample, then add it to the end of the queue.</p>
<p><p>
								<pre class="Plum_Code_Box"><code class="javascript">import flash.media.Microphone;
import flash.events.SampleDataEvent;
import flash.media.Sound;

/*
Example 2:
Delay / comb filter
*/

const BUFFER_SIZE:int = 2048; // output buffer size
const MIN_SAFETY_BUFFER:int = 1024; // minimum collected input before output starts

// buffer used for comb filter effect
var delayQueue:Vector.&lt;Number&gt; = new Vector.&lt;Number&gt;();
var delayLength:int = 500; // length of the delay in samples
var delayFeedback:Number = 0.9; // strength of the delay effect.

var outputActive:Boolean = false; // will be set to true once MIN_SAFETY_BUFFER samples have been collected.

var mic:Microphone = Microphone.getMicrophone();
mic.rate = 44;
mic.setSilenceLevel(0); // you need to set this, or else the mic will stop sending data when it detects prolonged silence!
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);

var inputBuffer:Vector.&lt;Number&gt; = new Vector.&lt;Number&gt;(); // buffer in which we'll store input data

var playbackSound:Sound = new Sound();
playbackSound.addEventListener(SampleDataEvent.SAMPLE_DATA, soundSampleDataHandler);
playbackSound.play();


function micSampleDataHandler(event:SampleDataEvent):void 
{
  while(event.data.bytesAvailable)
  {
    var sample:Number = event.data.readFloat(); // microphone input is mono! 
    inputBuffer.push(sample);
  }
  if (!outputActive &amp;&amp; inputBuffer.length &gt;= MIN_SAFETY_BUFFER)
  {
    trace(&quot;starting playback!&quot;);
    outputActive = true;
  }
}

function soundSampleDataHandler(event:SampleDataEvent):void
{
  var outputBuffer:Vector.&lt;Number&gt;;
  // move samples from input to output buffer:
  if (outputActive)
  {
    // if playback is enabled, take BUFFER_SIZE number of samples from the input buffer...
    outputBuffer = inputBuffer.splice(0, BUFFER_SIZE);
    if (outputBuffer.length &lt; BUFFER_SIZE)
    {
      trace(&quot;buffer underflow!&quot;);
      while (outputBuffer.length &lt; BUFFER_SIZE) outputBuffer.push(0);
    }
  } else
  {
    // ... otherwise create an empty output buffer of the right size
    outputBuffer = new Vector.&lt;Number&gt;(BUFFER_SIZE);
  }
  
  // process samples and add them to the SampleDataEvent's data.
  for (var i:int=0; i&lt;BUFFER_SIZE; i++)
  {
    var currentSample:Number = outputBuffer[i];
    // delay effect / comb filter:

    // if the delay queue has reached its target length, take the sample at the beginning and 
    // mix it with currentSample
    if (delayQueue.length &gt; delayLength) 
    {
      var delayedSample:Number = delayQueue.shift();
      currentSample += delayedSample*delayFeedback;
    }

    // push current sample to the delay queue's end
    // note: we're adding the already processed sample back into the queue, so we get
    // feedback of feedback, etc.
    delayQueue.push(currentSample);

    event.data.writeFloat(currentSample); // left channel
    event.data.writeFloat(currentSample); // right channel
  }
}
</code>
									</pre>
							</p></p>
<p>2.) Perform frequency shifting! Frequency shifting is actually really easy to do – just multiply the samples with a sine wave. (The rather complicated technical explanation for why this works is that multiplication in the time domain is the same as convolution in the frequency domain, and in the frequency domain, a sine wave is just an ideal impulse, offset by its frequency – hopefully I&#8217;ll be able to make this clearer in a future article!)<br />
Frequency shifting adds non-harmonic content and gives a pretty cool metallic character to human speech. Note that it&#8217;s different from pitch shifting which basically multiplies all the frequencies that make up a given sound with a constant, whereas frequency shifting adds a constant offset to all the frequencies in a sound.</p>
<p><p>
								<pre class="Plum_Code_Box"><code class="javascript">import flash.media.Microphone;
import flash.events.SampleDataEvent;
import flash.media.Sound;

/*
Example 3:
Frequency shifter
*/

const BUFFER_SIZE:int = 2048; // output buffer size
const MIN_SAFETY_BUFFER:int = 1024; // minimum collected input before output starts

var freqShiftPhase:Number = 0; // phase of the sine wave used for frequency shifting
var freqShiftDeltaPhase:Number = 75*2*Math.PI/44100; // phase increase per sample

var outputActive:Boolean = false; // will be set to true once MIN_SAFETY_BUFFER samples have been collected.

var mic:Microphone = Microphone.getMicrophone();
mic.rate = 44;
mic.setSilenceLevel(0); // you need to set this, or else the mic will stop sending data when it detects prolonged silence!
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);

var inputBuffer:Vector.&lt;Number&gt; = new Vector.&lt;Number&gt;(); // buffer in which we'll store input data

var playbackSound:Sound = new Sound();
playbackSound.addEventListener(SampleDataEvent.SAMPLE_DATA, soundSampleDataHandler);
playbackSound.play();


function micSampleDataHandler(event:SampleDataEvent):void 
{
  while(event.data.bytesAvailable)
  {
    var sample:Number = event.data.readFloat(); // microphone input is mono! 
    inputBuffer.push(sample);
  }
  if (!outputActive &amp;&amp; inputBuffer.length &gt;= MIN_SAFETY_BUFFER)
  {
    trace(&quot;starting playback!&quot;);
    outputActive = true;
  }
}

function soundSampleDataHandler(event:SampleDataEvent):void
{
  var outputBuffer:Vector.&lt;Number&gt;;
  // move samples from input to output buffer:
  if (outputActive)
  {
    // if playback is enabled, take BUFFER_SIZE number of samples from the input buffer...
    outputBuffer = inputBuffer.splice(0, BUFFER_SIZE);
    if (outputBuffer.length &lt; BUFFER_SIZE)
    {
      trace(&quot;buffer underflow!&quot;);
      while (outputBuffer.length &lt; BUFFER_SIZE) outputBuffer.push(0);
    }
  } else
  {
    // ... otherwise create an empty output buffer of the right size
    outputBuffer = new Vector.&lt;Number&gt;(BUFFER_SIZE);
  }
  
  // process samples and add them to the SampleDataEvent's data.
  for (var i:int=0; i&lt;BUFFER_SIZE; i++)
  {
    var currentSample:Number = outputBuffer[i];
    
    freqShiftPhase += freqShiftDeltaPhase;
    currentSample *= Math.sin(freqShiftPhase);
    
    event.data.writeFloat(currentSample); // left channel
    event.data.writeFloat(currentSample); // right channel
  }
}
</code>
									</pre>
							</p></p>
<p>3.) Take the first two ideas and add low frequency oscillators to them. If you modify the delay time of the comb filter (code example 2) with an LFO, you get your basic flanger! If you modify the frequency of the sine wave you multiply the input with (code example 3), you get a bouncy, cartoony effect, of which I&#8217;m not quite sure whether it actually has a name.<br />
The following code produces a flanger effect by moving back and forth the index of the sample in the delay queue, the one that we add in as feedback. Note the somewhat dirty, distorted sound of the effect: That&#8217;s due to the fact that we perform no interpolation whatsoever. In the future, I&#8217;ll write a separate tutorial on flanging and chorus effects in which we&#8217;ll clean this up, but this is Robot Building 101, so a little dirt won&#8217;t hurt us!</p>
<p><p>
								<pre class="Plum_Code_Box"><code class="javascript">import flash.media.Microphone;
import flash.events.SampleDataEvent;
import flash.media.Sound;

/*
Example 4:
Adding an LFO to produce flanging
*/

const BUFFER_SIZE:int = 2048; // output buffer size
const MIN_SAFETY_BUFFER:int = 1024; // minimum collected input before output starts

// buffer used for comb filter effect
var delayQueue:Vector.&lt;Number&gt; = new Vector.&lt;Number&gt;();
var delayLength:int = 100; // length of the delay in samples
var delayFeedback:Number = 0.8; // strength of the delay effect.
// lfo to transform the comb filter into a flanger
var lfoPhase:Number = 0; // phase of the LFO
var lfoDeltaPhase:Number = 0.5*2*Math.PI/44100; // phase increase per sample
var lfoModStrength:Number = 20; // strength of the lfo, given in +/- sample offset

var outputActive:Boolean = false; // will be set to true once MIN_SAFETY_BUFFER samples have been collected.

var mic:Microphone = Microphone.getMicrophone();
mic.rate = 44;
mic.setSilenceLevel(0); // you need to set this, or else the mic will stop sending data when it detects prolonged silence!
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);

var inputBuffer:Vector.&lt;Number&gt; = new Vector.&lt;Number&gt;(); // buffer in which we'll store input data

var playbackSound:Sound = new Sound();
playbackSound.addEventListener(SampleDataEvent.SAMPLE_DATA, soundSampleDataHandler);
playbackSound.play();


function micSampleDataHandler(event:SampleDataEvent):void 
{
  while(event.data.bytesAvailable)
  {
    var sample:Number = event.data.readFloat(); // microphone input is mono! 
    inputBuffer.push(sample);
  }
  if (!outputActive &amp;&amp; inputBuffer.length &gt;= MIN_SAFETY_BUFFER)
  {
    trace(&quot;starting playback!&quot;);
    outputActive = true;
  }
}

function soundSampleDataHandler(event:SampleDataEvent):void
{
  var outputBuffer:Vector.&lt;Number&gt;;
  // move samples from input to output buffer:
  if (outputActive)
  {
    // if playback is enabled, take BUFFER_SIZE number of samples from the input buffer...
    outputBuffer = inputBuffer.splice(0, BUFFER_SIZE);
    if (outputBuffer.length &lt; BUFFER_SIZE)
    {
      trace(&quot;buffer underflow!&quot;);
      while (outputBuffer.length &lt; BUFFER_SIZE) outputBuffer.push(0);
    }
  } else
  {
    // ... otherwise create an empty output buffer of the right size
    outputBuffer = new Vector.&lt;Number&gt;(BUFFER_SIZE);
  }
  
  // process samples and add them to the SampleDataEvent's data.
  for (var i:int=0; i&lt;BUFFER_SIZE; i++)
  {
    var currentSample:Number = outputBuffer[i];
    // flanger

    // if the delay queue has reached its target length, take the sample at the beginning and 
    // mix it with currentSample
    if (delayQueue.length &gt; delayLength) 
    {
      delayQueue.shift(); // remove first sample
      
      lfoPhase += lfoDeltaPhase;
      var delayedSample:Number = delayQueue[ Math.floor( Math.sin(lfoPhase)*lfoModStrength )+lfoModStrength ];
      
      currentSample += delayedSample*delayFeedback;
    }

    // push current sample to the delay queue's end
    delayQueue.push(currentSample);

    event.data.writeFloat(currentSample); // left channel
    event.data.writeFloat(currentSample); // right channel
  }
}
</code>
									</pre>
							</p></p>
<p>Now put all of these together! Note what happens when you change the order of operations (it should have an effect, because the frequency shifting is a non-linear operation)! Experiment and find other ways to put dings and dents into the input material! What happens to the sound when you apply a non-linear function to each sample, such as taking its cube (hint: distortion!)? What happens to the sound when you change your frequency shifting implementation to use a triangle wave instead of a sine (hint: I have no idea, go try it out!)?</p>
<p>I&#8217;ll leave you with another combination of the above ideas, just as another starting point. The final code sample combines a flanger with a frequency shifting effect. Let&#8217;s hope it never achieves sentience!</p>
<p><p>
								<pre class="Plum_Code_Box"><code class="javascript">import flash.media.Microphone;
import flash.events.SampleDataEvent;
import flash.media.Sound;

/*
Example 5:
Hacking away...
*/

const BUFFER_SIZE:int = 2048; // output buffer size
const MIN_SAFETY_BUFFER:int = 1024; // minimum collected input before output starts

// buffer used for comb filter effect
var delayQueue:Vector.&lt;Number&gt; = new Vector.&lt;Number&gt;();
var delayLength:int = 800; // length of the delay in samples
var delayFeedback:Number = 0.8; // strength of the delay effect.
// lfo to transform the comb filter into a flanger
var lfoPhase:Number = 0; // phase of the LFO
var lfoDeltaPhase:Number = 20*2*Math.PI/44100; // phase increase per sample
var lfoModStrength:Number = 100; // strength of the lfo, given in +/- sample offset

var freqShiftPhase:Number = 0; // phase of the sine wave used for frequency shifting
var freqShiftDeltaPhase:Number = 1800*2*Math.PI/44100; // phase increase per sample


var outputActive:Boolean = false; // will be set to true once MIN_SAFETY_BUFFER samples have been collected.

var mic:Microphone = Microphone.getMicrophone();
mic.rate = 44;
mic.setSilenceLevel(0); // you need to set this, or else the mic will stop sending data when it detects prolonged silence!
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);

var inputBuffer:Vector.&lt;Number&gt; = new Vector.&lt;Number&gt;(); // buffer in which we'll store input data

var playbackSound:Sound = new Sound();
playbackSound.addEventListener(SampleDataEvent.SAMPLE_DATA, soundSampleDataHandler);
playbackSound.play();


function micSampleDataHandler(event:SampleDataEvent):void 
{
  while(event.data.bytesAvailable)
  {
    var sample:Number = event.data.readFloat(); // microphone input is mono! 
    inputBuffer.push(sample);
  }
  if (!outputActive &amp;&amp; inputBuffer.length &gt;= MIN_SAFETY_BUFFER)
  {
    trace(&quot;starting playback!&quot;);
    outputActive = true;
  }
}

function soundSampleDataHandler(event:SampleDataEvent):void
{
  var outputBuffer:Vector.&lt;Number&gt;;
  // move samples from input to output buffer:
  if (outputActive)
  {
    // if playback is enabled, take BUFFER_SIZE number of samples from the input buffer...
    outputBuffer = inputBuffer.splice(0, BUFFER_SIZE);
    if (outputBuffer.length &lt; BUFFER_SIZE)
    {
      trace(&quot;buffer underflow!&quot;);
      while (outputBuffer.length &lt; BUFFER_SIZE) outputBuffer.push(0);
    }
  } else
  {
    // ... otherwise create an empty output buffer of the right size
    outputBuffer = new Vector.&lt;Number&gt;(BUFFER_SIZE);
  }
  
  // process samples and add them to the SampleDataEvent's data.
  for (var i:int=0; i&lt;BUFFER_SIZE; i++)
  {
    var currentSample:Number = outputBuffer[i];

    
    if (delayQueue.length &gt; delayLength) 
    {
      delayQueue.shift(); // remove first sample
      
      lfoPhase += lfoDeltaPhase;
      var delayedSample:Number = delayQueue[ Math.floor( Math.sin(lfoPhase)*lfoModStrength )+lfoModStrength ];
      
      currentSample += delayedSample*delayFeedback;
      delayQueue.push(currentSample);

      freqShiftPhase += freqShiftDeltaPhase * Math.sin(lfoPhase*0.4);
      
      currentSample = currentSample*0.6 + 0.4*Math.sin(freqShiftPhase)*currentSample;
      
    } else delayQueue.push(currentSample);


    event.data.writeFloat(currentSample); // left channel
    event.data.writeFloat(currentSample); // right channel
  }
}
</code>
									</pre>
							</p></p>
]]></content:encoded>
			<wfw:commentRss>http://philippseifried.com/blog/2011/10/20/dynamic-audio-in-as3-part-3-robot-voice/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Realtime audio processing in Flash, part 2: The basics</title>
		<link>http://philippseifried.com/blog/2011/10/13/dynamic-audio-in-as3-part-2-basics/</link>
		<comments>http://philippseifried.com/blog/2011/10/13/dynamic-audio-in-as3-part-2-basics/#comments</comments>
		<pubDate>Thu, 13 Oct 2011 17:49:51 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[AS3]]></category>
		<category><![CDATA[Audio]]></category>
		<category><![CDATA[Tutorials]]></category>

		<guid isPermaLink="false">http://philippseifried.com/blog/?p=227</guid>
		<description><![CDATA[<p>In this second installment of my series of tutorials on dynamic sound in ActionScript, I&#8217;ll discuss the different parts of the sound API and show you how to extract single samples from a sound that&#8217;s in memory or coming from the microphone, as well as how to generate simple dynamic audio in real time.</p> <p>As [...]]]></description>
			<content:encoded><![CDATA[<p>In this second installment of my series of tutorials on dynamic sound in ActionScript, I&#8217;ll discuss the different parts of the sound API and show you how to extract single samples from a sound that&#8217;s in memory or coming from the microphone, as well as how to generate simple dynamic audio in real time.</p>
<p>As I wrote in <a title="Dynamic audio in Flash, part 1" href="http://philippseifried.com/blog/2011/10/07/dynamic-audio-in-as3-part-1/">part 1</a>, the sound API introduced in Flash Player 10 is essentially just a set of methods and classes that let you access individual samples in a sound. Just as the introduction of the BitmapData class enabled you to manipulate and read pixels in bitmap images and from a webcam&#8217;s input, the sound API lets you process sound in memory or coming from microphone input, at the sample level.</p>
<p>There are two basic ingredients you need to understand in order to cook up dynamic audio – ByteArrays and SampleDataEvents:</p>
<p>&nbsp;</p>
<p><span id="more-227"></span></p>
<h2>ByteArrays</h2>
<p>ByteArrays are the format in which you&#8217;ll be dealing with the raw data. Whenever you read the samples contained in an audio clip, write samples to the sound output or receive new samples from the mic, you&#8217;ll be working with a ByteArray instance.</p>
<p>If you&#8217;ve never had to deal with ByteArrays in AS3 before, don&#8217;t fret – in practice you&#8217;ll usually just read and write Numbers from and to them, or convert them to Vector.&lt;Number&gt;s to do any advanced work.</p>
<p>A ByteArray is basically an array of values of larger types (such as Numbers or even serialized Objects), sliced up into bytes and packed together into one big stream. ByteArrays store an index into the data (called the <em>position</em>), which is automatically incremented whenever you read from or write into the stream. So, for instance, if you have a ByteArray and you call its readFloat() method, you&#8217;ll get back a 32 bit (= 4 byte) Number value, giving a float representation of whatever the next 4 bytes in the stream contain, and the <em>position </em>is then incremented by 4. The next time you call readFloat(), the next floating point Number value will be returned.</p>
<div id="attachment_246" class="wp-caption aligncenter" style="width: 425px"><a href="http://philippseifried.com/blog/wp-content/uploads/2011/10/bytearray.png"><img class="size-full wp-image-246" title="ByteArray" src="http://philippseifried.com/blog/wp-content/uploads/2011/10/bytearray.png" alt="" width="415" height="54" /></a><p class="wp-caption-text">A ByteArray in Flash is essentially a stream in which arbitrary data can be stored together. Use methods like readFloat(), readDouble(), etc. to extract contents at the current position.</p></div>
<p>ByteArrays allow you to work very close to the metal (by AS3&#8242;s standards), and they provide a bit of additional useful functionality, such as data compression. For our purposes, though, all you should need to know is that you can write the next sample into a ByteArray by using writeFloat(), and while the <em>position </em>of a ByteArray is smaller than its <em>length</em> (which is also given in bytes), you can always call readFloat() to extract the next sample.</p>
<p>Let&#8217;s look at a quick example to show you how this works in practice:</p>
<p>Suppose you have an instance of class Sound, called <em>sound</em>, and suppose you also have an instance of class ByteArray, called <em>data </em>(which you can simply create with <em>data = new ByteArray(); </em>):</p>
<p><em>sound.extract(data, sound.length*44100); // writes the sound&#8217;s samples into the ByteArray</em></p>
<p>The first parameter of <em>Sound.extract()</em> is the ByteArray into which you extract the sound&#8217;s sample data. If the ByteArray already has data, the extract() method starts overwriting this data at the ByteArray&#8217;s current position and appends where needed. The second parameter of Sound.extract() specifies the number of samples to extract. A sample is two 32-bit floating point Numbers, one for each stereo channel. With the exception of mic input, when you&#8217;re dealing with dynamic audio in Flash, you&#8217;re <em><strong>always</strong></em><em> </em>dealing with 44.1 kHz stereo audio, given in normalized (i.e. full amplitude == 1.0) 32-bit floating point. Therefore, if sound.length gives you the length of a sound in seconds, sound.length*44100 gives you the number of samples there are in the complete Sound instance.</p>
<p>So, suppose you&#8217;ve extracted a sound&#8217;s sample data in this manner. How do you access it?</p>
<p><p>
								<pre class="Plum_Code_Box"><code class="javascript">data.position = 0; // reset the ByteArray's index pointer
while (data.bytesAvailable) // while there's more data at the ByteArray's current position:
{
  var leftSample:Number = data.readFloat();
  var rightSample:Number = data.readFloat();
  trace(&quot;Sample Data: &quot;+leftSample+&quot; (left), &quot;+rightSample+&quot; (right).&quot;);
}</code>
									</pre>
							</p></p>
<p>This will continue reading from the ByteArray as long as more data is available. Each iteration of the loop, two 32-bit values are read from the ByteArray, one for each stereo channel. In this case, we simply trace them out (<strong>note:</strong> as this has 44.1K trace calls per second of audio, it will probably cause trouble for sound files that aren&#8217;t super-short!), and again note how they stay between -1 and 1, as 1 is the maximum amplitude of a sample!</p>
<p>If we were to do a lot of processing on the sound samples, especially if the operations we performed took multiple input samples into account for any given output sample, we would probably push these into two Vector.&lt;Number&gt; instances, like so:</p>
<p><p>
								<pre class="Plum_Code_Box"><code class="javascript">data.position = 0; // reset the ByteArray's index
var leftChannel:Vector.&lt;Number&gt; = new Vector.&lt;Number&gt;();
var rightChannel:Vector.&lt;Number&gt; = new Vector.&lt;Number&gt;();
while (data.bytesAvailable) // while there's still data in the ByteArray:
{
  leftChannel.push(data.readFloat());
  rightChannel.push(data.readFloat());
}</code>
									</pre>
							</p></p>
<p>&nbsp;</p>
<h2>SampleDataEvent</h2>
<p>A pre-loaded Sound, such as one instantiated from the library, is already completely in memory, which is why we can extract its complete data at once. With sound that&#8217;s coming from the microphone, new samples come in all the time, so we&#8217;ll need to process them as they arrive. This (and the creation of dynamic audio output) is where the SampleDataEvent class comes in.</p>
<p>Suppose you want to capture incoming sound data from the microphone, and append it to the leftChannel and rightChannel vectors we instantiated in the last section. Note that these are Vector.&lt;Number&gt; objects, not ByteArrays:</p>
<p><p>
								<pre class="Plum_Code_Box"><code class="javascript">var mic:Microphone = Microphone.getMicrophone();

mic.rate = 44; // sets the mic's sampling rate to 44.1 kHz (note: not 44.0!)
// [...] set various attributes for the microphone, such as echo suppression, etc.
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);

function micSampleDataHandler(event:SampleDataEvent):void
{
  // event.data is a ByteArray containing sample data!
  while(event.data.bytesAvailable)
  {
    var sample:Number = event.data.readFloat();
    // microphone input is mono!
    leftChannel.push(sample);
    rightChannel.push(sample);
  }
}</code>
									</pre>
							</p></p>
<p>What&#8217;s happening here is that we have an instance of type Microphone dispatching SampleDataEvents, whenever new data comes in from the mic input. This new data comes in the shape of a ByteArray, again with 44.1 kHz 32-bit data (you can actually specify one of a few different possible sampling rates for the mic, however if you want to use the microphone input in dynamic playback, it&#8217;s prudent to use the same rate as the sound output will have). Whenever a SampleDataEvent happens, we read in the data as long as there&#8217;s more, and append it to the two Vector.&lt;Number&gt; instances we created somewhere outside. Note that input always comes in mono format, so we append the same sample to both channels.</p>
<p>&nbsp;</p>
<h2>Sound output!</h2>
<p>If we&#8217;re analyzing a Sound in memory, we simply extract its sample data into a ByteArray. If we analyze sound coming from a microphone, we listen to the SampleDataEvent. With each new SampleDataEvent, new sample data from the microphone is coming in, and we can take this data and append it to a Vector, which will then contain a recording of all microphone input since its instantiation.</p>
<p>On the opposite end, dynamic sound output uses SampleDataEvents as well! The way this works is that you instantiate an empty Sound object, add a SAMPLE_DATA event listener and then call play() on the instantiated sound. The Sound will dispatch the SAMPLE_DATA event whenever it needs more samples to continue playing, at which point your event handler should supply anywhere between 2048 and 8192 new samples (remember that, strictly speaking, a single sample means two floating point Numbers, one for each stereo channel!). If you supply less than 2048 samples, the sound assumes that it is supposed to finish. It plays the last samples you added and then stops.</p>
<p>The number of samples your SAMPLE_DATA event listener generates every time it is called is called the buffer size. A small buffer minimizes the latency, at the cost of increased risk of buffer underflows (the audio stuttering that happens if the application can&#8217;t generate enough samples to keep up). As I pointed out in the <a title="Realtime audio processing in Flash, part 1: Introduction" href="http://philippseifried.com/blog/2011/10/07/dynamic-audio-in-as3-part-1/">last article</a>, your latency is unavoidably going to be horrible on some platforms anyway, so in most cases, there&#8217;s not much point in sticking to the lowest possible buffer size at all cost. Note that the buffer size doesn&#8217;t need to be a power of two but can be any number in range 2048&#8230;8192.</p>
<p>With all that out of the way, let&#8217;s take a look at some sample code! The following piece of code simply generates stereo noise (at full volume – turn down your headphones!):</p>
<p><p>
								<pre class="Plum_Code_Box"><code class="javascript">var sound:Sound = new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, soundSampleDataHandler);
sound.play();
function soundSampleDataHandler(event:SampleDataEvent):void
{
  // We can add an arbitrary amount of new sample data to event.data, anywhere between 2048 and 8192.
  // Anything less than 2048, though, and the sound will play until the end and then stop!
  for (var i:int = 0; i&lt;2048; i++)
  {
    event.data.writeFloat(Math.random()*2-1); // random float -1...+1 for left channel
    event.data.writeFloat(Math.random()*2-1); // random float -1...+1 for right channel
  }
}</code>
									</pre>
							</p></p>
<p>After all we&#8217;ve discussed, this should be fairly straight-forward: Whenever the sound buffer is empty, soundSampleDataHandler() is called. This event handler then proceeds to create 2048 samples, each of which contains two random numbers (one per channel), between -1 and +1. These are written to the ByteArray in the SampleDataEvent handled by soundSampleDataHandler().</p>
<p>Suppose we didn&#8217;t want to produce random noise; instead, let&#8217;s look at what we&#8217;d need to do in order to produce a single A1 sine tone at the standard pitch of 440 Hz:</p>
<p><p>
								<pre class="Plum_Code_Box"><code class="javascript">var sound:Sound = new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, soundSampleDataHandler);
sound.play();

var currentPhase:Number = 0;
var deltaPhase:Number = 440/44100;

function soundSampleDataHandler(event:SampleDataEvent):void
{
  for (var i:int = 0; i&lt;2048; i++)
  {
    currentPhase += deltaPhase;
    
    // note: this is unoptimized – normally you'd multiply deltaPhase by Math.PI*2 and remove that part here!
    var currentValue:Number = Math.sin(currentPhase*Math.PI*2); 

    event.data.writeFloat(currentValue);
    event.data.writeFloat(currentValue);
  }
}</code>
									</pre>
							</p></p>
<p>A sine wave has a period of 2*PI, and a 440 Hz tone means that each second the resulting wave needs to repeat 440 times. 440*2*PI divided by the sample rate (=44100) gives us how much we need to increase the phase of the signal per sample.</p>
<p>This is pretty much the basis for the common oscillators you&#8217;ll encounter in a software synth, except that it&#8217;s usually simpler to have your phase go from 0 to 1 (instead of to 2PI): For a sine wave, you&#8217;ll take the sine of the phase * 2PI. For a square wave, you&#8217;d simply check if the phase%1 is above or below 0.5 and set 1 or 0 accordingly. From there on out, you should have no trouble creating triangle or sawtooth waves!</p>
<p>In this tutorial, we&#8217;ve discussed all major parts of the AS3 dynamic sound API. In the next installment we&#8217;ll put it all together and look at how to manipulate microphone input and turn your voice into a horrible robot! Sounds like fun? Stay tuned!</p>
]]></content:encoded>
			<wfw:commentRss>http://philippseifried.com/blog/2011/10/13/dynamic-audio-in-as3-part-2-basics/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Realtime audio processing in Flash, part 1: Introduction</title>
		<link>http://philippseifried.com/blog/2011/10/07/dynamic-audio-in-as3-part-1/</link>
		<comments>http://philippseifried.com/blog/2011/10/07/dynamic-audio-in-as3-part-1/#comments</comments>
		<pubDate>Fri, 07 Oct 2011 16:41:11 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[AS3]]></category>
		<category><![CDATA[Audio]]></category>
		<category><![CDATA[Tutorials]]></category>

		<guid isPermaLink="false">http://philippseifried.com/blog/?p=204</guid>
		<description><![CDATA[<p>In this series of advanced ActionScript tutorials, I&#8217;ll give some practical examples on how to work with the sound API introduced in Flash Player 10 to process audio in real time (filtering, adding effects, etc.) or synthesize sound from scratch. My goal is to evolve this into a series of articles that starts at the [...]]]></description>
			<content:encoded><![CDATA[<p>In this series of advanced ActionScript tutorials, I&#8217;ll give some practical examples on how to work with the sound API introduced in Flash Player 10 to process audio in real time (filtering, adding effects, etc.) or synthesize sound from scratch. My goal is to evolve this into a series of articles that starts at the very beginning but goes a good deal farther than other AS3 audio tutorials, which tend to explain the API and then stop there, as if the rest were easy to figure out on your own.</p>
<p>The current roadmap is as follows (I&#8217;ll edit this if plans change):</p>
<p>Part 1 gives an overview of what the API is and what it can and cannot do.<br />
<a title="Realtime audio processing in Flash, part 2: The basics" href="http://philippseifried.com/blog/2011/10/13/dynamic-audio-in-as3-part-2-basics/">Part 2</a> will explore the different parts that make up the API.<br />
In <a title="Realtime audio processing in Flash, part 3" href="http://philippseifried.com/blog/2011/10/20/dynamic-audio-in-as3-part-3-robot-voice/">part 3</a>, we&#8217;ll put all of that to use and create a simple effect that takes input from a microphone and turns it into a robot voice.<br />
In <a title="Realtime audio processing, part 4: Comb filters, Flangers and Chorus effects – a bit of theory" href="http://philippseifried.com/blog/2011/11/01/dynamic-audio-4-comb-filters-flangers-and-chorus-effects/">part 4</a>, we review a bit of audio theory and explain how flangers, comb filters and chorus effects work.<br />
In <a title="Realtime audio processing in Flash, part 5: Antialiasing and pitching" href="http://philippseifried.com/blog/2011/12/27/dynamic-audio-in-as3-part-5-antialiasing-and-pitching/">part 5</a>, we&#8217;ll discuss interpolation by looking at pitch shifting implementations.</p>
<p>From there on out, we&#8217;ll start working on a sound manager that will allow you to mix and seamlessly string together pieces of a song. In a Flash game, this would give you the ability to create much less repetitive music while conserving file size. In the installments following that, we&#8217;ll extend the sound manager with a flexible effect architecture. To think of what you&#8217;ll be able to do with this, picture a Flash game in which entering a cavern would add a low pass filter and reverb to the game music. Finally, at the end of the series, I&#8217;ll give you some pointers on how to write your own software synthesizer in Flash.</p>
<p><span id="more-204"></span></p>
<p>In between, I&#8217;ll probably also write some posts to explain some basic audio concepts or show you how to write specific effects, so if that&#8217;s of interest to you, it might be worth keeping an eye out for updates even if you&#8217;re not particularly interested in AS3 development. Once we get the basics out of the way, the concepts are pretty independent of language or API.</p>
<p>&nbsp;</p>
<h2>The sound API</h2>
<p>Back in the olden days, all that we could do to make some noise in ActionScript was to instantiate Sound objects from an SWF&#8217;s library, or load or stream them from the web. There were a few things one could tweak about a Sound instance, such as setting its volume and pitching or panning it, but the overall scope of what you could do with audio in Flash was very limited.</p>
<p>A few very smart folks came up with hacks that allowed dynamic audio processing by manipulating SWFs containing sound objects in memory (I think that&#8217;s how it worked – I didn&#8217;t pay as much attention at the time, so I confess I&#8217;m fuzzy on the details). These workarounds didn&#8217;t work consistently across player versions and platforms though, prompting the inception of the “<a href="http://www.make-some-noise.info/">Adobe, MAKE SOME NOISE</a>” campaign. Adobe listened and added dynamic sound in Flash Player 10.</p>
<p>The introduction of the new sound API in ActionScript was a milestone similar to when Adobe first added the ability to directly manipulate pixels in a BitmapData: for most people it made little difference, but for those who like or need to work close to the metal, it opened up a world of possibilities to explore.</p>
<p>Granted, just as people still position Sprites and Bitmaps on a stage, you most likely won&#8217;t be abandoning <em>Sound.play()</em> any time soon. For most use cases the conventional method of instantiating and playing Sound objects will be sufficient, and it comes with less latency and less overhead, both in CPU intensity and development time.</p>
<p>&nbsp;</p>
<h2>What it can do</h2>
<p>Just as the introduction of BitmapData objects enabled us to access an array of pixels that make up a bitmap image, the sound API enables us to access an array of samples. These samples are either part of a pre-loaded sound, part of a sound that is playing right now (so we can manipulate and change the sound output), or part of the sound input coming from a microphone.</p>
<p>That&#8217;s it. You basically get the equivalent of reading pixels in a pre-loaded bitmap, writing pixels to a bitmap in memory and reading pixels from a webcam. You&#8217;ll have to code any fancy processing yourself – and that includes even the basics that have already been (and continue to be) possible with Sound and SoundChannel objects, such as panning or changing the volume of the sound samples.</p>
<p>The sound API lets you process and synthesize sound in pretty much any manner you can imagine – you&#8217;ll just have to write the necessary ActionScript code yourself.</p>
<p>For an amazing example of what can be done, check out the <a title="Hobnox Audiotool" href="http://www.audiotool.com/">Hobnox Audiotool</a>!</p>
<p>&nbsp;</p>
<h2>What it can&#8217;t do</h2>
<p>I&#8217;m sad to say you won&#8217;t be able to write the next version of <a href="http://en.wikipedia.org/wiki/Reason_(software)">Reason</a> in Flash any time soon! (You might however be able to write the next version of <a href="http://en.wikipedia.org/wiki/ReBirth_RB-338">ReBirth</a>, so stay with me a little longer!)</p>
<p>There are three reasons why:</p>
<p><strong>No MIDI</strong> – ActionScript still doesn&#8217;t support MIDI input. To my understanding, Hobnox Audiotool gets around this by actually using a Java Applet for MIDI support and relaying the MIDI input to the Flash application via a local connection. It&#8217;s a wonderfully resourceful solution, but it relies on Java applet support (which is not always present and often broken) and apparently <a href="http://wiki.audiotool.com/doku.php?id=setups:midi">needs the user to redo the MIDI mapping every time they open an arrangement</a>.</p>
<p><strong>Audio processing is CPU intensive</strong> – dynamic audio in Flash is always a 44100 Hz stereo stream of 32-bit floating point data (in Flash this means the samples are of type Number). In the best case, you&#8217;ll be calculating 88200 numbers per second in ActionScript, and ActionScript is horribly slow. For any non-trivial application this means you&#8217;ll be doing some serious optimization of your code, and even then, there&#8217;s a limit to what you&#8217;ll be able to achieve.</p>
<p><strong>Latency </strong>– the single biggest issue with the sound API in Flash, however, is latency. Latency is the delay between triggering an audio event and the event actually emanating from your speakers. For any live input (e.g. playing notes on a keyboard), you&#8217;ll want to keep latency as low as possible, probably somewhere below 10 milliseconds and definitely below 30 (for comparison, in a 160 BPM song, a 16<sup>th</sup> note is ~94ms long).<br />
The most obvious contribution to latency in digital audio usually comes from the sound buffer size. The sound API lets you set this anywhere between 2048 and 8192 samples, so right off the bat your minimum latency will be 2048 samples at 44100 samples per second, amounting to 46ms. That, however, is just the start: <a title="Tinic Uro on latency in Flash's sound API" href="http://www.kaourantin.net/2008/05/adobe-is-making-some-noise-part-2.html">according to Adobe&#8217;s Tinic Uro</a>, the Flash player has an internal buffer, which adds 200 to 500 milliseconds latency (note that the same article gave the minimum buffer size as 512, but that information is outdated). That&#8217;s a fifth to half a second delay between the moment you hit a note on your keyboard and the moment you hear that note on your speaker. To be honest, I&#8217;m not even sure if that covers the actual latency of the sound card driver – which is <a href="http://en.wikipedia.org/wiki/Windows_audio_components#KMixer">traditionally horrible on Windows systems</a> – or if that is added on top of that. I do however know that the sound API&#8217;s latency can be very different across platforms. While it seems to be a lot less noticeable on Mac, we&#8217;ve had huge delays on some Windows XP machines.<br />
Either way, writing a software instrument in Flash that reacts to a skilled player&#8217;s live input is out of the question (unless the latency issues are fixed in future Flash players).</p>
<p>Writing something like <a href="http://en.wikipedia.org/wiki/ReBirth_RB-338">ReBirth</a> on the other hand is entirely possible. If your synthesizer is controlled by turning notes on and off in a sampler type user interface, latency problems become negligible. And while you probably wouldn&#8217;t want a Flash game&#8217;s sound effects to go through a significant delay, you could still have its music be affected by in-game events in interesting ways without a 200ms lag becoming too much of a problem.</p>
]]></content:encoded>
			<wfw:commentRss>http://philippseifried.com/blog/2011/10/07/dynamic-audio-in-as3-part-1/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

