cycfi / q

C++ Library for Audio Digital Signal Processing
MIT License
1.12k stars 146 forks source link

Example of using signal_conditioner with shared_ptr #41

Closed xavriley closed 1 year ago

xavriley commented 2 years ago

I have two projects using qlib - one is a VAMP plugin for non-realtime analysis and feature extraction, the other is a SuperCollider plugin. Both of these use cases have an initialization step, followed by a processing step which works on a frame by frame basis.

With the pitch_detector class I'm able to use the following during the init:

    std::shared_ptr<cycfi::q::pitch_detector> m_pd = std::shared_ptr<cycfi::q::pitch_detector>(new cycfi::q::pitch_detector(
                q::frequency(m_lowestPitch),
                q::frequency(m_highestPitch),
                m_inputSampleRate,
                q::decibel(q::detail::db2a(m_threshold))));

and then call

bool is_ready = m_pd->operator()(s);

during the processing loop. However the same trick doesn't appear to work for the new signal_conditioner class as the compiler complains about it being a template function https://github.com/cycfi/q/blob/master/q_lib/include/q/fx/signal_conditioner.hpp#L43

I'm not sure what the magic spell is to assign a template class to a shared_ptr or indeed whether this is the right thing to do in the first place! I assume that the signal conditioner is stateful and that the state needs to be persisted across calls to process each frame. Do you have any recommendations for how to get this to work?

djowel commented 2 years ago

Show me the code that does not work.

djowel commented 2 years ago

q::decibel(q::detail::db2a(m_threshold))));

BTW, this does not look good. detail is private and should not be used by clients. Also, why are m_lowestPitch and m_highestPitch not q::frequency types? While this works, it is subverting the strict type system, like using void*.

I know, I know... off-topic... I just can't resist advocating good modern c++ usage :-)

xavriley commented 2 years ago

😅 I'll try to tidy up the code a bit - some things are a hangover from when I was figuring things out.

Code that doesn't work:

// in header
std::shared_ptr<cycfi::q::signal_conditioner> m_pp;
// in initializer
...
auto m_pp = std::shared_ptr<cycfi::q::pitch_detector>(new cycfi::q::pitch_detector(
                q::frequency(m_lowestPitch),
                q::frequency(m_highestPitch),
                m_inputSampleRate,
                q::decibel(q::detail::db2a(m_threshold))));

...
// inside process function
float s = m_pp->operator(inputBuffers[0][i]);

It compiles but with a note:

QlibPitch.cpp:277:21: note: in instantiation of function template specialization 'cycfi::q::signal_conditioner::signal_conditioner<cycfi::q::signal_conditioner::config>' requested here
    auto sig_cond = cycfi::q::signal_conditioner(

and then segfaults when I try to run it. I 100% expect this to be an issue with my code - I'm mainly just trying to understand how to share an instance of the signal conditioner across several different function calls.

djowel commented 2 years ago

// in initializer ... auto m_pp = std::shared_ptr(new cycfi::q::pitch_detector( q::frequency(m_lowestPitch), q::frequency(m_highestPitch), m_inputSampleRate, q::decibel(q::detail::db2a(m_threshold))));

This does not make sense. m_pp is a std::shared_ptr<cycfi::q::signal_conditioner> and you are assigning a std::shared_ptr<cycfi::q::pitch_detector> to it?