
hey mike- i think the trouble with your program is that it sends all the events as fast as possible and then terminates -- it is insensitive to their timestamps. probably portmidi's buffer receives them all, starts the first one, and then is destroyed before ever emitting the corresponding off message, leaving the first note hung. i've just been learning haskell and have been using portmidi as a vehicle. here's a demo i've been working on, it seems to work well. it uses Data.Heap (http://hackage.haskell.org/package/heap) as a priority queue to manage the timestamp scheduling and allow you to schedule events nonmonotonically (portmidi expects you to handle this). it also shows how to use the random generator i noticed you asked about. http://code.google.com/p/h1ccup/source/browse/trunk/theory/haskell/src/PMTes... i would really appreciate any style/design critiques that anyone could offer! some specific q's: 1) the 'addNote' function has a local predicate 'match', which winds up being called on every member in the queue twice -- but memoizing it would be hard because it is used by the heap's higher order functions ('filter' and 'partition'). how should i handle this? can i expect/rely on any auto-memoizing? 2) because a noteOff event in midi turns off a note no matter how many previous noteOns occurred, 'addNote' needs to be careful to not schedule a noteOff during a previously scheduled note, and remove any previously scheduled noteOff that would prematurely cut off the new note. the alg is a little tricky and it would be nice to "prove" its correctness -- and there are enough degrees of freedom that QuickCheck would not explore the relevant corner cases. how would one approach this from a curry-howard perspective? 3) i would like the most natural possible musical EDSL, but currently have to write things like 'Dotted $ Triplet $ NoteDur Eighth' rather than 'Dotted Triplet Eighth'. it would be worse if i added a layer of symbols to prevent arbitrary nesting and ordering of 'Dotted' and 'Triplet'. can this be addressed? 4) threadDelay seems to have a resolution of 10ms. isn't that awfully high? (i'm on OSX 10.5.8, ghc 6.10.3). could this possibly because i forkIO the producer, but not the consumer (only the consumer threadDelays)? 5) ski on #haskell wrote 'asksTo' for me (see http://tunes.org/~nef//logs/haskell/09.08.06) -- but it is not as polymorphic as we'd like -- it only works for ReaderT's instead of all MonadReader's. 'asksTo' is for accessing more than one MVar simultaneously inside a ReaderT, one needs to avoid nesting liftIO's (see http://hpaste.org/fastcgi/hpaste.fcgi/view?id=7915). why not allow nested liftIO's, and if there is a good reason, is there a more general solution than asksTo? 6) i would (neurotically) like to work under 'default ()', but this causes zillions of problems that i can't figure out. is there a good strategy for getting all your numbers to work polymorphically? 7) i want to write as paradigmatically as possible, so style advice is very helpful! i've tried to follow the guidelines from the wiki. i didn't see any opportunities to define a new monad, but maybe there are some? the 'addNote' and 'drain' functions, which do the queueing and dequeuing, seem uglier than necessary -- any way to decompose them better? -e On Wed, 14 Oct 2009, Michael Mossey wrote:
Can someone give me an example of Sound.PortMidi use? I'm having trouble. This program has bugs---makes sound only intermittently, and seems to have set up some kind of loop that is sending midi messages continuously even after terminating the program:
import Sound.PortMidi import Foreign.C msgs = [ (0::CULong,PMMsg 0x9c 0x40 0x40) , (500, PMMsg 0x8c 0x40 0x40) , (1000, PMMsg 0x9c 0x41 0x40) , (1500, PMMsg 0x8c 0x41 0x40) ] main = do let deviceId = 12 initialize >>= print getDeviceInfo deviceId >>= print startTime <- time let evts = map (\(t,msg) -> PMEvent msg (t+startTime)) msgs result <- openOutput deviceId 10 case result of Right err -> putStrLn ("After open: " ++ show err) Left stream -> do result <- writeEvents stream evts putStrLn ("After write: " ++ show result) close stream return () terminate >>= print