-- | Sequencer type diagram. module Music.Theory.Diagram.Sequencer where import Data.Char {- base -} import System.FilePath {- filepath -} import System.Process {- process -} import Text.Printf {- base -} import Music.Theory.Math (R) {- hmt -} import qualified Music.Theory.Dynamic_Mark as T {- hmt -} import qualified Music.Theory.Time.Seq as T {- hmt -} -- | Point type P2 = (R,R) -- | Greyscale colour. type Grey = R -- | Coloured rectangle as (lower-left,upper-right,greyscale). type C_Rect = (P2,P2,Grey) -- | 'C_Rect' with identifier. type K_Rect = (Int,C_Rect) -- | Gnuplot string for 'K_Rect'. k_rect_gnuplot :: K_Rect -> String k_rect_gnuplot (i,((x0,y0),(x1,y1),c)) = let fmt = "set object %d rect from %f,%f to %f,%f fc rgbcolor \"#%02x%02x%02x\"" c' = floor (c * 255) :: Int in printf fmt i x0 y0 x1 y1 c' c' c' -- | Sequencer plot options, (image-size,x-range,y-range). For -- standard midi data x-range is the time window and y-range is the -- gamut. type Seq_Plot_Opt = ((Int,Int),(R,R),(R,R)) -- | Sane defaults for size and gamut. default_seq_plot_opt :: (R,R) -> Seq_Plot_Opt default_seq_plot_opt x = ((1200,400),x,(21,108)) -- | Add identifiers. to_k_rect :: [C_Rect] -> [K_Rect] to_k_rect = zip [1..] -- | Names for SVG terminal have character restrictions. clean_name :: String -> String clean_name = let f c = if isAlphaNum c then c else '_' in map f sequencer_plot_rect :: Seq_Plot_Opt -> FilePath -> String -> [C_Rect] -> IO () sequencer_plot_rect ((w,h),(x0,x1),(y0,y1)) dir nm sq = do let nm_plot = dir nm <.> "plot" nm_svg = dir nm <.> "svg" x_range = concat ["[",show x0,":",show x1,"]"] y_range = concat ["[",show y0,":",show y1,"]"] pre = [concat ["set terminal svg name \"",clean_name nm,"\" size ",show w,",", show h] ,"set output '" ++ nm_svg ++ "'" ,"set tics font \"cmr10, 10\"" ,"unset key" ,concat ["set xrange ",x_range] ,concat ["set yrange ",y_range] ,"set bars 0"] post = ["plot \"/dev/null\" with xyerrorbars lc rgbcolor \"black\""] writeFile nm_plot (unlines (pre ++ map k_rect_gnuplot (to_k_rect sq) ++ post)) _ <- system ("gnuplot " ++ nm_plot) return () -- * MIDI -- | Linear amplitude to grey scale (0 = white, 1 = black). -- -- > map (floor . (* 255) . amp_to_grey (-60)) [0,0.25,0.5,0.75,1] == [255,51,25,10,0] amp_to_grey :: R -> R -> R amp_to_grey z am = let db = max (T.amp_db am) z z' = abs z in 1 - ((db + z') / z') -- | Midi velocity number to linear amplitude. vel_to_amp :: Int -> R vel_to_amp vel = fromIntegral vel / 127 -- | Midi velocity number to grey scale. vel_to_grey :: R -> Int -> R vel_to_grey z = amp_to_grey z . vel_to_amp -- | Midi sequence data. type Sequencer_Midi n = T.Wseq R (n,n) -- | Convert 'Sequencer_Midi' node to 'C_Rect'. sequencer_midi_to_rect :: Real n => ((R,R),(n,n)) -> C_Rect sequencer_midi_to_rect ((st,du),(mnn,vel)) = let x0 = st x1 = st + du y0 = realToFrac mnn y1 = y0 + 1 c = vel_to_grey (-60) (floor (realToFrac vel)) in ((x0,y0),(x1,y1),c) -- | Plot 'Sequencer_Midi'. sequencer_plot_midi :: Real n => Seq_Plot_Opt -> FilePath -> String -> Sequencer_Midi n -> IO () sequencer_plot_midi opt dir nm = sequencer_plot_rect opt dir nm . map sequencer_midi_to_rect