
Hi, An earlier version of this question is cross-posted at StackOverflow http://stackoverflow.com/questions/26437770/timing-inaccuracy-in-haskell-thr... . 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... 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

On Sat, Oct 18, 2014 at 9:36 PM, Jeffrey Brown
threadDelay is guaranteed http://hackage.haskell.org/package/base-4.7.0.1/docs/Control-Concurrent.html...
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.)
This isn't going to be solved completely within Haskell; the behavior of threadDelay is the behavior of the system scheduler for a normal process, possibly modified by garbage collection delays. You'll need to switch the process to a realtime scheduling class to remove --- or at least reduce --- the OS's contribution to the uncertainties in sleep times. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

if you care about precise timing (at the cost of of some loss of throughput)
you can compile your code with -fno-omit-yields, and also the context
switching timer
see eg
http://www.haskell.org/ghc/docs/7.4.2/html/users_guide/using-concurrent.html
default context switching rate for threads is -C0.02 (20ms), but you can
set the rate as small as you like, and -C0 will trigger a context switch at
every yield.
happy experimenting!
On Sat, Oct 18, 2014 at 10:19 PM, Brandon Allbery
On Sat, Oct 18, 2014 at 9:36 PM, Jeffrey Brown
wrote: threadDelay is guaranteed http://hackage.haskell.org/package/base-4.7.0.1/docs/Control-Concurrent.html...
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.)
This isn't going to be solved completely within Haskell; the behavior of threadDelay is the behavior of the system scheduler for a normal process, possibly modified by garbage collection delays. You'll need to switch the process to a realtime scheduling class to remove --- or at least reduce --- the OS's contribution to the uncertainties in sleep times.
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

to clarify, the -C flag is an RTS flag On Sun, Oct 19, 2014 at 12:48 AM, Carter Schonwald < carter.schonwald@gmail.com> wrote:
if you care about precise timing (at the cost of of some loss of throughput) you can compile your code with -fno-omit-yields, and also the context switching timer see eg http://www.haskell.org/ghc/docs/7.4.2/html/users_guide/using-concurrent.html
default context switching rate for threads is -C0.02 (20ms), but you can set the rate as small as you like, and -C0 will trigger a context switch at every yield.
happy experimenting!
On Sat, Oct 18, 2014 at 10:19 PM, Brandon Allbery
wrote: On Sat, Oct 18, 2014 at 9:36 PM, Jeffrey Brown
wrote: threadDelay is guaranteed http://hackage.haskell.org/package/base-4.7.0.1/docs/Control-Concurrent.html...
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.)
This isn't going to be solved completely within Haskell; the behavior of threadDelay is the behavior of the system scheduler for a normal process, possibly modified by garbage collection delays. You'll need to switch the process to a realtime scheduling class to remove --- or at least reduce --- the OS's contribution to the uncertainties in sleep times.
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Jeffrey Brown wrote:
An earlier version of this question is cross-posted at StackOverflow http://stackoverflow.com/questions/26437770/timing-inaccuracy-in-haskell-thr... ..
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... 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

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-Syst... http://hackage.haskell.org/package/definitive-reactive-1.0/docs/Data-Reactiv... 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

On Sun, Oct 19, 2014 at 2:50 PM, Jeffrey Brown
I therefore suspect I have to teach myself, as Brandon suggested, a realtime scheduler.
Unless you're running on embedded hardware, you're probably looking for something like sched_setscheduler(2) (or perhaps more helpfully, chrt(1)) on Linux. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

Doh! Right. So probably this then?
https://hackage.haskell.org/package/posix-realtime
On Sun, Oct 19, 2014 at 11:54 AM, Brandon Allbery
On Sun, Oct 19, 2014 at 2:50 PM, Jeffrey Brown
wrote: I therefore suspect I have to teach myself, as Brandon suggested, a realtime scheduler.
Unless you're running on embedded hardware, you're probably looking for something like sched_setscheduler(2) (or perhaps more helpfully, chrt(1)) on Linux.
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On Sun, Oct 19, 2014 at 3:03 PM, Jeffrey Brown
Doh! Right. So probably this then? https://hackage.haskell.org/package/posix-realtime
Possibly, although I think you'll find that specifying a realtime scheduler class requires extra permissions, so `sudo chrt ...` will be more helpful unless you can manipulate your account's or the program's Linux kernel capabilities. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

You mean I should call chrt from the command line after starting the
process, to modify its priority?
This documentation
http://hackage.haskell.org/package/posix-realtime-0.0.0.1/src/System/Posix/R...
for
the posix-realtime package
https://hackage.haskell.org/package/posix-realtime says that it has a
sched_setscheduler function. That supposedly exists on my OS (OS X 10.9).
The chrt command, on the other hand, it does not recognize.
On Sun, Oct 19, 2014 at 12:07 PM, Brandon Allbery
On Sun, Oct 19, 2014 at 3:03 PM, Jeffrey Brown
wrote: Doh! Right. So probably this then? https://hackage.haskell.org/package/posix-realtime
Possibly, although I think you'll find that specifying a realtime scheduler class requires extra permissions, so `sudo chrt ...` will be more helpful unless you can manipulate your account's or the program's Linux kernel capabilities.
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On Sun, Oct 19, 2014 at 3:44 PM, Jeffrey Brown
You mean I should call chrt from the command line after starting the process, to modify its priority?
Normally you'd use it to run the program. But, as I said at least twice, it's Linux specific.
This documentation http://hackage.haskell.org/package/posix-realtime-0.0.0.1/src/System/Posix/R... for the posix-realtime package https://hackage.haskell.org/package/posix-realtime says that it has a sched_setscheduler function. That supposedly exists on my OS (OS X 10.9). The chrt command, on the other hand,
Does it warn you that you need privileges (probably root) to switch to a higher scheduler level? Normally realtime and other high priority schedulers require elevated privileges, whereas lower priority schedulers such as SCHED_IDLE don't. I should note that I see no manpage for sched_setscheduler on 10.9.5, nor do I find it in any of the dylibs in /usr/lib. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

I think threadDelay is the wrong operation here. Its fundamentally a
primitive for **spreading out** compute, rather than *scheduling compute*.
one (crazy) idea might be the following, say you wanna do an event every X
milli seconds (with some error tolerance about the precise timing),
do a threadDelay for 1/2-2/3rds X, then poll the time constantly :)
OTOH, as other folks are suggesting, theres probably fundamentally better
primitives already available.
On Sun, Oct 19, 2014 at 5:11 PM, Brandon Allbery
On Sun, Oct 19, 2014 at 3:44 PM, Jeffrey Brown
wrote: You mean I should call chrt from the command line after starting the process, to modify its priority?
Normally you'd use it to run the program. But, as I said at least twice, it's Linux specific.
This documentation http://hackage.haskell.org/package/posix-realtime-0.0.0.1/src/System/Posix/R... for the posix-realtime package https://hackage.haskell.org/package/posix-realtime says that it has a sched_setscheduler function. That supposedly exists on my OS (OS X 10.9). The chrt command, on the other hand,
Does it warn you that you need privileges (probably root) to switch to a higher scheduler level? Normally realtime and other high priority schedulers require elevated privileges, whereas lower priority schedulers such as SCHED_IDLE don't.
I should note that I see no manpage for sched_setscheduler on 10.9.5, nor do I find it in any of the dylibs in /usr/lib.
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

I was going to suggest something like this. The usual approach in audio software is to have a main processing loop that never yields (or calls threadDelay, or blocking operations). This loop runs at a fixed rate, and is responsible for handling the scheduling, mixing, writing to output, etc. You can have other threads that work on other processes, but if they're time-sensitive again you wouldn't want them to be de-scheduled, unless you can reschedule them in advance then wait. Currently it's hard to do this very reliably in Haskell because the RTS has no support for thread priorities or anything like that. Even if you use posix-realtime, you'd still need to be aware that the Haskell RTS can unschedule threads, or move calculations around. And a stop-the-world GC could theoretically happen at any time. That said, I've had decent success doing real-time work so long as the CPUs don't get completely saturated. An alternate approach is to hand off the real audio processing to something like csound or OSC. At least with csound I know it's possible to pipe raw audio data via the API, and I imagine OSC has a facility for this as well. John L On Mon, Oct 20, 2014 at 5:22 AM, Carter Schonwald < carter.schonwald@gmail.com> wrote:
I think threadDelay is the wrong operation here. Its fundamentally a primitive for **spreading out** compute, rather than *scheduling compute*. one (crazy) idea might be the following, say you wanna do an event every X milli seconds (with some error tolerance about the precise timing), do a threadDelay for 1/2-2/3rds X, then poll the time constantly :)
OTOH, as other folks are suggesting, theres probably fundamentally better primitives already available.
On Sun, Oct 19, 2014 at 5:11 PM, Brandon Allbery
wrote: On Sun, Oct 19, 2014 at 3:44 PM, Jeffrey Brown
wrote: You mean I should call chrt from the command line after starting the process, to modify its priority?
Normally you'd use it to run the program. But, as I said at least twice, it's Linux specific.
This documentation http://hackage.haskell.org/package/posix-realtime-0.0.0.1/src/System/Posix/R... for the posix-realtime package https://hackage.haskell.org/package/posix-realtime says that it has a sched_setscheduler function. That supposedly exists on my OS (OS X 10.9). The chrt command, on the other hand,
Does it warn you that you need privileges (probably root) to switch to a higher scheduler level? Normally realtime and other high priority schedulers require elevated privileges, whereas lower priority schedulers such as SCHED_IDLE don't.
I should note that I see no manpage for sched_setscheduler on 10.9.5, nor do I find it in any of the dylibs in /usr/lib.
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Again thank you to everybody!
Brandon, I see no warnings in the documentation regarding privilege
requirement for raising a scheduler's priority -- the strings "priv" and
"user" are absent, and "su" only appears twice. Also I did not understand
until today that while Mac is a variety of Unix, it is not a variety
of Linux.
Carter and John, I will try the polling method you suggest. A polling cycle
with a period of 20 ms would be fast enough for me. I don't need Haskell to
handle audio math, just to send control signals (to Max/MSP). As soon as I
figure out how to get UDP from Haskell to Max, I'll run a test. Hopefully
the problem I'm seeing derives from print, not threadDelay. (threadDelay
accepts its time argument in millionths of a second! That would seem
to suggest it was designed to handle at least the speed I'm hoping for ...)
On Sun, Oct 19, 2014 at 2:11 PM, Brandon Allbery
On Sun, Oct 19, 2014 at 3:44 PM, Jeffrey Brown
wrote: You mean I should call chrt from the command line after starting the process, to modify its priority?
Normally you'd use it to run the program. But, as I said at least twice, it's Linux specific.
This documentation http://hackage.haskell.org/package/posix-realtime-0.0.0.1/src/System/Posix/R... for the posix-realtime package https://hackage.haskell.org/package/posix-realtime says that it has a sched_setscheduler function. That supposedly exists on my OS (OS X 10.9). The chrt command, on the other hand,
Does it warn you that you need privileges (probably root) to switch to a higher scheduler level? Normally realtime and other high priority schedulers require elevated privileges, whereas lower priority schedulers such as SCHED_IDLE don't.
I should note that I see no manpage for sched_setscheduler on 10.9.5, nor do I find it in any of the dylibs in /usr/lib.
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

Jeffrey Brown wrote
Also I did not understand until today that while Mac is a variety of Unix, it is not a variety of Linux.
There's a bunch of POSIX (well, SuSv4) features to do with threads or scheduling that are missing in OS X. In Apple's defence, they are optional. Examples include sched_{get,set}{param,scheduler}, barriers, and spin locks. I'm sure I've forgotten some. Oh yeah, robust mutexes. Several locking functions with timeouts too. These are of course not the only differences. There are apparently quite a few things that can be done nicely in OS X using Mach facilities, but I have not yet succeeded in finding useful documentation for Mach, which has probably kept me out of more trouble than I realise.

On Mon, Oct 20, 2014 at 1:14 AM,
There are apparently quite a few things that can be done nicely in OS X using Mach facilities, but I have not yet succeeded in finding useful documentation for Mach, which has probably kept me out of more trouble than I realise.
Apple pushes grand central dispatch (aka libdispatch) for concurrency. Not clear if it'll help in this case, though. It's interesting technology, and at least the userland parts are open source, and have been ported to FreeBSD as the libdispatch port.

Jeffrey Brown wrote:
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
I would be hesitant to attribute your problem to the scheduler. An alternative explanation could be the following: The sound file played by the terminal when it encounters the \BEL character is longer than 100ms. A new sound will be played only when the previous sound has finished playing, so the terminal will queue \BEL characters until the previous ones have finished. But since they arrive too fast, there will never have any pause in between and you hear a constant rhythm. I can't test this theory because the \BEL character doesn't work in my Terminal application, but I have just listened to a kick drum sample on autorepeat and got a tempo of less than 200bpm. This means that the file is significantly longer than 100ms (or that starting the file from the beginning again takes a long time, but I did not notice any extended silence). An length of 100ms would correspond to 600bpm. To test this theory, you can try to sweep through lower tempi like 200bpm-500bpm and look for the threshold when the actual tempo plateaus even though you are increasing the scheduling tempo. This should be correspond to the length of the audio file. Best regards, Heinrich Apfelmus -- http://apfelmus.nfshost.com

On Tue, Oct 21, 2014 at 4:05 AM, Heinrich Apfelmus < apfelmus@quantentunnel.de> wrote:
I would be hesitant to attribute your problem to the scheduler. An alternative explanation could be the following: The sound file played by the terminal when it encounters the \BEL character is longer than 100ms. A new sound will be played only when the previous sound has finished playing,
On most systems I've used, this isn't true; they're either suppressed or they overlap, depending on system. (I've had to work around this.) -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On a separate thread
http://haskell.1045720.n5.nabble.com/How-to-send-a-string-somewhere-via-UDP-...,
Rohan Drape showed me how to send OSC. The following test, which simplifies
the old one and replaces printf with OSC, demonstrates timing as perfect as
my ears are able to distinguish.
import Control.Concurrent
import Control.Monad
import System.IO
import Sound.OSC
main = do
hSetBuffering stdout NoBuffering
mapM_ note (cycle [1,1,2])
withMax = withTransport (openUDP "127.0.0.1" 9000)
beat = 60000 -- 60 ms, measured in µs
note :: Int -> IO ()
note n = do
withMax (sendMessage (Message "sin0 frq 100" []))
-- set sine wave 0 to frequency 100
withMax (sendMessage (Message "sin0 amp 1" []))
-- set sine wave 0 to amplitude 1
threadDelay $ beat * n
withMax (sendMessage (Message "sin0 amp 0" []))
-- set sine wave 0 to amplitude 0
threadDelay $ beat * n
This means I can use Haskell instead of Python or SuperCollider. I am
beside myself with excitement.
Thanks again, everyone!
On Tue, Oct 21, 2014 at 2:24 AM, Brandon Allbery
On Tue, Oct 21, 2014 at 4:05 AM, Heinrich Apfelmus < apfelmus@quantentunnel.de> wrote:
I would be hesitant to attribute your problem to the scheduler. An alternative explanation could be the following: The sound file played by the terminal when it encounters the \BEL character is longer than 100ms. A new sound will be played only when the previous sound has finished playing,
On most systems I've used, this isn't true; they're either suppressed or they overlap, depending on system. (I've had to work around this.)
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

The following test [...]
perhaps note that each withTransport creates a new connection. so you may want to group activities under one connection. in which case IO actions can be "lifted" into the transport action. also, hosc has pauseThread, a real-valued (in seconds) threadDelay. also, that OSC message layout, with everything in the address, is a little odd? more traditional would be either (Message "/sin0/frq" [float 100]) or as below? best, rohan main = withMax $ mapM_ note (cycle [1,1,2]) withMax = withTransport (openUDP "127.0.0.1" 9000) sin0 param val = sendMessage (Message "/sin0" [string param,float val]) pause = liftIO . pauseThread . (* 0.6) note n = do sin0 "frq" 100 sin0 "amp" 1 pause n sin0 "amp" 0 pause n

On a separate thread
http://haskell.1045720.n5.nabble.com/How-to-send-a-string-somewhere-via-UDP-...,
Rohan Drape showed me how to send OSC. The following test, which simplifies
the old one and replaces printf with OSC, demonstrates timing as perfect as
my ears are able to distinguish.
import Control.Concurrent
import Control.Monad
import System.IO
import Sound.OSC
main = do
hSetBuffering stdout NoBuffering
mapM_ note (cycle [1,1,2])
withMax = withTransport (openUDP "127.0.0.1" 9000)
beat = 60000 -- 60 ms, measured in µs
note :: Int -> IO ()
note n = do
withMax (sendMessage (Message "sin0 frq 100" []))
-- set sine wave 0 to frequency 100
withMax (sendMessage (Message "sin0 amp 1" []))
-- set sine wave 0 to amplitude 1
threadDelay $ beat * n
withMax (sendMessage (Message "sin0 amp 0" []))
-- set sine wave 0 to amplitude 0
threadDelay $ beat * n
This means I can use Haskell instead of Python or SuperCollider. I am
beside myself with excitement.
Thanks again, everyone!
On Tue, Oct 21, 2014 at 2:24 AM, Brandon Allbery
On Tue, Oct 21, 2014 at 4:05 AM, Heinrich Apfelmus < apfelmus@quantentunnel.de> wrote:
I would be hesitant to attribute your problem to the scheduler. An alternative explanation could be the following: The sound file played by the terminal when it encounters the \BEL character is longer than 100ms. A new sound will be played only when the previous sound has finished playing,
On most systems I've used, this isn't true; they're either suppressed or they overlap, depending on system. (I've had to work around this.)
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

I think it would be much easier to take a different approach. For example one of these: * Use an audio library * Write your own audio synthesis John Lato already said something about the usual approach to audio software. I will just sketch out how to write very simple audio synthesis: * You need a sample rate! * Think in terms of a modular audio synthesizer * Write your own oscillator. * Write an "amplifier", which scales the oscilators amplitude up and down. Whenever you want the oscilator to not make sound you have to mute the amplifier. * Write an envelop generator, which generates an envelop for your amplifier. * Write a sequencer, which controlls the envelop generator. The sequencer should take your prefered encoding (like 'cycle [1,1,2]') * Write the output of the amplifier straight into the audio-out buffer of your OS. I don't know how you do that exactly. You have to find out. But I do know that on Linux you could also just write the output of your program to standard-out and pipe it into ALSA on the command line with the padsp command. Advantages of this approach: * IO-Monad is used only on the highest level of abstraction. * If you write straight into the audio-out buffer of your OS, the OS will suspend and schedule your process as needed. Whenever the audio-out buffer is full, your process will be blocked by the OS on the write system call. You don't have to think much about scheduling. * it's easy. Most of the components could be a single and simple function. Just my 2 cents, Alexander On 10/22/2014 05:14 AM, Jeffrey Brown wrote:
On a separate thread http://haskell.1045720.n5.nabble.com/How-to-send-a-string-somewhere-via-UDP-..., Rohan Drape showed me how to send OSC. The following test, which simplifies the old one and replaces printf with OSC, demonstrates timing as perfect as my ears are able to distinguish.
import Control.Concurrent import Control.Monad import System.IO import Sound.OSC main = do hSetBuffering stdout NoBuffering mapM_ note (cycle [1,1,2]) withMax = withTransport (openUDP "127.0.0.1" 9000) beat = 60000 -- 60 ms, measured in µs note :: Int -> IO () note n = do withMax (sendMessage (Message "sin0 frq 100" [])) -- set sine wave 0 to frequency 100 withMax (sendMessage (Message "sin0 amp 1" [])) -- set sine wave 0 to amplitude 1 threadDelay $ beat * n withMax (sendMessage (Message "sin0 amp 0" [])) -- set sine wave 0 to amplitude 0 threadDelay $ beat * n
This means I can use Haskell instead of Python or SuperCollider. I am beside myself with excitement.
Thanks again, everyone!
On Tue, Oct 21, 2014 at 2:24 AM, Brandon Allbery
mailto:allbery.b@gmail.com> wrote: On Tue, Oct 21, 2014 at 4:05 AM, Heinrich Apfelmus
mailto:apfelmus@quantentunnel.de> wrote: I would be hesitant to attribute your problem to the scheduler. An alternative explanation could be the following: The sound file played by the terminal when it encounters the \BEL character is longer than 100ms. A new sound will be played only when the previous sound has finished playing,
On most systems I've used, this isn't true; they're either suppressed or they overlap, depending on system. (I've had to work around this.)
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com mailto:allbery.b@gmail.com ballbery@sinenomine.net mailto:ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org mailto:Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

(Eventually I intend to replace the printf statement with OSC output to a sound generator.)
OSC packets can have NTP timestamps. So one approach is to forward timestamp outgoing packets by a small constant amount. Enough so that the synthesiser still receives the most delayed packets before they're due. Then the synthesiser schedules the messages accurately. This can solve the concurrency issue as well. If you need highly accurate scheduling in the language perhaps consider SuperCollider? http://audiosynth.com/ Best, Rohan

El Oct 19, 2014, a las 21:29, Rohan Drape
(Eventually I intend to replace the printf statement with OSC output to a sound generator.)
OSC packets can have NTP timestamps.
[...]
If you need highly accurate scheduling in the language perhaps consider SuperCollider?
http://supercollider.github.io/ is a more recent site - that link has a 2006 conference as the only "Upcoming Event" Tom
participants (10)
-
Alexander Scharinger
-
amindfv@gmail.com
-
Brandon Allbery
-
Carter Schonwald
-
Heinrich Apfelmus
-
Jeffrey Brown
-
John Lato
-
Mike Meyer
-
ok@cs.otago.ac.nz
-
Rohan Drape