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:
2020-01-08 01:53:29 -08:00
parent 529f0ab3b9
commit 15fd253130
6 changed files with 516 additions and 241 deletions

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();
};
};