]> git.treefish.org Git - seamulator.git/commitdiff
make some noise
authorAlexander Schmidt <alex@treefish.org>
Mon, 22 Feb 2021 10:59:56 +0000 (11:59 +0100)
committerAlexander Schmidt <alex@treefish.org>
Mon, 22 Feb 2021 10:59:56 +0000 (11:59 +0100)
.gitignore
cmake_modules/FindSTK.cmake [new file with mode: 0644]
include/dot.h [new file with mode: 0644]
include/synthesizer.h [new file with mode: 0644]
include/watersurfacefwd.h
src/CMakeLists.txt
src/dot.cpp [new file with mode: 0644]
src/seamulator.cpp
src/synthesizer.cpp [new file with mode: 0644]

index 4aaa26ff9c7a0cf5627b9880b39b6e5343e3614a..74d0bce2f22913e29d475cc081465c760f118c52 100644 (file)
@@ -1,2 +1,3 @@
 ./build/
 build/
+*~
diff --git a/cmake_modules/FindSTK.cmake b/cmake_modules/FindSTK.cmake
new file mode 100644 (file)
index 0000000..a919405
--- /dev/null
@@ -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 (file)
index 0000000..28792ff
--- /dev/null
@@ -0,0 +1,33 @@
+#include <mutex>
+
+#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 (file)
index 0000000..5d38053
--- /dev/null
@@ -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 <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;
+
+};
index 7568abc11aa3cc906d1aa936cca436c4a5048c84..3f9e70626fb8dbff03033c97e610abf00c52e2c6 100644 (file)
@@ -22,4 +22,6 @@
 #include <memory>
 
 class WaterSurface;
+
 using WaterSurfacePtr = std::shared_ptr<WaterSurface>;
+using ConstWaterSurfacePtr = std::shared_ptr<const WaterSurface>;
index f859744f9c4dd88e473db7e8b94b794e9660b0ca..75990a0ab0033f2764f255f5a5c31ea83349d8bf 100644 (file)
@@ -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 (file)
index 0000000..d052a24
--- /dev/null
@@ -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<std::mutex> 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<std::mutex> lock{m_mutex};
+
+    return m_noise.tick();
+}
index 42111091113a0a384c7427a540f83d923d0c3417..aec8da13377c6053d2a865c84c4b8e31a70a4a89 100644 (file)
@@ -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> seaView;
+std::unique_ptr<Synthesizer> synthesizer;
 
 struct Settings {
   double windSpeed;
@@ -62,13 +64,14 @@ int main(int argc, char** argv)
 
   std::srand(std::time(0));
   surface = std::make_shared<WaterSurface>(settings.latticeSize,
-                                          settings.latticeExtend);
+                                           settings.latticeExtend);
   sea = std::make_shared<Sea>(surface,
-                             settings.windSpeed,
-                             settings.amplitudeFactor);
+                              settings.windSpeed,
+                              settings.amplitudeFactor);
   seaView = std::make_unique<SeaView>(settings.latticeExtend * 1.5,
-                                     INIT_VIEW_AZIMUTH,
-                                     INIT_VIEW_ALTITUDE);
+                                      INIT_VIEW_AZIMUTH,
+                                      INIT_VIEW_ALTITUDE);
+  synthesizer = std::make_unique<Synthesizer>(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 (file)
index 0000000..b2254c0
--- /dev/null
@@ -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 <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( &parameters, 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;
+}