module Sound.MED.Generic.Tempo (
  MEDTempo(..),
  Mode(..),
  song0Tempo,
  song2Tempo,

  update,
  toTime,
  ) where

import Sound.MED.Generic.Block(Cmd,Val)

import qualified Sound.MED.Raw.MMD0Song as MMD0Song
import qualified Sound.MED.Raw.MMD2Song as MMD2Song

import Sound.MED.Basic.Amiga

import Data.Bits (testBit, (.&.))
import Data.Bool.HT (if')

data MEDTempo = MEDTempo
  { MEDTempo -> Mode
mode :: Mode,
    MEDTempo -> Int
primary, MEDTempo -> Int
secondary :: Int
  }

data Mode = Speed | Octa | BPM {Mode -> Int
linesPerBeat :: Int}

tempoMode :: UBYTE -> UBYTE -> Mode
tempoMode :: UBYTE -> UBYTE -> Mode
tempoMode UBYTE
flags UBYTE
flags2 =
  Bool -> Mode -> Mode -> Mode
forall a. Bool -> a -> a -> a
if' (UBYTE -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit UBYTE
flags Int
6) Mode
Octa (Mode -> Mode) -> Mode -> Mode
forall a b. (a -> b) -> a -> b
$
  Bool -> Mode -> Mode -> Mode
forall a. Bool -> a -> a -> a
if' (UBYTE -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit UBYTE
flags2 Int
5) (Int -> Mode
BPM (Int -> Mode) -> Int -> Mode
forall a b. (a -> b) -> a -> b
$ UBYTE -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (UBYTE
flags2 UBYTE -> UBYTE -> UBYTE
forall a. Bits a => a -> a -> a
.&. UBYTE
0x1F) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
  Mode
Speed

song0Tempo :: MMD0Song.MMD0Song -> MEDTempo
song0Tempo :: MMD0Song -> MEDTempo
song0Tempo MMD0Song
song =
  MEDTempo :: Mode -> Int -> Int -> MEDTempo
MEDTempo
    { mode :: Mode
mode = UBYTE -> UBYTE -> Mode
tempoMode (MMD0Song -> UBYTE
MMD0Song.flags MMD0Song
song) (MMD0Song -> UBYTE
MMD0Song.flags2 MMD0Song
song)
    , primary :: Int
primary = UWORD -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (UWORD -> Int) -> UWORD -> Int
forall a b. (a -> b) -> a -> b
$ MMD0Song -> UWORD
MMD0Song.deftempo MMD0Song
song
    , secondary :: Int
secondary = UBYTE -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (UBYTE -> Int) -> UBYTE -> Int
forall a b. (a -> b) -> a -> b
$ MMD0Song -> UBYTE
MMD0Song.tempo2 MMD0Song
song
    }

song2Tempo :: MMD2Song.MMD2Song -> MEDTempo
song2Tempo :: MMD2Song -> MEDTempo
song2Tempo MMD2Song
song =
  MEDTempo :: Mode -> Int -> Int -> MEDTempo
MEDTempo
    { mode :: Mode
mode = UBYTE -> UBYTE -> Mode
tempoMode (MMD2Song -> UBYTE
MMD2Song.flags MMD2Song
song) (MMD2Song -> UBYTE
MMD2Song.flags2 MMD2Song
song)
    , primary :: Int
primary = UWORD -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (UWORD -> Int) -> UWORD -> Int
forall a b. (a -> b) -> a -> b
$ MMD2Song -> UWORD
MMD2Song.deftempo MMD2Song
song
    , secondary :: Int
secondary = UBYTE -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (UBYTE -> Int) -> UBYTE -> Int
forall a b. (a -> b) -> a -> b
$ MMD2Song -> UBYTE
MMD2Song.tempo2 MMD2Song
song
    }


{- |
Interpret tempo related commands.
-}
update :: MEDTempo -> (Cmd, Val) -> MEDTempo
update :: MEDTempo -> (Int, Int) -> MEDTempo
update MEDTempo
tempo (Int
cmd,Int
val) =
  case Int
cmd of
    Int
0x09 -> MEDTempo
tempo{secondary :: Int
secondary = Int -> Int -> Int
forall a. Integral a => a -> a -> a
mod (Int
valInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1) Int
0x20 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1}
    Int
0x0F ->
      if Int
0 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
val Bool -> Bool -> Bool
&& Int
val Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0xF0
        then MEDTempo
tempo{primary :: Int
primary = Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
val}
        else MEDTempo
tempo
    Int
_ -> MEDTempo
tempo


toTime :: Fractional a => MEDTempo -> a
toTime :: MEDTempo -> a
toTime (MEDTempo Mode
mode_ Int
tempo1 Int
tempo2) =
  Mode -> Int -> a
forall a. Fractional a => Mode -> Int -> a
timeFromPrimary Mode
mode_ Int
tempo1 a -> a -> a
forall a. Num a => a -> a -> a
* Int -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
tempo2

-- numbers taken from uade/amigasrc/players/med/common/proplayer.a
ciabFreq :: Fractional a => a
ciabFreq :: a
ciabFreq = a
715909

timerDiv :: Fractional a => a
timerDiv :: a
timerDiv = a
474326

_sttempo :: Fractional a => a
_sttempo :: a
_sttempo = a
2416.3

{-
Measured from output of uade.
In theory it should be 300 (50 Hz times 6 divisions).
-}
sttempoMeasured :: Fractional a => a
sttempoMeasured :: a
sttempoMeasured = a
293.70

octaTempo :: Fractional a => a
octaTempo :: a
octaTempo = a
390.70

timeFromPrimary :: Fractional a => Mode -> Int -> a
timeFromPrimary :: Mode -> Int -> a
timeFromPrimary Mode
mode_ Int
tempo =
  case Mode
mode_ of
    BPM Int
lpb -> a
60 a -> a -> a
forall a. Fractional a => a -> a -> a
/ (Int -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
tempo a -> a -> a
forall a. Num a => a -> a -> a
* a
6 a -> a -> a
forall a. Num a => a -> a -> a
* Int -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
lpb)
    Mode
Octa -> Int -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
10 Int
tempo) a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
forall a. Fractional a => a
octaTempo
    Mode
Speed ->
      if Int
tempoInt -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<=Int
10
        -- then fromIntegral tempo * sttempo / ciabFreq
        then Int -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
tempo a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
forall a. Fractional a => a
sttempoMeasured
        else a
forall a. Fractional a => a
timerDiv a -> a -> a
forall a. Fractional a => a -> a -> a
/ (a
forall a. Fractional a => a
ciabFreq a -> a -> a
forall a. Num a => a -> a -> a
* Int -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
tempo)