diff --git a/CMakeLists.txt b/CMakeLists.txt index b747649..9759420 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,8 @@ set(CMAKE_CXX_STANDARD 11) include_directories(include) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + find_package(PulseAudio) if(PULSEAUDIO_FOUND) include_directories(${PULSEAUDIO_INCLUDE_DIR}) @@ -15,7 +17,12 @@ if (CURSES_FOUND) include_directories(${CURSES_INCLUDE_DIRS}) endif() -set(SINESYNTH_LIBS ${PULSEAUDIO_LIBRARY} ${CURSES_LIBRARIES} pulse-simple tinfo) +find_package(MPFR) +if (MPFR_FOUND) + include_directories(${MPFR_INCLUDE_DIRS}) +endif() + +set(SINESYNTH_LIBS ${PULSEAUDIO_LIBRARY} ${CURSES_LIBRARIES} ${MPFR_LIBRARIES} pulse-simple tinfo) file(GLOB SOURCES "src/*.cpp") diff --git a/cmake/FindMPFR.cmake b/cmake/FindMPFR.cmake new file mode 100644 index 0000000..ebdbc55 --- /dev/null +++ b/cmake/FindMPFR.cmake @@ -0,0 +1,72 @@ +# Try to find the MPFR library +# See http://www.mpfr.org/ +# +# This module supports requiring a minimum version, e.g. you can do +# find_package(MPFR 2.3.0) +# to require version 2.3.0 to newer of MPFR. +# +# Once done this will define +# +# MPFR_FOUND - system has MPFR lib with correct version +# MPFR_INCLUDES - the MPFR include directory +# MPFR_LIBRARIES - the MPFR library +# MPFR_VERSION - MPFR version + +# Copyright (c) 2006, 2007 Montel Laurent, +# Copyright (c) 2008, 2009 Gael Guennebaud, +# Copyright (c) 2010 Jitse Niesen, +# Copyright (c) 2015 Jack Poulson, +# Redistribution and use is allowed according to the terms of the BSD license. + +find_path(MPFR_INCLUDES NAMES mpfr.h PATHS $ENV{GMPDIR} $ENV{MPFRDIR} + ${INCLUDE_INSTALL_DIR}) + +# Set MPFR_FIND_VERSION to 1.0.0 if no minimum version is specified +if(NOT MPFR_FIND_VERSION) + if(NOT MPFR_FIND_VERSION_MAJOR) + set(MPFR_FIND_VERSION_MAJOR 1) + endif() + if(NOT MPFR_FIND_VERSION_MINOR) + set(MPFR_FIND_VERSION_MINOR 0) + endif() + if(NOT MPFR_FIND_VERSION_PATCH) + set(MPFR_FIND_VERSION_PATCH 0) + endif() + set(MPFR_FIND_VERSION + "${MPFR_FIND_VERSION_MAJOR}.${MPFR_FIND_VERSION_MINOR}.${MPFR_FIND_VERSION_PATCH}") +endif() + +if(MPFR_INCLUDES) + # Query MPFR_VERSION + file(READ "${MPFR_INCLUDES}/mpfr.h" _mpfr_version_header) + + string(REGEX MATCH "define[ \t]+MPFR_VERSION_MAJOR[ \t]+([0-9]+)" + _mpfr_major_version_match "${_mpfr_version_header}") + set(MPFR_MAJOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+MPFR_VERSION_MINOR[ \t]+([0-9]+)" + _mpfr_minor_version_match "${_mpfr_version_header}") + set(MPFR_MINOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+MPFR_VERSION_PATCHLEVEL[ \t]+([0-9]+)" + _mpfr_patchlevel_version_match "${_mpfr_version_header}") + set(MPFR_PATCHLEVEL_VERSION "${CMAKE_MATCH_1}") + + set(MPFR_VERSION + ${MPFR_MAJOR_VERSION}.${MPFR_MINOR_VERSION}.${MPFR_PATCHLEVEL_VERSION}) + + # Check whether found version exceeds minimum required + if(${MPFR_VERSION} VERSION_LESS ${MPFR_FIND_VERSION}) + set(MPFR_VERSION_OK FALSE) + message(STATUS "MPFR version ${MPFR_VERSION} found in ${MPFR_INCLUDES}, " + "but at least version ${MPFR_FIND_VERSION} is required") + else() + set(MPFR_VERSION_OK TRUE) + endif() +endif() + +find_library(MPFR_LIBRARIES mpfr + PATHS $ENV{GMPDIR} $ENV{MPFRDIR} ${LIB_INSTALL_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(MPFR DEFAULT_MSG + MPFR_INCLUDES MPFR_LIBRARIES MPFR_VERSION_OK) +mark_as_advanced(MPFR_INCLUDES MPFR_LIBRARIES) diff --git a/include/SineSynth.h b/include/SineSynth.h index 39b39d2..d2d6b85 100755 --- a/include/SineSynth.h +++ b/include/SineSynth.h @@ -1,12 +1,13 @@ #pragma once #include -#include #include #include #include #include #include +#include +#include using std::shared_ptr; using std::vector; @@ -15,13 +16,18 @@ using std::cos; using std::for_each; using std::sin; -class Generator { +template +class Generator +{ public: - virtual float getSample() = 0; - virtual void getSamples(size_t nSamples, float* buf); + virtual dataType getSample() = 0; + virtual void getSamples(size_t nSamples, dataType *buf); virtual float getFrequency() const; - virtual void setFrequency(float _frequency); + virtual void setFrequency(float frequency); + + virtual float getAmplitude() const; + virtual void setAmplitude(float amplitude); virtual bool isEnabled() const; virtual void enable(); @@ -30,11 +36,11 @@ public: virtual ~Generator(); // disallow copy of virtual interface - Generator(Generator const&) = delete; - Generator& operator=(Generator const&) = delete; + Generator(Generator const &) = delete; + Generator &operator=(Generator const &) = delete; protected: - Generator(uint32_t _sampleRate, float _frequency, float _amplitude); + Generator(uint32_t _sampleRate, float _frequency, float _amplitude = 1); uint32_t sampleRate; float frequency; float amplitude; @@ -43,64 +49,94 @@ protected: private: }; -class SineGenerator : public Generator { +template +class SineGenerator : public Generator +{ public: - SineGenerator(uint32_t _sampleRate, float _frequency, float _amplitude); + SineGenerator(uint32_t _sampleRate, float _frequency, float _amplitude = 1); - float getSample() final; + dataType getSample() final; void setFrequency(float _frequency) final; + void setFrequency(mpfr_t frequency); virtual ~SineGenerator(); private: - float instPhase; - float phaseIncr; + mpfr_t instPhase; + mpfr_t phaseIncr; + + // Constant 2*pi + mpfr_t PI_2; + + mpfr_t tmpA; }; -class NoiseGenerator : public Generator { +template +class NoiseGenerator : public Generator +{ public: - NoiseGenerator(uint32_t _sampleRate, uint8_t rate, float _amplitude); + enum NOISE_TYPE + { + WHITE, + }; - float getSample() final; + NoiseGenerator(uint32_t _sampleRate, NOISE_TYPE type, float _amplitude = 1); + + dataType getSample() final; virtual ~NoiseGenerator(); - uint8_t rate; - static const uint16_t rateTable[]; - private: - int16_t level; - uint16_t sampleCount; + std::mt19937_64 rand_gen; }; -class SampleGenerator : public Generator { +template +class JTestGenerator : public Generator +{ public: - SampleGenerator(uint32_t _sampleRate, uint16_t _playbackRate, float _amplitude, size_t nSamples, const float* samples); - SampleGenerator(uint32_t _sampleRate, uint16_t _playbackRate, float _amplitude, size_t nSamples, const int16_t* samples); + JTestGenerator(uint32_t sampleRate, uint8_t nbits); - float getSample() final; - virtual ~SampleGenerator(); - - uint16_t playbackRate; + dataType getSample() final; + virtual ~JTestGenerator(); private: - vector sample; - float oldest, older, old; - uint32_t counter; - static const uint16_t gaussTable[]; + std::vector loop; + typename std::vector::const_iterator pos; }; -class SineSynth { +template +class SweepGenerator : public Generator +{ +public: + SweepGenerator(uint32_t sampleRate, float startFreq, float endFreq, float length, float amplitude = 1); + virtual ~SweepGenerator(); + + dataType getSample() final; + float getFrequency() const; + + void setAmplitude(float amplitude); + +private: + SineGenerator sg; + mpfr_t startFreq; + mpfr_t curFreq; + mpfr_t freqStep; + mpfr_t endFreq; +}; + +template +class SineSynth +{ public: SineSynth(uint32_t _sampleRate); - void addSynth(shared_ptr); - vector> getSynths() const; + void addSynth(shared_ptr>); + vector>> getSynths() const; void clearSynths(); - void getSamples(size_t nSamples, float* buf); + void getSamples(size_t nSamples, dataType *buf); private: uint32_t sampleRate; - vector> generators; + vector>> generators; }; \ No newline at end of file diff --git a/src/SineSynth.cpp b/src/SineSynth.cpp index 92a4383..4929976 100755 --- a/src/SineSynth.cpp +++ b/src/SineSynth.cpp @@ -1,196 +1,286 @@ #include "SineSynth.h" #include +#include +#include -Generator::Generator(uint32_t _sampleRate, float _frequency, float _amplitude) - : sampleRate(_sampleRate) - , frequency(_frequency) - , amplitude(_amplitude) - , enabled(false) +template +Generator::Generator(uint32_t _sampleRate, float _frequency, float _amplitude) + : sampleRate(_sampleRate), frequency(_frequency), amplitude(_amplitude), enabled(false) { } -Generator::~Generator() {} +template +Generator::~Generator() {} -void Generator::getSamples(size_t nSamples, float* buf) +template +void Generator::getSamples(size_t nSamples, dataType *buf) { - for (; nSamples > 0; nSamples--) { + for (; nSamples > 0; nSamples--) + { *buf++ = getSample(); } } -float Generator::getFrequency() const { return frequency; } +template +float Generator::getFrequency() const { return this->frequency; } -void Generator::setFrequency(float _frequency) { frequency = _frequency; } +template +void Generator::setFrequency(float frequency) { this->frequency = frequency; } -bool Generator::isEnabled() const { return enabled; } +template +float Generator::getAmplitude() const { return this->amplitude; } -void Generator::enable() { enabled = true; } +template +void Generator::setAmplitude(float amplitude) { this->amplitude = amplitude; } -void Generator::disable() { enabled = false; } +template +bool Generator::isEnabled() const { return enabled; } -SineGenerator::SineGenerator( - uint32_t _sampleRate, float _frequency, float _amplitude = 1) - : Generator(_sampleRate, _frequency, _amplitude) - , instPhase(0) +template +void Generator::enable() { enabled = true; } + +template +void Generator::disable() { enabled = false; } + +template +SineGenerator::SineGenerator( + uint32_t _sampleRate, float _frequency, float _amplitude) + : Generator(_sampleRate, _frequency, _amplitude) { + mpfr_init_set_ui(instPhase, 0, MPFR_RNDN); + mpfr_init(phaseIncr); + mpfr_init(PI_2); + mpfr_init(tmpA); + + mpfr_const_pi(PI_2, MPFR_RNDN); + mpfr_mul_ui(PI_2, PI_2, 2, MPFR_RNDN); + setFrequency(_frequency); - enable(); - std::cout << "Constructed SineGenerator with freq " << frequency - << " phaseIncr " << phaseIncr << std::endl; + this->enable(); + std::cout << "Constructed SineGenerator with freq " << this->frequency + << " phaseIncr " << mpfr_get_d(phaseIncr, MPFR_RNDN) << " rads/cycle" << std::endl; } -SineGenerator::~SineGenerator() {} - -float SineGenerator::getSample() +template +SineGenerator::~SineGenerator() { - if (instPhase > 2 * M_PI) - instPhase -= 2 * M_PI; - return cos(instPhase += phaseIncr) * amplitude; + mpfr_clear(instPhase); + mpfr_clear(phaseIncr); + mpfr_clear(PI_2); + mpfr_clear(tmpA); } -void SineGenerator::setFrequency(float _frequency) +template <> +float SineGenerator::getSample() { - frequency = _frequency; - phaseIncr = 2 * M_PI * frequency / sampleRate; + mpfr_add(instPhase, instPhase, phaseIncr, MPFR_RNDN); + if (mpfr_cmp(instPhase, PI_2) > 0) + { + mpfr_sub(instPhase, instPhase, PI_2, MPFR_RNDN); + } + mpfr_cos(tmpA, instPhase, MPFR_RNDN); + mpfr_mul_d(tmpA, tmpA, this->amplitude, MPFR_RNDN); + + return mpfr_get_flt(tmpA, MPFR_RNDN); } -SineSynth::SineSynth(uint32_t _sampleRate) +template <> +uint32_t SineGenerator::getSample() +{ + mpfr_add(instPhase, instPhase, phaseIncr, MPFR_RNDN); + if (mpfr_cmp(instPhase, PI_2) > 0) + { + mpfr_sub(instPhase, instPhase, PI_2, MPFR_RNDN); + } + mpfr_cos(tmpA, instPhase, MPFR_RNDN); + mpfr_mul_d(tmpA, tmpA, this->amplitude, MPFR_RNDN); + mpfr_mul_ui(tmpA, tmpA, (uint32_t)INT32_MAX+1, MPFR_RNDN); + + return (uint32_t)(mpfr_get_si(tmpA, MPFR_RNDN)); +} + +//phaseIncr = 2 * M_PI * this->frequency / this->sampleRate; +template +void SineGenerator::setFrequency(mpfr_t _frequency) +{ + mpfr_mul(phaseIncr, PI_2, _frequency, MPFR_RNDN); + mpfr_div_ui(phaseIncr, phaseIncr, this->sampleRate, MPFR_RNDN); + this->frequency = mpfr_get_flt(_frequency, MPFR_RNDN); +} + +template +void SineGenerator::setFrequency(float _frequency) +{ + mpfr_mul_d(phaseIncr, PI_2, _frequency, MPFR_RNDN); // 2*pi*frequency + mpfr_div_ui(phaseIncr, phaseIncr, this->sampleRate, MPFR_RNDN); // 2*pi*frequency / samplerate + this->frequency = _frequency; +} + +template +SineSynth::SineSynth(uint32_t _sampleRate) : sampleRate(_sampleRate) { } -void SineSynth::addSynth(shared_ptr synth) +template +void SineSynth::addSynth(shared_ptr> synth) { generators.push_back(synth); } -vector> SineSynth::getSynths() const +template +vector>> SineSynth::getSynths() const { return generators; } -void SineSynth::clearSynths() { generators.resize(0); } +template +void SineSynth::clearSynths() { generators.resize(0); } -void SineSynth::getSamples(size_t nSamples, float* buf) +template +void SineSynth::getSamples(size_t nSamples, dataType *buf) { - for (; nSamples > 0; nSamples--) { + for (; nSamples > 0; nSamples--) + { *buf = 0; for_each(std::begin(generators), std::end(generators), - [buf](std::shared_ptr g) { - if (g->isEnabled()) - *buf += g->getSample(); - }); + [buf](std::shared_ptr> g) { + if (g->isEnabled()) + *buf += g->getSample(); + }); buf++; } } -const uint16_t NoiseGenerator::rateTable[] - = { 0, 2048, 1536, 1280, 1024, 768, 640, 512, 384, 320, 256, 192, 160, 128, - 96, 80, 64, 48, 40, 32, 24, 20, 16, 12, 10, 8, 6, 5, 4, 3, 2, 1 }; - -NoiseGenerator::NoiseGenerator( - uint32_t _sampleRate, uint8_t _rate, float _amplitude) - : Generator(_sampleRate, 0, _amplitude) - , level(-0x4000) - , sampleCount(0) - , rate(_rate) +template +NoiseGenerator::NoiseGenerator( + uint32_t _sampleRate, NOISE_TYPE type, float _amplitude) + : Generator(_sampleRate, 0, _amplitude), rand_gen() { - enable(); + this->enable(); } -NoiseGenerator::~NoiseGenerator() {} +template +NoiseGenerator::~NoiseGenerator() {} -float NoiseGenerator::getSample() +template +dataType NoiseGenerator::getSample() { - if (rate == 0) - return 0; - if (sampleCount++ % rateTable[rate] == 0) { - auto retLevel = level; - level = ((level >> 1) & 0x3fff) - | (((level & 0x01) ^ ((level & 0x02) >> 1)) << 14); - return (retLevel / 32768.0f) * amplitude; + return std::uniform_real_distribution(-this->amplitude, std::nextafter(this->amplitude, std::numeric_limits::max()))(rand_gen); +} + +template +JTestGenerator::JTestGenerator(uint32_t sampleRate, uint8_t nbits) : Generator(sampleRate, 0, 1) +{ + size_t loopLength; + if (sampleRate % 250 == 0) + loopLength = sampleRate / 250; + else if (sampleRate % 245 == 0) + loopLength = sampleRate / 245; + else + { + std::cerr << "Unable to generate non-integral fundamental frequency for JTest"; + exit(-1); } - return (level / 32768.0f) * amplitude; + + this->loop.resize(loopLength); + + // Build signal + const auto shift = nbits - 8; + const uint8_t posbase = 0x40; + const uint8_t negbase = 0xc0; + + const auto posval_first = posbase << shift; + const auto negval_first = negbase << shift; + const auto posval_last = posval_first - 1; + const auto negval_last = negval_first - 1; + + for (auto i = 0; i < loopLength / 2; i += 4) + { + this->loop[i + 0] = negval_first; + this->loop[i + 1] = negval_first; + this->loop[i + 2] = posval_first; + this->loop[i + 3] = posval_first; + } + for (auto i = loopLength / 2; i < loopLength; i += 4) + { + this->loop[i + 0] = negval_last; + this->loop[i + 1] = negval_last; + this->loop[i + 2] = posval_last; + this->loop[i + 3] = posval_last; + } + + this->pos = this->loop.begin(); } -const uint16_t SampleGenerator::gaussTable[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, - 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, - 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x6, 0x6, 0x6, - 0x6, 0x7, 0x7, 0x7, 0x8, 0x8, 0x8, 0x9, 0x9, 0x9, 0xa, 0xa, 0xa, 0xb, 0xb, - 0xb, 0xc, 0xc, 0xd, 0xd, 0xe, 0xe, 0xf, 0xf, 0xf, 0x10, 0x10, 0x11, 0x11, - 0x12, 0x13, 0x13, 0x14, 0x14, 0x15, 0x15, 0x16, 0x17, 0x17, 0x18, 0x18, - 0x19, 0x1a, 0x1b, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1f, 0x20, 0x20, 0x21, - 0x22, 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, - 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, - 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x40, 0x41, 0x42, 0x43, 0x45, 0x46, 0x47, - 0x49, 0x4a, 0x4c, 0x4d, 0x4e, 0x50, 0x51, 0x53, 0x54, 0x56, 0x57, 0x59, - 0x5a, 0x5c, 0x5e, 0x5f, 0x61, 0x63, 0x64, 0x66, 0x68, 0x6a, 0x6b, 0x6d, - 0x6f, 0x71, 0x73, 0x75, 0x76, 0x78, 0x7a, 0x7c, 0x7e, 0x80, 0x82, 0x84, - 0x86, 0x89, 0x8b, 0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9a, 0x9c, 0x9f, - 0xa1, 0xa3, 0xa6, 0xa8, 0xab, 0xad, 0xaf, 0xb2, 0xb4, 0xb7, 0xba, 0xbc, - 0xbf, 0xc1, 0xc4, 0xc7, 0xc9, 0xcc, 0xcf, 0xd2, 0xd4, 0xd7, 0xda, 0xdd, - 0xe0, 0xe3, 0xe6, 0xe9, 0xec, 0xef, 0xf2, 0xf5, 0xf8, 0xfb, 0xfe, 0x101, - 0x104, 0x107, 0x10b, 0x10e, 0x111, 0x114, 0x118, 0x11b, 0x11e, 0x122, 0x125, - 0x129, 0x12c, 0x130, 0x133, 0x137, 0x13a, 0x13e, 0x141, 0x145, 0x148, 0x14c, - 0x150, 0x153, 0x157, 0x15b, 0x15f, 0x162, 0x166, 0x16a, 0x16e, 0x172, 0x176, - 0x17a, 0x17d, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x19a, 0x19e, 0x1a2, - 0x1a6, 0x1aa, 0x1ae, 0x1b2, 0x1b7, 0x1bb, 0x1bf, 0x1c3, 0x1c8, 0x1cc, 0x1d0, - 0x1d5, 0x1d9, 0x1dd, 0x1e2, 0x1e6, 0x1eb, 0x1ef, 0x1f3, 0x1f8, 0x1fc, 0x201, - 0x205, 0x20a, 0x20f, 0x213, 0x218, 0x21c, 0x221, 0x226, 0x22a, 0x22f, 0x233, - 0x238, 0x23d, 0x241, 0x246, 0x24b, 0x250, 0x254, 0x259, 0x25e, 0x263, 0x267, - 0x26c, 0x271, 0x276, 0x27b, 0x280, 0x284, 0x289, 0x28e, 0x293, 0x298, 0x29d, - 0x2a2, 0x2a6, 0x2ab, 0x2b0, 0x2b5, 0x2ba, 0x2bf, 0x2c4, 0x2c9, 0x2ce, 0x2d3, - 0x2d8, 0x2dc, 0x2e1, 0x2e6, 0x2eb, 0x2f0, 0x2f5, 0x2fa, 0x2ff, 0x304, 0x309, - 0x30e, 0x313, 0x318, 0x31d, 0x322, 0x326, 0x32b, 0x330, 0x335, 0x33a, 0x33f, - 0x344, 0x349, 0x34e, 0x353, 0x357, 0x35c, 0x361, 0x366, 0x36b, 0x370, 0x374, - 0x379, 0x37e, 0x383, 0x388, 0x38c, 0x391, 0x396, 0x39b, 0x39f, 0x3a4, 0x3a9, - 0x3ad, 0x3b2, 0x3b7, 0x3bb, 0x3c0, 0x3c5, 0x3c9, 0x3ce, 0x3d2, 0x3d7, 0x3dc, - 0x3e0, 0x3e5, 0x3e9, 0x3ed, 0x3f2, 0x3f6, 0x3fb, 0x3ff, 0x403, 0x408, 0x40c, - 0x410, 0x415, 0x419, 0x41d, 0x421, 0x425, 0x42a, 0x42e, 0x432, 0x436, 0x43a, - 0x43e, 0x442, 0x446, 0x44a, 0x44e, 0x452, 0x455, 0x459, 0x45d, 0x461, 0x465, - 0x468, 0x46c, 0x470, 0x473, 0x477, 0x47a, 0x47e, 0x481, 0x485, 0x488, 0x48c, - 0x48f, 0x492, 0x496, 0x499, 0x49c, 0x49f, 0x4a2, 0x4a6, 0x4a9, 0x4ac, 0x4af, - 0x4b2, 0x4b5, 0x4b7, 0x4ba, 0x4bd, 0x4c0, 0x4c3, 0x4c5, 0x4c8, 0x4cb, 0x4cd, - 0x4d0, 0x4d2, 0x4d5, 0x4d7, 0x4d9, 0x4dc, 0x4de, 0x4e0, 0x4e3, 0x4e5, 0x4e7, - 0x4e9, 0x4eb, 0x4ed, 0x4ef, 0x4f1, 0x4f3, 0x4f5, 0x4f6, 0x4f8, 0x4fa, 0x4fb, - 0x4fd, 0x4ff, 0x500, 0x502, 0x503, 0x504, 0x506, 0x507, 0x508, 0x50a, 0x50b, - 0x50c, 0x50d, 0x50e, 0x50f, 0x510, 0x511, 0x511, 0x512, 0x513, 0x514, 0x514, - 0x515, 0x516, 0x516, 0x517, 0x517, 0x517, 0x518, 0x518, 0x518, 0x518, 0x518, - 0x519, 0x519 }; - -SampleGenerator::SampleGenerator(uint32_t _sampleRate, uint16_t _playbackRate, - float _amplitude, size_t nSamples, const float* samples) - : Generator(_sampleRate, 0, _amplitude) - , playbackRate(_playbackRate) - , counter(0) +template +dataType JTestGenerator::getSample() { - oldest = older = old = 0; - enable(); - sample.resize(nSamples); - for (auto i = std::begin(sample); i != std::end(sample); i++) - *i = *samples++; + if (this->pos == this->loop.end()) + this->pos = this->loop.begin(); + return *(this->pos++); } -SampleGenerator::~SampleGenerator() {} +template +JTestGenerator::~JTestGenerator() {} -float SampleGenerator::getSample() +template +SweepGenerator::SweepGenerator(uint32_t sampleRate, float startFreq, float endFreq, float length, float amplitude) : Generator(sampleRate, startFreq, amplitude), + sg(sampleRate, startFreq, amplitude) { - counter += playbackRate; - if ((counter >> 12) > sample.size()) - counter &= 0xfff; - - auto gaussIndex = (counter >> 4) & 0x3f; - float out = ((gaussTable[0xff - gaussIndex] / 32768.0f * oldest) / 1024); - out += ((gaussTable[0x1ff - gaussIndex] / 32768.0f * older) / 1024); - out += ((gaussTable[0x100 + gaussIndex] / 32768.0f * old) / 1024); - out += ((gaussTable[0x000 + gaussIndex] / 32768.0f * sample[counter>>12]) / 1024); - out /= 2; + this->enable(); + mpfr_init(freqStep); + mpfr_init_set_d(this->startFreq, startFreq, MPFR_RNDN); + mpfr_init_set(curFreq, this->startFreq, MPFR_RNDN); + mpfr_init_set_d(this->endFreq, endFreq, MPFR_RNDN); - oldest = older; - older = old; - old = sample[counter>>12]; + // Linear frequency step = ((endFreq - startFreq) / length) / sampleRate + mpfr_sub(freqStep, this->endFreq, this->startFreq, MPFR_RNDN); + mpfr_div_d(freqStep, freqStep, length, MPFR_RNDN); + mpfr_div_ui(freqStep, freqStep, sampleRate, MPFR_RNDN); +} - return out * amplitude; -} \ No newline at end of file +template +SweepGenerator::~SweepGenerator() +{ + mpfr_clear(startFreq); + mpfr_clear(curFreq); + mpfr_clear(endFreq); + mpfr_clear(freqStep); +} + +template +dataType SweepGenerator::getSample() +{ + dataType sample = sg.getSample(); + mpfr_add(curFreq, curFreq, freqStep, MPFR_RNDN); + if (mpfr_cmp(curFreq, endFreq) > 0) + { + mpfr_set(curFreq, startFreq, MPFR_RNDN); + } + sg.setFrequency(curFreq); + return sample; +} + +template +float SweepGenerator::getFrequency() const +{ + return mpfr_get_flt(curFreq, MPFR_RNDN); +} + +template +void SweepGenerator::setAmplitude(float amplitude) { + sg.setAmplitude(amplitude); + this->amplitude = amplitude; +} + +template class NoiseGenerator; +template class SineGenerator; +template class SweepGenerator; +template class SineSynth; + +template class SineSynth; +template class SineGenerator; +template class JTestGenerator; +template class SweepGenerator; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index fedc67c..39f6db5 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,9 @@ -#include +#include #include #include #include +#include +#include #include #include @@ -9,57 +11,17 @@ #include "SineSynth.h" -WINDOW* win; +WINDOW *win; -constexpr int SAMPLERATE = 32000; -constexpr size_t BUFSIZE = 64; +constexpr int SAMPLERATE = 48000; +constexpr size_t BUFSIZE = (48000/1000)*4; -void majorChord(SineSynth& synth, const double root) -{ - wprintw(win, "majorChord root: %f\n", root); - if (synth.getSynths().size() != 3) { - synth.clearSynths(); - auto sg = std::make_shared(SAMPLERATE, root, 0.1); - synth.addSynth(sg); - sg = std::make_shared( - SAMPLERATE, root * pow(2, 4.0 / 12.0), 0.1); - synth.addSynth(sg); - sg = std::make_shared( - SAMPLERATE, root * pow(2, 7.0 / 12.0), 0.1); - synth.addSynth(sg); - } else { - auto synths = synth.getSynths(); - synths[0]->setFrequency(root); - synths[1]->setFrequency(root * pow(2, 4.0 / 12.0)); - synths[2]->setFrequency(root * pow(2, 7.0 / 12.0)); - } -} +#define SAMPTYPE uint32_t -void minorChord(SineSynth& synth, const double root) -{ - wprintw(win, "minorChord root: %f\n", root); - if (synth.getSynths().size() != 3) { - synth.clearSynths(); - auto sg = std::make_shared(SAMPLERATE, root, 0.1); - synth.addSynth(sg); - sg = std::make_shared( - SAMPLERATE, root * pow(2, 3.0 / 12.0), 0.1); - synth.addSynth(sg); - sg = std::make_shared( - SAMPLERATE, root * pow(2, 7.0 / 12.0), 0.1); - synth.addSynth(sg); - } else { - auto synths = synth.getSynths(); - synths[0]->setFrequency(root); - synths[1]->setFrequency(root * pow(2, 3.0 / 12.0)); - synths[2]->setFrequency(root * pow(2, 7.0 / 12.0)); - } -} - -WINDOW* +WINDOW * init_curses() { - WINDOW* win = initscr(); + WINDOW *win = initscr(); nodelay(win, TRUE); keypad(win, TRUE); cbreak(); @@ -67,24 +29,25 @@ init_curses() return win; } -pa_simple* +pa_simple * init_pulse() { - static const pa_sample_spec ss = { .format = PA_SAMPLE_FLOAT32, - .rate = SAMPLERATE, - .channels = 1 }; + static const pa_sample_spec ss = {.format = PA_SAMPLE_S32NE, + .rate = SAMPLERATE, + .channels = 1}; int error = 0; - pa_simple* s = pa_simple_new(NULL, - "sinesynth", - PA_STREAM_PLAYBACK, - NULL, - "playback", - &ss, - NULL, - NULL, - &error); - if (error) { + pa_simple *s = pa_simple_new(NULL, + "sinesynth", + PA_STREAM_PLAYBACK, + NULL, + "playback", + &ss, + NULL, + NULL, + &error); + if (error) + { std::cerr << "Error initializing PulseAudio: " << pa_strerror(error) << " (" << error << ")" << std::endl; exit(-1); @@ -93,81 +56,48 @@ init_pulse() return s; } -const float testSample[] = { 0.0, - 0.0980171403295606, - 0.19509032201612825, - 0.29028467725446233, - 0.3826834323650898, - 0.47139673682599764, - 0.5555702330196022, - 0.6343932841636455, - 0.7071067811865475, - 0.773010453362737, - 0.8314696123025452, - 0.8819212643483549, - 0.9238795325112867, - 0.9569403357322089, - 0.9807852804032304, - 0.9951847266721968, - 1.0, - 0.9951847266721969, - 0.9807852804032304, - 0.9569403357322089, - 0.9238795325112867, - 0.881921264348355, - 0.8314696123025455, - 0.7730104533627371, - 0.7071067811865476, - 0.6343932841636455, - 0.5555702330196022, - 0.47139673682599786, - 0.3826834323650899, - 0.2902846772544624, - 0.1950903220161286, - 0.09801714032956083, - 1.2246467991473532e-16, - -0.09801714032956059, - -0.19509032201612836, - -0.2902846772544621, - -0.38268343236508967, - -0.47139673682599764, - -0.555570233019602, - -0.6343932841636453, - -0.7071067811865475, - -0.7730104533627367, - -0.8314696123025452, - -0.8819212643483549, - -0.9238795325112865, - -0.9569403357322088, - -0.9807852804032303, - -0.9951847266721969, - -1.0, - -0.9951847266721969, - -0.9807852804032304, - -0.9569403357322089, - -0.9238795325112866, - -0.881921264348355, - -0.8314696123025455, - -0.7730104533627369, - -0.7071067811865477, - -0.6343932841636459, - -0.5555702330196022, - -0.4713967368259979, - -0.3826834323650904, - -0.2902846772544625, - -0.19509032201612872, - -0.0980171403295605 }; +template +void smpte_imd_test(SineSynth &synth) { + synth.clearSynths(); + synth.addSynth(std::make_shared>( + SAMPLERATE, 60, 0.8 + )); + synth.addSynth(std::make_shared>( + SAMPLERATE, 7000, 0.2 + )); +} -int main(int argc, char* argv[]) +template +void ccif_imd_test(SineSynth &synth, float amplitude) { + assert(amplitude >= 0 && amplitude <= 1); + synth.clearSynths(); + synth.addSynth(std::make_shared>( + SAMPLERATE, 19000, amplitude / 2 + )); + synth.addSynth(std::make_shared>( + SAMPLERATE, 20000, amplitude / 2 + )); +} + +int main(int argc, char *argv[]) { - SineSynth synth(SAMPLERATE); - auto noise = std::make_shared(SAMPLERATE, 0x1f, 0.1); - //synth.addSynth(noise); - auto sampled = std::make_shared(SAMPLERATE, 0x1000, 0.1, sizeof(testSample)/sizeof(float), testSample); - synth.addSynth(sampled); - - sampled->enable(); - noise->disable(); + SineSynth synth(SAMPLERATE); + auto gen = std::make_shared>(SAMPLERATE, 1000, 1); + synth.addSynth(gen); + SAMPTYPE buf2[BUFSIZE]; + synth.getSamples(BUFSIZE, buf2); + for (auto i = 0; i < BUFSIZE; i++) + std::cout << i << "," << (int32_t)buf2[i] << std::endl; + + return 0; + + // auto gen = std::make_shared>(SAMPLERATE, 10, 10000, 5, 1); + // synth.addSynth(gen); + + // auto gen2 = std::make_shared>(SAMPLERATE, 32); + // synth.addSynth(gen2); + + //smpte_imd_test(synth); auto s = init_pulse(); auto win = init_curses(); @@ -177,31 +107,44 @@ int main(int argc, char* argv[]) int error = 0; pa_usec_t latency = pa_simple_get_latency(s, &error); wprintw(win, - "Error: %s (%d) Latency: %d usec\n", - pa_strerror(error), - error, - latency); + "Error: %s (%d) Latency: %d usec\n", + pa_strerror(error), + error, + latency); - float buf[BUFSIZE]; + SAMPTYPE buf[BUFSIZE]; long long i; mvwprintw(win, 2, 0, "Loops: "); - mvwprintw(win, 3, 0, "Rate: "); - for (i = 0;; i++) { + mvwprintw(win, 3, 0, "Freq: "); + mvwprintw(win, 4, 0, "Ampl: "); + for (i = 0;; i++) + { auto c = getch(); - switch (c) { + switch (c) + { case KEY_UP: - sampled->playbackRate+=8; - sampled->playbackRate &= 0x3fff; + gen->setFrequency(gen->getFrequency() + 100); break; case KEY_DOWN: - sampled->playbackRate-=8; - sampled->playbackRate &= 0x3fff; + gen->setFrequency(gen->getFrequency() - 100); break; + case '+': + gen->setAmplitude(gen->getAmplitude() + 0.1); + break; + case '-': + gen->setAmplitude(gen->getAmplitude() - 0.1); + break; + case 'q': + case 'Q': + endwin(); + return 0; } + synth.getSamples(BUFSIZE, buf); pa_simple_write(s, buf, sizeof(buf), &error); - mvwprintw(win, 2, 8, "%d", i); - mvwprintw(win, 3, 7, "%2x", sampled->playbackRate); + mvwprintw(win, 2, 8, "%6d", i); + mvwprintw(win, 3, 8, "%6.0f", gen->getFrequency()); + mvwprintw(win, 4, 9, "%1.3f (%f)", gen->getAmplitude(), 20 * log10(gen->getAmplitude()/1)); } endwin();