Thanks, everybody! Those were incredibly informative answers. This list is amazing.

Heinrich's suggestion of compiling with "-threaded" was by itself sufficient to get the first rhythm to sound right.

The second rhythm, however, still blurs into a string of beeps of the same duration even after I throw all of Heinrich's and Carter's suggestions at it by doing this:

ghc --make fast -threaded -fno-omit-yields -with-rtsopts -C0

(although I should admit to not really knowing what I'm doing when I run that) where fast.hs is this:

import Control.Concurrent
import Text.Printf
import Control.Monad
import System.IO
main = do
hSetBuffering stdout NoBuffering
mapM_ note (cycle [1,1,2])
beat = round (10^6 / 10) -- measured in microseconds
note :: Int -> IO ()
note n = do
threadDelay $ beat * n
printf "\BEL\n"

I therefore suspect I have to teach myself, as Brandon suggested, a realtime scheduler. This seems like a general, powerful thing:
http://hackage.haskell.org/package/atom
Hayoo also returns a lot of domain-specific results, e.g. from the Sound and Data.Reactive libraries:
http://hackage.haskell.org/package/alsa-0.4/docs/Sound-Alsa-Sequencer.html
http://hackage.haskell.org/package/midi-0.2.1.3/docs/Sound-MIDI-Message-System-RealTime.html#
http://hackage.haskell.org/package/definitive-reactive-1.0/docs/Data-Reactive.html
but I suspect a general tool like Atom would be more useful so I'll start there.

Thanks again,
Jeff


On Sun, Oct 19, 2014 at 12:40 AM, Heinrich Apfelmus <apfelmus@quantentunnel.de> wrote:
Jeffrey Brown wrote:
An earlier version of this question is cross-posted at StackOverflow
<http://stackoverflow.com/questions/26437770/timing-inaccuracy-in-haskell-threaddelay>
..

What should I use for precise (and eventually, concurrency-compatible)
timing in Haskell? I want to make rhythms. I tried the following to produce
a repeating rhythm in which one note is twice as long as the other two.
(That rhythm is encoded by the list [1,1,2].)

    import Control.Concurrent
    import Text.Printf
    import Control.Monad

    main = mapM_ note (cycle [1,1,2])

    beat = round (10^6 / 4) -- measured in microseconds

    note :: Int -> IO ()
    note n = do
        threadDelay $ beat * n
        printf "\BEL\n"

When I run it the long note is three times as long as the others, not
twice. If I speed it up, by changing the number 4 to a 10, the rhythm is
destroyed completely: the notes all have the same length.

threadDelay is guaranteed
<http://hackage.haskell.org/package/base-4.7.0.1/docs/Control-Concurrent.html#v:threadDelay>
to
wait at least as long, but potentially longer than, what the caller
specifies. Another potential problem could be buffering in printf.
(Eventually I intend to replace the printf statement with OSC output to a
sound generator.)

Thanks,
Jeff

I found that that compiling a program with the -threaded option greatly improves the precision of  threadDelay  for the purpose of making rhythms.

Your program is simple enough that this shouldn't make a difference, though. Note that there may also be issues with  stdout  buffering. Try

    System.IO.hSetBuffering stdout NoBuffering

before the main loop.

Best regards,
Heinrich Apfelmus

--
http://apfelmus.nfshost.com


_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe