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)
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")

View File

@ -4,24 +4,29 @@
#include <cstdint>
#include <iterator>
#include <memory>
#include <random>
#include <string>
#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::vector;
using std::cos;
using std::for_each;
using std::sin;
template <typename dataType>
class Generator
{
using namespace boost::multiprecision;
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:
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 <typename dataType>
class SineGenerator : public Generator<dataType>
{
template <typename dataType> class SineGenerator : public Generator<dataType> {
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<dataType> sampleCache;
typename vector<dataType>::const_iterator cachePos;
const bool caching;
bool cacheInvalid;
};
template <typename dataType>
class NoiseGenerator : public Generator<dataType>
{
template <typename dataType> class NoiseGenerator : public Generator<dataType> {
public:
enum NOISE_TYPE
{
enum NOISE_TYPE {
WHITE,
};
@ -90,9 +100,7 @@ private:
std::mt19937_64 rand_gen;
};
template <typename dataType>
class JTestGenerator : public Generator<dataType>
{
template <typename dataType> class JTestGenerator : public Generator<dataType> {
public:
JTestGenerator(uint32_t sampleRate, uint8_t nbits);
@ -104,29 +112,27 @@ private:
typename std::vector<dataType>::const_iterator pos;
};
template <typename dataType>
class SweepGenerator : public Generator<dataType>
{
template <typename dataType> class SweepGenerator : public Generator<dataType> {
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<dataType> 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 <typename dataType>
class SineSynth
{
template <typename dataType> class SineSynth {
public:
SineSynth(uint32_t _sampleRate);
@ -134,7 +140,7 @@ public:
vector<shared_ptr<Generator<dataType>>> getSynths() const;
void clearSynths();
void getSamples(size_t nSamples, dataType *buf);
void getSamples(size_t nSamples, dataType* buf);
private:
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 <iostream>
#include <random>
#include <cassert>
#include <fstream>
#include <iostream>
#include <limits>
#include <random>
#include <type_traits>
template <typename dataType>
Generator<dataType>::Generator(uint32_t _sampleRate, float _frequency, float _amplitude)
: sampleRate(_sampleRate), frequency(_frequency), amplitude(_amplitude), enabled(false)
Generator<dataType>::Generator(
uint32_t _sampleRate, float _frequency, float _amplitude)
: sampleRate(_sampleRate)
, frequency(_frequency)
, amplitude(_amplitude)
, enabled(true)
{
}
template <typename dataType>
Generator<dataType>::~Generator() {}
template <typename dataType> Generator<dataType>::~Generator() {}
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();
}
}
template <typename dataType>
float Generator<dataType>::getFrequency() const { return this->frequency; }
template <typename dataType> float Generator<dataType>::getFrequency() const
{
return this->frequency;
}
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>
float Generator<dataType>::getAmplitude() const { return this->amplitude; }
void Generator<dataType>::setAmplitude(float amplitude)
{
assert(amplitude <= 1);
this->amplitude = amplitude;
}
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>
bool Generator<dataType>::isEnabled() const { return enabled; }
template <typename dataType> bool Generator<dataType>::isEnabled() const
{
return enabled;
}
template <typename dataType>
void Generator<dataType>::enable() { enabled = true; }
template <typename dataType> void Generator<dataType>::enable()
{
enabled = true;
}
template <typename dataType>
void Generator<dataType>::disable() { enabled = false; }
template <typename dataType> void Generator<dataType>::disable()
{
enabled = false;
}
template <typename dataType>
SineGenerator<dataType>::SineGenerator(
uint32_t _sampleRate, float _frequency, float _amplitude)
uint32_t _sampleRate, float _frequency, float _amplitude, bool caching)
: 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);
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 <typename dataType>
SineGenerator<dataType>::~SineGenerator()
{
mpfr_clear(instPhase);
mpfr_clear(phaseIncr);
mpfr_clear(PI_2);
mpfr_clear(tmpA);
}
template <typename dataType> SineGenerator<dataType>::~SineGenerator() {}
template <>
float SineGenerator<float>::getSample()
template <typename dataType> void SineGenerator<dataType>::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<uint32_t>::getSample()
template <typename dataType>
dataType SineGenerator<dataType>::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<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>
void SineGenerator<dataType>::setFrequency(mpfr_t _frequency)
template <typename dataType> dataType SineGenerator<dataType>::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<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>
void SineGenerator<dataType>::setFrequency(float _frequency)
template <typename dataType> dataType SineGenerator<dataType>::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 <typename dataType>
SineSynth<dataType>::SineSynth(uint32_t _sampleRate)
: sampleRate(_sampleRate)
void SineGenerator<dataType>::setFrequency(MP_TYPE frequency)
{
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;
}
template <typename dataType>
void SineSynth<dataType>::clearSynths() { generators.resize(0); }
template <typename dataType> void SineSynth<dataType>::clearSynths()
{
generators.resize(0);
}
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;
for_each(std::begin(generators), std::end(generators),
[buf](std::shared_ptr<Generator<dataType>> g) {
@ -156,31 +253,41 @@ void SineSynth<dataType>::getSamples(size_t nSamples, dataType *buf)
template <typename dataType>
NoiseGenerator<dataType>::NoiseGenerator(
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>
NoiseGenerator<dataType>::~NoiseGenerator() {}
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)
JTestGenerator<dataType>::JTestGenerator(uint32_t sampleRate, uint8_t nbits)
: Generator<dataType>(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<dataType>::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<dataType>::JTestGenerator(uint32_t sampleRate, uint8_t nbits) : G
this->pos = this->loop.begin();
}
template <typename dataType>
dataType JTestGenerator<dataType>::getSample()
template <typename dataType> dataType JTestGenerator<dataType>::getSample()
{
if (this->pos == this->loop.end())
this->pos = this->loop.begin();
return *(this->pos++);
}
template <typename dataType>
JTestGenerator<dataType>::~JTestGenerator() {}
template <typename dataType> JTestGenerator<dataType>::~JTestGenerator() {}
template <typename dataType>
SweepGenerator<dataType>::SweepGenerator(uint32_t sampleRate, float startFreq, float endFreq, float length, float amplitude) : Generator<dataType>(sampleRate, startFreq, amplitude),
sg(sampleRate, startFreq, amplitude)
SweepGenerator<dataType>::SweepGenerator(uint32_t sampleRate, float startFreq,
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
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 <typename dataType>
SweepGenerator<dataType>::~SweepGenerator()
{
mpfr_clear(startFreq);
mpfr_clear(curFreq);
mpfr_clear(endFreq);
mpfr_clear(freqStep);
}
template <typename dataType> SweepGenerator<dataType>::~SweepGenerator() {}
template <typename dataType>
dataType SweepGenerator<dataType>::getSample()
template <typename dataType> dataType SweepGenerator<dataType>::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<dataType>::getSample()
template <typename dataType>
float SweepGenerator<dataType>::getFrequency() const
{
return mpfr_get_flt(curFreq, MPFR_RNDN);
return Generator<dataType>::getFrequency();
}
template <typename dataType>
void SweepGenerator<dataType>::setAmplitude(float amplitude) {
void SweepGenerator<dataType>::setAmplitude(float amplitude)
{
sg.setAmplitude(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 SineGenerator<float>;
template class SweepGenerator<float>;
template class SineSynth<float>;
template class NoiseGenerator<uint32_t>;
template class SineSynth<uint32_t>;
template class SineGenerator<uint32_t>;
template class JTestGenerator<uint32_t>;

View File

@ -1,8 +1,8 @@
#include <cassert>
#include <chrono>
#include <iostream>
#include <memory>
#include <string>
#include <chrono>
#include <thread>
#include <curses.h>
@ -10,18 +10,18 @@
#include <pulse/simple.h>
#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 <typename T>
void smpte_imd_test(SineSynth<T> &synth) {
template <typename T> void smpte_imd_test(SineSynth<T>& synth)
{
synth.clearSynths();
synth.addSynth(std::make_shared<SineGenerator<T>>(
SAMPLERATE, 60, 0.8
));
synth.addSynth(std::make_shared<SineGenerator<T>>(
SAMPLERATE, 7000, 0.2
));
synth.addSynth(std::make_shared<SineGenerator<T>>(SAMPLERATE, 60, 0.8));
synth.addSynth(std::make_shared<SineGenerator<T>>(SAMPLERATE, 7000, 0.2));
}
template <typename T>
void ccif_imd_test(SineSynth<T> &synth, float amplitude) {
template <typename T> void ccif_imd_test(SineSynth<T>& synth, float amplitude)
{
assert(amplitude >= 0 && amplitude <= 1);
synth.clearSynths();
synth.addSynth(std::make_shared<SineGenerator<T>>(
SAMPLERATE, 19000, amplitude / 2
));
synth.addSynth(std::make_shared<SineGenerator<T>>(
SAMPLERATE, 20000, amplitude / 2
));
synth.addSynth(
std::make_shared<SineGenerator<T>>(SAMPLERATE, 19000, amplitude / 2));
synth.addSynth(
std::make_shared<SineGenerator<T>>(SAMPLERATE, 20000, amplitude / 2));
}
int main(int argc, char *argv[])
int main(int argc, char* argv[])
{
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;
// auto gen = std::make_shared<SweepGenerator<SAMPTYPE>>(SAMPLERATE, 10, 10000, 5, 1);
// synth.addSynth(gen);
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 gen2 = std::make_shared<JTestGenerator<uint32_t>>(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,10 +98,7 @@ 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,
wprintw(win, "Error: %s (%d) Latency: %d usec\n", pa_strerror(error), error,
latency);
SAMPTYPE buf[BUFSIZE];
@ -117,11 +106,9 @@ int main(int argc, char *argv[])
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<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;
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();