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)
|
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")
|
||||||
|
|
||||||
|
@ -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
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 "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,20 +231,21 @@ 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) {
|
||||||
if (g->isEnabled())
|
if (g->isEnabled())
|
||||||
*buf += g->getSample();
|
*buf += g->getSample();
|
||||||
});
|
});
|
||||||
buf++;
|
buf++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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>;
|
||||||
|
156
src/main.cpp
156
src/main.cpp
@ -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,22 +98,17 @@ 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",
|
latency);
|
||||||
pa_strerror(error),
|
|
||||||
error,
|
|
||||||
latency);
|
|
||||||
|
|
||||||
SAMPTYPE buf[BUFSIZE];
|
SAMPTYPE buf[BUFSIZE];
|
||||||
long long i;
|
long long i;
|
||||||
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();
|
||||||
|
Loading…
Reference in New Issue
Block a user