Fixed many bugs, IMD tests working, wav writing

* Fixed many sample generation bugs
* IMD tests now generating as expected
* WAV writing fundamentals in place
This commit is contained in:
Keenan Tims 2020-01-08 01:53:29 -08:00
parent 529f0ab3b9
commit 15fd253130
6 changed files with 516 additions and 241 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build
*.wav

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 2.6) cmake_minimum_required(VERSION 2.6)
project(sinesynth) project(sinesynth)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 17)
include_directories(include) include_directories(include)
@ -17,12 +17,17 @@ if (CURSES_FOUND)
include_directories(${CURSES_INCLUDE_DIRS}) include_directories(${CURSES_INCLUDE_DIRS})
endif() endif()
find_package(Boost)
if (Boost_FOUND)
include_directories(${BOOST_INCLUDE_DIRS})
endif()
find_package(MPFR) find_package(MPFR)
if (MPFR_FOUND) if (MPFR_FOUND)
include_directories(${MPFR_INCLUDE_DIRS}) include_directories(${MPFR_INCLUDE_DIRS})
endif() 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") file(GLOB SOURCES "src/*.cpp")

View File

@ -4,24 +4,29 @@
#include <cstdint> #include <cstdint>
#include <iterator> #include <iterator>
#include <memory> #include <memory>
#include <random>
#include <string> #include <string>
#include <vector> #include <vector>
#include <random>
#include <mpfr.h> #include <boost/math/constants/constants.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <boost/multiprecision/mpfr.hpp>
using std::shared_ptr; using std::shared_ptr;
using std::vector; using std::vector;
using std::cos;
using std::for_each; using std::for_each;
using std::sin;
template <typename dataType> using namespace boost::multiprecision;
class Generator
{ typedef cpp_bin_float_double MP_TYPE;
const MP_TYPE two_pi = boost::math::constants::two_pi<MP_TYPE>();
template <typename dataType> class Generator {
public: public:
virtual dataType getSample() = 0; 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 float getFrequency() const;
virtual void setFrequency(float frequency); virtual void setFrequency(float frequency);
@ -29,6 +34,9 @@ public:
virtual float getAmplitude() const; virtual float getAmplitude() const;
virtual void setAmplitude(float amplitude); virtual void setAmplitude(float amplitude);
virtual float getAmplitudeDb() const;
virtual void setAmplitudeDb(float amplitude);
virtual bool isEnabled() const; virtual bool isEnabled() const;
virtual void enable(); virtual void enable();
virtual void disable(); virtual void disable();
@ -36,8 +44,8 @@ public:
virtual ~Generator(); virtual ~Generator();
// disallow copy of virtual interface // disallow copy of virtual interface
Generator(Generator const &) = delete; Generator(Generator const&) = delete;
Generator &operator=(Generator const &) = delete; Generator& operator=(Generator const&) = delete;
protected: protected:
Generator(uint32_t _sampleRate, float _frequency, float _amplitude = 1); Generator(uint32_t _sampleRate, float _frequency, float _amplitude = 1);
@ -49,35 +57,37 @@ protected:
private: private:
}; };
template <typename dataType> template <typename dataType> class SineGenerator : public Generator<dataType> {
class SineGenerator : public Generator<dataType>
{
public: 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; dataType getSample() final;
void setFrequency(float _frequency) final; void setFrequency(float _frequency) final;
void setFrequency(mpfr_t frequency); void setFrequency(MP_TYPE frequency);
void setAmplitude(float amplitude);
virtual ~SineGenerator(); virtual ~SineGenerator();
private: private:
mpfr_t instPhase; MP_TYPE instPhase;
mpfr_t phaseIncr; MP_TYPE phaseIncr;
// Constant 2*pi void dirtyCache();
mpfr_t PI_2; dataType unCachedGetSample();
dataType cachedGetSample();
mpfr_t tmpA; vector<dataType> sampleCache;
typename vector<dataType>::const_iterator cachePos;
const bool caching;
bool cacheInvalid;
}; };
template <typename dataType> template <typename dataType> class NoiseGenerator : public Generator<dataType> {
class NoiseGenerator : public Generator<dataType>
{
public: public:
enum NOISE_TYPE enum NOISE_TYPE {
{
WHITE, WHITE,
}; };
@ -90,9 +100,7 @@ private:
std::mt19937_64 rand_gen; std::mt19937_64 rand_gen;
}; };
template <typename dataType> template <typename dataType> class JTestGenerator : public Generator<dataType> {
class JTestGenerator : public Generator<dataType>
{
public: public:
JTestGenerator(uint32_t sampleRate, uint8_t nbits); JTestGenerator(uint32_t sampleRate, uint8_t nbits);
@ -104,29 +112,27 @@ private:
typename std::vector<dataType>::const_iterator pos; typename std::vector<dataType>::const_iterator pos;
}; };
template <typename dataType> template <typename dataType> class SweepGenerator : public Generator<dataType> {
class SweepGenerator : public Generator<dataType>
{
public: 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(); virtual ~SweepGenerator();
dataType getSample() final; dataType getSample() final;
float getFrequency() const; float getFrequency() const;
void setAmplitude(float amplitude); void setAmplitude(float amplitude);
void setFrequency(float frequency);
private: private:
SineGenerator<dataType> sg; SineGenerator<dataType> sg;
mpfr_t startFreq; float length;
mpfr_t curFreq; MP_TYPE startFreq;
mpfr_t freqStep; MP_TYPE curFreq;
mpfr_t endFreq; MP_TYPE freqStep;
}; };
template <typename dataType> template <typename dataType> class SineSynth {
class SineSynth
{
public: public:
SineSynth(uint32_t _sampleRate); SineSynth(uint32_t _sampleRate);
@ -134,7 +140,7 @@ public:
vector<shared_ptr<Generator<dataType>>> getSynths() const; vector<shared_ptr<Generator<dataType>>> getSynths() const;
void clearSynths(); void clearSynths();
void getSamples(size_t nSamples, dataType *buf); void getSamples(size_t nSamples, dataType* buf);
private: private:
uint32_t sampleRate; uint32_t sampleRate;

137
include/WavWriter.h Normal file
View File

@ -0,0 +1,137 @@
#pragma once
#include <cstdint>
#include <array>
#include <fstream>
#include <ostream>
#include <string>
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 <typename T> void writeSamples(const T data[], size_t n)
{
f.write(reinterpret_cast<const char*>(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<const char*>(&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<const char*>(&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<const char*>(&cksize), sizeof(cksize));
of.write(reinterpret_cast<const char*>(&parent->format),
sizeof(parent->format));
of.write(reinterpret_cast<const char*>(&parent->nChannels),
sizeof(parent->nChannels));
of.write(reinterpret_cast<const char*>(&parent->nSamplesPerSec),
sizeof(parent->nSamplesPerSec));
uint32_t byteRate = parent->nSamplesPerSec * parent->nChannels
* parent->nBitsPerSample / 8;
of.write(
reinterpret_cast<const char*>(&byteRate), sizeof(byteRate));
uint16_t blockAlign
= parent->nChannels * parent->nBitsPerSample / 8;
of.write(
reinterpret_cast<const char*>(&blockAlign), sizeof(blockAlign));
of.write(reinterpret_cast<const char*>(&parent->nBitsPerSample),
sizeof(parent->nBitsPerSample));
if (parent->format != WAVE_FORMAT_PCM)
of.write(
reinterpret_cast<const char*>(&cbSize), sizeof(cbSize));
if (parent->format == WAVE_FORMAT_EXTENSIBLE) {
of.write(reinterpret_cast<const char*>(&parent->nBitsPerSample),
sizeof(parent->nBitsPerSample));
of.write(reinterpret_cast<const char*>(&dwChannelMask),
sizeof(dwChannelMask));
of.write(reinterpret_cast<const char*>(SubFormat),
sizeof(SubFormat));
}
}
};
struct factChunk : public Chunk {
factChunk(WavWriter* parent)
: Chunk(parent, "fact"){};
uint32_t cksize = 4;
uint32_t dwSampleLength();
};
};

View File

@ -1,125 +1,221 @@
#include "SineSynth.h" #include "SineSynth.h"
#include <iostream>
#include <random>
#include <cassert> #include <cassert>
#include <fstream>
#include <iostream>
#include <limits>
#include <random>
#include <type_traits>
template <typename dataType> template <typename dataType>
Generator<dataType>::Generator(uint32_t _sampleRate, float _frequency, float _amplitude) Generator<dataType>::Generator(
: sampleRate(_sampleRate), frequency(_frequency), amplitude(_amplitude), enabled(false) uint32_t _sampleRate, float _frequency, float _amplitude)
: sampleRate(_sampleRate)
, frequency(_frequency)
, amplitude(_amplitude)
, enabled(true)
{ {
} }
template <typename dataType> template <typename dataType> Generator<dataType>::~Generator() {}
Generator<dataType>::~Generator() {}
template <typename dataType> template <typename dataType>
void Generator<dataType>::getSamples(size_t nSamples, dataType *buf) void Generator<dataType>::getSamples(size_t nSamples, dataType* buf)
{ {
for (; nSamples > 0; nSamples--) for (; nSamples > 0; nSamples--) {
{
*buf++ = getSample(); *buf++ = getSample();
} }
} }
template <typename dataType> template <typename dataType> float Generator<dataType>::getFrequency() const
float Generator<dataType>::getFrequency() const { return this->frequency; } {
return this->frequency;
}
template <typename dataType> template <typename dataType>
void Generator<dataType>::setFrequency(float frequency) { this->frequency = frequency; } void Generator<dataType>::setFrequency(float frequency)
{
this->frequency = frequency;
}
template <typename dataType> float Generator<dataType>::getAmplitude() const
{
return this->amplitude;
}
template <typename dataType> float Generator<dataType>::getAmplitudeDb() const
{
return 10 * log10(this->getAmplitude());
}
template <typename dataType> template <typename dataType>
float Generator<dataType>::getAmplitude() const { return this->amplitude; } void Generator<dataType>::setAmplitude(float amplitude)
{
assert(amplitude <= 1);
this->amplitude = amplitude;
}
template <typename dataType> template <typename dataType>
void Generator<dataType>::setAmplitude(float amplitude) { this->amplitude = amplitude; } void Generator<dataType>::setAmplitudeDb(float amplitude)
{
assert(amplitude <= 0);
this->setAmplitude(pow(10, amplitude / 10));
}
template <typename dataType> template <typename dataType> bool Generator<dataType>::isEnabled() const
bool Generator<dataType>::isEnabled() const { return enabled; } {
return enabled;
}
template <typename dataType> template <typename dataType> void Generator<dataType>::enable()
void Generator<dataType>::enable() { enabled = true; } {
enabled = true;
}
template <typename dataType> template <typename dataType> void Generator<dataType>::disable()
void Generator<dataType>::disable() { enabled = false; } {
enabled = false;
}
template <typename dataType> template <typename dataType>
SineGenerator<dataType>::SineGenerator( SineGenerator<dataType>::SineGenerator(
uint32_t _sampleRate, float _frequency, float _amplitude) uint32_t _sampleRate, float _frequency, float _amplitude, bool caching)
: Generator<dataType>(_sampleRate, _frequency, _amplitude) : Generator<dataType>(_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); setFrequency(_frequency);
this->enable(); // std::cout << "Constructed SineGenerator with freq " << this->frequency
std::cout << "Constructed SineGenerator with freq " << this->frequency // << " phaseIncr " << phaseIncr << " rads/cycle" << std::endl;
<< " phaseIncr " << mpfr_get_d(phaseIncr, MPFR_RNDN) << " rads/cycle" << std::endl;
} }
template <typename dataType> template <typename dataType> SineGenerator<dataType>::~SineGenerator() {}
SineGenerator<dataType>::~SineGenerator()
{
mpfr_clear(instPhase);
mpfr_clear(phaseIncr);
mpfr_clear(PI_2);
mpfr_clear(tmpA);
}
template <> template <typename dataType> void SineGenerator<dataType>::dirtyCache()
float SineGenerator<float>::getSample()
{ {
mpfr_add(instPhase, instPhase, phaseIncr, MPFR_RNDN); if (caching) {
if (mpfr_cmp(instPhase, PI_2) > 0) cacheInvalid = true;
{ sampleCache.clear();
mpfr_sub(instPhase, instPhase, PI_2, MPFR_RNDN); 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 <> template <typename dataType>
uint32_t SineGenerator<uint32_t>::getSample() dataType SineGenerator<dataType>::unCachedGetSample()
{ {
mpfr_add(instPhase, instPhase, phaseIncr, MPFR_RNDN); static MP_TYPE sample;
if (mpfr_cmp(instPhase, PI_2) > 0) dataType val;
{
mpfr_sub(instPhase, instPhase, PI_2, MPFR_RNDN); sample = sin(instPhase) * this->getAmplitude();
if constexpr (std::is_floating_point<dataType>::value)
val = sample.convert_to<dataType>();
else if constexpr (std::is_signed<dataType>::value) {
sample *= std::numeric_limits<dataType>::max();
val = sample.convert_to<dataType>();
} else {
if (sample > 0) {
sample *= std::numeric_limits<dataType>::max() / 2;
val = sample.convert_to<dataType>();
} else {
sample *= std::numeric_limits<dataType>::max() / 2;
val = sample.convert_to<dataType>() * -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 <typename dataType> dataType SineGenerator<dataType>::cachedGetSample()
template <typename dataType>
void SineGenerator<dataType>::setFrequency(mpfr_t _frequency)
{ {
mpfr_mul(phaseIncr, PI_2, _frequency, MPFR_RNDN); static MP_TYPE sample;
mpfr_div_ui(phaseIncr, phaseIncr, this->sampleRate, MPFR_RNDN);
this->frequency = mpfr_get_flt(_frequency, MPFR_RNDN); if (!cacheInvalid) {
if (cachePos == sampleCache.end())
cachePos = sampleCache.begin();
return *cachePos++;
}
sample = sin(instPhase) * this->getAmplitude();
dataType val;
if constexpr (std::is_floating_point<dataType>::value)
val = sample.convert_to<dataType>();
else if constexpr (std::is_signed<dataType>::value) {
sample *= std::numeric_limits<dataType>::max();
val = sample.convert_to<dataType>();
} else {
if (sample > 0) {
sample *= std::numeric_limits<dataType>::max() / 2;
val = sample.convert_to<dataType>();
} else {
sample *= std::numeric_limits<dataType>::max() / 2;
val = sample.convert_to<dataType>() * -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<dataType>::value)
doneCaching = val == 0 && 0 > sampleCache.back();
else
doneCaching = val == 0
&& 0 > static_cast<typename std::make_signed<dataType>::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 <typename dataType> template <typename dataType> dataType SineGenerator<dataType>::getSample()
void SineGenerator<dataType>::setFrequency(float _frequency)
{ {
mpfr_mul_d(phaseIncr, PI_2, _frequency, MPFR_RNDN); // 2*pi*frequency return (caching) ? cachedGetSample() : unCachedGetSample();
mpfr_div_ui(phaseIncr, phaseIncr, this->sampleRate, MPFR_RNDN); // 2*pi*frequency / samplerate
this->frequency = _frequency;
} }
template <typename dataType> template <typename dataType>
SineSynth<dataType>::SineSynth(uint32_t _sampleRate) void SineGenerator<dataType>::setFrequency(MP_TYPE frequency)
: sampleRate(_sampleRate) {
phaseIncr = (two_pi * frequency) / this->sampleRate;
this->frequency = frequency.convert_to<float>();
dirtyCache();
}
template <typename dataType>
void SineGenerator<dataType>::setFrequency(float frequency)
{
phaseIncr = (two_pi * frequency) / this->sampleRate;
this->frequency = frequency;
dirtyCache();
}
template <typename dataType>
void SineGenerator<dataType>::setAmplitude(float amplitude)
{
Generator<dataType>::setAmplitude(amplitude);
dirtyCache();
}
template <typename dataType>
SineSynth<dataType>::SineSynth(uint32_t sampleRate)
: sampleRate(sampleRate)
{ {
} }
@ -135,14 +231,15 @@ vector<shared_ptr<Generator<dataType>>> SineSynth<dataType>::getSynths() const
return generators; return generators;
} }
template <typename dataType> template <typename dataType> void SineSynth<dataType>::clearSynths()
void SineSynth<dataType>::clearSynths() { generators.resize(0); } {
generators.resize(0);
}
template <typename dataType> template <typename dataType>
void SineSynth<dataType>::getSamples(size_t nSamples, dataType *buf) void SineSynth<dataType>::getSamples(size_t nSamples, dataType* buf)
{ {
for (; nSamples > 0; nSamples--) for (; nSamples > 0; nSamples--) {
{
*buf = 0; *buf = 0;
for_each(std::begin(generators), std::end(generators), for_each(std::begin(generators), std::end(generators),
[buf](std::shared_ptr<Generator<dataType>> g) { [buf](std::shared_ptr<Generator<dataType>> g) {
@ -156,31 +253,41 @@ void SineSynth<dataType>::getSamples(size_t nSamples, dataType *buf)
template <typename dataType> template <typename dataType>
NoiseGenerator<dataType>::NoiseGenerator( NoiseGenerator<dataType>::NoiseGenerator(
uint32_t _sampleRate, NOISE_TYPE type, float _amplitude) uint32_t _sampleRate, NOISE_TYPE type, float _amplitude)
: Generator<dataType>(_sampleRate, 0, _amplitude), rand_gen() : Generator<dataType>(_sampleRate, 0, _amplitude)
, rand_gen()
{ {
this->enable(); }
template <typename dataType> NoiseGenerator<dataType>::~NoiseGenerator() {}
template <typename dataType> dataType NoiseGenerator<dataType>::getSample()
{
if constexpr (std::is_floating_point<dataType>::value) {
auto sample
= std::uniform_real_distribution<dataType>(-this->getAmplitude(),
std::nextafter(this->getAmplitude(),
std::numeric_limits<dataType>::max()))(rand_gen);
return sample;
} else {
auto sample = std::uniform_int_distribution<dataType>(
std::numeric_limits<dataType>::min(),
std::numeric_limits<dataType>::max())(rand_gen);
return sample;
}
} }
template <typename dataType> template <typename dataType>
NoiseGenerator<dataType>::~NoiseGenerator() {} JTestGenerator<dataType>::JTestGenerator(uint32_t sampleRate, uint8_t nbits)
: Generator<dataType>(sampleRate, 0, 1)
template <typename dataType>
dataType NoiseGenerator<dataType>::getSample()
{
return std::uniform_real_distribution<float>(-this->amplitude, std::nextafter(this->amplitude, std::numeric_limits<float>::max()))(rand_gen);
}
template <typename dataType>
JTestGenerator<dataType>::JTestGenerator(uint32_t sampleRate, uint8_t nbits) : Generator<dataType>(sampleRate, 0, 1)
{ {
size_t loopLength; size_t loopLength;
if (sampleRate % 250 == 0) if (sampleRate % 250 == 0)
loopLength = sampleRate / 250; loopLength = sampleRate / 250;
else if (sampleRate % 245 == 0) else if (sampleRate % 245 == 0)
loopLength = sampleRate / 245; loopLength = sampleRate / 245;
else else {
{ std::cerr << "Unable to generate non-integral fundamental frequency "
std::cerr << "Unable to generate non-integral fundamental frequency for JTest"; "for JTest";
exit(-1); exit(-1);
} }
@ -196,15 +303,13 @@ JTestGenerator<dataType>::JTestGenerator(uint32_t sampleRate, uint8_t nbits) : G
const auto posval_last = posval_first - 1; const auto posval_last = posval_first - 1;
const auto negval_last = negval_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 + 0] = negval_first;
this->loop[i + 1] = negval_first; this->loop[i + 1] = negval_first;
this->loop[i + 2] = posval_first; this->loop[i + 2] = posval_first;
this->loop[i + 3] = 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 + 0] = negval_last;
this->loop[i + 1] = negval_last; this->loop[i + 1] = negval_last;
this->loop[i + 2] = posval_last; this->loop[i + 2] = posval_last;
@ -214,51 +319,36 @@ JTestGenerator<dataType>::JTestGenerator(uint32_t sampleRate, uint8_t nbits) : G
this->pos = this->loop.begin(); this->pos = this->loop.begin();
} }
template <typename dataType> template <typename dataType> dataType JTestGenerator<dataType>::getSample()
dataType JTestGenerator<dataType>::getSample()
{ {
if (this->pos == this->loop.end()) if (this->pos == this->loop.end())
this->pos = this->loop.begin(); this->pos = this->loop.begin();
return *(this->pos++); return *(this->pos++);
} }
template <typename dataType> template <typename dataType> JTestGenerator<dataType>::~JTestGenerator() {}
JTestGenerator<dataType>::~JTestGenerator() {}
template <typename dataType> template <typename dataType>
SweepGenerator<dataType>::SweepGenerator(uint32_t sampleRate, float startFreq, float endFreq, float length, float amplitude) : Generator<dataType>(sampleRate, startFreq, amplitude), SweepGenerator<dataType>::SweepGenerator(uint32_t sampleRate, float startFreq,
sg(sampleRate, startFreq, amplitude) float endFreq, float length, float amplitude)
: Generator<dataType>(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 // Linear frequency step = ((endFreq - startFreq) / length) / sampleRate
mpfr_sub(freqStep, this->endFreq, this->startFreq, MPFR_RNDN); freqStep = ((this->getFrequency() - startFreq) / length) / sampleRate;
mpfr_div_d(freqStep, freqStep, length, MPFR_RNDN);
mpfr_div_ui(freqStep, freqStep, sampleRate, MPFR_RNDN);
} }
template <typename dataType> template <typename dataType> SweepGenerator<dataType>::~SweepGenerator() {}
SweepGenerator<dataType>::~SweepGenerator()
{
mpfr_clear(startFreq);
mpfr_clear(curFreq);
mpfr_clear(endFreq);
mpfr_clear(freqStep);
}
template <typename dataType> template <typename dataType> dataType SweepGenerator<dataType>::getSample()
dataType SweepGenerator<dataType>::getSample()
{ {
dataType sample = sg.getSample(); dataType sample = sg.getSample();
mpfr_add(curFreq, curFreq, freqStep, MPFR_RNDN); curFreq += freqStep;
if (mpfr_cmp(curFreq, endFreq) > 0) if (curFreq > this->getFrequency())
{ curFreq = startFreq;
mpfr_set(curFreq, startFreq, MPFR_RNDN);
}
sg.setFrequency(curFreq); sg.setFrequency(curFreq);
return sample; return sample;
} }
@ -266,20 +356,31 @@ dataType SweepGenerator<dataType>::getSample()
template <typename dataType> template <typename dataType>
float SweepGenerator<dataType>::getFrequency() const float SweepGenerator<dataType>::getFrequency() const
{ {
return mpfr_get_flt(curFreq, MPFR_RNDN); return Generator<dataType>::getFrequency();
} }
template <typename dataType> template <typename dataType>
void SweepGenerator<dataType>::setAmplitude(float amplitude) { void SweepGenerator<dataType>::setAmplitude(float amplitude)
{
sg.setAmplitude(amplitude); sg.setAmplitude(amplitude);
this->amplitude = amplitude; this->amplitude = amplitude;
} }
template <typename dataType>
void SweepGenerator<dataType>::setFrequency(float frequency)
{
Generator<dataType>::setFrequency(frequency);
curFreq = startFreq;
freqStep = ((this->getFrequency() - startFreq) / length) / this->sampleRate;
sg.setFrequency(curFreq);
}
template class NoiseGenerator<float>; template class NoiseGenerator<float>;
template class SineGenerator<float>; template class SineGenerator<float>;
template class SweepGenerator<float>; template class SweepGenerator<float>;
template class SineSynth<float>; template class SineSynth<float>;
template class NoiseGenerator<uint32_t>;
template class SineSynth<uint32_t>; template class SineSynth<uint32_t>;
template class SineGenerator<uint32_t>; template class SineGenerator<uint32_t>;
template class JTestGenerator<uint32_t>; template class JTestGenerator<uint32_t>;

View File

@ -1,8 +1,8 @@
#include <cassert> #include <cassert>
#include <chrono>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <string> #include <string>
#include <chrono>
#include <thread> #include <thread>
#include <curses.h> #include <curses.h>
@ -10,18 +10,18 @@
#include <pulse/simple.h> #include <pulse/simple.h>
#include "SineSynth.h" #include "SineSynth.h"
#include "WavWriter.h"
WINDOW *win; WINDOW* win;
constexpr int SAMPLERATE = 48000; constexpr int SAMPLERATE = 192000;
constexpr size_t BUFSIZE = (48000/1000)*4; constexpr size_t BUFSIZE = 512;
#define SAMPTYPE uint32_t typedef uint32_t SAMPTYPE;
WINDOW * WINDOW* init_curses()
init_curses()
{ {
WINDOW *win = initscr(); WINDOW* win = initscr();
nodelay(win, TRUE); nodelay(win, TRUE);
keypad(win, TRUE); keypad(win, TRUE);
cbreak(); cbreak();
@ -29,75 +29,67 @@ init_curses()
return win; return win;
} }
pa_simple * pa_simple* init_pulse()
init_pulse()
{ {
static const pa_sample_spec ss = {.format = PA_SAMPLE_S32NE, static const pa_sample_spec ss
.rate = SAMPLERATE, = { .format = PA_SAMPLE_S32NE, .rate = SAMPLERATE, .channels = 1 };
.channels = 1};
int error = 0; int error = 0;
pa_simple *s = pa_simple_new(NULL, pa_simple* s = pa_simple_new(NULL, "sinesynth", PA_STREAM_PLAYBACK, NULL,
"sinesynth", "playback", &ss, NULL, NULL, &error);
PA_STREAM_PLAYBACK, if (error) {
NULL, std::cerr << "Error initializing PulseAudio: " << pa_strerror(error)
"playback", << " (" << error << ")" << std::endl;
&ss,
NULL,
NULL,
&error);
if (error)
{
std::cerr << "Error initializing PulseAudio: " << pa_strerror(error) << " ("
<< error << ")" << std::endl;
exit(-1); exit(-1);
} }
return s; return s;
} }
template <typename T> template <typename T> void smpte_imd_test(SineSynth<T>& synth)
void smpte_imd_test(SineSynth<T> &synth) { {
synth.clearSynths(); synth.clearSynths();
synth.addSynth(std::make_shared<SineGenerator<T>>( synth.addSynth(std::make_shared<SineGenerator<T>>(SAMPLERATE, 60, 0.8));
SAMPLERATE, 60, 0.8 synth.addSynth(std::make_shared<SineGenerator<T>>(SAMPLERATE, 7000, 0.2));
));
synth.addSynth(std::make_shared<SineGenerator<T>>(
SAMPLERATE, 7000, 0.2
));
} }
template <typename T> template <typename T> void ccif_imd_test(SineSynth<T>& synth, float amplitude)
void ccif_imd_test(SineSynth<T> &synth, float amplitude) { {
assert(amplitude >= 0 && amplitude <= 1); assert(amplitude >= 0 && amplitude <= 1);
synth.clearSynths(); synth.clearSynths();
synth.addSynth(std::make_shared<SineGenerator<T>>( synth.addSynth(
SAMPLERATE, 19000, amplitude / 2 std::make_shared<SineGenerator<T>>(SAMPLERATE, 19000, amplitude / 2));
)); synth.addSynth(
synth.addSynth(std::make_shared<SineGenerator<T>>( std::make_shared<SineGenerator<T>>(SAMPLERATE, 20000, amplitude / 2));
SAMPLERATE, 20000, amplitude / 2
));
} }
int main(int argc, char *argv[]) int main(int argc, char* argv[])
{ {
SineSynth<SAMPTYPE> synth(SAMPLERATE); SineSynth<SAMPTYPE> synth(SAMPLERATE);
auto gen = std::make_shared<SineGenerator<SAMPTYPE>>(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; std::shared_ptr<Generator<SAMPTYPE>> gen
= std::make_shared<SineGenerator<SAMPTYPE>>(SAMPLERATE, 1000, 1);
// auto gen = std::make_shared<SweepGenerator<SAMPTYPE>>(SAMPLERATE, 10, 10000, 5, 1); // auto gen = std::make_shared<SweepGenerator<SAMPTYPE>>(SAMPLERATE, 10,
// synth.addSynth(gen); // 10000, 5, 1);
// auto gen2 = std::make_shared<JTestGenerator<uint32_t>>(SAMPLERATE, 32); // auto gen2 = std::make_shared<JTestGenerator<uint32_t>>(SAMPLERATE, 32);
// synth.addSynth(gen2); // 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 s = init_pulse();
auto win = init_curses(); auto win = init_curses();
@ -106,10 +98,7 @@ int main(int argc, char *argv[])
int error = 0; int error = 0;
pa_usec_t latency = pa_simple_get_latency(s, &error); pa_usec_t latency = pa_simple_get_latency(s, &error);
wprintw(win, wprintw(win, "Error: %s (%d) Latency: %d usec\n", pa_strerror(error), error,
"Error: %s (%d) Latency: %d usec\n",
pa_strerror(error),
error,
latency); latency);
SAMPTYPE buf[BUFSIZE]; SAMPTYPE buf[BUFSIZE];
@ -117,11 +106,9 @@ int main(int argc, char *argv[])
mvwprintw(win, 2, 0, "Loops: "); mvwprintw(win, 2, 0, "Loops: ");
mvwprintw(win, 3, 0, "Freq: "); mvwprintw(win, 3, 0, "Freq: ");
mvwprintw(win, 4, 0, "Ampl: "); mvwprintw(win, 4, 0, "Ampl: ");
for (i = 0;; i++) for (i = 0;; i++) {
{
auto c = getch(); auto c = getch();
switch (c) switch (c) {
{
case KEY_UP: case KEY_UP:
gen->setFrequency(gen->getFrequency() + 100); gen->setFrequency(gen->getFrequency() + 100);
break; break;
@ -129,10 +116,46 @@ int main(int argc, char *argv[])
gen->setFrequency(gen->getFrequency() - 100); gen->setFrequency(gen->getFrequency() - 100);
break; break;
case '+': case '+':
gen->setAmplitude(gen->getAmplitude() + 0.1); gen->setAmplitudeDb(gen->getAmplitudeDb() + 1);
break; break;
case '-': 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<SineGenerator<SAMPTYPE>>(
SAMPLERATE, gen->getFrequency(), gen->getAmplitude());
synth.clearSynths();
synth.addSynth(gen);
break;
case 'w':
gen = std::make_shared<SweepGenerator<SAMPTYPE>>(
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<NoiseGenerator<SAMPTYPE>>(SAMPLERATE,
NoiseGenerator<SAMPTYPE>::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<JTestGenerator<SAMPTYPE>>(SAMPLERATE, 32);
synth.clearSynths();
synth.addSynth(gen);
break; break;
case 'q': case 'q':
case 'Q': case 'Q':
@ -144,7 +167,8 @@ int main(int argc, char *argv[])
pa_simple_write(s, buf, sizeof(buf), &error); pa_simple_write(s, buf, sizeof(buf), &error);
mvwprintw(win, 2, 8, "%6d", i); mvwprintw(win, 2, 8, "%6d", i);
mvwprintw(win, 3, 8, "%6.0f", gen->getFrequency()); 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(); endwin();