#define BUFFER_SIZE 2048
class dsp_tutorial_nopreset : public dsp_impl_base
{
SoundTouch * p_soundtouch;
int m_rate, m_ch, m_ch_mask;
pfc::array_t<soundtouch::SAMPLETYPE>samplebuf;
unsigned buffered;
private:
void insert_chunks()
{
uint samples = p_soundtouch->numSamples();
if (!samples) return;
samplebuf.grow_size(BUFFER_SIZE * m_ch);
soundtouch::SAMPLETYPE * src = samplebuf.get_ptr();
do
{
samples = p_soundtouch->receiveSamples(src, BUFFER_SIZE);
if (samples > 0)
{
audio_chunk * chunk = insert_chunk(samples * m_ch);
chunk->set_data_32(src, samples, m_ch, m_rate);
}
}
while (samples != 0);
}
public:
dsp_tutorial_nopreset() {
m_rate = 0;
m_ch = 0;
m_ch_mask = 0;
p_soundtouch=0;
buffered=0;
}
~dsp_tutorial_nopreset(){
if (p_soundtouch) delete p_soundtouch;
}
// Every DSP type is identified by a GUID.
static GUID g_get_guid() {
// Create these with guidgen.exe.
// {A7FBA855-56D4-46AC-8116-8B2A8DF2FB34}
static const GUID guid =
{ 0xa7fba855, 0x56d4, 0x46ac, { 0x81, 0x16, 0x8b, 0x2a, 0x8d, 0xf2, 0xfb, 0x34 } };
return guid;
}
// We also need a name, so the user can identify the DSP.
// The name we use here does not describe what the DSP does,
// so it would be a bad name. We can excuse this, because it
// doesn't do anything useful anyway.
static void g_get_name(pfc::string_base & p_out) {
p_out = "Pitch Shift";
}
virtual void on_endoftrack(abort_callback & p_abort) {
// This method is called when a track ends.
// We need to do the same thing as flush(), so we just call it.
}
virtual void on_endofplayback(abort_callback & p_abort) {
// This method is called on end of playback instead of flush().
// We need to do the same thing as flush(), so we just call it.
if (p_soundtouch)
{
insert_chunks();
if (buffered)
{
p_soundtouch->putSamples(samplebuf.get_ptr(), buffered);
buffered = 0;
}
p_soundtouch->flush();
insert_chunks();
delete p_soundtouch;
p_soundtouch = 0;
}
}
// The framework feeds input to our DSP using this method.
// Each chunk contains a number of samples with the same
// stream characteristics, i.e. same sample rate, channel count
// and channel configuration.
virtual bool on_chunk(audio_chunk * chunk, abort_callback & p_abort) {
t_size sample_count = chunk->get_sample_count();
audio_sample * src = chunk->get_data();
if (p_soundtouch && (m_ch != chunk->get_channels() || m_rate != chunk->get_srate()))
{
p_soundtouch->flush();
insert_chunks();
delete p_soundtouch;
p_soundtouch=0;
}
if ( chunk->get_srate() != m_rate || chunk->get_channels() != m_ch || chunk->get_channel_config() != m_ch_mask )
{
m_rate = chunk->get_srate();
m_ch = chunk->get_channels();
m_ch_mask = chunk->get_channel_config();
p_soundtouch = new SoundTouch;
if (!p_soundtouch) return 0;
p_soundtouch->setSampleRate(m_rate);
p_soundtouch->setChannels(m_ch);
p_soundtouch->setPitchSemiTones((float)12.0f);
p_soundtouch->setSetting(SETTING_USE_AA_FILTER,1);
p_soundtouch->setSetting(SETTING_AA_FILTER_LENGTH,128);
p_soundtouch->setSetting(SETTING_USE_QUICKSEEK,0);
}
soundtouch::SAMPLETYPE * dst;
samplebuf.grow_size(BUFFER_SIZE * m_ch);
while (sample_count)
{
unsigned todo = BUFFER_SIZE - buffered;
if (todo > sample_count) todo = sample_count;
dst = samplebuf.get_ptr() + buffered * m_ch;
for (unsigned i = 0, j = todo * m_ch; i < j; i++)
{
*dst++ = (soundtouch::SAMPLETYPE) (*src++);
}
sample_count -= todo;
buffered += todo;
if (buffered == BUFFER_SIZE)
{
p_soundtouch->putSamples(samplebuf.get_ptr(), buffered);
buffered = 0;
insert_chunks();
}
}
return true;
}
virtual void flush() {
if (p_soundtouch)
{
p_soundtouch->clear();
}
}
virtual double get_latency() {
return (p_soundtouch && m_rate) ? ((double)(p_soundtouch->numSamples() + buffered) / (double)m_rate) : 0;
}
virtual bool need_track_change_mark() {
// Return true if you need to know exactly when a new track starts.
// Beware that this may break gapless playback, as at least all the
// DSPs before yours have to be flushed.
// To picture this, consider the case of a reverb DSP which outputs
// the sum of the input signal and a delayed copy of the input signal.
// In the case of a single track:
// Input signal: 01234567
// Delayed signal: 01234567
// For two consecutive tracks with the same stream characteristics:
// Input signal: 01234567abcdefgh
// Delayed signal: 01234567abcdefgh
// If the DSP chain contains a DSP that requires a track change mark,
// the chain will be flushed between the two tracks:
// Input signal: 01234567 abcdefgh
// Delayed signal: 01234567 abcdefgh
return false;
}
};
// We need a service factory to make the DSP known to the system.
// DSPs use special service factories that implement a static dsp_entry
// that provides information about the DSP. The static methods in our
// DSP class our used to provide the implementation of this entry class.
// The entry is used to instantiate an instance of our DSP when it is needed.
// We use the "nopreset" version of the DSP factory which blanks out
// preset support.
// Note that there can be multiple instances of a DSP which are used in
// different threads.
static dsp_factory_nopreset_t<dsp_tutorial_nopreset> foo_dsp_tutorial_nopreset;
Don't fucking bother me. Again.