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:
		
							
								
								
									
										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,21 +4,26 @@
 | 
			
		||||
#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);
 | 
			
		||||
@@ -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();
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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)
 | 
			
		||||
{
 | 
			
		||||
    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() {}
 | 
			
		||||
 | 
			
		||||
template <typename dataType> void SineGenerator<dataType>::dirtyCache()
 | 
			
		||||
{
 | 
			
		||||
    if (caching) {
 | 
			
		||||
        cacheInvalid = true;
 | 
			
		||||
        sampleCache.clear();
 | 
			
		||||
        instPhase = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename dataType>
 | 
			
		||||
SineGenerator<dataType>::~SineGenerator()
 | 
			
		||||
dataType SineGenerator<dataType>::unCachedGetSample()
 | 
			
		||||
{
 | 
			
		||||
    mpfr_clear(instPhase);
 | 
			
		||||
    mpfr_clear(phaseIncr);
 | 
			
		||||
    mpfr_clear(PI_2);
 | 
			
		||||
    mpfr_clear(tmpA);
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
float SineGenerator<float>::getSample()
 | 
			
		||||
{
 | 
			
		||||
    mpfr_add(instPhase, instPhase, phaseIncr, MPFR_RNDN);
 | 
			
		||||
    if (mpfr_cmp(instPhase, PI_2) > 0)
 | 
			
		||||
    {
 | 
			
		||||
        mpfr_sub(instPhase, instPhase, PI_2, MPFR_RNDN);
 | 
			
		||||
    }
 | 
			
		||||
    mpfr_cos(tmpA, instPhase, MPFR_RNDN);
 | 
			
		||||
    mpfr_mul_d(tmpA, tmpA, this->amplitude, MPFR_RNDN);
 | 
			
		||||
    instPhase += phaseIncr;
 | 
			
		||||
 | 
			
		||||
    return mpfr_get_flt(tmpA, MPFR_RNDN);
 | 
			
		||||
    return val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
uint32_t SineGenerator<uint32_t>::getSample()
 | 
			
		||||
template <typename dataType> dataType SineGenerator<dataType>::cachedGetSample()
 | 
			
		||||
{
 | 
			
		||||
    mpfr_add(instPhase, instPhase, phaseIncr, MPFR_RNDN);
 | 
			
		||||
    if (mpfr_cmp(instPhase, PI_2) > 0)
 | 
			
		||||
    {
 | 
			
		||||
        mpfr_sub(instPhase, instPhase, PI_2, MPFR_RNDN);
 | 
			
		||||
    }
 | 
			
		||||
    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);
 | 
			
		||||
    static MP_TYPE sample;
 | 
			
		||||
    
 | 
			
		||||
    return (uint32_t)(mpfr_get_si(tmpA, MPFR_RNDN));
 | 
			
		||||
    if (!cacheInvalid) {
 | 
			
		||||
        if (cachePos == sampleCache.end())
 | 
			
		||||
            cachePos = sampleCache.begin();
 | 
			
		||||
        return *cachePos++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
//phaseIncr = 2 * M_PI * this->frequency / this->sampleRate;
 | 
			
		||||
template <typename dataType>
 | 
			
		||||
void SineGenerator<dataType>::setFrequency(mpfr_t _frequency)
 | 
			
		||||
    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> dataType SineGenerator<dataType>::getSample()
 | 
			
		||||
{
 | 
			
		||||
    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);
 | 
			
		||||
    return (caching) ? cachedGetSample() : unCachedGetSample();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename dataType>
 | 
			
		||||
void SineGenerator<dataType>::setFrequency(float _frequency)
 | 
			
		||||
void SineGenerator<dataType>::setFrequency(MP_TYPE frequency)
 | 
			
		||||
{
 | 
			
		||||
    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;
 | 
			
		||||
    phaseIncr = (two_pi * frequency) / this->sampleRate;
 | 
			
		||||
    this->frequency = frequency.convert_to<float>();
 | 
			
		||||
    dirtyCache();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename dataType>
 | 
			
		||||
SineSynth<dataType>::SineSynth(uint32_t _sampleRate)
 | 
			
		||||
    : sampleRate(_sampleRate)
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
    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>;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										146
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										146
									
								
								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,16 +10,16 @@
 | 
			
		||||
#include <pulse/simple.h>
 | 
			
		||||
 | 
			
		||||
#include "SineSynth.h"
 | 
			
		||||
#include "WavWriter.h"
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
    nodelay(win, TRUE);
 | 
			
		||||
@@ -29,76 +29,68 @@ 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[])
 | 
			
		||||
{
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    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();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user