Proper use of setMixLevelsInput

I am working on a binding from Haskell to FMOD and I have run into a problem. I am trying to figure out if it is my binding code or if I am improperly using FMOD.

I combined 4 mono .wav files into a single .fsbank file. I want to play this file, but be able to set levels on the 4 channels within the sound. I used setMixLevelsInput and passed in an array of 4 zeroes, but all 4 channels play at their normal volumes. Is setMixLevelsInput the correct function to use in this situation?

Here is my main module

module MixBank2 where

import Control.Monad.Trans.Except (ExceptT (..), catchE, runExceptT, throwE)
import Data.Foldable (toList)
import Data.Sequence (Seq)
import qualified Data.Sequence as Sequence
import Fmod.LowLevel.Refined.Channel (Channel)
import qualified Fmod.LowLevel.Refined.Channel as Channel
import Fmod.LowLevel.Refined.Sound (Sound)
import qualified Fmod.LowLevel.Refined.Sound as Sound
import Fmod.LowLevel.Refined.System (System)
import qualified Fmod.LowLevel.Refined.System as System
import Fmod.LowLevel.Refined.Tokens
import Text.Read (readMaybe)

main :: IO (Either Result ())
main = runExceptT $ do
  s <- System.create
  System.init s 32 [FMOD_INIT_NORMAL] ()
  snd <- System.createStream s "/home/bwroga/test.fsb" [FMOD_LOOP_NORMAL]
  snd' <- Sound.getSubSound snd 0
  c <- System.playSound s snd' Nothing False
  let sq = Sequence.fromList $ [0,0,0,0]
  Channel.setMixLevelsInput c (fromIntegral <$> toList sq)
  System.update s
  ask s c sq
  Sound.release snd
  System.release s
  lift $ print "Done :]"

ask :: System -> Channel -> Seq Int -> ExceptT Result IO ()
ask s c sq = do
  lift $ putStrLn ""
  lift $ putStrLn "Toggle Channel (0-3), quit with q"
  choice <- lift getLine
  case choice of
    x | Just y <- ((readMaybe x) :: Maybe Int)
      , 0 <= y && y <= 3 ->
          do let sq' = Sequence.adjust ((^2) . subtract 1) y sq
             Channel.setMixLevelsInput c (fromIntegral <$> toList sq')
             System.update s
             ask s c sq'
    "q" -> ExceptT $ return $ Right ()
    _ -> do lift $ putStrLn "Invalid choice"
            ask s c sq

lift :: (Monad m) => m a -> ExceptT e m a
lift = ExceptT . fmap Right

This is the definition of setMixLevelsInput in the module Fmod.LowLevel.Refined.Channel
setMixLevelsInput :: Channel -> [Double] -> ExceptT Result IO ()
setMixLevelsInput (Channel c) ls = ExceptT $
  do withArray (realToFrac <$> ls) $ \ls' ->
       do r <- Result <$> fmodChannelSetMixLevelsInput c ls' (fromIntegral $ length ls)
          if r /= FMOD_OK
          then return $ Left r
          else return $ Right ()

This is the definition of fmodChannelSetMixLevelsInput in Fmod.LowLevel.Raw.Channel
foreign import ccall unsafe "fmod.h FMOD_Channel_SetMixLevelsInput"
  fmodChannelSetMixLevelsInput :: Ptr FmodChannel -> Ptr CFloat -> CInt -> IO FmodResult

If I use setMixMatrix instead, I can get it to work.

main :: IO (Either Result ())
main = runExceptT $ do
  s <- System.create
  System.init s 32 [FMOD_INIT_NORMAL] ()
  snd <- System.createStream s "/home/bwroga/test.fsb" [FMOD_LOOP_NORMAL]
  snd' <- Sound.getSubSound snd 0
  c <- System.playSound s snd' Nothing False
  let sq = Sequence.fromList $ [0,0,0,0]
  Channel.setMixMatrix c (fromIntegral <$> toList sq) 1 4
  System.update s
  ask s c sq
  Sound.release snd
  System.release s
  lift $ print "Done :]"

ask :: System -> Channel -> Seq Int -> ExceptT Result IO ()
ask s c sq = do
  lift $ putStrLn ""
  lift $ putStrLn "Toggle Channel (0-3), quit with q"
  choice <- lift getLine
  case choice of
    x | Just y <- ((readMaybe x) :: Maybe Int)
      , 0 <= y && y <= 3 ->
          do let sq' = Sequence.adjust ((^2) . subtract 1) y sq
             Channel.setMixMatrix c (fromIntegral <$> toList sq') 1 4
             System.update s
             ask s c sq'
    "q" -> ExceptT $ return $ Right ()
    _ -> do lift $ putStrLn "Invalid choice"
            ask s c sq

lift :: (Monad m) => m a -> ExceptT e m a
lift = ExceptT . fmap Right

It's seems weird that a 1x4 matrix works here, I expected that I would have to use a 2x4 matrix.

To rule out it being a haskell/binding problem, I edited the play_stream example that came in the library download. I changed it so it loads my test sound file and then added this after the call to play_sound:

float levels[] = {0.0f, 0.0f, 0.0f, 0.0f};
result = channel->setMixLevelsInput (levels, 4);
ERRCHECK(result);

This had no effect. All 4 channels of the sound played at normal volume.

In case it helps, I am developing on Ubuntu 16.04.

Hello Ben,

The setMixLevelsInput should work the way you’re expecting it to. Could you please paste a snippet of your code here for us to take a look at? There may be something else that is getting in the way.

Thanks,
Richard

Hi Richard, I added relevant code to my answer. Thanks.

Apologies, it looks like setMixLevelsInput is not working currently. I’ll have this patched for the next release.

The workaround now is to use setMixMatrix.

The output count of your matrix simply sets the output format for the DSP (the fader in this case) that is being used to set the matrix. You can set the output channel count to whatever you want.

In the fixed version setMixLevelsInput , the default matrix will be inchannels x inchannels based on the sound playing. setMixLevelsInput will just scale those numbers.

To change the format to stereo or something later, you have to either add a new DSP after the fader and set its format with DSP::setChannelFormat, or just use setMixMatrix like you have.