packages feed

hmeap-0.9: cmd/play.hs

import Control.Monad
import Data.Function
import Data.List
import qualified Sound.Analysis.Meapsoft as M
import Sound.OpenSoundControl
import Sound.SC3
import System.Environment

grain :: UGen -> UGen
grain b = p * e
    where s = control KR "start" 0.0
          d = control KR "dur" 1.0
          g = control KR "gain" 1.0
          r = bufRateScale KR b
          q = bufSampleRate KR b
          z = envTrapezoid 0.95 0.5 d g
          e = envGen KR 1 1 0 1 RemoveSynth z
          p = playBuf 1 b r 1 (s * q) NoLoop DoNothing

mk_grain :: (Transport t) => t -> Double -> Double -> IO ()
mk_grain fd s d =
    let xs = [ ("start", s)
             , ("dur", d) ]
    in send fd (s_new "grain" (-1) AddToTail 1 xs)

tone :: UGen
tone = p * e
    where f = control KR "freq" 0.0
          d = control KR "dur" 1.0
          g = control KR "gain" 0.1
          z = envTrapezoid 0.95 0.5 d g
          e = envGen KR 1 1 0 1 RemoveSynth z
          p = saw AR (MCE [f, f*2, f*3]) * (MCE [0.5, 0.2, 0.1])

mk_tone :: (Transport t) => t -> Double -> Double -> IO ()
mk_tone fd f d =
    let xs = [ ("freq", f)
             , ("dur", d) ]
    in send fd (s_new "tone" (-1) AddToTail 1 xs)

-- (Temporal_Scalar, [Segments], [Freq])
type Spec = (Double, [(Double, Double)], [Double])

plyr :: Transport t => t -> Spec -> IO ()
plyr _ (_, [], _) = return ()
plyr fd (t, (s,d):xs, f:fs) = do mk_grain fd s d
                                 mk_tone fd f (d * t * 0.25)
                                 pauseThread (d * t)
                                 plyr fd (t, xs, fs)
plyr _ _ = undefined

run_grn :: FilePath -> FilePath -> (M.MEAP -> Spec) -> IO ()
run_grn sf ff rule = withSC3 g
    where g fd = do reset fd
                    let g0 = out 0 (grain 10)
                        g1 = out 0 tone
                    _ <- async fd (b_allocRead 10 sf 0 0)
                    _ <- async fd (d_recv (synthdef "grain" g0))
                    _ <- async fd (d_recv (synthdef "tone" g1))
                    (Right m) <- M.read_meap ff
                    plyr fd (rule m)

col :: String -> M.MEAP -> [Double]
col n m =
    let f = M.required_feature n (M.features m)
        j = M.feature_column f
    in M.column_l m j

freq_1 :: M.MEAP -> [Double]
freq_1 = col "AvgFreqSimple"

-- forwards
rule_1 :: M.MEAP -> Spec
rule_1 c = (0.75, M.segments_l c, freq_1 c)

freq_2 :: M.MEAP -> [Double]
freq_2 = col "AvgSpecCentroid"

-- backwards
rule_2 :: M.MEAP -> Spec
rule_2 c = (0.5, reverse (M.segments_l c), reverse (freq_2 c))

gen_order_1 :: M.MEAP -> String -> [Int]
gen_order_1 m f =
    let cs = zip [0..] (col f m)
        cs' = sortBy (compare `on` snd) cs
    in map fst cs'

apply_order :: [Int] -> [a] -> [a]
apply_order o xs = map (\i -> xs !! i) o

srt_rule :: String -> (M.MEAP -> [Double]) -> M.MEAP -> Spec
srt_rule n r m =
    let o = gen_order_1 m n
        s = apply_order o (M.segments_l m)
        f = apply_order o (r m)
    in (0.5, s, f)

-- sorted by segment length (ascending)
rule_3 :: M.MEAP -> Spec
rule_3 = srt_rule "chunk_length" freq_1

-- sorted by frequency (ascending)
rule_4 :: M.MEAP -> Spec
rule_4 = srt_rule "AvgFreqSimple" freq_1

-- sorted by spectral centroid (ascending)
rule_5 :: M.MEAP -> Spec
rule_5 = srt_rule "AvgSpecCentroid" freq_2

-- sorted by spectral stability (ascending)
rule_6 :: M.MEAP -> Spec
rule_6 = srt_rule "SpectralStability" freq_2

gen_sel_1 :: M.MEAP -> String -> (Double -> Bool) -> [Int]
gen_sel_1 m f p =
    let cs = zip [0..] (col f m)
        cs' = filter (p . snd) cs
    in map fst cs'

sel_rule :: String -> (Double -> Bool) -> (M.MEAP -> [Double]) -> M.MEAP -> Spec
sel_rule n p r m =
    let o = gen_sel_1 m n p
        s = apply_order o (M.segments_l m)
        f = apply_order o (r m)
    in (0.5, s, f)

-- filtered by frequency (> 600)
rule_7 :: M.MEAP -> Spec
rule_7 = sel_rule "AvgFreqSimple" (> 600) freq_1

-- filtered by spectral centroid (> 1200)
rule_8 :: M.MEAP -> Spec
rule_8 = sel_rule "AvgSpecCentroid" (> 1200) freq_2

interleave :: [a] -> [a] -> [a]
interleave [] b = b
interleave a [] = a
interleave (a:as) (b:bs) = a : b : interleave as bs

-- interleaving of rules 4 & 5
rule_9 :: M.MEAP -> Spec
rule_9 m =
    let (_, s4, f4) = rule_4 m
        (_, s5, f5) = rule_5 m
    in (0.75, interleave s4 s5, interleave f4 f5)

reverse_spec :: Spec -> Spec
reverse_spec (t, x, f) = (t, reverse x, reverse f)

-- reverse of rule 9
rule_10 :: M.MEAP -> Spec
rule_10 = reverse_spec . rule_9

scale_start_only :: Double -> Spec -> Spec
scale_start_only n (t, x, f) = (t, map g x, f)
    where g (s, d) = (s * n, d)

-- rule 10 with start times compressed but durations kept equal
rule_11 :: M.MEAP -> Spec
rule_11 = (scale_start_only 0.5) . rule_10

scale_dur_only :: Double -> Spec -> Spec
scale_dur_only n (t, x, f) = (t, map g x, f)
    where g (s, d) = (s, d * n)

-- rule 7 with durations compressed but start times retained
rule_12 :: M.MEAP -> Spec
rule_12 = (scale_dur_only 0.5) . rule_7

fix_dur :: Double -> Spec -> Spec
fix_dur n (t, x, f) = (t, map g x, f)
    where g (s, _) = (s, n)

-- rule 7 with durations fixed at 0.25 seconds
rule_13 :: M.MEAP -> Spec
rule_13 = (fix_dur 0.25) . rule_7

rules :: [M.MEAP -> Spec]
rules = [ rule_1
        , rule_2
        , rule_3
        , rule_4
        , rule_5
        , rule_6
        , rule_7
        , rule_8
        , rule_9
        , rule_10
        , rule_11
        , rule_12
        , rule_13 ]

main :: IO ()
main = do
  a <- getArgs
  unless (length a == 3) (error "audio-file feature-file rule-number")
  let [w_fn, f_fn, r] = a
      rn = read r - 1
  unless (rn >= 0 && rn < length rules) (error "unknown rule")
  run_grn w_fn f_fn ( rules !! rn )