From 15fd253130351f223b19cd4a28ba8c69abd272b1 Mon Sep 17 00:00:00 2001 From: Keenan Tims Date: Wed, 8 Jan 2020 01:53:29 -0800 Subject: [PATCH] Fixed many bugs, IMD tests working, wav writing * Fixed many sample generation bugs * IMD tests now generating as expected * WAV writing fundamentals in place --- .gitignore | 2 + CMakeLists.txt | 9 +- include/SineSynth.h | 86 ++++++----- include/WavWriter.h | 137 +++++++++++++++++ src/SineSynth.cpp | 367 ++++++++++++++++++++++++++++---------------- src/main.cpp | 156 +++++++++++-------- 6 files changed, 516 insertions(+), 241 deletions(-) create mode 100644 .gitignore create mode 100644 include/WavWriter.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8726743 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +*.wav \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 9759420..18e5187 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.6) project(sinesynth) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) include_directories(include) @@ -17,12 +17,17 @@ if (CURSES_FOUND) include_directories(${CURSES_INCLUDE_DIRS}) endif() +find_package(Boost) +if (Boost_FOUND) + include_directories(${BOOST_INCLUDE_DIRS}) +endif() + find_package(MPFR) if (MPFR_FOUND) include_directories(${MPFR_INCLUDE_DIRS}) endif() -set(SINESYNTH_LIBS ${PULSEAUDIO_LIBRARY} ${CURSES_LIBRARIES} ${MPFR_LIBRARIES} pulse-simple tinfo) +set(SINESYNTH_LIBS ${PULSEAUDIO_LIBRARY} ${CURSES_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} pulse-simple tinfo) file(GLOB SOURCES "src/*.cpp") diff --git a/include/SineSynth.h b/include/SineSynth.h index d2d6b85..a7bb516 100755 --- a/include/SineSynth.h +++ b/include/SineSynth.h @@ -4,24 +4,29 @@ #include #include #include +#include #include #include -#include -#include + +#include +#include +#include using std::shared_ptr; using std::vector; -using std::cos; using std::for_each; -using std::sin; -template -class Generator -{ +using namespace boost::multiprecision; + +typedef cpp_bin_float_double MP_TYPE; + +const MP_TYPE two_pi = boost::math::constants::two_pi(); + +template class Generator { public: virtual dataType getSample() = 0; - virtual void getSamples(size_t nSamples, dataType *buf); + virtual void getSamples(size_t nSamples, dataType* buf); virtual float getFrequency() const; virtual void setFrequency(float frequency); @@ -29,6 +34,9 @@ public: virtual float getAmplitude() const; virtual void setAmplitude(float amplitude); + virtual float getAmplitudeDb() const; + virtual void setAmplitudeDb(float amplitude); + virtual bool isEnabled() const; virtual void enable(); virtual void disable(); @@ -36,8 +44,8 @@ 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 = 1); @@ -49,35 +57,37 @@ protected: private: }; -template -class SineGenerator : public Generator -{ +template class SineGenerator : public Generator { public: - SineGenerator(uint32_t _sampleRate, float _frequency, float _amplitude = 1); + SineGenerator(uint32_t _sampleRate, float _frequency, float _amplitude = 1, + bool caching = true); dataType getSample() final; void setFrequency(float _frequency) final; - void setFrequency(mpfr_t frequency); + void setFrequency(MP_TYPE frequency); + + void setAmplitude(float amplitude); virtual ~SineGenerator(); private: - mpfr_t instPhase; - mpfr_t phaseIncr; + MP_TYPE instPhase; + MP_TYPE phaseIncr; - // Constant 2*pi - mpfr_t PI_2; + void dirtyCache(); + dataType unCachedGetSample(); + dataType cachedGetSample(); - mpfr_t tmpA; + vector sampleCache; + typename vector::const_iterator cachePos; + const bool caching; + bool cacheInvalid; }; -template -class NoiseGenerator : public Generator -{ +template class NoiseGenerator : public Generator { public: - enum NOISE_TYPE - { + enum NOISE_TYPE { WHITE, }; @@ -90,9 +100,7 @@ private: std::mt19937_64 rand_gen; }; -template -class JTestGenerator : public Generator -{ +template class JTestGenerator : public Generator { public: JTestGenerator(uint32_t sampleRate, uint8_t nbits); @@ -104,29 +112,27 @@ private: typename std::vector::const_iterator pos; }; -template -class SweepGenerator : public Generator -{ +template class SweepGenerator : public Generator { public: - SweepGenerator(uint32_t sampleRate, float startFreq, float endFreq, float length, float amplitude = 1); + 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); + void setFrequency(float frequency); private: SineGenerator sg; - mpfr_t startFreq; - mpfr_t curFreq; - mpfr_t freqStep; - mpfr_t endFreq; + float length; + MP_TYPE startFreq; + MP_TYPE curFreq; + MP_TYPE freqStep; }; -template -class SineSynth -{ +template class SineSynth { public: SineSynth(uint32_t _sampleRate); @@ -134,7 +140,7 @@ public: vector>> getSynths() const; void clearSynths(); - void getSamples(size_t nSamples, dataType *buf); + void getSamples(size_t nSamples, dataType* buf); private: uint32_t sampleRate; diff --git a/include/WavWriter.h b/include/WavWriter.h new file mode 100644 index 0000000..a7940de --- /dev/null +++ b/include/WavWriter.h @@ -0,0 +1,137 @@ +#pragma once + +#include + +#include +#include +#include +#include + +using std::array; +using std::ofstream; +using std::ostream; +using std::string; + +class WavWriter { +public: + enum FormatCode : uint16_t { + WAVE_FORMAT_PCM = 0x01, + WAVE_FORMAT_IEEE_FLOAT = 0x03, + WAVE_FORMAT_ALAW = 0x06, + WAVE_FORMAT_MULAW = 0x07, + WAVE_FORMAT_EXTENSIBLE = 0xfffe, + }; + + WavWriter(const string& filename, WavWriter::FormatCode format, + uint16_t nChannels, uint16_t nBitsPerSample, uint32_t nSamplesPerSec) + : f(filename, std::ofstream::binary) + , format(format) + , nChannels(nChannels) + , nBitsPerSample(nBitsPerSample) + , nSamplesPerSec(nSamplesPerSec) + { + f.write("RIFF\0\0\0\0WAVE", 12); + updateHeader(); + f.seekp(0, std::ios_base::end); + }; + ~WavWriter() { f.close(); } + + template void writeSamples(const T data[], size_t n) + { + f.write(reinterpret_cast(data), n); + updateHeader(); + }; + + void close(); + +private: + uint32_t dataSize; + FormatCode format; + uint16_t nChannels; + uint32_t nSamplesPerSec; + uint16_t nBitsPerSample; + ofstream f; + + void updateHeader() + { + auto curPos = f.tellp(); + dataSize = curPos - std::streamoff(8); + f.seekp(0); + // Update main header total size + f.write("RIFF", 4); + f.write(reinterpret_cast(&dataSize), sizeof(dataSize)); + f.write("WAVE", 4); + // Write new format and data size + WavWriter::fmtChunk fmt(this); + fmt.writeTo(f); + f.write("data", 4); + uint32_t ckSize = curPos - f.tellp(); + f.write(reinterpret_cast(&ckSize), sizeof(ckSize)); + + f.seekp(curPos); + }; + + struct Chunk { + public: + char ckID[4]; + + protected: + Chunk(WavWriter* parent, const char* ckID) + : parent(parent) + , ckID{ ckID[0], ckID[1], ckID[2], ckID[3] } {}; + virtual void writeTo(ostream& of) = 0; + + WavWriter* parent; + }; + + struct fmtChunk : public Chunk { + fmtChunk(WavWriter* parent) + : Chunk(parent, "fmt ") + {}; + + uint32_t cksize; + uint16_t cbSize = 0; + uint32_t dwChannelMask; + char SubFormat[16]; + + void writeTo(ostream& of) + { + of.write(this->ckID, sizeof(this->ckID)); + cksize = parent->format == WAVE_FORMAT_PCM ? 16 : 18; + of.write(reinterpret_cast(&cksize), sizeof(cksize)); + of.write(reinterpret_cast(&parent->format), + sizeof(parent->format)); + of.write(reinterpret_cast(&parent->nChannels), + sizeof(parent->nChannels)); + of.write(reinterpret_cast(&parent->nSamplesPerSec), + sizeof(parent->nSamplesPerSec)); + uint32_t byteRate = parent->nSamplesPerSec * parent->nChannels + * parent->nBitsPerSample / 8; + of.write( + reinterpret_cast(&byteRate), sizeof(byteRate)); + uint16_t blockAlign + = parent->nChannels * parent->nBitsPerSample / 8; + of.write( + reinterpret_cast(&blockAlign), sizeof(blockAlign)); + of.write(reinterpret_cast(&parent->nBitsPerSample), + sizeof(parent->nBitsPerSample)); + if (parent->format != WAVE_FORMAT_PCM) + of.write( + reinterpret_cast(&cbSize), sizeof(cbSize)); + if (parent->format == WAVE_FORMAT_EXTENSIBLE) { + of.write(reinterpret_cast(&parent->nBitsPerSample), + sizeof(parent->nBitsPerSample)); + of.write(reinterpret_cast(&dwChannelMask), + sizeof(dwChannelMask)); + of.write(reinterpret_cast(SubFormat), + sizeof(SubFormat)); + } + } + }; + struct factChunk : public Chunk { + factChunk(WavWriter* parent) + : Chunk(parent, "fact"){}; + uint32_t cksize = 4; + uint32_t dwSampleLength(); + }; +}; \ No newline at end of file diff --git a/src/SineSynth.cpp b/src/SineSynth.cpp index 4929976..1381926 100755 --- a/src/SineSynth.cpp +++ b/src/SineSynth.cpp @@ -1,125 +1,221 @@ #include "SineSynth.h" -#include -#include #include +#include +#include +#include +#include + +#include template -Generator::Generator(uint32_t _sampleRate, float _frequency, float _amplitude) - : sampleRate(_sampleRate), frequency(_frequency), amplitude(_amplitude), enabled(false) +Generator::Generator( + uint32_t _sampleRate, float _frequency, float _amplitude) + : sampleRate(_sampleRate) + , frequency(_frequency) + , amplitude(_amplitude) + , enabled(true) { } -template -Generator::~Generator() {} +template Generator::~Generator() {} template -void Generator::getSamples(size_t nSamples, dataType *buf) +void Generator::getSamples(size_t nSamples, dataType* buf) { - for (; nSamples > 0; nSamples--) - { + for (; nSamples > 0; nSamples--) { *buf++ = getSample(); } } -template -float Generator::getFrequency() const { return this->frequency; } +template float Generator::getFrequency() const +{ + return this->frequency; +} template -void Generator::setFrequency(float frequency) { this->frequency = frequency; } +void Generator::setFrequency(float frequency) +{ + this->frequency = frequency; +} + +template float Generator::getAmplitude() const +{ + return this->amplitude; +} + +template float Generator::getAmplitudeDb() const +{ + return 10 * log10(this->getAmplitude()); +} template -float Generator::getAmplitude() const { return this->amplitude; } +void Generator::setAmplitude(float amplitude) +{ + assert(amplitude <= 1); + this->amplitude = amplitude; +} template -void Generator::setAmplitude(float amplitude) { this->amplitude = amplitude; } +void Generator::setAmplitudeDb(float amplitude) +{ + assert(amplitude <= 0); + this->setAmplitude(pow(10, amplitude / 10)); +} -template -bool Generator::isEnabled() const { return enabled; } +template bool Generator::isEnabled() const +{ + return enabled; +} -template -void Generator::enable() { enabled = true; } +template void Generator::enable() +{ + enabled = true; +} -template -void Generator::disable() { enabled = false; } +template void Generator::disable() +{ + enabled = false; +} template SineGenerator::SineGenerator( - uint32_t _sampleRate, float _frequency, float _amplitude) + uint32_t _sampleRate, float _frequency, float _amplitude, bool caching) : Generator(_sampleRate, _frequency, _amplitude) + , caching(caching) + , cacheInvalid(true) { - 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); - this->enable(); - std::cout << "Constructed SineGenerator with freq " << this->frequency - << " phaseIncr " << mpfr_get_d(phaseIncr, MPFR_RNDN) << " rads/cycle" << std::endl; + // std::cout << "Constructed SineGenerator with freq " << this->frequency + // << " phaseIncr " << phaseIncr << " rads/cycle" << std::endl; } -template -SineGenerator::~SineGenerator() -{ - mpfr_clear(instPhase); - mpfr_clear(phaseIncr); - mpfr_clear(PI_2); - mpfr_clear(tmpA); -} +template SineGenerator::~SineGenerator() {} -template <> -float SineGenerator::getSample() +template void SineGenerator::dirtyCache() { - mpfr_add(instPhase, instPhase, phaseIncr, MPFR_RNDN); - if (mpfr_cmp(instPhase, PI_2) > 0) - { - mpfr_sub(instPhase, instPhase, PI_2, MPFR_RNDN); + if (caching) { + cacheInvalid = true; + sampleCache.clear(); + instPhase = 0; } - mpfr_cos(tmpA, instPhase, MPFR_RNDN); - mpfr_mul_d(tmpA, tmpA, this->amplitude, MPFR_RNDN); - - return mpfr_get_flt(tmpA, MPFR_RNDN); } -template <> -uint32_t SineGenerator::getSample() +template +dataType SineGenerator::unCachedGetSample() { - mpfr_add(instPhase, instPhase, phaseIncr, MPFR_RNDN); - if (mpfr_cmp(instPhase, PI_2) > 0) - { - mpfr_sub(instPhase, instPhase, PI_2, MPFR_RNDN); + static MP_TYPE sample; + dataType val; + + sample = sin(instPhase) * this->getAmplitude(); + + if constexpr (std::is_floating_point::value) + val = sample.convert_to(); + else if constexpr (std::is_signed::value) { + sample *= std::numeric_limits::max(); + val = sample.convert_to(); + } else { + if (sample > 0) { + sample *= std::numeric_limits::max() / 2; + val = sample.convert_to(); + } else { + sample *= std::numeric_limits::max() / 2; + val = sample.convert_to() * -1; + } } - 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)); + instPhase += phaseIncr; + + return val; } -//phaseIncr = 2 * M_PI * this->frequency / this->sampleRate; -template -void SineGenerator::setFrequency(mpfr_t _frequency) +template dataType SineGenerator::cachedGetSample() { - 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); + static MP_TYPE sample; + + if (!cacheInvalid) { + if (cachePos == sampleCache.end()) + cachePos = sampleCache.begin(); + return *cachePos++; + } + + sample = sin(instPhase) * this->getAmplitude(); + + dataType val; + + if constexpr (std::is_floating_point::value) + val = sample.convert_to(); + else if constexpr (std::is_signed::value) { + sample *= std::numeric_limits::max(); + val = sample.convert_to(); + } else { + if (sample > 0) { + sample *= std::numeric_limits::max() / 2; + val = sample.convert_to(); + } else { + sample *= std::numeric_limits::max() / 2; + val = sample.convert_to() * -1; + } + } + + // Advance the phase for the next loop + instPhase += phaseIncr; + + // If we're about to store a zero crossing and rising slope, we are at a + // cycle boundary and a loop + bool doneCaching = false; + if (!sampleCache.empty()) { + if constexpr (std::is_floating_point::value) + doneCaching = val == 0 && 0 > sampleCache.back(); + else + doneCaching = val == 0 + && 0 > static_cast::type>( + sampleCache.back()); + } + + if (doneCaching) { + cacheInvalid = false; + cachePos = sampleCache.begin(); + return *cachePos++; + } + + // Only store the samples we send + sampleCache.push_back(val); + + return val; } -template -void SineGenerator::setFrequency(float _frequency) +template dataType SineGenerator::getSample() { - 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; + return (caching) ? cachedGetSample() : unCachedGetSample(); } template -SineSynth::SineSynth(uint32_t _sampleRate) - : sampleRate(_sampleRate) +void SineGenerator::setFrequency(MP_TYPE frequency) +{ + phaseIncr = (two_pi * frequency) / this->sampleRate; + this->frequency = frequency.convert_to(); + dirtyCache(); +} + +template +void SineGenerator::setFrequency(float frequency) +{ + phaseIncr = (two_pi * frequency) / this->sampleRate; + this->frequency = frequency; + dirtyCache(); +} + +template +void SineGenerator::setAmplitude(float amplitude) +{ + Generator::setAmplitude(amplitude); + dirtyCache(); +} + +template +SineSynth::SineSynth(uint32_t sampleRate) + : sampleRate(sampleRate) { } @@ -135,20 +231,21 @@ vector>> SineSynth::getSynths() const return generators; } -template -void SineSynth::clearSynths() { generators.resize(0); } +template void SineSynth::clearSynths() +{ + generators.resize(0); +} template -void SineSynth::getSamples(size_t nSamples, dataType *buf) +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++; } } @@ -156,31 +253,41 @@ void SineSynth::getSamples(size_t nSamples, dataType *buf) template NoiseGenerator::NoiseGenerator( uint32_t _sampleRate, NOISE_TYPE type, float _amplitude) - : Generator(_sampleRate, 0, _amplitude), rand_gen() + : Generator(_sampleRate, 0, _amplitude) + , rand_gen() { - this->enable(); +} + +template NoiseGenerator::~NoiseGenerator() {} + +template dataType NoiseGenerator::getSample() +{ + if constexpr (std::is_floating_point::value) { + auto sample + = std::uniform_real_distribution(-this->getAmplitude(), + std::nextafter(this->getAmplitude(), + std::numeric_limits::max()))(rand_gen); + return sample; + } else { + auto sample = std::uniform_int_distribution( + std::numeric_limits::min(), + std::numeric_limits::max())(rand_gen); + return sample; + } } template -NoiseGenerator::~NoiseGenerator() {} - -template -dataType NoiseGenerator::getSample() -{ - 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) +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"; + else { + std::cerr << "Unable to generate non-integral fundamental frequency " + "for JTest"; exit(-1); } @@ -196,15 +303,13 @@ JTestGenerator::JTestGenerator(uint32_t sampleRate, uint8_t nbits) : G const auto posval_last = posval_first - 1; const auto negval_last = negval_first - 1; - for (auto i = 0; i < loopLength / 2; i += 4) - { + 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) - { + 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; @@ -214,51 +319,36 @@ JTestGenerator::JTestGenerator(uint32_t sampleRate, uint8_t nbits) : G this->pos = this->loop.begin(); } -template -dataType JTestGenerator::getSample() +template dataType JTestGenerator::getSample() { if (this->pos == this->loop.end()) this->pos = this->loop.begin(); return *(this->pos++); } -template -JTestGenerator::~JTestGenerator() {} +template JTestGenerator::~JTestGenerator() {} template -SweepGenerator::SweepGenerator(uint32_t sampleRate, float startFreq, float endFreq, float length, float amplitude) : Generator(sampleRate, startFreq, amplitude), - sg(sampleRate, startFreq, amplitude) +SweepGenerator::SweepGenerator(uint32_t sampleRate, float startFreq, + float endFreq, float length, float amplitude) + : Generator(sampleRate, endFreq, amplitude) + , sg(sampleRate, startFreq, amplitude, false) + , startFreq(startFreq) + , curFreq(startFreq) + , length(length) { - 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); - // 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); + freqStep = ((this->getFrequency() - startFreq) / length) / sampleRate; } -template -SweepGenerator::~SweepGenerator() -{ - mpfr_clear(startFreq); - mpfr_clear(curFreq); - mpfr_clear(endFreq); - mpfr_clear(freqStep); -} +template SweepGenerator::~SweepGenerator() {} -template -dataType SweepGenerator::getSample() +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); - } + curFreq += freqStep; + if (curFreq > this->getFrequency()) + curFreq = startFreq; sg.setFrequency(curFreq); return sample; } @@ -266,20 +356,31 @@ dataType SweepGenerator::getSample() template float SweepGenerator::getFrequency() const { - return mpfr_get_flt(curFreq, MPFR_RNDN); + return Generator::getFrequency(); } template -void SweepGenerator::setAmplitude(float amplitude) { +void SweepGenerator::setAmplitude(float amplitude) +{ sg.setAmplitude(amplitude); this->amplitude = amplitude; } +template +void SweepGenerator::setFrequency(float frequency) +{ + Generator::setFrequency(frequency); + curFreq = startFreq; + freqStep = ((this->getFrequency() - startFreq) / length) / this->sampleRate; + sg.setFrequency(curFreq); +} + template class NoiseGenerator; template class SineGenerator; template class SweepGenerator; template class SineSynth; +template class NoiseGenerator; template class SineSynth; template class SineGenerator; template class JTestGenerator; diff --git a/src/main.cpp b/src/main.cpp index 39f6db5..d285728 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,8 @@ #include +#include #include #include #include -#include #include #include @@ -10,18 +10,18 @@ #include #include "SineSynth.h" +#include "WavWriter.h" -WINDOW *win; +WINDOW* win; -constexpr int SAMPLERATE = 48000; -constexpr size_t BUFSIZE = (48000/1000)*4; +constexpr int SAMPLERATE = 192000; +constexpr size_t BUFSIZE = 512; -#define SAMPTYPE uint32_t +typedef uint32_t SAMPTYPE; -WINDOW * -init_curses() +WINDOW* init_curses() { - WINDOW *win = initscr(); + WINDOW* win = initscr(); nodelay(win, TRUE); keypad(win, TRUE); cbreak(); @@ -29,75 +29,67 @@ init_curses() return win; } -pa_simple * -init_pulse() +pa_simple* init_pulse() { - static const pa_sample_spec ss = {.format = PA_SAMPLE_S32NE, - .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) - { - std::cerr << "Error initializing PulseAudio: " << pa_strerror(error) << " (" - << error << ")" << std::endl; + 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); } return s; } -template -void smpte_imd_test(SineSynth &synth) { +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 - )); + synth.addSynth(std::make_shared>(SAMPLERATE, 60, 0.8)); + synth.addSynth(std::make_shared>(SAMPLERATE, 7000, 0.2)); } -template -void ccif_imd_test(SineSynth &synth, float amplitude) { +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 - )); + synth.addSynth( + std::make_shared>(SAMPLERATE, 19000, amplitude / 2)); + synth.addSynth( + std::make_shared>(SAMPLERATE, 20000, amplitude / 2)); } -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { 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); + std::shared_ptr> gen + = std::make_shared>(SAMPLERATE, 1000, 1); + // auto gen = std::make_shared>(SAMPLERATE, 10, + // 10000, 5, 1); // auto gen2 = std::make_shared>(SAMPLERATE, 32); // synth.addSynth(gen2); - //smpte_imd_test(synth); + // smpte_imd_test(synth); + + synth.addSynth(gen); + + // ccif_imd_test(synth,1); + + // SAMPTYPE buf2[BUFSIZE]; + + // WavWriter w("test.wav", WavWriter::WAVE_FORMAT_IEEE_FLOAT, 1, sizeof(SAMPTYPE) * 8, SAMPLERATE); + // for (auto i = 0; i < SAMPLERATE / BUFSIZE; i++) { + // synth.getSamples(BUFSIZE, buf2); + // w.writeSamples(buf2, sizeof(buf2)); + // } + + // return 0; auto s = init_pulse(); auto win = init_curses(); @@ -106,22 +98,17 @@ 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); + wprintw(win, "Error: %s (%d) Latency: %d usec\n", pa_strerror(error), error, + latency); SAMPTYPE buf[BUFSIZE]; long long i; mvwprintw(win, 2, 0, "Loops: "); mvwprintw(win, 3, 0, "Freq: "); mvwprintw(win, 4, 0, "Ampl: "); - for (i = 0;; i++) - { + for (i = 0;; i++) { auto c = getch(); - switch (c) - { + switch (c) { case KEY_UP: gen->setFrequency(gen->getFrequency() + 100); break; @@ -129,10 +116,46 @@ int main(int argc, char *argv[]) gen->setFrequency(gen->getFrequency() - 100); break; case '+': - gen->setAmplitude(gen->getAmplitude() + 0.1); + gen->setAmplitudeDb(gen->getAmplitudeDb() + 1); break; case '-': - gen->setAmplitude(gen->getAmplitude() - 0.1); + gen->setAmplitudeDb(gen->getAmplitudeDb() - 1); + break; + case 's': + mvwprintw(win, 0, 0, "MODE: Sine "); + gen = std::make_shared>( + SAMPLERATE, gen->getFrequency(), gen->getAmplitude()); + synth.clearSynths(); + synth.addSynth(gen); + break; + case 'w': + gen = std::make_shared>( + SAMPLERATE, 10, 10000, 5, gen->getAmplitude()); + mvwprintw(win, 0, 0, "MODE: Sweep %d -> %d over %ds", 10, gen->getFrequency(), 5); + synth.clearSynths(); + synth.addSynth(gen); + break; + case 'n': + mvwprintw(win, 0, 0, "MODE: Noise (white) "); + gen = std::make_shared>(SAMPLERATE, + NoiseGenerator::WHITE, gen->getAmplitude()); + synth.clearSynths(); + synth.addSynth(gen); + break; + case 'i': + mvwprintw(win, 0, 0, "MODE: SMPTE IMD (4:1 60hz:7000hz)"); + smpte_imd_test(synth); + break; + case 'I': + mvwprintw(win, 0, 0, "MODE: CCIF IMD (1:1 19000hz:20000hz)"); + ccif_imd_test(synth, gen->getAmplitude()); + break; + case 'j': + mvwprintw( + win, 0, 0, "MODE: J-test "); + gen = std::make_shared>(SAMPLERATE, 32); + synth.clearSynths(); + synth.addSynth(gen); break; case 'q': case 'Q': @@ -144,7 +167,8 @@ int main(int argc, char *argv[]) pa_simple_write(s, buf, sizeof(buf), &error); 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)); + mvwprintw(win, 4, 9, "%1.3f (%f)", gen->getAmplitude(), + 20 * log10(gen->getAmplitude() / 1)); } endwin();