Hacker News new | comments | show | ask | jobs | submit login
Apple Lossless Decoder in Coffeescript (github.com)
81 points by jensnockert 1810 days ago | hide | past | web | 23 comments | favorite



devongovett/jensnockert: what was your experience with using CoffeeScript for such low level code? Did you need to consciously avoid certain CoffeeScript features that could negatively impact performance?


Overall positive, but as with any programming language, you have to be careful sometimes. For example, the for loop in CoffeeScript can be slow if you aren't careful:

    for i in [0...10]
      doSomething()
is slower than

    for i in [0...10] by 1
      doSomething()
because of the way it is compiled, so we always include the `by 1` as an optimization.

Another one is that if you have a function and the last thing in that function is a loop of some kind, CoffeeScript will try to return an array from that function collecting the results of the loop. So we explicitly put a return statement at the end of the function to avoid this and the performance penalties.

There are probably a few more to be careful of, but overall performance characteristics are similar to JS. I'm sure Jens has some thoughts too, but overall I think it was a positive experience.


This is the difference in compiled output for those examples:

  var i;
  
  for (i = 0; i < 10; i++) {
    doSomething();
  }
  
  for (i = 0; i < 10; i += 1) {
    doSomething();
  }
http://jashkenas.github.com/coffee-script/#try:for%20i%20in%...


Oops, I should have written it using a variable instead... i.e.

  for i in [0...test]
    doSomething()

  for i in [0...test] by 1
    doSomething()
compiles to:

  var i;

  for (i = 0; 0 <= test ? i < test : i > test; 0 <= test ? i++ : i--) {
    doSomething();
  }

  for (i = 0; i < test; i += 1) {
    doSomething();
  }
the latter one being much faster because it isn't testing the direction the counter should be going all the time.


This is one of the grayer areas. After your experience writing this decoder ... would you prefer CoffeeScript to keep its loops that can go in either direction, or would you prefer loops to always iterate upwards, and have to be explicit if you'd like to count from "[100..1] by -1" ?


I've programmed in dozens of languages, and I can't think of another one where loops could automatically run either up or down depending on the start and end values.

Not knowing CoffeeScript well, when I see this code:

n = -10 for i in [0...n] doSomething()

I'd expect it to call doSomething zero times. At least that's what I'd expect in JavaScript or any of the other languages I've used.


I agree with you at least 40%, and that's originally how CoffeeScript behaved. Would you still feel that way if you had explicitly written:

    for i in [10..0]
I've opened a ticket to discuss this further: https://github.com/jashkenas/coffee-script/issues/1952


Hmmm, well I think it's better if they can go in any direction by default otherwise people will be confused. The `by 1` should be considered an optimization IMO.


What if you compiled it something like this:

  for i in [a...b]
becomes

  if(a <= b) {
      for(i = a; i < b; i++) {
          // Code
      }
  } else {
      for(i = a; i > b; i--) {
          // Code again
      }
  }
Or otherwise moved the extra conditions outside the loop so they only get computed once?


Unfortunately, that requires repeating the entire body of the loop, and so isn't workable/acceptable for our purposes. If you'd like to see the original conversation that led to the current compilation, it's all available on the GitHub issues.


I'll have a look for the discussion. Any idea if the branches are more expensive than some multiplies?

    step = (a<=b)?1:-1;
    for (i = a; step*i < step*b; i += step) {
      doSomething();
    }
I guess if the branch predictor is any good it could be slower with multiplies, but I have no idea what to expect in a dynamic language.


Mostly avoided the regular for loops, replacing them with `for ... by 1`, to make sure the compiler knows in which direction to iterate.

Otherwise it is pretty similar to javascript, most coffeescript constructs map pretty simply down to javascript, so optimization tips for one language mostly applies to the other.


I enjoyed reading this source, and will probably use your Buffer/Stream classes in a project I'm working on. Thanks!


You're welcome!


See a demo here: http://codecs.ofmlabs.org/


Perhaps I am missing something obvious here (very possible as I do not work with audio decoding) but the demo at http://codecs.ofmlabs.org/ appears to playback the MP3 (not tested the ALAC file yet) too quickly. The site reports the MP3 as lasting 4:25 whereas manually downloading the track from the above website Winamp says the track is 5:14 long and playback is noticeably slower.

Is this by design or a bug in the code (or a bug in Chrome (16-stable) that I used to check out the demo site)?


Well technically it is a missing feature in Chrome that we tried to work around to varying degrees of success. The Chrome audio API has no way to change the sample rate and uses whatever the native hardware sample rate is. We didn't have time to finish implementing sample rate conversion in JS (but it is coming) so we cheated a bit by dynamically loading a different file based on some common hardware sample rates. It seems that sometimes Chrome reports the sample rate wrong or something though, and we have seen this issue at least once before and will be looking into it and reporting browser bugs accordingly. Rest assured that this was just a hack, and real sample rate conversion is JS is coming along soon! :)


@devongovett: Have you considered pushing the annotated source for this to your "gh-pages" branch? It would make for a great read.


Would take quite some time annotating the source, not really our first priority. (We have more codecs on our hitlist)

But if someone wants to do it, I think both me and @devongovett would be ready to help out a bit.

(We usually hang out in #ofmlabs on freenode, if someone wants to speak with us)


We could do, although there aren't that many comments... Not sure it would help much at this point.


Stop wasting your time by rewriting existing JavaScript implementations in other languages that compile to JavaScript!


Hm..? The only ALAC implementations we know of is the Apple one (in C++), David Hammerton's one in C, and ours in Coffeescript.


Wow! Great work!




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact

Search: