Game Boy APU emulation. Provides an Apu object that can synthesize audio to an internal buffer when stepping for a given number of cycles. The emulated registers can be read/written to, which can be used along side a full Game Boy emulator, or to just generate audio when playing a module.
See also
Pan Docs for details about the hardware being emulated.
Procs
proc availableSamples(a: Apu): int {....raises: [], tags: [], forbids: [].}
- Gets the amount of samples available in the buffer. takeSamples will take this exact amount of samples when called Source Edit
func channelFrequency(a: Apu; chno: ChannelId): int {....raises: [], tags: [], forbids: [].}
-
Gets the current frequency setting for the channel, for diagnostic purposes. Channels 1-3 will result in a value from 0 to 2047. Channel 4 will result in the contents of its NR43 register.
Example:
Source Editvar a = Apu.init(44100) assert a.channelFrequency(ch2) == 0
func channelMix(a: Apu; chno: ChannelId): MixMode {....raises: [], tags: [], forbids: [].}
-
Gets the current mix mode for the given channel. Provided as an alternative to reading ar51.
Example:
Source Editvar a = Apu.init(44100) a.writeRegister(0x26, 0x80) # NR52 <- 0x80 a.writeRegister(0x25, 0b00110101) # NR51 <- 0x35 assert a.channelMix(ch1) == mixMiddle assert a.channelMix(ch2) == mixRight assert a.channelMix(ch3) == mixLeft assert a.channelMix(ch4) == mixMute
func channelVolume(a: Apu; chno: ChannelId): int {....raises: [], tags: [], forbids: [].}
-
Returns a number from 0 to 15 resprenting the current volume level of the channel. For channels with an enevelope, this level is the current volume of the envelope. For the Wave channel, this value is the maximum possible determined by the wave volume setting (NR32).
Example:
Source Editvar a = Apu.init(44100) assert a.channelVolume(ch1) == 0 assert a.channelVolume(ch3) == 0 a.writeRegister(0x26, 0x80) # NR52 <- 0x80 a.writeRegister(0x12, 0xD0) # NR12 <- 0xD0 a.writeRegister(0x14, 0x80) # NR14 <- 0x80 a.writeRegister(0x1C, 0x20) # NR32 <- 0x20 assert a.channelVolume(ch1) == 0xD assert a.channelVolume(ch3) == 0xF
-
Initializes an Apu with the given samplerate and internal buffer size that contains a single frame with the given framerate. samplerate and framerate are in Hz and must be greater than 0.
The returned Apu is in its default, or hardware reset, state. The volume step is set to a default of 0.625f.
Example:
Source Editvar a = Apu.init(24000, 60.0) # 24000 Hz samplerate with a 60 Hz framerate
func readRegister(a: var Apu; reg: uint8): uint8 {....raises: [], tags: [], forbids: [].}
-
Reads the register at address reg. This proc emulates the behavior of reading the memory-mapped registers on an actual Game Boy. Since some registers are write-only (ie frequency), attempting to read these registers will result in their bits being read back as all 1s.
reg is the memory address of the register in the 0xFF00 page, so to read rNR10, 0xFF10, you would call this proc with reg = 0x10u8
The read occurs at the apu's current timestep, run the apu beforehand if you want the read to occur at a certain point in time.
The proc will return 0xFF for any invalid reg address.
Example:
Source Editvar a = Apu.init(44100) # sound is OFF, all reads should result in 0xFF assert a.readRegister(0x10) == 0xFFu8 # NR52 should be 0x70 assert a.readRegister(0x26) == 0x70u8
proc removeSamples(a: var Apu) {....raises: [], tags: [], forbids: [].}
- Removes all samples in the sample buffer. Source Edit
-
Resets the apu to its initial state. Should behave similarly to a hardware reset. The internal sample buffer is also cleared.
Example:
Source Editvar a = Apu.init(44100) a.writeRegister(0x26, 0x80) a.writeRegister(0x12, 0xF2) assert a.readRegister(0x12) == 0xF2u8 a.reset() assert a.readRegister(0x12) == 0xFFu8 assert a.availableSamples == 0
-
Runs the apu a for a given number of cycles. The internal sample buffer is updated with new samples from the run. Use takeSamples afterwards to collect them for processing, or removeSamples to discard them.
Example:
Source Editvar a = Apu.init(44100, 1.0) a.run(4194304) # 1 second assert a.availableSamples == 44100 # another call to run will overrun the buffer # to empty the buffer do either: # a.takeSamples(buffer) # a.removeSamples()
proc runToFrame(a: var Apu) {....raises: [], tags: [], forbids: [].}
-
Runs the apu a for the required number of cycles to complete a frame. The amount of cycles that is run is determined by a's current time and the framerate setting.
Example:
Source Editvar a = Apu.init(48000, 60.0) a.runToFrame() # there are 800 samples in a frame # and 69905.06 cycles in a frame # so we step 69905 cycles which results in 799 samples instead # (eventually there will be one frame with 800 samples) assert a.availableSamples == 799
proc setBufferSize(a: var Apu; samples: int) {....raises: [], tags: [], forbids: [].}
- Sets the apu's internal buffer to the given number of samples. This will destroy the contents of the buffer so it is recommended you call takeSamples first. Source Edit
proc setFramerate(a: var Apu; framerate: float) {....raises: [], tags: [], forbids: [].}
- Changes the size of frame to the given framerate. The APU must be reset when changing the framerate. Source Edit
proc setSamplerate(a: var Apu; samplerate: int) {....raises: [], tags: [], forbids: [].}
- Sets the samplerate of the generated audio. The internal sample buffer is left untouched, so it is recommended you call takeSamples beforehand. Source Edit
proc takeSamples(a: var Apu; buf: var seq[Pcm]) {....raises: [], tags: [], forbids: [].}
- Takes out the entire sample buffer and puts it into the given buf. The buf's len will be set to a.availableSamples * 2 and will have the contents of the apu's sample buffer. Source Edit
-
Gets the apu's current time, in cycles. Calling takeSamples resets the time to 0.
Example:
Source Editvar a = Apu.init(44100) assert a.time == 0 a.run(1000) assert a.time == 1000 a.run(100) assert a.time == 1100 a.removeSamples() assert a.time == 0 # takeSamples will also reset time to 0
proc writeRegister(a: var Apu; reg, value: uint8) {....raises: [], tags: [], forbids: [].}
-
Writes value to the apu's register at reg address. Like readRegister, this proc emulates writing to the Game Boy's memory-mapped APU registers. Writes to any unknown address are ignored. Writes to read-only portions of registers are also ignored. Like readRegister, the write occurs at the apu's current time.
Example:
Source Editvar a = Apu.init(44100) a.writeRegister(0x12, 0xC0) # APU is off, write is ignored a.writeRegister(0x26, 0x80) # turn APU on assert a.readRegister(0x12) == 0x00 a.writeRegister(0x12, 0xC0) assert a.readRegister(0x12) == 0xC0 a.writeRegister(0xD3, 0xCC) # invalid register, write is ignored