softglow's notebook

Dispatches from the Depths of a Super Nintendo

Where the Samples Are

Looking at the higan v093 source, it’s fairly obvious where the DSP code lives, but how does the sound escape it?

Sending samples to the Interface

In the performance accuracy profile, the process actually begins down in echo_27(), implemented in sfc/dsp/echo.cpp. It is there that the elusive audio.sample() method is called, which is part of Audio, over in sfc/system/audio.cpp. In the case where there’s no coprocessor, it passes directly into interface->audioSample.

In the complicated case where there is a coprocessor, then the audio heads into a DSP buffer, to be averaged in audio.flush() with the coprocessor’s buffer; the final samples are clamped to 16 bits and finally routed through the same interface->audioSample there.

Interface

Sharp eyes will have noticed the arrow operator, indicating that interface is a pointer. It’s a pointer to the binding of the current interface, which is to say: it’s a Interface *interface where the type is struct Interface : Emulator::Interface::Bind.

Looking from the outside in, target-ethos/bootstrap.cpp creates the binding-interface, creates and appends all the known system emulators to a vector, then iterates over the vector to set each system emulator’s binding to the single global binding that it just created.

Although audioSample is virtual, I haven’t found anything yet which actually overrides it.

audioSample & dspaudio

The implementation of audioSample in target-ethos/interface/interface.cpp does very little:

signed samples[] = {lsample, rsample};
dspaudio.sample(samples);
while(dspaudio.pending()) {
    dspaudio.read(samples);
    audio.sample(samples[0], samples[1]);
}

The dspaudio is a nall::DSP declared in target-ethos/ethos.cpp and implemented in nall/dsp/core.hpp. Pretty much, samples go into the DSP, they get resampled to the output sample rate, and then those results can be read out again.

The trick here is that the audio visible in this particular scope is actually AudioInterface audio from ruby/ruby.cpp. Platform audio drivers live under ruby/audio/*.cpp and AudioInterface::sample passes the samples along to whatever driver happens to be connected.

tl;dr

If the madness hasn’t taken me:

  1. The system DSP produces samples and passes it into the system audio chain.
  2. The system audio chain passes samples into the platform interface.
  3. The platform interface pumps samples through the resampler.
  4. The resulting samples are pushed into the platform audio driver.