module Nes.APU.State.Noise (
Noise (..),
newNoise,
getNoiseOutput,
tickNoise,
tickShiftRegister,
getPeriodValue,
) where
import Data.Bits
import Data.List ((!?))
import Data.Maybe (fromMaybe)
import Data.Word
import Nes.APU.State.Envelope
import Nes.APU.State.LengthCounter
data Noise = MkN
{ Noise -> Bool
useBit6ForFeedback :: {-# UNPACK #-} !Bool
, Noise -> Envelope
envelope :: !Envelope
, Noise -> LengthCounter
lengthCounter :: !LengthCounter
, Noise -> Word16
shiftRegister :: {-# UNPACK #-} !Word16
, Noise -> Int
period :: {-# UNPACK #-} !Int
, Noise -> Int
timer :: {-# UNPACK #-} !Int
}
newNoise :: Noise
newNoise :: Noise
newNoise = MkN{Bool
Int
Word16
Envelope
LengthCounter
useBit6ForFeedback :: Bool
envelope :: Envelope
lengthCounter :: LengthCounter
shiftRegister :: Word16
period :: Int
timer :: Int
envelope :: Envelope
useBit6ForFeedback :: Bool
shiftRegister :: Word16
lengthCounter :: LengthCounter
period :: Int
timer :: Int
..}
where
envelope :: Envelope
envelope = Envelope
newEnvelope
useBit6ForFeedback :: Bool
useBit6ForFeedback = Bool
False
shiftRegister :: Word16
shiftRegister = Word16
1
lengthCounter :: LengthCounter
lengthCounter = LengthCounter
newLengthCounter
period :: Int
period = Int
0
timer :: Int
timer = Int
0
getPeriodValue :: Int -> Int
getPeriodValue :: Int -> Int
getPeriodValue Int
idx = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
4 ([Int
4, Int
8, Int
16, Int
32, Int
64, Int
96, Int
128, Int
160, Int
202, Int
254, Int
380, Int
508, Int
762, Int
1016, Int
2034, Int
4068] [Int] -> Int -> Maybe Int
forall a. [a] -> Int -> Maybe a
!? Int
idx)
instance HasLengthCounter Noise where
getLengthCounter :: Noise -> LengthCounter
getLengthCounter = Noise -> LengthCounter
lengthCounter
setLengthCounter :: LengthCounter -> Noise -> Noise
setLengthCounter LengthCounter
lc Noise
n = Noise
n{lengthCounter = lc}
instance HasEnvelope Noise where
getEnvelope :: Noise -> Envelope
getEnvelope = Noise -> Envelope
envelope
setEnvelope :: Envelope -> Noise -> Noise
setEnvelope Envelope
e Noise
n = Noise
n{envelope = e}
tickNoise :: Noise -> Noise
tickNoise :: Noise -> Noise
tickNoise Noise
n = Noise -> Noise
tickCallback (Noise -> Noise) -> Noise -> Noise
forall a b. (a -> b) -> a -> b
$ Noise
n{timer = newTimer}
where
newTimer :: Int
newTimer = if Noise -> Int
timer Noise
n Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 then Noise -> Int
period Noise
n else Noise -> Int
timer Noise
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
tickCallback :: Noise -> Noise
tickCallback = if Noise -> Int
timer Noise
n Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 then Noise -> Noise
tickShiftRegister else Noise -> Noise
forall a. a -> a
id
tickShiftRegister :: Noise -> Noise
tickShiftRegister :: Noise -> Noise
tickShiftRegister Noise
n = Noise
n{shiftRegister = shift2}
where
shift0 :: Word16
shift0 = Noise -> Word16
shiftRegister Noise
n
xorBit :: Int
xorBit = if Noise -> Bool
useBit6ForFeedback Noise
n then Int
6 else Int
1
feeback :: Bool
feeback = (Word16
shift0 Word16 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
`testBit` Int
0) Bool -> Bool -> Bool
forall a. Bits a => a -> a -> a
.^. (Word16
shift0 Word16 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
`testBit` Int
xorBit)
shift1 :: Word16
shift1 = Word16
shift0 Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`shiftR` Int
1
shift2 :: Word16
shift2 = if Bool
feeback then Word16
shift1 Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`setBit` Int
14 else Word16
shift1
getNoiseOutput :: Noise -> Int
getNoiseOutput :: Noise -> Int
getNoiseOutput Noise
n =
if Bool
shiftBit0IsSet Bool -> Bool -> Bool
|| Noise -> Bool
forall a. HasLengthCounter a => a -> Bool
isSilencedByLengthCounter Noise
n
then Int
0
else Envelope -> Int
getEnvelopeOutput (Envelope -> Int) -> Envelope -> Int
forall a b. (a -> b) -> a -> b
$ Noise -> Envelope
envelope Noise
n
where
shiftBit0IsSet :: Bool
shiftBit0IsSet = Noise -> Word16
shiftRegister Noise
n Word16 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
`testBit` Int
0