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:
@ -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();
|
||||
};
|
||||
};
|
Reference in New Issue
Block a user