{"id":55,"date":"2010-11-23T00:21:05","date_gmt":"2010-11-23T08:21:05","guid":{"rendered":"http:\/\/www.earlevel.com\/main\/?p=55"},"modified":"2010-12-05T14:01:52","modified_gmt":"2010-12-05T22:01:52","slug":"towards-a-practical-oversampling-filter","status":"publish","type":"post","link":"https:\/\/www.earlevel.com\/main\/2010\/11\/23\/towards-a-practical-oversampling-filter\/","title":{"rendered":"Towards practical resampling"},"content":{"rendered":"<p>In a <a href=\"http:\/\/www.earlevel.com\/main\/2007\/07\/03\/sample-rate-conversion\">previous article<\/a>, we looked at sample rate conversion in the frequency domain. Let&#8217;s take a quick second look in the time domain as reinforcement of principles behind sample rate conversion, before developing a practical rate convertor.<\/p>\n<p>In an ideal digital to analog convertor, we output a pulse train in which each pulse&#8217;s amplitude corresponds to a sample value. The pulse train is processed by a lowpass &#8220;reconstruction&#8221; filter, to arrive at our smooth analog signal output. Theoretically, we want a perfect impulse into a perfect &#8220;brick wall&#8221; lowpass filter\u2014one with no phase shift.<\/p>\n<p>A perfect impulse into our perfect lowpass filter would give an output response of the sinc function; here is the &#8220;normalized&#8221; sinc function:<\/p>\n<p>sin (\u03c0 * x) \/ (\u03c0 * x) { or 1, for x = 0 }<\/p>\n<p>Here&#8217;s what it looks like, as does the output of our perfect filter when pinged by a perfect unit impulse (the curve tails off infinitely in both directions):<\/p>\n<p><a href=\"http:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/sinc.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-59\" title=\"sinc\" src=\"http:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/sinc.png\" alt=\"\" width=\"531\" height=\"209\" srcset=\"https:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/sinc.png 531w, https:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/sinc-300x118.png 300w\" sizes=\"(max-width: 531px) 100vw, 531px\" \/><\/a><\/p>\n<p>When we feed the filter with our impulse train, representing output samples, the individually responses for each impulse add together to make the analog output waveform. Here&#8217;s an example with three non-zero samples:<\/p>\n<p><a href=\"http:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/sum-of-sincs.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-58\" title=\"sum-of-sincs\" src=\"http:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/sum-of-sincs.png\" alt=\"\" width=\"531\" height=\"208\" srcset=\"https:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/sum-of-sincs.png 531w, https:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/sum-of-sincs-300x117.png 300w\" sizes=\"(max-width: 531px) 100vw, 531px\" \/><\/a><\/p>\n<p>From this it should be apparent how to convert samples rates\u2014sum those individual impulse responses for every new point that you need to determine. For instance, if you want to double the sample rate, simply calculate the points on that curve that are half-way between existing samples.<\/p>\n<p>Need further convincing? Before moving on, let&#8217;s take a look at a digital square wave (-1,-1,-1,-1,+1,+1,+1,+1\u2026), and sum the individual sinc impulse responses:<\/p>\n<p><a href=\"http:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/square-wave-reconstruction.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-57\" title=\"square-wave-reconstruction\" src=\"http:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/square-wave-reconstruction.png\" alt=\"\" width=\"531\" height=\"205\" srcset=\"https:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/square-wave-reconstruction.png 531w, https:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/square-wave-reconstruction-300x115.png 300w\" sizes=\"(max-width: 531px) 100vw, 531px\" \/><\/a><\/p>\n<p>You probably recognize that as the look of a bandlimited square wave, so it looks like we&#8217;re on the right track.<\/p>\n<h3>Making it practical<\/h3>\n<p>The problem with our filter is in its perfection. Even if we could make this ideal filter from real-world components, we would wait forever for the output\u2014the output would build slowly for an infinite amount of time until it peaked at a height corresponding to the input impulse, then die out over an infinite amount of time.<\/p>\n<p>Fortunately, the sinc function gets smaller and smaller in each direction. We could let it run in both directions until the &#8220;lobes&#8221; got so small that they wouldn&#8217;t contribute to the quantized output, and truncate it from there. That&#8217;s still a long ways, which translates into a lot of output delay and a lot of calculations for the task we&#8217;re building up to. Fortunately, since the lobes are getting smaller and smaller, we can make a rough approximation by hastening their retreat with a window function that tapers the curve quicker. (Tapering gives better results than simply chopping off the ends\u2014a rectangular window.)<\/p>\n<p>Luckily for us, the difference between ideal and our tapered approximation is small, error-wise. This shouldn&#8217;t be surprising, since we know that real-world lowpass filters are far from perfect in cutoff slope, yet we find them very useful and audibly pleasing.<\/p>\n<p>In addition to scaling the sinc function with the window, this is a good time to normalize the function for unity gain. The DC gain of the filter is the sum of the coefficients\u2014this is true of any FIR filter. (This should be apparent, if you consider the output for the case of all input samples being 1.0.) The gain will vary depending on the filter settings, and be pretty close to the inverse of the factor used to calculate the sinc (for a filter with cutoff set to half the audio band, it will be about two, for instance). We simply divide each coefficient by the sum of the coefficients to achieve unity gain.<\/p>\n<h3>Putting the pieces together<\/h3>\n<p>Before continuing, here are some observations to keep in mind:<\/p>\n<p>1) We chose the sinc function because it is the impulse response of a lowpass filter with an infinitely steep slope at cutoff (&#8220;brick wall&#8221;), and linear phase. (We may not always require linear phase, but in the general case, that&#8217;s what we want.)<\/p>\n<p>2) We use a window function to convert our ideal filter into a practical one. It&#8217;s not the only way. We could trade off errors in a different way (the Park-McClellan algorithm, for instance). This article uses a windowed-sinc function because it&#8217;s a popular choice, and is easy to understand and implement.<\/p>\n<p>There are many popular choices for window functions. Any of them give better results than simply truncating the sinc curve. The Kaiser window is an excellent choice for audio; it&#8217;s more complicated to calculate than most, but has the advantage of being able to set the level of tradeoff between stopband attenuation and sharpness of the cutoff easily. The cutoff steepness is dictated mainly by the number of side-lobes of the sinc response that we keep, hence the length of the filter. And because we&#8217;ve deviated from the perfect filter, we&#8217;ll need to set the cutoff below the ideal.<\/p>\n<p>Here is what our normalized windowed sinc looks like, along with its components:<\/p>\n<p><a href=\"http:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/windowed-sinc.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-56\" title=\"windowed-sinc\" src=\"http:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/windowed-sinc.png\" alt=\"\" width=\"533\" height=\"192\" srcset=\"https:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/windowed-sinc.png 533w, https:\/\/www.earlevel.com\/main\/wp-content\/uploads\/2010\/11\/windowed-sinc-300x108.png 300w\" sizes=\"(max-width: 533px) 100vw, 533px\" \/><\/a><\/p>\n<h3>Coming next<\/h3>\n<p>Next up, we&#8217;ll have a have a windowed-sinc calculator that will help us compute coefficients for a high-quality sample rate convertor.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In a previous article, we looked at sample rate conversion in the frequency domain. Let&#8217;s take a quick second look in the time domain as reinforcement of principles behind sample rate conversion, before developing a practical rate convertor. In an &hellip; <a href=\"https:\/\/www.earlevel.com\/main\/2010\/11\/23\/towards-a-practical-oversampling-filter\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,8,19,15,6],"tags":[],"_links":{"self":[{"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/posts\/55"}],"collection":[{"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/comments?post=55"}],"version-history":[{"count":1,"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/posts\/55\/revisions"}],"predecessor-version":[{"id":60,"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/posts\/55\/revisions\/60"}],"wp:attachment":[{"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/media?parent=55"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/categories?post=55"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/tags?post=55"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}