{- ToDo: - avoid busy wait on ALSA events -} module Main where import Common import qualified Sound.ALSA.Sequencer.Event as Event import qualified Sound.ALSA.Sequencer as SndSeq import Graphics.UI.WX (Prop((:=)), set, get, selection, command, on, button, close, container, hfloatCentre, widget, fill, layout, margin, column, select, spinCtrl, text, ) import qualified Graphics.UI.WX as WX import Control.Monad (when, ) programs :: [String] programs = "FM tine" : "FM Fender piano" : "Percussive saw" : "Filtered saw" : "Bell" : "Noisy bell" : "Square" : "Brass" : "Soft string" : "FM string" : "Cosine string" : "Arc sine string" : "Arc triangle string" : "Arc square string" : "Arc saw string" : "Wind" : "Syllable to" : "Syllable ma" : "Syllable ten" : "Syllable sa" : "Syllable lat" : "Syllable has" : "Syllable kell" : "Syllable in" : "Syllable leip" : "Syllable zig" : "Syllable gra" : "Syllable phen" : "Syllable the" : "Syllable o" : "Syllable rie" : [] updateSelection :: WX.SpinCtrl () -> WX.SingleListBox () -> Event.T -> IO () updateSelection chan list ev = case Event.body ev of Event.CtrlEv Event.PgmChange ctrlEv -> do midiChan <- get chan selection when (midiChan == fromIntegral (Event.ctrlChannel ctrlEv)) $ set list [selection := fromIntegral (Event.ctrlValue ctrlEv)] _ -> return () makeGUI :: Sequencer SndSeq.DuplexMode -> IO () makeGUI sequ = do f <- WX.frame [text := "MIDI Program Change"] p <- WX.panel f [] {- order of creation matters for TAB-cycling if 'chan' and 'list' are swapped, then the program crashes on Quit It seems, that closing the application triggers a selection. This may access an invalidated 'chan'. -} list <- WX.singleListBox p [ WX.items := programs ] chan <- spinCtrl p 0 15 [] set list [ on select := do c <- get chan selection pgm <- get list selection when (0<=pgm && pgm<128) $ sendProgram sequ c pgm ] _ <- WX.timer f [ WX.interval := 100, on command := getWaitingEvents sequ >>= mapM_ (updateSelection chan list) ] quit <- button p [text := "Quit", on command := close f] set f [layout := container p $ margin 10 $ column 5 [fill (widget list), hfloatCentre (widget chan), hfloatCentre (widget quit)]] sendProgram :: Sequencer SndSeq.DuplexMode -> Int -> Int -> IO () sendProgram h chan pgm = sendEvent h $ Event.CtrlEv Event.PgmChange $ Event.Ctrl { Event.ctrlChannel = fromIntegral chan, Event.ctrlParam = 0, Event.ctrlValue = fromIntegral pgm } main :: IO () main = withSequencer "Program change" $ WX.start . makeGUI