ttuegel / alsa-mixer

Haskell bindings to the ALSA mixer API
BSD 3-Clause "New" or "Revised" License
7 stars 3 forks source link

Add normalized or mapped to Volume #10

Closed crocket closed 4 years ago

crocket commented 4 years ago

I created the following source code by referring to get_normalized_volume from alsa-utils/alsa-mixer/volume_mapping.c

{-# LANGUAGE ScopedTypeVariables #-}
import qualified Sound.ALSA.Mixer as AM

maxLinearDbScale :: AM.CLong
maxLinearDbScale = 24
gainMute :: AM.CLong
gainMute = -9999999

useLinearDbScale :: AM.CLong -> AM.CLong -> Bool
useLinearDbScale l h =
  (h - l) <= maxLinearDbScale * 100

getNormalizedDb :: AM.CLong -> AM.CLong -> AM.CLong -> Int
getNormalizedDb dB low high =
  let l :: Double = fromIntegral low
      h :: Double = fromIntegral high
      v :: Double = fromIntegral dB in
    if useLinearDbScale low high
    then round $ (v-l)/(h-l)*100.0
    else let norm = 10.0 ** ((v - h) / 6000.0) in
      if low == gainMute
      then round $ norm * 100.0
      else let minNorm = 10.0 ** ((l - h) / 6000.0) in
        round $ (norm - minNorm) / (1 - minNorm) * 100.0

getNormalizedVal :: AM.CLong -> AM.CLong -> AM.CLong -> Int
getNormalizedVal val low high =
  let v :: Double = fromIntegral val
      l :: Double = fromIntegral low
      h :: Double = fromIntegral high in
    round $ (v - l) / (h - l) * 100.0

getNormalizedVolume :: AM.Control -> IO (Maybe Int)
getNormalizedVolume c = do
  case AM.playback (AM.volume c) of
    Nothing -> return Nothing
    Just vol -> do
      let dB = AM.dB vol
          chan = head $ AM.channels dB
      (lowDb, highDb) <- AM.getRangeDb vol
      if lowDb >= highDb
        then do (low, high) <- AM.getRange vol
                if low == high
                  then return Nothing
                  else do val <- AM.getChannel chan $ AM.value vol
                          case val of
                            Nothing -> return Nothing
                            Just v -> return $ Just $
                              getNormalizedVal v low high
        else do v <- AM.getChannel chan dB
                case v of
                  Nothing -> return Nothing
                  Just v' -> return $ Just $
                    getNormalizedDb v' lowDb highDb
crocket commented 4 years ago

It turns out that normalized volume is not really useful. Decibel is more informative.