我想用Haskell的printf制作节奏。以下应该产生重复节奏,其中一个音符是另外两个音符的两倍。 (该节奏由列表[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 miliseconds
note :: Int -> IO ()
note n = do
threadDelay $ beat * n
printf "\BEL\n"
当我跑它时,长音符听起来大约是其他音符的三倍,而不是两倍。如果我加快速度,通过将数字4改为10,节奏就会完全被破坏:音符都具有相同的长度。
刷新率是否有变化?如果我想要精确计时,threadDelay不是要使用的服务吗?
答案 0 :(得分:3)
如果我想要精确计时,threadDelay不是要使用的服务吗?
不,not at all:
threadDelay :: Int -> IO () Source
暂停当前线程达到给定的微秒数(仅限GHC)。
无法保证在延迟过期时,线程会立即重新安排,,但线程将永远不会继续比指定时间更早运行。
然而,在我的机器上(Win 8.1 x64 i5-3570k@3.4GHz),节奏运行良好。话虽如此,\BEL
并不是创造节拍的好方法:
\BEL
声音取决于操作系统(如果以该频率播放,Windows 8中的声音可怕),\BEL
是否阻止。如果后者发生,则最终会得到大致相同的长度,因为每个\BEL
都会阻止,而threadDelay
会比实际的\BEL
声音短。
答案 1 :(得分:2)
问题似乎是打印,而不是线程。 Haskell-Cafe的Rohan Drape向我展示了如何使用OSC而不是打印。以下测试的时间,使用OSC,是我的耳朵与完美无法区分。我让它向Max / MSP中的正弦波振荡器发送指令。
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
谢谢大家!
答案 2 :(得分:0)