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:
- The system DSP produces samples and passes it into the system audio chain.
- The system audio chain passes samples into the platform interface.
- The platform interface pumps samples through the resampler.
- The resulting samples are pushed into the platform audio driver.