Example #1
0
 def __init__(self):
     self.envelope = Envelope()
     self.divider = Divider()
     self.shift = 0x0000
     self.length_counter = 0x00
     self.is_enabled = False
     self.registers = [0x00, 0x00, 0x00]
Example #2
0
class Sweep:
    def __init__(self):
        self.is_enabled = False
        self.reload = False
        self.divider = Divider()
        self.negative_flag = False
        self.shift = 0

    def reset(self):
        self.is_enabled = False
        self.reload = False
        self.divider = Divider()

    def step(self):
        """Returns true if pulse period needs to be adjusted"""
        return_value = False
        if self.reload:
            if self.divider.counter == 0 and self.is_enabled:
                return_value = True
            self.divider.reload()
            self.reload = False  # Done reloading
        elif self.divider.step() and self.is_enabled:
            self.divider.reload()
            return_value = True

        return return_value
Example #3
0
    def __init__(self):
        self.divider = Divider()
        self.length_counter = 0x00
        self.linear_counter = LinearCounter()
        self.sequencer = Sequencer()

        self.is_enabled = False
        self.registers = [0x00, 0x00, 0x00]
Example #4
0
    def __init__(self):
        self.divider = Divider()
        self.envelope = Envelope()
        self.sequencer = Sequencer()
        self.sweep = Sweep()
        self.length_counter = 0x00

        self.is_enabled = False

        # Used with Pulse1 for negative updates of the period
        # The period is decreased by one more unit
        self.period_minus_one = False
        self.registers = [0x00, 0x00, 0x00, 0x00]
Example #5
0
class Envelope:
    def __init__(self):
        self.start = False
        self.loop = False
        self.divider = Divider()
        self.counter = 0x00  # 1 byte

    def reset(self):
        self.start = False
        self.loop = False
        self.divider.reset()
        self.counter = 0x00

    def step(self):
        if self.start:
            self.start = False
            self.counter = 0x0F
            self.divider.reload()
        elif self.divider.step():
            if self.counter > 0:
                self.counter = (self.counter - 1) & 0xFF
            elif self.loop:
                self.counter = 0x0F
Example #6
0
class Noise:

    LENGTH_COUNTER_DATA = [
        10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16,
        24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30
    ]

    PERIOD_DATA = [
        4,
        8,
        16,
        32,
        64,
        96,
        128,
        160,
        202,
        254,
        380,
        508,
        762,
        1016,
        2034,
        4068,
    ]

    def __init__(self):
        self.envelope = Envelope()
        self.divider = Divider()
        self.shift = 0x0000
        self.length_counter = 0x00
        self.is_enabled = False
        self.registers = [0x00, 0x00, 0x00]

    def reset(self):
        self.is_enabled = False
        self.registers = [0x00, 0x00, 0x00]
        self.divider.reset()
        self.length_counter = 0x00
        self.envelope.reset()
        self.shift = 0x0000

    def enable(self):
        self.is_enabled = True

    def disable(self):
        self.is_enabled = False
        self.length_counter = 0x00

    def read_register(self, address):
        if address == 0x400c:
            return self.registers[0]
        elif address == 0x400e:
            return self.registers[1]
        elif address == 0x400f:
            return self.registers[2]
        else:
            raise NoiseError("Invalid read access to register {}".format(
                hex(address)))

    def write_register(self, address, value):
        if address == 0x400c:
            # --lc.vvvv, length counter halt, constant volume/envelope flag, volume/envelope divider period
            self.registers[0] = value
            self.envelope.loop = ((value >> 5) & 1) == 1
            self.envelope.divider.period = value & 0x0F
        elif address == 0x400e:
            # M---.PPPP Mode, period
            self.registers[1] = value
            self.divider.period = self.PERIOD_DATA[value & 0x0F]
            self.divider.reload()
        elif address == 0x400F:
            # llll.l--- Length counter load and envelope restart
            self.registers[2] = value
            self.envelope.start = True
            if self.is_enabled:
                self.length_counter = self.LENGTH_COUNTER_DATA[value >> 3]
        else:
            raise NoiseError("Invalid write access to register {}".format(
                hex(address)))

    def output(self):
        if not self.is_enabled:
            return 0
        if self.shift & 0x0001 != 0:
            return 0
        if self.length_counter == 0:
            return 0
        if (self.registers[0] >> 4) & 0x01:  # Constant volume flag
            return self.envelope.counter
        else:
            return self.registers[0] & 0x0F
Example #7
0
 def __init__(self):
     self.is_enabled = False
     self.reload = False
     self.divider = Divider()
     self.negative_flag = False
     self.shift = 0
Example #8
0
 def reset(self):
     self.is_enabled = False
     self.reload = False
     self.divider = Divider()
Example #9
0
 def __init__(self):
     self.start = False
     self.loop = False
     self.divider = Divider()
     self.counter = 0x00  # 1 byte
Example #10
0
class Triangle:

    LENGTH_COUNTER_DATA = [
        10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16,
        24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30
    ]

    def __init__(self):
        self.divider = Divider()
        self.length_counter = 0x00
        self.linear_counter = LinearCounter()
        self.sequencer = Sequencer()

        self.is_enabled = False
        self.registers = [0x00, 0x00, 0x00]

    def reset(self):
        self.is_enabled = False
        self.registers = [0x00, 0x00, 0x00]
        self.divider.reset()
        self.length_counter = 0x00
        self.linear_counter.reset()
        self.sequencer.reset()

    def enable(self):
        self.is_enabled = True

    def disable(self):
        self.is_enabled = False
        self.length_counter = 0x00

    def read_register(self, address):
        if address == 0x4008:
            return self.registers[0]
        elif address == 0x400A:
            return self.registers[1]
        elif address == 0x400B:
            return self.registers[2]
        else:
            raise TriangleError("Invalid read access to register {}".format(
                hex(address)))

    def write_register(self, address, value):
        if address == 0x4008:
            # CRRR.RRRR, control flag + counter reload value
            self.registers[0] = value
            self.linear_counter.control = value >> 7
            self.linear_counter.reload_value = value & 0x7F
        elif address == 0x400A:
            # LLLL.LLLL, timer low
            self.registers[1] = value
            self.divider.period = (self.divider.period & 0x0700) | value
        elif address == 0x400B:
            # llll.lHHH, length counter load and timer high
            self.registers[2] = value
            if self.is_enabled:
                self.length_counter = self.LENGTH_COUNTER_DATA[value >> 3]
            self.divider.period = (self.divider.period & 0x00FF) | (
                (value >> 3) << 8)
            self.linear_counter.reload = True
        else:
            raise TriangleError("Invalid write access to register {}".format(
                hex(address)))

    def output(self):
        if not self.is_enabled:
            return 0
        if self.linear_counter == 0:
            return 0
        if self.length_counter == 0:
            return 0
        return self.sequencer.output_value
Example #11
0
class Pulse:

    LENGTH_COUNTER_DATA = [
        10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16,
        24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30
    ]

    SEQUENCER_DATA = [[0b0, 0b1, 0b0, 0b0, 0b0, 0b0, 0b0, 0b0],
                      [0b0, 0b1, 0b1, 0b0, 0b0, 0b0, 0b0, 0b0],
                      [0b0, 0b1, 0b1, 0b1, 0b1, 0b0, 0b0, 0b0],
                      [0b1, 0b0, 0b0, 0b1, 0b1, 0b1, 0b1, 0b1]]

    def __init__(self):
        self.divider = Divider()
        self.envelope = Envelope()
        self.sequencer = Sequencer()
        self.sweep = Sweep()
        self.length_counter = 0x00

        self.is_enabled = False

        # Used with Pulse1 for negative updates of the period
        # The period is decreased by one more unit
        self.period_minus_one = False
        self.registers = [0x00, 0x00, 0x00, 0x00]

    def reset(self):
        self.is_enabled = False
        self.registers = [0x00, 0x00, 0x00, 0x00]
        self.divider.reset()
        self.envelope.reset()
        self.sequencer.reset()
        self.sweep.reset()
        self.length_counter = 0x00

    def enable(self):
        self.is_enabled = True

    def disable(self):
        self.is_enabled = False
        self.length_counter = 0x00

    def read_register(self, address):
        if 0x4000 <= address < 0x4004:
            return self.registers[address - 0x4000]
        elif 0x4004 <= address < 0x4008:
            return self.registers[address - 0x4004]
        else:
            raise PulseError("Invalid read access to register {}".format(
                hex(address)))

    def write_register(self, address, value):
        if address == 0x4000 or address == 0x4004:
            # CONTROL register
            self.registers[0] = value
            self.envelope.counter = value & 0x0F
            self.envelope.loop = (value >> 5) & 0x01 == 1
            self.sequencer.values = self.SEQUENCER_DATA[value >> 6]
        elif address == 0x4001 or address == 0x4005:
            # SWEEP register
            self.registers[1] = value
            # EPPPNSSS
            self.sweep.is_enabled = value >> 7 == 1  # E
            self.sweep.divider.period = (value >> 4) & 0b111  # PPP
            self.sweep.negative_flag = (value >> 3) & 0x01  # N
            self.sweep.shift = (value & 0x07)  # SSS
            self.sweep.reload = True
        elif address == 0x4002 or address == 0x4006:
            # TIMER LOW register
            self.registers[2] = value
            self.divider.period = (self.divider.period & 0x0700) | value
        elif address == 0x4003 or address == 0x4007:
            self.registers[3] = value
            if self.is_enabled:
                self.length_counter = self.LENGTH_COUNTER_DATA[value >> 3]
            self.divider.period = (self.divider.period & 0x00FF) | (
                (value & 0x07) << 8)
            self.sequencer.reset()
            self.envelope.start = True
        else:
            raise PulseError("Invalid write access to register {}".format(
                hex(address)))

    def get_period(self):
        """The sweep unit continuously calculates each channel's target period in this way:
        - A barrel shifter shifts the channel's 11-bit raw timer period right by the shift count, producing the change amount.
        - If the negate flag is true, the change amount is made negative.
        - The target period is the sum of the current period and the change amount.
        """
        current_period = self.divider.period
        sweep_shift = self.sweep.shift
        diff = current_period >> sweep_shift
        """The two pulse channels have their adders' carry inputs wired differently,
        which produces different results when each channel's change amount is made negative:
        - Pulse 1 adds the ones' complement (−c − 1). Making 20 negative produces a change amount of −21.
        - Pulse 2 adds the two's complement (−c). Making 20 negative produces a change amount of −20.
        """
        if self.sweep.negative_flag:
            diff = -diff
            if self.period_minus_one:
                diff -= 1

        return current_period + diff

    def output(self):
        """The mixer receives the current envelope volume except when
        - The sequencer output is zero, or
        - overflow from the sweep unit's adder is silencing the channel, or
        - the length counter is zero, or
        - the timer has a value less than eight."""
        if self.sequencer.output_value == 0:
            return 0
        if self.divider.period < 8 or self.get_period() > 0x07FF:
            return 0
        if self.length_counter == 0:
            return 0

        if (self.registers[0] >> 4) & 1:  # Constant Volume
            return self.envelope.counter
        else:
            return self.registers[0] & 0x0F