--- /dev/null
+/**
+ * Copyright (C) 2021 Alexander Schmidt
+ *
+ * This file is part of Seamulator.
+ *
+ * Seamulator is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Seamulator 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Seamulator. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "watersurfacefwd.h"
+
+#include <chrono>
+#include <mutex>
+#include <thread>
+
+#include "SineWave.h"
+#include "RtWvOut.h"
+#include "BlowBotl.h"
+#include "Plucked.h"
+
+#include "dot.h"
+
+class Synthesizer {
+
+public:
+ Synthesizer(ConstWaterSurfacePtr surface);
+ ~Synthesizer();
+
+ void tick();
+
+private:
+ const ConstWaterSurfacePtr m_surface;
+
+ bool m_stop = false;
+
+ std::chrono::time_point<std::chrono::system_clock> m_startTime;
+ double m_lastRuntime;
+ double m_pos = 0.0;
+ double m_vel = 0.0;
+
+
+ double m_pos2 = 0.0;
+ double m_vel2 = 0.0;
+
+ double m_velMax1 = 0.0;
+ double m_velMax2 = 0.0;
+
+ double getRuntime() const;
+
+ // int audioTick(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
+ // double streamTime, RtAudioStreamStatus status, void *dataPointer );
+
+ void audioLoop();
+
+ std::vector<std::unique_ptr<Dot>> m_dots;
+
+ stk::Plucked m_sine1;
+ stk::Plucked m_sine2;
+ //RtAudio m_dac;
+ std::unique_ptr<stk::RtWvOut> m_dac;
+
+ std::thread m_audioThread;
+
+};
--- /dev/null
+/**
+ * Copyright (C) 2021 Alexander Schmidt
+ *
+ * This file is part of Seamulator.
+ *
+ * Seamulator is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Seamulator 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Seamulator. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "synthesizer.h"
+
+#include <cmath>
+#include <functional>
+#include <iostream>
+
+#include <Stk.h>
+#include <Echo.h>
+#include <Chorus.h>
+#include <FreeVerb.h>
+#include <PitShift.h>
+#include <PRCRev.h>
+#include <NRev.h>
+
+#include "watersurface.h"
+
+using namespace std::placeholders;
+
+Synthesizer::Synthesizer(ConstWaterSurfacePtr surface) :
+ m_surface{std::move(surface)},
+ m_startTime{std::chrono::system_clock::now()},
+ m_lastRuntime{getRuntime()}
+{
+ stk::Stk::setSampleRate( 44100.0 );
+ stk::Stk::showWarnings( true );
+
+ m_dac = std::make_unique<stk::RtWvOut>(2);
+
+ //m_sine1.setFrequency( 441.0 );
+ //m_sine2.setFrequency( 520.0 );
+
+ //m_sine1.noteOn(40.0, 1.0);
+ // m_sine2.noteOn(74.0, 1.0);
+
+ //for (int i = 0; i < 10; ++i) {
+// m_dots.emplace_back( std::make_unique<Dot>(m_surface->at(i, 0), 440.0) );
+ //}
+
+ m_dots.emplace_back( std::make_unique<Dot>(m_surface->at(0, 0), 939.85) );
+ m_dots.emplace_back( std::make_unique<Dot>(m_surface->at(0, 10), 704.09) );
+ m_dots.emplace_back( std::make_unique<Dot>(m_surface->at(0, 20), 469.92) );
+ m_dots.emplace_back( std::make_unique<Dot>(m_surface->at(0, 30), 352.04) );
+ m_dots.emplace_back( std::make_unique<Dot>(m_surface->at(0, 40), 279.42) );
+
+ m_dots.emplace_back( std::make_unique<Dot>(m_surface->at(10, 10), 1054.94) );
+ m_dots.emplace_back( std::make_unique<Dot>(m_surface->at(20, 10), 704.09) );
+ m_dots.emplace_back( std::make_unique<Dot>(m_surface->at(30, 10), 527.47) );
+ m_dots.emplace_back( std::make_unique<Dot>(m_surface->at(40, 10), 418.65) );
+ m_dots.emplace_back( std::make_unique<Dot>(m_surface->at(50, 10), 313.64) );
+
+ m_audioThread = std::thread( [this]{audioLoop();} );
+
+ // std::cout << "NoteOn 0.0 1 64.000000 64.0" << std::endl;
+ // std::cout << std::flush;
+
+ // Figure out how many bytes in an StkFloat and setup the RtAudio stream.
+// RtAudio::StreamParameters parameters;
+// parameters.deviceId = m_dac.getDefaultOutputDevice();
+// parameters.nChannels = 1;
+// RtAudioFormat format = ( sizeof(stk::StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
+// unsigned int bufferFrames = stk::RT_BUFFER_SIZE;
+// try {
+// // auto aTick = std::bind(&Synthesizer::audioTick, this, _1, _2, _3, _4, _5, _6);
+// std::function<int (void*, void*, unsigned int, double, unsigned int, void*)> bla = [this](void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
+// double streamTime, RtAudioStreamStatus status, void *dataPointer) -> int {
+// return 0;
+// };
+// m_dac.openStream( ¶meters, NULL, format, (unsigned int)stk::Stk::sampleRate(), &bufferFrames, bla,
+// (void *)&m_sine );
+// }
+// catch ( RtAudioError &error ) {
+// // error.printMessage();
+// // goto cleanup;
+// throw;
+// }
+
+// m_sine.setFrequency(440.0);
+// try {
+// m_dac.startStream();
+// }
+// catch ( RtAudioError &error ) {
+// // error.printMessage();
+// // goto cleanup;
+// throw;
+// }
+}
+
+Synthesizer::~Synthesizer()
+{
+ m_stop = true;
+ m_audioThread.join();
+}
+
+void Synthesizer::tick()
+{
+ const auto runtime = getRuntime();
+ const auto deltaT = runtime - m_lastRuntime;
+
+ for (auto& dot : m_dots) {
+ dot->advance(deltaT);
+ }
+
+// const auto posBefore = m_pos;
+// const auto posBefore2 = m_pos2;
+
+// m_vel += -1.0 * ( m_pos - m_surface->at(0, 0).getHeight() ) * deltaT - deltaT * m_vel * 0.1;
+// m_pos += m_vel * deltaT;
+
+// //std::cout << m_vel << std::endl;
+
+// std::cout << m_pos2 << std::endl;
+
+// m_vel2 += -10.0 * ( m_pos2 - m_surface->at(10, 10).getHeight() ) * deltaT - deltaT * m_vel2 * 0.1;
+// m_pos2 += m_vel2 * deltaT;
+
+// m_velMax1 = std::max(m_velMax1, m_vel);
+// m_velMax2 = std::max(m_velMax2, m_vel2);
+
+// if (posBefore * m_pos < 0.0) {
+// // std::cout << "ControlChange 0.0 2 7 " << 100 * std::fabs(m_vel) << std::endl;
+// //m_sine1.noteOn(
+// // std::cout << "NoteOn 0.0 2 75 64" << std::endl;
+// // std::cout << std::flush;
+// m_sine1.noteOn(m_pos >= 0 ? 440.0 : 329.628, m_vel / m_velMax1);
+// }
+
+// if (posBefore2 * m_pos2 < 0.0) {
+// // std::cout << "ControlChange 0.0 1 7 " << 100 * std::fabs(m_vel) << std::endl;
+// // std::cout << "NoteOn 0.0 1 44 64" << std::endl;
+// // std::cout << "NoteOff 0.1 1 44 64" << std::endl;
+// // std::cout << std::flush;
+// m_sine2.noteOn(m_pos2 >= 0 ? 440.0 : 329.628, m_vel2 / m_velMax2);
+// }
+
+// // std::cout << std::fabs(m_vel) * 10000 << std::endl;
+
+// // m_sine1.controlChange(128, std::fabs(m_vel) * 10000);
+
+// // std::cout << "ControlChange 0.0 1 7 " << 100 * std::fabs(m_vel) << std::endl;
+// // std::cout << "PitchChange 0.0 1 " << 42 + 0.1*m_pos << std::endl;
+
+// // std::cout << "ControlChange 0.0 1 7 " << 64 + 100 * m_vel << std::endl;
+// //std::cout << "AfterTouch 0.0 1 " << 64 + 10 * m_vel << std::endl;
+// //std::cout << "PitchChange 0.0 1 " << 64 + 10*m_vel << std::endl;
+
+// std::cout << std::flush;
+
+
+
+ m_lastRuntime = runtime;
+}
+
+void Synthesizer::audioLoop()
+{
+ stk::PRCRev effect0(0.5);
+ stk::Echo effect;
+ stk::Chorus chorus{6000};
+ stk::StkFrames frames(88200, 2);
+ while (!m_stop) {
+ for (int i = 0; i < 88200; i++) {
+ frames(i, 0) *= 0;
+ frames(i, 1) *= 0;
+ for (std::size_t dotIdx = 0; dotIdx < m_dots.size(); ++dotIdx) {
+ frames(i, dotIdx % 2 == 0 ? 1 : 0) += m_dots[dotIdx]->tick();
+ }
+ //frames(i,1) = frames(i,0);
+ }
+ // frames = effect0.tick(frames);
+// frames = effect.tick(frames);
+ frames = chorus.tick(frames);
+
+ m_dac->tick(frames);
+ }
+}
+
+double Synthesizer::getRuntime() const
+{
+ auto timeNow = std::chrono::system_clock::now();
+ auto durationMs =
+ std::chrono::duration_cast<std::chrono::milliseconds>(timeNow - m_startTime);
+ return durationMs.count() / 1000.0;
+}