diff --git a/7.x/src/stk/CMakeLists.txt b/7.x/src/stk/CMakeLists.txt index bd809aa..b98aa7c 100644 --- a/7.x/src/stk/CMakeLists.txt +++ b/7.x/src/stk/CMakeLists.txt @@ -53,5 +53,23 @@ if(BUILD_STK_OPCODES) endif() endif() + # Native unit tests exercise both adapters without an audio device or engine. + if(NOT WASM) + find_package(GTest QUIET) + if(GTest_FOUND) + add_executable(stk_adapter_tests tests/stk_adapter_test.cpp src/Stk.cpp) + set_target_properties(stk_adapter_tests PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON) + target_include_directories(stk_adapter_tests PRIVATE + ${CSOUND_INCLUDE_DIRS} + "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/include") + target_compile_definitions(stk_adapter_tests PRIVATE BUILD_PLUGINS) + target_link_libraries(stk_adapter_tests PRIVATE GTest::gtest_main) + add_test(NAME stk_adapter_tests COMMAND stk_adapter_tests) + endif() + endif() + message(STATUS "Building STK opcodes") endif() diff --git a/7.x/src/stk/STKInstrumentAdapter.hpp b/7.x/src/stk/STKInstrumentAdapter.hpp new file mode 100644 index 0000000..0b94e27 --- /dev/null +++ b/7.x/src/stk/STKInstrumentAdapter.hpp @@ -0,0 +1,327 @@ +/* + This file is part of Csound. + Copyright (C) 2005 Michael Gogins. + + The Csound Library is free software; you can redistribute it + and/or modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + Csound is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with Csound; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA +*/ +#pragma once + +#include "Stk.h" +#include "OpcodeBase.hpp" + +namespace csound { + +template +class STKInstrumentAdapter : public OpcodeBase> { +public: + // Outputs. + cs_float *aoutput; + // Inputs. + cs_float *ifrequency; + cs_float *igain; + cs_float *kcontroller0; + cs_float *kvalue0; + cs_float *kcontroller1; + cs_float *kvalue1; + cs_float *kcontroller2; + cs_float *kvalue2; + cs_float *kcontroller3; + cs_float *kvalue3; + cs_float *kcontroller4; + cs_float *kvalue4; + cs_float *kcontroller5; + cs_float *kvalue5; + cs_float *kcontroller6; + cs_float *kvalue6; + cs_float *kcontroller7; + cs_float *kvalue7; + // State. + T *instrument; + bool released; + cs_float oldkcontroller0; + cs_float oldkvalue0; + cs_float oldkcontroller1; + cs_float oldkvalue1; + cs_float oldkcontroller2; + cs_float oldkvalue2; + cs_float oldkcontroller3; + cs_float oldkvalue3; + cs_float oldkcontroller4; + cs_float oldkvalue4; + cs_float oldkcontroller5; + cs_float oldkvalue5; + cs_float oldkcontroller6; + cs_float oldkvalue6; + cs_float oldkcontroller7; + cs_float oldkvalue7; + STKInstrumentAdapter() : instrument(0) {} + int32_t init(CSOUND *csound) { + // Constructors size delay lines and filters from STK's shared sample rate. + // Set it on reinit too, before noteOn() retunes an existing instrument. + stk::Stk::setSampleRate(static_cast(this->opds.insdshead->esr)); + if (!instrument) { + try { + instrument = new T(); + } catch (stk::StkError &e) { + return csound->InitError(csound, "STK: %s", e.getMessage().c_str()); + } + } + instrument->noteOn(*ifrequency, *igain); + released = false; + oldkcontroller0 = -1.0; + oldkvalue0 = -1.0; + oldkcontroller1 = -1.0; + oldkvalue1 = -1.0; + oldkcontroller2 = -1.0; + oldkvalue2 = -1.0; + oldkcontroller3 = -1.0; + oldkvalue3 = -1.0; + oldkcontroller4 = -1.0; + oldkvalue4 = -1.0; + oldkcontroller5 = -1.0; + oldkvalue5 = -1.0; + oldkcontroller6 = -1.0; + oldkvalue6 = -1.0; + oldkcontroller7 = -1.0; + oldkvalue7 = -1.0; + return OK; + } + int32_t kontrol(CSOUND *csound) { + if (!released) { + if (*kcontroller0 != oldkcontroller0 || *kvalue0 != oldkvalue0) { + instrument->controlChange(static_cast(*kcontroller0), *kvalue0); + oldkcontroller0 = *kcontroller0; + oldkvalue0 = *kvalue0; + } + if (*kcontroller1 != oldkcontroller1 || *kvalue1 != oldkvalue1) { + instrument->controlChange(static_cast(*kcontroller1), *kvalue1); + oldkcontroller1 = *kcontroller1; + oldkvalue1 = *kvalue1; + } + if (*kcontroller2 != oldkcontroller2 || *kvalue2 != oldkvalue2) { + instrument->controlChange(static_cast(*kcontroller2), *kvalue2); + oldkcontroller2 = *kcontroller2; + oldkvalue2 = *kvalue2; + } + if (*kcontroller3 != oldkcontroller3 || *kvalue3 != oldkvalue3) { + instrument->controlChange(static_cast(*kcontroller3), *kvalue3); + oldkcontroller3 = *kcontroller3; + oldkvalue3 = *kvalue3; + } + if (*kcontroller4 != oldkcontroller4 || *kvalue4 != oldkvalue4) { + instrument->controlChange(static_cast(*kcontroller4), *kvalue4); + oldkcontroller4 = *kcontroller4; + oldkvalue4 = *kvalue4; + } + if (*kcontroller5 != oldkcontroller5 || *kvalue5 != oldkvalue5) { + instrument->controlChange(static_cast(*kcontroller5), *kvalue5); + oldkcontroller5 = *kcontroller5; + oldkvalue5 = *kvalue5; + } + if (*kcontroller6 != oldkcontroller6 || *kvalue6 != oldkvalue6) { + instrument->controlChange(static_cast(*kcontroller6), *kvalue6); + oldkcontroller6 = *kcontroller6; + oldkvalue6 = *kvalue6; + } + if (*kcontroller7 != oldkcontroller7 || *kvalue7 != oldkvalue7) { + instrument->controlChange(static_cast(*kcontroller7), *kvalue7); + oldkcontroller7 = *kcontroller7; + oldkvalue7 = *kvalue7; + } + } + uint32_t offset = this->kperiodOffset(); + uint32_t end = this->kperiodEnd(); + uint32_t nsmps = this->ksmps(); + for (uint32_t i = 0; i < offset; i++) { + aoutput[i] = 0; + } + if (!released) { + for (uint32_t i = offset; i < end; i++) { + aoutput[i] = instrument->tick(); + } + } else { + for (uint32_t i = offset; i < end; i++) { + aoutput[i] = 0; + } + } + for (uint32_t i = end; i < nsmps; i++) { + aoutput[i] = 0; + } + return OK; + } + int32_t deinit(CSOUND *csound) { + if (instrument) { + delete instrument; + instrument = 0; + } + return OK; + } + static int32_t deinit_(CSOUND *csound, void *opcode) { + return reinterpret_cast *>(opcode)->deinit(csound); + } +}; + +template +class STKInstrumentAdapter1 : public OpcodeBase> { +public: + // Outputs. + cs_float *aoutput; + // Inputs. + cs_float *ifrequency; + cs_float *igain; + cs_float *kcontroller0; + cs_float *kvalue0; + cs_float *kcontroller1; + cs_float *kvalue1; + cs_float *kcontroller2; + cs_float *kvalue2; + cs_float *kcontroller3; + cs_float *kvalue3; + cs_float *kcontroller4; + cs_float *kvalue4; + cs_float *kcontroller5; + cs_float *kvalue5; + cs_float *kcontroller6; + cs_float *kvalue6; + cs_float *kcontroller7; + cs_float *kvalue7; + // State. + T *instrument; + bool released; + cs_float oldkcontroller0; + cs_float oldkvalue0; + cs_float oldkcontroller1; + cs_float oldkvalue1; + cs_float oldkcontroller2; + cs_float oldkvalue2; + cs_float oldkcontroller3; + cs_float oldkvalue3; + cs_float oldkcontroller4; + cs_float oldkvalue4; + cs_float oldkcontroller5; + cs_float oldkvalue5; + cs_float oldkcontroller6; + cs_float oldkvalue6; + cs_float oldkcontroller7; + cs_float oldkvalue7; + STKInstrumentAdapter1() : instrument(0) {} + int32_t init(CSOUND *csound) { + // Constructors size delay lines and filters from STK's shared sample rate. + // Set it on reinit too, before noteOn() retunes an existing instrument. + stk::Stk::setSampleRate(static_cast(this->opds.insdshead->esr)); + if (!instrument) { + try { + instrument = new T((stk::StkFloat)10.0); + } catch (stk::StkError &e) { + return csound->InitError(csound, "STK: %s", e.getMessage().c_str()); + } + } + instrument->noteOn(*ifrequency, *igain); + released = false; + oldkcontroller0 = -1.0; + oldkvalue0 = -1.0; + oldkcontroller1 = -1.0; + oldkvalue1 = -1.0; + oldkcontroller2 = -1.0; + oldkvalue2 = -1.0; + oldkcontroller3 = -1.0; + oldkvalue3 = -1.0; + oldkcontroller4 = -1.0; + oldkvalue4 = -1.0; + oldkcontroller5 = -1.0; + oldkvalue5 = -1.0; + oldkcontroller6 = -1.0; + oldkvalue6 = -1.0; + oldkcontroller7 = -1.0; + oldkvalue7 = -1.0; + return OK; + } + int32_t kontrol(CSOUND *csound) { + if (!released) { + if (*kcontroller0 != oldkcontroller0 || *kvalue0 != oldkvalue0) { + instrument->controlChange(static_cast(*kcontroller0), *kvalue0); + oldkcontroller0 = *kcontroller0; + oldkvalue0 = *kvalue0; + } + if (*kcontroller1 != oldkcontroller1 || *kvalue1 != oldkvalue1) { + instrument->controlChange(static_cast(*kcontroller1), *kvalue1); + oldkcontroller1 = *kcontroller1; + oldkvalue1 = *kvalue1; + } + if (*kcontroller2 != oldkcontroller2 || *kvalue2 != oldkvalue2) { + instrument->controlChange(static_cast(*kcontroller2), *kvalue2); + oldkcontroller2 = *kcontroller2; + oldkvalue2 = *kvalue2; + } + if (*kcontroller3 != oldkcontroller3 || *kvalue3 != oldkvalue3) { + instrument->controlChange(static_cast(*kcontroller3), *kvalue3); + oldkcontroller3 = *kcontroller3; + oldkvalue3 = *kvalue3; + } + if (*kcontroller4 != oldkcontroller4 || *kvalue4 != oldkvalue4) { + instrument->controlChange(static_cast(*kcontroller4), *kvalue4); + oldkcontroller4 = *kcontroller4; + oldkvalue4 = *kvalue4; + } + if (*kcontroller5 != oldkcontroller5 || *kvalue5 != oldkvalue5) { + instrument->controlChange(static_cast(*kcontroller5), *kvalue5); + oldkcontroller5 = *kcontroller5; + oldkvalue5 = *kvalue5; + } + if (*kcontroller6 != oldkcontroller6 || *kvalue6 != oldkvalue6) { + instrument->controlChange(static_cast(*kcontroller6), *kvalue6); + oldkcontroller6 = *kcontroller6; + oldkvalue6 = *kvalue6; + } + if (*kcontroller7 != oldkcontroller7 || *kvalue7 != oldkvalue7) { + instrument->controlChange(static_cast(*kcontroller7), *kvalue7); + oldkcontroller7 = *kcontroller7; + oldkvalue7 = *kvalue7; + } + } + uint32_t offset = this->kperiodOffset(); + uint32_t end = this->kperiodEnd(); + uint32_t nsmps = this->ksmps(); + for (uint32_t i = 0; i < offset; i++) { + aoutput[i] = 0; + } + if (!released) { + for (uint32_t i = offset; i < end; i++) { + aoutput[i] = instrument->tick(); + } + } else { + for (uint32_t i = offset; i < end; i++) { + aoutput[i] = 0; + } + } + for (uint32_t i = end; i < nsmps; i++) { + aoutput[i] = 0; + } + return OK; + } + int32_t deinit(CSOUND *csound) { + if (instrument) { + delete instrument; + instrument = 0; + } + return OK; + } + static int32_t deinit_(CSOUND *csound, void *opcode) { + return reinterpret_cast *>(opcode)->deinit(csound); + } +}; + +} // namespace csound diff --git a/7.x/src/stk/stkOpcodes.cpp b/7.x/src/stk/stkOpcodes.cpp index d672079..5db1c94 100644 --- a/7.x/src/stk/stkOpcodes.cpp +++ b/7.x/src/stk/stkOpcodes.cpp @@ -97,306 +97,12 @@ extern "C" { #include } -#include "OpcodeBase.hpp" +#include "STKInstrumentAdapter.hpp" using namespace stk; using namespace csound; using namespace std; -template -class STKInstrumentAdapter : public OpcodeBase> { -public: - // Outputs. - cs_float *aoutput; - // Inputs. - cs_float *ifrequency; - cs_float *igain; - cs_float *kcontroller0; - cs_float *kvalue0; - cs_float *kcontroller1; - cs_float *kvalue1; - cs_float *kcontroller2; - cs_float *kvalue2; - cs_float *kcontroller3; - cs_float *kvalue3; - cs_float *kcontroller4; - cs_float *kvalue4; - cs_float *kcontroller5; - cs_float *kvalue5; - cs_float *kcontroller6; - cs_float *kvalue6; - cs_float *kcontroller7; - cs_float *kvalue7; - // State. - T *instrument; - bool released; - cs_float oldkcontroller0; - cs_float oldkvalue0; - cs_float oldkcontroller1; - cs_float oldkvalue1; - cs_float oldkcontroller2; - cs_float oldkvalue2; - cs_float oldkcontroller3; - cs_float oldkvalue3; - cs_float oldkcontroller4; - cs_float oldkvalue4; - cs_float oldkcontroller5; - cs_float oldkvalue5; - cs_float oldkcontroller6; - cs_float oldkvalue6; - cs_float oldkcontroller7; - cs_float oldkvalue7; - STKInstrumentAdapter() : instrument(0) {} - int32_t init(CSOUND *csound) { - if (!instrument) { - try { - instrument = new T(); - } catch (StkError &e) { - return csound->InitError(csound, "STK: %s", e.getMessage().c_str()); - } - } - instrument->noteOn(*ifrequency, *igain); - released = false; - oldkcontroller0 = -1.0; - oldkvalue0 = -1.0; - oldkcontroller1 = -1.0; - oldkvalue1 = -1.0; - oldkcontroller2 = -1.0; - oldkvalue2 = -1.0; - oldkcontroller3 = -1.0; - oldkvalue3 = -1.0; - oldkcontroller4 = -1.0; - oldkvalue4 = -1.0; - oldkcontroller5 = -1.0; - oldkvalue5 = -1.0; - oldkcontroller6 = -1.0; - oldkvalue6 = -1.0; - oldkcontroller7 = -1.0; - oldkvalue7 = -1.0; - return OK; - } - int32_t kontrol(CSOUND *csound) { - if (!released) { - if (*kcontroller0 != oldkcontroller0 || *kvalue0 != oldkvalue0) { - instrument->controlChange(static_cast(*kcontroller0), *kvalue0); - oldkcontroller0 = *kcontroller0; - oldkvalue0 = *kvalue0; - } - if (*kcontroller1 != oldkcontroller1 || *kvalue1 != oldkvalue1) { - instrument->controlChange(static_cast(*kcontroller1), *kvalue1); - oldkcontroller1 = *kcontroller1; - oldkvalue1 = *kvalue1; - } - if (*kcontroller2 != oldkcontroller2 || *kvalue2 != oldkvalue2) { - instrument->controlChange(static_cast(*kcontroller2), *kvalue2); - oldkcontroller2 = *kcontroller2; - oldkvalue2 = *kvalue2; - } - if (*kcontroller3 != oldkcontroller3 || *kvalue3 != oldkvalue3) { - instrument->controlChange(static_cast(*kcontroller3), *kvalue3); - oldkcontroller3 = *kcontroller3; - oldkvalue3 = *kvalue3; - } - if (*kcontroller4 != oldkcontroller4 || *kvalue4 != oldkvalue4) { - instrument->controlChange(static_cast(*kcontroller4), *kvalue4); - oldkcontroller4 = *kcontroller4; - oldkvalue4 = *kvalue4; - } - if (*kcontroller5 != oldkcontroller5 || *kvalue5 != oldkvalue5) { - instrument->controlChange(static_cast(*kcontroller5), *kvalue5); - oldkcontroller5 = *kcontroller5; - oldkvalue5 = *kvalue5; - } - if (*kcontroller6 != oldkcontroller6 || *kvalue6 != oldkvalue6) { - instrument->controlChange(static_cast(*kcontroller6), *kvalue6); - oldkcontroller6 = *kcontroller6; - oldkvalue6 = *kvalue6; - } - if (*kcontroller7 != oldkcontroller7 || *kvalue7 != oldkvalue7) { - instrument->controlChange(static_cast(*kcontroller7), *kvalue7); - oldkcontroller7 = *kcontroller7; - oldkvalue7 = *kvalue7; - } - } - uint32_t offset = this->kperiodOffset(); - uint32_t end = this->kperiodEnd(); - uint32_t nsmps = this->ksmps(); - for (uint32_t i = 0; i < offset; i++) { - aoutput[i] = 0; - } - if (!released) { - for (uint32_t i = offset; i < end; i++) { - aoutput[i] = instrument->tick(); - } - } else { - for (uint32_t i = offset; i < end; i++) { - aoutput[i] = 0; - } - } - for (uint32_t i = end; i < nsmps; i++) { - aoutput[i] = 0; - } - return OK; - } - int32_t deinit(CSOUND *csound) { - if (instrument) { - delete instrument; - instrument = 0; - } - return OK; - } - static int32_t deinit_(CSOUND *csound, void *opcode) { - return reinterpret_cast *>(opcode)->deinit(csound); - } -}; - -template -class STKInstrumentAdapter1 : public OpcodeBase> { -public: - // Outputs. - cs_float *aoutput; - // Inputs. - cs_float *ifrequency; - cs_float *igain; - cs_float *kcontroller0; - cs_float *kvalue0; - cs_float *kcontroller1; - cs_float *kvalue1; - cs_float *kcontroller2; - cs_float *kvalue2; - cs_float *kcontroller3; - cs_float *kvalue3; - cs_float *kcontroller4; - cs_float *kvalue4; - cs_float *kcontroller5; - cs_float *kvalue5; - cs_float *kcontroller6; - cs_float *kvalue6; - cs_float *kcontroller7; - cs_float *kvalue7; - // State. - T *instrument; - bool released; - cs_float oldkcontroller0; - cs_float oldkvalue0; - cs_float oldkcontroller1; - cs_float oldkvalue1; - cs_float oldkcontroller2; - cs_float oldkvalue2; - cs_float oldkcontroller3; - cs_float oldkvalue3; - cs_float oldkcontroller4; - cs_float oldkvalue4; - cs_float oldkcontroller5; - cs_float oldkvalue5; - cs_float oldkcontroller6; - cs_float oldkvalue6; - cs_float oldkcontroller7; - cs_float oldkvalue7; - STKInstrumentAdapter1() : instrument(0) {} - int32_t init(CSOUND *csound) { - if (!instrument) { - try { - instrument = new T((StkFloat)10.0); - } catch (StkError &e) { - return csound->InitError(csound, "STK: %s", e.getMessage().c_str()); - } - } - instrument->noteOn(*ifrequency, *igain); - released = false; - oldkcontroller0 = -1.0; - oldkvalue0 = -1.0; - oldkcontroller1 = -1.0; - oldkvalue1 = -1.0; - oldkcontroller2 = -1.0; - oldkvalue2 = -1.0; - oldkcontroller3 = -1.0; - oldkvalue3 = -1.0; - oldkcontroller4 = -1.0; - oldkvalue4 = -1.0; - oldkcontroller5 = -1.0; - oldkvalue5 = -1.0; - oldkcontroller6 = -1.0; - oldkvalue6 = -1.0; - oldkcontroller7 = -1.0; - oldkvalue7 = -1.0; - return OK; - } - int32_t kontrol(CSOUND *csound) { - if (!released) { - if (*kcontroller0 != oldkcontroller0 || *kvalue0 != oldkvalue0) { - instrument->controlChange(static_cast(*kcontroller0), *kvalue0); - oldkcontroller0 = *kcontroller0; - oldkvalue0 = *kvalue0; - } - if (*kcontroller1 != oldkcontroller1 || *kvalue1 != oldkvalue1) { - instrument->controlChange(static_cast(*kcontroller1), *kvalue1); - oldkcontroller1 = *kcontroller1; - oldkvalue1 = *kvalue1; - } - if (*kcontroller2 != oldkcontroller2 || *kvalue2 != oldkvalue2) { - instrument->controlChange(static_cast(*kcontroller2), *kvalue2); - oldkcontroller2 = *kcontroller2; - oldkvalue2 = *kvalue2; - } - if (*kcontroller3 != oldkcontroller3 || *kvalue3 != oldkvalue3) { - instrument->controlChange(static_cast(*kcontroller3), *kvalue3); - oldkcontroller3 = *kcontroller3; - oldkvalue3 = *kvalue3; - } - if (*kcontroller4 != oldkcontroller4 || *kvalue4 != oldkvalue4) { - instrument->controlChange(static_cast(*kcontroller4), *kvalue4); - oldkcontroller4 = *kcontroller4; - oldkvalue4 = *kvalue4; - } - if (*kcontroller5 != oldkcontroller5 || *kvalue5 != oldkvalue5) { - instrument->controlChange(static_cast(*kcontroller5), *kvalue5); - oldkcontroller5 = *kcontroller5; - oldkvalue5 = *kvalue5; - } - if (*kcontroller6 != oldkcontroller6 || *kvalue6 != oldkvalue6) { - instrument->controlChange(static_cast(*kcontroller6), *kvalue6); - oldkcontroller6 = *kcontroller6; - oldkvalue6 = *kvalue6; - } - if (*kcontroller7 != oldkcontroller7 || *kvalue7 != oldkvalue7) { - instrument->controlChange(static_cast(*kcontroller7), *kvalue7); - oldkcontroller7 = *kcontroller7; - oldkvalue7 = *kvalue7; - } - } - uint32_t offset = this->kperiodOffset(); - uint32_t end = this->kperiodEnd(); - uint32_t nsmps = this->ksmps(); - for (uint32_t i = 0; i < offset; i++) { - aoutput[i] = 0; - } - if (!released) { - for (uint32_t i = offset; i < end; i++) { - aoutput[i] = instrument->tick(); - } - } else { - for (uint32_t i = offset; i < end; i++) { - aoutput[i] = 0; - } - } - for (uint32_t i = end; i < nsmps; i++) { - aoutput[i] = 0; - } - return OK; - } - int32_t deinit(CSOUND *csound) { - if (instrument) { - delete instrument; - instrument = 0; - } - return OK; - } - static int32_t deinit_(CSOUND *csound, void *opcode) { - return reinterpret_cast *>(opcode)->deinit(csound); - } -}; - extern "C" { OENTRY oentries[] = { { diff --git a/7.x/src/stk/tests/stk_adapter_test.cpp b/7.x/src/stk/tests/stk_adapter_test.cpp new file mode 100644 index 0000000..d249d01 --- /dev/null +++ b/7.x/src/stk/tests/stk_adapter_test.cpp @@ -0,0 +1,75 @@ +#include +#include "STKInstrumentAdapter.hpp" + +// Observe the rate the instrument sees, without depending on any sound model. +struct SampleRateProbe { + double constructedAt = stk::Stk::sampleRate(); + double noteStartedAt = 0; + + SampleRateProbe() = default; + explicit SampleRateProbe(stk::StkFloat) {} + + void noteOn(stk::StkFloat, stk::StkFloat) { + noteStartedAt = stk::Stk::sampleRate(); + } +}; + +template +class StkAdapterTest : public ::testing::Test { +protected: + INSDS instrument = {}; + cs_float frequency = 440; + cs_float gain = 0.5; + Adapter adapter; + + void SetUp() override { + stk::Stk::setSampleRate(44100); + adapter.opds.insdshead = &instrument; + adapter.ifrequency = &frequency; + adapter.igain = &gain; + } + + void TearDown() override { + adapter.deinit(nullptr); + stk::Stk::setSampleRate(44100); + } +}; + +// Default construction (e.g. BeeThree) and a lowest-frequency argument (Saxofony). +using Adapters = ::testing::Types< + csound::STKInstrumentAdapter, + csound::STKInstrumentAdapter1>; +TYPED_TEST_SUITE(StkAdapterTest, Adapters); + +TYPED_TEST(StkAdapterTest, SetsSampleRateBeforeConstructingInstrument) { + this->instrument.esr = 48000; + ASSERT_EQ(this->adapter.init(nullptr), OK); + + EXPECT_DOUBLE_EQ(this->adapter.instrument->constructedAt, 48000); + EXPECT_DOUBLE_EQ(this->adapter.instrument->noteStartedAt, 48000); +} + +TYPED_TEST(StkAdapterTest, NewInstrumentsFollowSampleRateChanges) { + for (double rate : {48000, 96000, 44100}) { + SCOPED_TRACE(rate); + this->instrument.esr = rate; + ASSERT_EQ(this->adapter.init(nullptr), OK); + + EXPECT_DOUBLE_EQ(this->adapter.instrument->constructedAt, rate); + EXPECT_DOUBLE_EQ(this->adapter.instrument->noteStartedAt, rate); + this->adapter.deinit(nullptr); + } +} + +TYPED_TEST(StkAdapterTest, ReinitializationSetsSampleRateBeforeNoteOn) { + this->instrument.esr = 48000; + ASSERT_EQ(this->adapter.init(nullptr), OK); + auto *existing = this->adapter.instrument; + + // Another initialization may have changed STK's shared sample rate. + stk::Stk::setSampleRate(44100); + ASSERT_EQ(this->adapter.init(nullptr), OK); + + EXPECT_EQ(this->adapter.instrument, existing); + EXPECT_DOUBLE_EQ(this->adapter.instrument->noteStartedAt, 48000); +}