2014-10-18 5 views
0

Я хочу сделать ритмы с печатью Haskell's. Следующее должно произвести повторяющийся ритм, в котором одна нота в два раза длиннее двух других. (Этот ритм кодируется в списке [1,1,2].)Неточность синхронизации в Haskell threadDelay

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

О системах, с которыми я знаком, выводя BEL-блоки на мгновение до того, как программа продолжится, что испортит вашу математику. – luqui

+0

@luqui: И в Windows он не блокируется, что испортится с вашими ушами: /. – Zeta

+0

Почему вы используете 'printf' вместо' print'? –

ответ

2

is threadDelay не сервис для использования, если я хочу точное время?

Нет, not at all:

threadDelay :: Int -> IO() Source 

Приостановка текущий поток на заданное число микросекунд (только GHC).

Нет гарантии, что поток будет перенесен незамедлительно после истечения задержки,, но поток никогда не будет продолжать работать раньше указанного.

Однако на моей машине (Win 8.1 x64 [email protected]) ритм работает нормально. Это, как говорится, \BEL не очень хороший способ, чтобы создать ритм:

  1. \BEL звука зависит от операционной системы (звук ужасный в Windows 8, если играл на этой частоте),
  2. это ISN» t очистить ли \BEL блоков.

Если последнее произойдет, вы в конечном итоге с примерно такой же длины, так как каждый \BEL будет блокировать и threadDelay короче фактического \BEL звука.

+0

Этот ответ был бы намного полезнее, если бы он предложил несколько лучших способов. – luqui

+0

@luqui: Я не очень хорошо знаком с целым «созданием музыки с кодом» (пока), извините :(Если вы что-то знаете об этом, не стесняйтесь редактировать этот ответ (сообщество wiki сейчас) или предоставить новый ответ :). – Zeta

0

Скорее всего, вам придется полагаться на поддержку ОС или внутренние компоненты GHC. Например, я использовал GHC.Event для этой цели с

registerTimeout :: TimerManager -> Int -> TimeoutCallback -> IO TimeoutKey 

Funtion. Но тогда он также будет асинхронным с обратными вызовами. Также это специфический GHC.

Другие варианты - это библиотеки таймеров на hackage, но не уверены, что если они все переносимы или они могут использоваться в Windows, но большинство из них поддерживает ОС, так как для точного времени вам нужны аппаратные таймеры.

2

Проблема, похоже, была печать, а не нарезка. Rohan Drape в Haskell-Cafe показал мне, как использовать 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 

Спасибо, всем!

 Смежные вопросы

  • Нет связанных вопросов^_^