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:
parent
529f0ab3b9
commit
15fd253130
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
build
|
||||
*.wav
|
@ -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")
|
||||
|
||||
|
@ -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
137
include/WavWriter.h
Normal 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();
|
||||
};
|
||||
};
|
@ -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>;
|
||||
|
154
src/main.cpp
154
src/main.cpp
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user