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.