From: Alexander Schmidt Date: Mon, 22 Feb 2021 10:59:56 +0000 (+0100) Subject: make some noise X-Git-Url: http://git.treefish.org/~alex/seamulator.git/commitdiff_plain/ba8f356e863ed4a418f93c155e64eecaea0819bf make some noise --- diff --git a/.gitignore b/.gitignore index 4aaa26f..74d0bce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ ./build/ build/ +*~ diff --git a/cmake_modules/FindSTK.cmake b/cmake_modules/FindSTK.cmake new file mode 100644 index 0000000..a919405 --- /dev/null +++ b/cmake_modules/FindSTK.cmake @@ -0,0 +1,23 @@ +FIND_PATH(STK_INCLUDE_DIR Stk.h + /usr/include/stk + /usr/local/include/stk + ${CMAKE_INSTALL_PREFIX}/include/stk + ${CMAKE_FIND_ROOT_PATH}/include/stk) + +FIND_LIBRARY(STK_LIBRARY NAMES stk + PATH /usr/lib /usr/local/lib ${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_FIND_ROOT_PATH}/lib) + +IF (STK_INCLUDE_DIR AND STK_LIBRARY) + SET(STK_FOUND TRUE) +ENDIF (STK_INCLUDE_DIR AND STK_LIBRARY) + +IF (STK_FOUND) + IF (NOT STK_FIND_QUIETLY) + MESSAGE(STATUS "Found STK: ${STK_LIBRARY}") + SET(HAVE_STK TRUE) + ENDIF (NOT STK_FIND_QUIETLY) +ELSE (STK_FOUND) + IF (STK_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find STK") + ENDIF (STK_FIND_REQUIRED) +ENDIF (STK_FOUND) diff --git a/include/dot.h b/include/dot.h new file mode 100644 index 0000000..28792ff --- /dev/null +++ b/include/dot.h @@ -0,0 +1,33 @@ +#include + +#include "Plucked.h" +#include "Brass.h" +#include "Bowed.h" +#include "Guitar.h" +#include "Mandolin.h" +#include "Sitar.h" +#include "BeeThree.h" + +#include "surfacepoint.h" + +class Dot { + +public: + Dot(const SurfacePoint& point, double frequency); + + void advance(const double deltaT); + stk::StkFloat tick(); + +private: + std::mutex m_mutex; + + stk::Plucked m_noise; + + const SurfacePoint& m_point; + const double m_frequency; + + double m_pos = 0.0; + double m_vel = 0.0; + double m_maxAbsVel = 0.0; + +}; diff --git a/include/synthesizer.h b/include/synthesizer.h new file mode 100644 index 0000000..5d38053 --- /dev/null +++ b/include/synthesizer.h @@ -0,0 +1,76 @@ +/** + * 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 . + */ + +#pragma once + +#include "watersurfacefwd.h" + +#include +#include +#include + +#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 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> m_dots; + + stk::Plucked m_sine1; + stk::Plucked m_sine2; + //RtAudio m_dac; + std::unique_ptr m_dac; + + std::thread m_audioThread; + +}; diff --git a/include/watersurfacefwd.h b/include/watersurfacefwd.h index 7568abc..3f9e706 100644 --- a/include/watersurfacefwd.h +++ b/include/watersurfacefwd.h @@ -22,4 +22,6 @@ #include class WaterSurface; + using WaterSurfacePtr = std::shared_ptr; +using ConstWaterSurfacePtr = std::shared_ptr; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f859744..75990a0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,17 +1,27 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") +add_definitions(-D__LINUX_ALSA__ -D__LITTLE_ENDIAN__) + find_package(OpenGL REQUIRED) find_package(GLUT REQUIRED) find_package(fftw3 REQUIRED) +find_package(STK REQUIRED) find_package(Boost 1.40 COMPONENTS program_options REQUIRED) +find_package(ALSA REQUIRED) +find_package(Threads REQUIRED) include_directories(${seamulator_SOURCE_DIR}/include - ${OPENGL_INCLUDE_DIRS} ${GLUT_INCLUDE_DIRS} - ${FFTW_INCLUDES} ${Boost_INCLUDE_DIR}) + ${OPENGL_INCLUDE_DIRS} ${GLUT_INCLUDE_DIRS} + ${FFTW_INCLUDES} ${Boost_INCLUDE_DIR} ${STK_INCLUDE_DIR} + ${ALSA_INCLUDE_DIRS}) add_executable(seamulator seamulator.cpp - sea.cpp watersurface.cpp surfacepoint.cpp - seaview.cpp) + sea.cpp watersurface.cpp surfacepoint.cpp + seaview.cpp synthesizer.cpp dot.cpp) target_link_libraries(seamulator ${OPENGL_LIBRARIES} ${GLUT_LIBRARY} - ${FFTW_LIBRARIES} ${Boost_LIBRARIES}) + ${FFTW_LIBRARIES} ${Boost_LIBRARIES} + ${STK_LIBRARY} ${ALSA_LIBRARIES} ${}) + + +target_link_libraries(seamulator PRIVATE Threads::Threads) diff --git a/src/dot.cpp b/src/dot.cpp new file mode 100644 index 0000000..d052a24 --- /dev/null +++ b/src/dot.cpp @@ -0,0 +1,43 @@ +#include "dot.h" + +Dot::Dot(const SurfacePoint& point, double frequency) : + m_point{point}, + m_frequency{frequency} +{ + m_noise.noteOn(frequency, 1.0); +// m_noise.setFrequency(frequency); +} + +void Dot::advance(double deltaT) +{ +// std::lock_guard lock{m_mutex}; + + //deltaT *= 10; + + auto posBefore = m_pos; + + m_vel += -std::pow(m_frequency / 440.0, 2.0) * 1.0 * ( m_pos - m_point.getHeight() ) * deltaT; + m_vel -= m_vel * 0.01 * deltaT; + m_pos += m_vel * deltaT; + + const auto absVel = std::fabs(m_vel); + m_maxAbsVel = std::max(absVel, m_maxAbsVel); + + //std::cout << m_pos << std::endl; + + if (posBefore * m_pos < 0.0) { +// // std::cout << m_vel / m_maxVel << std::endl; + m_noise.noteOn(m_pos >= 0 ? m_frequency : m_frequency*0.5, absVel / m_maxAbsVel); +// //m_noise.pluck(absVel / m_maxAbsVel); +// m_noise.controlChange(4, 1.0 * absVel / m_maxAbsVel); + } + +// m_noise.controlChange(128, 128.0 * absVel / m_maxAbsVel); +} + +stk::StkFloat Dot::tick() +{ +// std::lock_guard lock{m_mutex}; + + return m_noise.tick(); +} diff --git a/src/seamulator.cpp b/src/seamulator.cpp index 4211109..aec8da1 100644 --- a/src/seamulator.cpp +++ b/src/seamulator.cpp @@ -27,6 +27,7 @@ #include "sea.h" #include "seaview.h" +#include "synthesizer.h" #include "watersurface.h" const int INIT_WINDOW_POS_X{50}; @@ -40,6 +41,7 @@ const char WINDOW_TITLE[]{"seamulator"}; SeaPtr sea; WaterSurfacePtr surface; std::unique_ptr seaView; +std::unique_ptr synthesizer; struct Settings { double windSpeed; @@ -62,13 +64,14 @@ int main(int argc, char** argv) std::srand(std::time(0)); surface = std::make_shared(settings.latticeSize, - settings.latticeExtend); + settings.latticeExtend); sea = std::make_shared(surface, - settings.windSpeed, - settings.amplitudeFactor); + settings.windSpeed, + settings.amplitudeFactor); seaView = std::make_unique(settings.latticeExtend * 1.5, - INIT_VIEW_AZIMUTH, - INIT_VIEW_ALTITUDE); + INIT_VIEW_AZIMUTH, + INIT_VIEW_ALTITUDE); + synthesizer = std::make_unique(surface); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE); @@ -134,6 +137,7 @@ void glDisplayFunc() seaView->setupView(); sea->update(); surface->draw(); + synthesizer->tick(); glutSwapBuffers(); glutPostRedisplay(); diff --git a/src/synthesizer.cpp b/src/synthesizer.cpp new file mode 100644 index 0000000..b2254c0 --- /dev/null +++ b/src/synthesizer.cpp @@ -0,0 +1,201 @@ +/** + * 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 . + */ + +#include "synthesizer.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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(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(m_surface->at(i, 0), 440.0) ); + //} + + m_dots.emplace_back( std::make_unique(m_surface->at(0, 0), 939.85) ); + m_dots.emplace_back( std::make_unique(m_surface->at(0, 10), 704.09) ); + m_dots.emplace_back( std::make_unique(m_surface->at(0, 20), 469.92) ); + m_dots.emplace_back( std::make_unique(m_surface->at(0, 30), 352.04) ); + m_dots.emplace_back( std::make_unique(m_surface->at(0, 40), 279.42) ); + + m_dots.emplace_back( std::make_unique(m_surface->at(10, 10), 1054.94) ); + m_dots.emplace_back( std::make_unique(m_surface->at(20, 10), 704.09) ); + m_dots.emplace_back( std::make_unique(m_surface->at(30, 10), 527.47) ); + m_dots.emplace_back( std::make_unique(m_surface->at(40, 10), 418.65) ); + m_dots.emplace_back( std::make_unique(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 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(timeNow - m_startTime); + return durationMs.count() / 1000.0; +}