├── research ├── Fontana.pdf ├── Stilson.pdf ├── Huovilainen1.pdf ├── Huovilainen2.pdf ├── Stinchcombe.pdf └── DAngeloValimaki.pdf ├── .gitignore ├── src ├── LadderFilterBase.h ├── AudioDevice.h ├── MicrotrackerModel.h ├── NoiseGenerator.h ├── MusicDSPModel.h ├── KrajeskiModel.h ├── AudioDevice.cpp ├── util.h ├── ImprovedModel.h ├── HuovilainenModel.h ├── OberheimVariationModel.h ├── SimplifiedModel.h ├── RKSimulationModel.h ├── StilsonModel.h ├── Filters.h └── RingBuffer.h ├── LICENSE ├── MoogLadders.vcxproj ├── MoogLadders.sln ├── ExampleMain.cpp ├── MoogLadders.vcxproj.filters └── MoogLadders.vcxproj ├── README.md └── third_party └── rtaudio └── RtAudio.h /research/Fontana.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeallisonJS/MoogLadders/master/research/Fontana.pdf -------------------------------------------------------------------------------- /research/Stilson.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeallisonJS/MoogLadders/master/research/Stilson.pdf -------------------------------------------------------------------------------- /research/Huovilainen1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeallisonJS/MoogLadders/master/research/Huovilainen1.pdf -------------------------------------------------------------------------------- /research/Huovilainen2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeallisonJS/MoogLadders/master/research/Huovilainen2.pdf -------------------------------------------------------------------------------- /research/Stinchcombe.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeallisonJS/MoogLadders/master/research/Stinchcombe.pdf -------------------------------------------------------------------------------- /research/DAngeloValimaki.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeallisonJS/MoogLadders/master/research/DAngeloValimaki.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific files 2 | *.suo 3 | *.user 4 | 5 | # Build results 6 | [Dd]ebug/ 7 | [Rr]elease/ 8 | [Oo]bj/ 9 | 10 | *.ilk 11 | *.obj 12 | *.pch 13 | *.pdb 14 | *.log 15 | 16 | # Visual C++ cache files 17 | ipch/ 18 | *.opensdf 19 | *.sdf 20 | 21 | # Visual Studio profiler 22 | *.psess 23 | *.vsp 24 | *.vspx 25 | 26 | # XCode 27 | .DS_Store 28 | build/ 29 | *.pbxuser 30 | !default.pbxuser 31 | *.mode1v3 32 | !default.mode1v3 33 | *.mode2v3 34 | !default.mode2v3 35 | *.perspectivev3 36 | !default.perspectivev3 37 | xcuserdata 38 | *.xccheckout 39 | *.moved-aside 40 | DerivedData 41 | *.xcuserstate 42 | 43 | *.reapeaks 44 | 45 | local_ignore/ 46 | -------------------------------------------------------------------------------- /src/LadderFilterBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef LADDER_FILTER_BASE_H 4 | #define LADDER_FILTER_BASE_H 5 | 6 | #include "Util.h" 7 | 8 | class LadderFilterBase 9 | { 10 | public: 11 | 12 | LadderFilterBase(float sampleRate) : sampleRate(sampleRate) {} 13 | virtual ~LadderFilterBase() {} 14 | 15 | virtual void Process(float * samples, uint32_t n) = 0; 16 | virtual void SetResonance(float r) = 0; 17 | virtual void SetCutoff(float c) = 0; 18 | 19 | float GetResonance() { return resonance; } 20 | float GetCutoff() { return cutoff; } 21 | 22 | protected: 23 | 24 | float cutoff; 25 | float resonance; 26 | float sampleRate; 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/AudioDevice.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef AUDIO_DEVICE_H 4 | #define AUDIO_DEVICE_H 5 | 6 | // This file implements a simple sound file player based on RtAudio for testing / example purposes. 7 | 8 | #include "Util.h" 9 | #include "RingBuffer.h" 10 | #include "rtaudio/RtAudio.h" 11 | 12 | #include 13 | #include 14 | 15 | static const unsigned int FRAME_SIZE = 512; 16 | static const int CHANNELS = 2; 17 | static const int BUFFER_LENGTH = FRAME_SIZE * CHANNELS; 18 | 19 | struct DeviceInfo 20 | { 21 | int id; 22 | int numChannels; 23 | int sampleRate; 24 | unsigned int frameSize; 25 | bool isPlaying = false; 26 | }; 27 | 28 | class AudioDevice 29 | { 30 | NO_MOVE(AudioDevice); 31 | std::unique_ptr rtaudio; 32 | public: 33 | static void ListAudioDevices(); 34 | AudioDevice(int numChannels, int sampleRate, int deviceId = -1); 35 | ~AudioDevice(); 36 | bool Open(const int deviceId); 37 | bool Play(const std::vector & data); 38 | DeviceInfo info; 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /MoogLadders.vcxproj/MoogLadders.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30501.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MoogLadders", "MoogLadders.vcxproj", "{992E85A7-B590-477B-A1B2-8A04AAAD0E10}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug|x64 = Debug|x64 12 | Release|Win32 = Release|Win32 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {992E85A7-B590-477B-A1B2-8A04AAAD0E10}.Debug|Win32.ActiveCfg = Debug|Win32 17 | {992E85A7-B590-477B-A1B2-8A04AAAD0E10}.Debug|Win32.Build.0 = Debug|Win32 18 | {992E85A7-B590-477B-A1B2-8A04AAAD0E10}.Debug|x64.ActiveCfg = Debug|x64 19 | {992E85A7-B590-477B-A1B2-8A04AAAD0E10}.Debug|x64.Build.0 = Debug|x64 20 | {992E85A7-B590-477B-A1B2-8A04AAAD0E10}.Release|Win32.ActiveCfg = Release|Win32 21 | {992E85A7-B590-477B-A1B2-8A04AAAD0E10}.Release|Win32.Build.0 = Release|Win32 22 | {992E85A7-B590-477B-A1B2-8A04AAAD0E10}.Release|x64.ActiveCfg = Release|x64 23 | {992E85A7-B590-477B-A1B2-8A04AAAD0E10}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /src/MicrotrackerModel.h: -------------------------------------------------------------------------------- 1 | // Based on an implementation by Magnus Jonsson 2 | // https://github.com/magnusjonsson/microtracker (unlicense) 3 | 4 | #pragma once 5 | 6 | #ifndef MICROTRACKER_MODEL_H 7 | #define MICROTRACKER_MODEL_H 8 | 9 | #include "LadderFilterBase.h" 10 | #include "Util.h" 11 | 12 | class MicrotrackerMoog : public LadderFilterBase 13 | { 14 | 15 | public: 16 | 17 | MicrotrackerMoog(float sampleRate) : LadderFilterBase(sampleRate) 18 | { 19 | p0 = p1 = p2 = p3 = p32 = p33 = p34 = 0.0; 20 | SetCutoff(1000.0f); 21 | SetResonance(0.10f); 22 | } 23 | 24 | virtual ~MicrotrackerMoog() {} 25 | 26 | virtual void Process(float * samples, uint32_t n) override 27 | { 28 | double k = resonance * 4; 29 | for (int s = 0; s < n; ++s) 30 | { 31 | // Coefficients optimized using differential evolution 32 | // to make feedback gain 4.0 correspond closely to the 33 | // border of instability, for all values of omega. 34 | double out = p3 * 0.360891 + p32 * 0.417290 + p33 * 0.177896 + p34 * 0.0439725; 35 | 36 | p34 = p33; 37 | p33 = p32; 38 | p32 = p3; 39 | 40 | p0 += (fast_tanh(samples[s] - k * out) - fast_tanh(p0)) * cutoff; 41 | p1 += (fast_tanh(p0) - fast_tanh(p1)) * cutoff; 42 | p2 += (fast_tanh(p1) - fast_tanh(p2)) * cutoff; 43 | p3 += (fast_tanh(p2) - fast_tanh(p3)) * cutoff; 44 | 45 | samples[s] = out; 46 | } 47 | } 48 | 49 | virtual void SetResonance(float r) override 50 | { 51 | resonance = r; 52 | } 53 | 54 | virtual void SetCutoff(float c) override 55 | { 56 | cutoff = c * 2 * MOOG_PI / sampleRate; 57 | cutoff = moog_min(cutoff, 1); 58 | } 59 | 60 | private: 61 | 62 | double p0; 63 | double p1; 64 | double p2; 65 | double p3; 66 | double p32; 67 | double p33; 68 | double p34; 69 | }; 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /src/NoiseGenerator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef NOISE_GENERATOR_H 4 | #define NOISE_GENERATOR_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "Util.h" 13 | #include "Filters.h" 14 | 15 | struct WhiteNoiseSource 16 | { 17 | WhiteNoiseSource() : dist(-1, 1) {} 18 | std::mt19937 engine; 19 | std::uniform_real_distribution dist; 20 | }; 21 | 22 | // Full spectrum noise 23 | struct WhiteNoise : public WhiteNoiseSource 24 | { 25 | float operator()() { return dist(engine); } 26 | }; 27 | 28 | // Pink noise has a decrease of 3dB/Octave 29 | struct PinkNoise : public WhiteNoiseSource 30 | { 31 | float operator()() { return f.process(dist(engine)); } 32 | PinkingFilter f; 33 | }; 34 | 35 | // Brown noise has a decrease of 6dB/Octave 36 | struct BrownNoise : public WhiteNoiseSource 37 | { 38 | float operator()() { return f.process(dist(engine)); } 39 | BrowningFilter f; 40 | }; 41 | 42 | // Note! This noise is only valid for 44100 because of the hard-coded filter coefficients 43 | struct NoiseGenerator 44 | { 45 | enum NoiseType 46 | { 47 | WHITE, 48 | PINK, 49 | BROWN, 50 | }; 51 | 52 | std::vector produce(NoiseType t, int sampleRate, int channels, float seconds) 53 | { 54 | int samplesToGenerate = sampleRate * seconds * channels; 55 | std::vector samples; 56 | samples.resize(samplesToGenerate); 57 | 58 | switch (t) 59 | { 60 | case NoiseType::WHITE: 61 | { 62 | WhiteNoise n; 63 | for(int s = 0; s < samplesToGenerate; s++) samples[s] = n(); 64 | } break; 65 | case NoiseType::PINK: 66 | { 67 | PinkNoise n; 68 | for(int s = 0; s < samplesToGenerate; s++) samples[s] = n(); 69 | } break; 70 | case NoiseType::BROWN: 71 | { 72 | BrownNoise n; 73 | for(int s = 0; s < samplesToGenerate; s++) samples[s] = n(); 74 | } break; 75 | default: throw std::runtime_error("Invalid noise type"); 76 | } 77 | return samples; 78 | } 79 | 80 | }; 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /src/MusicDSPModel.h: -------------------------------------------------------------------------------- 1 | // This file is unlicensed and uncopyright as found at: 2 | // http://www.musicdsp.org/showone.php?id=24 3 | // Considering how widely this same code has been used in ~100 projects on GitHub with 4 | // various licenses, it might be reasonable to suggest that the license is CC-BY-SA 5 | 6 | #pragma once 7 | 8 | #ifndef MUSICDSP_MOOG_H 9 | #define MUSICDSP_MOOG_H 10 | 11 | #include "LadderFilterBase.h" 12 | #include "Util.h" 13 | 14 | class MusicDSPMoog : public LadderFilterBase 15 | { 16 | 17 | public: 18 | 19 | MusicDSPMoog(float sampleRate) : LadderFilterBase(sampleRate) 20 | { 21 | memset(stage, 0, sizeof(stage)); 22 | memset(delay, 0, sizeof(delay)); 23 | SetCutoff(1000.0f); 24 | SetResonance(0.10f); 25 | } 26 | 27 | virtual ~MusicDSPMoog() 28 | { 29 | 30 | } 31 | 32 | virtual void Process(float * samples, uint32_t n) override 33 | { 34 | for (int s = 0; s < n; ++s) 35 | { 36 | float x = samples[s] - resonance * stage[3]; 37 | 38 | // Four cascaded one-pole filters (bilinear transform) 39 | stage[0] = x * p + delay[0] * p - k * stage[0]; 40 | stage[1] = stage[0] * p + delay[1] * p - k * stage[1]; 41 | stage[2] = stage[1] * p + delay[2] * p - k * stage[2]; 42 | stage[3] = stage[2] * p + delay[3] * p - k * stage[3]; 43 | 44 | // Clipping band-limited sigmoid 45 | stage[3] -= (stage[3] * stage[3] * stage[3]) / 6.0; 46 | 47 | delay[0] = x; 48 | delay[1] = stage[0]; 49 | delay[2] = stage[1]; 50 | delay[3] = stage[2]; 51 | 52 | samples[s] = stage[3]; 53 | } 54 | } 55 | 56 | virtual void SetResonance(float r) override 57 | { 58 | resonance = r * (t2 + 6.0 * t1) / (t2 - 6.0 * t1); 59 | } 60 | 61 | virtual void SetCutoff(float c) override 62 | { 63 | cutoff = 2.0 * c / sampleRate; 64 | 65 | p = cutoff * (1.8 - 0.8 * cutoff); 66 | k = 2.0 * sin(cutoff * MOOG_PI * 0.5) - 1.0; 67 | t1 = (1.0 - p) * 1.386249; 68 | t2 = 12.0 + t1 * t1; 69 | 70 | SetResonance(resonance); 71 | } 72 | 73 | private: 74 | 75 | double stage[4]; 76 | double delay[4]; 77 | 78 | double p; 79 | double k; 80 | double t1; 81 | double t2; 82 | 83 | }; 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /MoogLadders.vcxproj/ExampleMain.cpp: -------------------------------------------------------------------------------- 1 | #if defined(_MSC_VER) 2 | #pragma comment(lib, "dsound.lib") 3 | #endif 4 | 5 | #include "AudioDevice.h" 6 | #include "NoiseGenerator.h" 7 | 8 | #include "StilsonModel.h" 9 | #include "OberheimVariationModel.h" 10 | #include "SimplifiedModel.h" 11 | #include "ImprovedModel.h" 12 | #include "HuovilainenModel.h" 13 | #include "KrajeskiModel.h" 14 | #include "RKSimulationModel.h" 15 | #include "MicrotrackerModel.h" 16 | #include "MusicDSPModel.h" 17 | #include "RKSimulationModel.h" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | int main() 24 | { 25 | AudioDevice::ListAudioDevices(); 26 | 27 | int desiredSampleRate = 44100; 28 | int desiredChannelCount = 2; 29 | AudioDevice device(desiredChannelCount, desiredSampleRate); 30 | device.Open(device.info.id); 31 | 32 | NoiseGenerator gen; 33 | 34 | std::vector noiseSamples = gen.produce(NoiseGenerator::NoiseType::WHITE, desiredSampleRate, desiredChannelCount, 3.0); 35 | 36 | StilsonMoog stilsonModel(desiredSampleRate); 37 | //stilsonModel.Process(noiseSamples.data(), noiseSamples.size()); 38 | 39 | SimplifiedMoog simplifiedModel(desiredSampleRate); 40 | //simplifiedModel.Process(noiseSamples.data(), noiseSamples.size()); 41 | 42 | HuovilainenMoog huovilainenModel(desiredSampleRate); 43 | //huovilainenModel.Process(noiseSamples.data(), noiseSamples.size()); 44 | 45 | ImprovedMoog improvedModel(desiredSampleRate); 46 | //improvedModel.Process(noiseSamples.data(), noiseSamples.size()); 47 | 48 | MicrotrackerMoog microtrackerModel(desiredSampleRate); 49 | //microtrackerModel.Process(noiseSamples.data(), noiseSamples.size()); 50 | 51 | MusicDSPMoog musicdspModel(desiredSampleRate); 52 | //musicdspModel.Process(noiseSamples.data(), noiseSamples.size()); 53 | 54 | KrajeskiMoog aaronModel(desiredSampleRate); 55 | //aaronModel.Process(noiseSamples.data(), noiseSamples.size()); 56 | 57 | RKSimulationMoog rkModel(desiredSampleRate); 58 | //rkModel.Process(noiseSamples.data(), noiseSamples.size()); 59 | 60 | OberheimVariationMoog oberheimModel(desiredSampleRate); 61 | //oberheimModel.Process(noiseSamples.data(), noiseSamples.size()); 62 | 63 | device.Play(noiseSamples); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /src/KrajeskiModel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef KRAJESKI_LADDER_H 4 | #define KRAJESKI_LADDER_H 5 | 6 | #include "LadderFilterBase.h" 7 | #include "Util.h" 8 | 9 | /* 10 | This class implements Tim Stilson's MoogVCF filter 11 | using 'compromise' poles at z = -0.3 12 | 13 | Several improments are built in, such as corrections 14 | for cutoff and resonance parameters, removal of the 15 | necessity of the separation table, audio rate update 16 | of cutoff and resonance and a smoothly saturating 17 | tanh() function, clamping output and creating inherent 18 | nonlinearities. 19 | 20 | This code is Unlicensed (i.e. public domain); in an email exchange on 21 | 4.21.2018 Aaron Krajeski stated: "That work is under no copyright. 22 | You may use it however you might like." 23 | 24 | Source: http://song-swap.com/MUMT618/aaron/Presentation/demo.html 25 | */ 26 | 27 | class KrajeskiMoog final : public LadderFilterBase 28 | { 29 | 30 | public: 31 | 32 | KrajeskiMoog(float sampleRate) : LadderFilterBase(sampleRate) 33 | { 34 | memset(state, 0, sizeof(state)); 35 | memset(delay, 0, sizeof(delay)); 36 | 37 | drive = 1.0; 38 | gComp = 1.0; 39 | 40 | SetCutoff(1000.0f); 41 | SetResonance(0.1f); 42 | } 43 | 44 | virtual ~KrajeskiMoog() { } 45 | 46 | virtual void Process(float * samples, const uint32_t n) override 47 | { 48 | for (int s = 0; s < n; ++s) 49 | { 50 | state[0] = tanh(drive * (samples[s] - 4 * gRes * (state[4] - gComp * samples[s]))); 51 | 52 | for(int i = 0; i < 4; i++) 53 | { 54 | state[i+1] = fclamp(g * (0.3 / 1.3 * state[i] + 1 / 1.3 * delay[i] - state[i + 1]) + state[i + 1], -1e30, 1e30); 55 | 56 | delay[i] = state[i]; 57 | } 58 | samples[s] = state[4]; 59 | } 60 | } 61 | 62 | virtual void SetResonance(float r) override 63 | { 64 | resonance = r; 65 | gRes = resonance * (1.0029 + 0.0526 * wc - 0.926 * pow(wc, 2) + 0.0218 * pow(wc, 3)); 66 | } 67 | 68 | virtual void SetCutoff(float c) override 69 | { 70 | cutoff = c; 71 | wc = 2 * MOOG_PI * cutoff / sampleRate; 72 | g = 0.9892 * wc - 0.4342 * pow(wc, 2) + 0.1381 * pow(wc, 3) - 0.0202 * pow(wc, 4); 73 | } 74 | 75 | private: 76 | 77 | double state[5]; 78 | double delay[5]; 79 | double wc; // The angular frequency of the cutoff. 80 | double g; // A derived parameter for the cutoff frequency 81 | double gRes; // A similar derived parameter for resonance. 82 | double gComp; // Compensation factor. 83 | double drive; // A parameter that controls intensity of nonlinearities. 84 | 85 | inline float fclamp(float in, float min, float max){ 86 | return fmin(fmax(in, min), max); 87 | } 88 | 89 | }; 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /src/AudioDevice.cpp: -------------------------------------------------------------------------------- 1 | #include "AudioDevice.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static RingBufferT buffer(BUFFER_LENGTH); 9 | 10 | static int rt_callback(void * output_buffer, void * input_buffer, unsigned int num_bufferframes, double stream_time, RtAudioStreamStatus status, void * user_data) 11 | { 12 | if (status) std::cerr << "[rtaudio] Buffer over or underflow" << std::endl; 13 | 14 | if (buffer.getAvailableRead()) 15 | { 16 | buffer.read((float*) output_buffer, BUFFER_LENGTH); 17 | } 18 | else 19 | { 20 | memset(output_buffer, 0, BUFFER_LENGTH * sizeof(float)); 21 | } 22 | 23 | return 0; 24 | } 25 | 26 | AudioDevice::AudioDevice(int numChannels, int sampleRate, int deviceId) 27 | { 28 | rtaudio = std::unique_ptr(new RtAudio); 29 | info.id = deviceId != -1 ? deviceId : rtaudio->getDefaultOutputDevice(); 30 | info.numChannels = numChannels; 31 | info.sampleRate = sampleRate; 32 | info.frameSize = FRAME_SIZE; 33 | } 34 | 35 | AudioDevice::~AudioDevice() 36 | { 37 | if (rtaudio) 38 | { 39 | rtaudio->stopStream(); 40 | if (rtaudio->isStreamOpen()) rtaudio->closeStream(); 41 | } 42 | } 43 | 44 | bool AudioDevice::Open(const int deviceId) 45 | { 46 | if (!rtaudio) throw std::runtime_error("rtaudio not created yet"); 47 | 48 | RtAudio::StreamParameters parameters; 49 | parameters.deviceId = info.id; 50 | parameters.nChannels = info.numChannels; 51 | parameters.firstChannel = 0; 52 | 53 | rtaudio->openStream(¶meters, NULL, RTAUDIO_FLOAT32, info.sampleRate, &info.frameSize, &rt_callback, (void*) & buffer); 54 | 55 | if (rtaudio->isStreamOpen()) 56 | { 57 | rtaudio->startStream(); 58 | return true; 59 | } 60 | 61 | return false; 62 | } 63 | 64 | void AudioDevice::ListAudioDevices() 65 | { 66 | std::unique_ptr tempDevice(new RtAudio); 67 | 68 | RtAudio::DeviceInfo info; 69 | unsigned int devices = tempDevice->getDeviceCount(); 70 | 71 | std::cout << "[rtaudio] Found: " << devices << " device(s)\n"; 72 | 73 | for (unsigned int i = 0; i < devices; ++i) 74 | { 75 | info = tempDevice->getDeviceInfo(i); 76 | std::cout << "\tDevice: " << i << " - " << info.name << std::endl; 77 | } 78 | std::cout << std::endl; 79 | } 80 | 81 | bool AudioDevice::Play(const std::vector & data) 82 | { 83 | if (!rtaudio->isStreamOpen()) return false; 84 | 85 | // Each frame is the (size/2) cause interleaved channels! 86 | int sizeInFrames = ((int) data.size()) / (BUFFER_LENGTH); 87 | 88 | int writeCount = 0; 89 | 90 | while(writeCount < sizeInFrames) 91 | { 92 | bool status = buffer.write((data.data() + (writeCount * BUFFER_LENGTH)), BUFFER_LENGTH); 93 | if (status) 94 | writeCount++; 95 | } 96 | 97 | return true; 98 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Moog Ladder Filters 2 | 3 | This project contains different digital implementations of the classic 4-pole, 24 dB/octave analog filter introduced in 1965. The filter is well-regarded to add a nice character to any sound source, either synthesized or acoustic. 4 | 5 | The ladder structure consists of four one-pole filters and a global negative feedback loop. Several academics have attempted to discretize this filter, and some academic publications on the topic can be found in the `research/` directory. 6 | 7 | The filter classes do not rely on external libraries and can be used with little to no modification in other DSP projects. Every filter has been modified from its original implementation for code clarity and/or runtime performance. The project includes a test app that will pass white noise through each of the implemented filter variants. 8 | 9 | # Filter Tuning & A Word of Warning 10 | Each model is unique. The newest is from 2015 while the oldest dates back over 20 years. Some try to remain true to their analog counterpart, where others are more approximate. The filters have not been rigorously verified for all combinations of cutoff, resonance, and sampling rate. Some are purposely built to self-oscillate, but beware the occasional blow-up with parameters that exceed some undiscovered value. 11 | 12 | # Models & Licenses 13 | 14 | “Closed-Source Friendly” indicates if the individual license permits redistribution in a closed-source product (like a VST plugin). Filtered output audio is fair game for any kind of sample library or music production, commercial or otherwise. In the case of copyright-only code, it is possible to contact the original author to request an explicit license. 15 | 16 | Implementation | License | Original Source | Closed-Source Friendly 17 | ------------- | ------------- | ----------------- | ----------------- 18 | Simplified | Custom | DAFX | No 19 | Huovilainen | LGPLv3 | CSound | If dynamically linked 20 | Stilson | Unlicense | Moog~ by D. Lowenfels | Yes 21 | Microtracker | Unlicense | Magnus Jonsson | Yes 22 | Krajeski | Unlicense | Aaron Krajeski | Yes 23 | MusicDSP | Suggested CC-BY-SA | MusicDSP.org | Yes 24 | Oberheim | Custom | Will Pirkle | Yes 25 | Improved | ISC | Via Author | Yes 26 | RKSimulation | BSD | Bob~ by Miller Puckette | Yes 27 | 28 | # ToDo 29 | 30 | Community contributions are welcome. 31 | 32 | * Several filters have extra parameters that could be exposed (drive, thermal coefficients, Q, etc). 33 | * Many filters could be easily modified for HPF or other types of output. 34 | * Filter response graphs. 35 | * The Huovilainen and Simplified models need to be oversampled and nyquist filtered. 36 | 37 | # License 38 | If not otherwise stated in the header of a file, all other code in this project is released under the Unlicense. 39 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef MOOG_UTIL_H 4 | #define MOOG_UTIL_H 5 | 6 | #include 7 | #include 8 | 9 | #define MOOG_E 2.71828182845904523536028747135266250 10 | #define MOOG_LOG2E 1.44269504088896340735992468100189214 11 | #define MOOG_LOG10E 0.434294481903251827651128918916605082 12 | #define MOOG_LN2 0.693147180559945309417232121458176568 13 | #define MOOG_LN10 2.30258509299404568401799145468436421 14 | #define MOOG_PI 3.14159265358979323846264338327950288 15 | #define MOOG_PI_2 1.57079632679489661923132169163975144 16 | #define MOOG_PI_4 0.785398163397448309615660845819875721 17 | #define MOOG_1_PI 0.318309886183790671537767526745028724 18 | #define MOOG_2_PI 0.636619772367581343075535053490057448 19 | #define MOOG_2_SQRTPI 1.12837916709551257389615890312154517 20 | #define MOOG_SQRT2 1.41421356237309504880168872420969808 21 | #define MOOG_SQRT1_2 0.707106781186547524400844362104849039 22 | #define MOOG_INV_PI_2 0.159154943091895 23 | 24 | #define NO_COPY(C) C(const C &) = delete; C & operator = (const C &) = delete 25 | #define NO_MOVE(C) NO_COPY(C); C(C &&) = delete; C & operator = (const C &&) = delete 26 | 27 | #define SNAP_TO_ZERO(n) if (! (n < -1.0e-8 || n > 1.0e-8)) n = 0; 28 | 29 | // Linear interpolation, used to crossfade a gain table 30 | inline float moog_lerp(float amount, float a, float b) 31 | { 32 | return (1.0f - amount) * a + amount * b; 33 | } 34 | 35 | inline float moog_min(float a, float b) 36 | { 37 | a = b - a; 38 | a += fabs(a); 39 | a *= 0.5f; 40 | a = b - a; 41 | return a; 42 | } 43 | 44 | // Clamp without branching 45 | // If input - _limit < 0, then it really substracts, and the 0.5 to make it half the 2 inputs. 46 | // If > 0 then they just cancel, and keeps input normal. 47 | // The easiest way to understand it is check what happends on both cases. 48 | inline float moog_saturate(float input) 49 | { 50 | float x1 = fabs(input + 0.95f); 51 | float x2 = fabs(input - 0.95f); 52 | return 0.5f * (x1 - x2); 53 | } 54 | 55 | // Imitate the (tanh) clipping function of a transistor pair. 56 | // to 4th order, tanh is x - x*x*x/3; this cubic's 57 | // plateaus are at +/- 1 so clip to 1 and evaluate the cubic. 58 | // This is pretty coarse - for instance if you clip a sinusoid this way you 59 | // can sometimes hear the discontinuity in 4th derivative at the clip point 60 | inline float clip(float value, float saturation, float saturationinverse) 61 | { 62 | float v2 = (value * saturationinverse > 1 ? 1 : 63 | (value * saturationinverse < -1 ? -1: 64 | value * saturationinverse)); 65 | return (saturation * (v2 - (1./3.) * v2 * v2 * v2)); 66 | } 67 | 68 | #define HZ_TO_RAD(f) (MOOG_PI_2 * f) 69 | #define RAD_TO_HZ(omega) (MOOG_INV_PI_2 * omega) 70 | 71 | #ifdef __GNUC__ 72 | #define ctz(N) __builtin_ctz(N) 73 | #else 74 | template 75 | inline int ctz(T x) 76 | { 77 | int p, b; 78 | for (p = 0, b = 1; !(b & x); b <<= 1, ++p) 79 | ; 80 | return p; 81 | } 82 | #endif 83 | 84 | inline double fast_tanh(double x) 85 | { 86 | double x2 = x * x; 87 | return x * (27.0 + x2) / (27.0 + 9.0 * x2); 88 | } 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /MoogLadders.vcxproj/MoogLadders.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {edf3460b-f488-448c-8c3f-85ffe776786c} 6 | 7 | 8 | {d70aecb5-ab52-436d-81c8-be23540f54e4} 9 | 10 | 11 | {ccf12589-b40a-4e34-988a-4d034139672e} 12 | 13 | 14 | {59f10d26-05be-481b-814b-519c15e7edec} 15 | 16 | 17 | {ea010cd3-b299-4159-9c1a-0182a3804524} 18 | 19 | 20 | 21 | 22 | third_party\RtAudio 23 | 24 | 25 | source\extra 26 | 27 | 28 | source 29 | 30 | 31 | 32 | 33 | third_party\RtAudio 34 | 35 | 36 | source\extra 37 | 38 | 39 | source\extra 40 | 41 | 42 | source\extra 43 | 44 | 45 | source\extra 46 | 47 | 48 | source\models 49 | 50 | 51 | source\models 52 | 53 | 54 | source\models 55 | 56 | 57 | source\models 58 | 59 | 60 | source\models 61 | 62 | 63 | source\models 64 | 65 | 66 | source\extra 67 | 68 | 69 | source\models 70 | 71 | 72 | source\models 73 | 74 | 75 | source\models 76 | 77 | 78 | source\models 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/ImprovedModel.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012 Stefano D'Angelo 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #ifndef IMPROVED_LADDER_H 20 | #define IMPROVED_LADDER_H 21 | 22 | #include "LadderFilterBase.h" 23 | 24 | /* 25 | This model is based on a reference implementation of an algorithm developed by 26 | Stefano D'Angelo and Vesa Valimaki, presented in a paper published at ICASSP in 2013. 27 | This improved model is based on a circuit analysis and compared against a reference 28 | Ngspice simulation. In the paper, it is noted that this particular model is 29 | more accurate in preserving the self-oscillating nature of the real filter. 30 | 31 | References: "An Improved Virtual Analog Model of the Moog Ladder Filter" 32 | Original Implementation: D'Angelo, Valimaki 33 | */ 34 | 35 | // Thermal voltage (26 milliwats at room temperature) 36 | #define VT 0.312 37 | 38 | class ImprovedMoog : public LadderFilterBase 39 | { 40 | public: 41 | 42 | ImprovedMoog(float sampleRate) : LadderFilterBase(sampleRate) 43 | { 44 | memset(V, 0, sizeof(V)); 45 | memset(dV, 0, sizeof(dV)); 46 | memset(tV, 0, sizeof(tV)); 47 | 48 | drive = 1.0f; 49 | 50 | SetCutoff(1000.0f); // normalized cutoff frequency 51 | SetResonance(0.1f); // [0, 4] 52 | } 53 | 54 | virtual ~ImprovedMoog() { } 55 | 56 | virtual void Process(float * samples, uint32_t n) override 57 | { 58 | double dV0, dV1, dV2, dV3; 59 | 60 | for (int i = 0; i < n; i++) 61 | { 62 | dV0 = -g * (tanh((drive * samples[i] + resonance * V[3]) / (2.0 * VT)) + tV[0]); 63 | V[0] += (dV0 + dV[0]) / (2.0 * sampleRate); 64 | dV[0] = dV0; 65 | tV[0] = tanh(V[0] / (2.0 * VT)); 66 | 67 | dV1 = g * (tV[0] - tV[1]); 68 | V[1] += (dV1 + dV[1]) / (2.0 * sampleRate); 69 | dV[1] = dV1; 70 | tV[1] = tanh(V[1] / (2.0 * VT)); 71 | 72 | dV2 = g * (tV[1] - tV[2]); 73 | V[2] += (dV2 + dV[2]) / (2.0 * sampleRate); 74 | dV[2] = dV2; 75 | tV[2] = tanh(V[2] / (2.0 * VT)); 76 | 77 | dV3 = g * (tV[2] - tV[3]); 78 | V[3] += (dV3 + dV[3]) / (2.0 * sampleRate); 79 | dV[3] = dV3; 80 | tV[3] = tanh(V[3] / (2.0 * VT)); 81 | 82 | samples[i] = V[3]; 83 | } 84 | } 85 | 86 | virtual void SetResonance(float r) override 87 | { 88 | resonance = r; 89 | } 90 | 91 | virtual void SetCutoff(float c) override 92 | { 93 | cutoff = c; 94 | x = (MOOG_PI * cutoff) / sampleRate; 95 | g = 4.0 * MOOG_PI * VT * cutoff * (1.0 - x) / (1.0 + x); 96 | } 97 | 98 | private: 99 | 100 | double V[4]; 101 | double dV[4]; 102 | double tV[4]; 103 | 104 | double x; 105 | double g; 106 | double drive; 107 | }; 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /src/HuovilainenModel.h: -------------------------------------------------------------------------------- 1 | // Based on implementation in CSound5 (LGPLv2.1) 2 | // https://github.com/csound/csound/blob/develop/COPYING 3 | 4 | #pragma once 5 | 6 | #ifndef HUOVILAINEN_LADDER_H 7 | #define HUOVILAINEN_LADDER_H 8 | 9 | #include "LadderFilterBase.h" 10 | 11 | /* 12 | Huovilainen developed an improved and physically correct model of the Moog 13 | Ladder filter that builds upon the work done by Smith and Stilson. This model 14 | inserts nonlinearities inside each of the 4 one-pole sections on account of the 15 | smoothly saturating function of analog transistors. The base-emitter voltages of 16 | the transistors are considered with an experimental value of 1.22070313 which 17 | maintains the characteristic sound of the analog Moog. This model also permits 18 | self-oscillation for resonances greater than 1. The model depends on five 19 | hyperbolic tangent functions (tanh) for each sample, and an oversampling factor 20 | of two (preferably higher, if possible). Although a more faithful 21 | representation of the Moog ladder, these dependencies increase the processing 22 | time of the filter significantly. Lastly, a half-sample delay is introduced for 23 | phase compensation at the final stage of the filter. 24 | 25 | References: Huovilainen (2004), Huovilainen (2010), DAFX - Zolzer (ed) (2nd ed) 26 | Original implementation: Victor Lazzarini for CSound5 27 | 28 | Considerations for oversampling: 29 | http://music.columbia.edu/pipermail/music-dsp/2005-February/062778.html 30 | http://www.synthmaker.co.uk/dokuwiki/doku.php?id=tutorials:oversampling 31 | */ 32 | 33 | class HuovilainenMoog : public LadderFilterBase 34 | { 35 | public: 36 | 37 | HuovilainenMoog(float sampleRate) : LadderFilterBase(sampleRate), thermal(0.000025) 38 | { 39 | memset(stage, 0, sizeof(stage)); 40 | memset(delay, 0, sizeof(delay)); 41 | memset(stageTanh, 0, sizeof(stageTanh)); 42 | SetCutoff(1000.0f); 43 | SetResonance(0.10f); 44 | } 45 | 46 | virtual ~HuovilainenMoog() 47 | { 48 | 49 | } 50 | 51 | virtual void Process(float * samples, uint32_t n) override 52 | { 53 | for (int s = 0; s < n; ++s) 54 | { 55 | // Oversample 56 | for (int j = 0; j < 2; j++) 57 | { 58 | float input = samples[s] - resQuad * delay[5]; 59 | delay[0] = stage[0] = delay[0] + tune * (tanh(input * thermal) - stageTanh[0]); 60 | for (int k = 1; k < 4; k++) 61 | { 62 | input = stage[k-1]; 63 | stage[k] = delay[k] + tune * ((stageTanh[k-1] = tanh(input * thermal)) - (k != 3 ? stageTanh[k] : tanh(delay[k] * thermal))); 64 | delay[k] = stage[k]; 65 | } 66 | // 0.5 sample delay for phase compensation 67 | delay[5] = (stage[3] + delay[4]) * 0.5; 68 | delay[4] = stage[3]; 69 | } 70 | samples[s] = delay[5]; 71 | } 72 | 73 | } 74 | 75 | virtual void SetResonance(float r) override 76 | { 77 | resonance = r; 78 | resQuad = 4.0 * resonance * acr; 79 | } 80 | 81 | virtual void SetCutoff(float c) override 82 | { 83 | cutoff = c; 84 | 85 | double fc = cutoff / sampleRate; 86 | double f = fc * 0.5; // oversampled 87 | double fc2 = fc * fc; 88 | double fc3 = fc * fc * fc; 89 | 90 | double fcr = 1.8730 * fc3 + 0.4955 * fc2 - 0.6490 * fc + 0.9988; 91 | acr = -3.9364 * fc2 + 1.8409 * fc + 0.9968; 92 | 93 | tune = (1.0 - exp(-((2 * MOOG_PI) * f * fcr))) / thermal; 94 | 95 | SetResonance(resonance); 96 | } 97 | 98 | private: 99 | 100 | double stage[4]; 101 | double stageTanh[3]; 102 | double delay[6]; 103 | 104 | double thermal; 105 | double tune; 106 | double acr; 107 | double resQuad; 108 | 109 | }; 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /src/OberheimVariationModel.h: -------------------------------------------------------------------------------- 1 | // See: http://www.willpirkle.com/forum/licensing-and-book-code/licensing-and-using-book-code/ 2 | // The license is "You may also use the code from the FX and Synth books without licensing or fees. 3 | // The code is for you to develop your own plugins for your own use or for commercial use." 4 | 5 | #pragma once 6 | 7 | #ifndef OBERHEIM_VARIATION_LADDER_H 8 | #define OBERHEIM_VARIATION_LADDER_H 9 | 10 | #include "LadderFilterBase.h" 11 | #include "Util.h" 12 | 13 | class VAOnePole 14 | { 15 | public: 16 | 17 | VAOnePole(float sr) : sampleRate(sr) 18 | { 19 | Reset(); 20 | } 21 | 22 | void Reset() 23 | { 24 | alpha = 1.0; 25 | beta = 0.0; 26 | gamma = 1.0; 27 | delta = 0.0; 28 | epsilon = 0.0; 29 | a0 = 1.0; 30 | feedback = 0.0; 31 | z1 = 0.0; 32 | } 33 | 34 | double Tick(double s) 35 | { 36 | s = s * gamma + feedback + epsilon * GetFeedbackOutput(); 37 | double vn = (a0 * s - z1) * alpha; 38 | double out = vn + z1; 39 | z1 = vn + out; 40 | return out; 41 | } 42 | 43 | void SetFeedback(double fb) { feedback = fb; } 44 | double GetFeedbackOutput(){ return beta * (z1 + feedback * delta); } 45 | void SetAlpha(double a) { alpha = a; }; 46 | void SetBeta(double b) { beta = b; }; 47 | 48 | private: 49 | 50 | float sampleRate; 51 | double alpha; 52 | double beta; 53 | double gamma; 54 | double delta; 55 | double epsilon; 56 | double a0; 57 | double feedback; 58 | double z1; 59 | }; 60 | 61 | class OberheimVariationMoog : public LadderFilterBase 62 | { 63 | 64 | public: 65 | 66 | OberheimVariationMoog(float sampleRate) : LadderFilterBase(sampleRate) 67 | { 68 | LPF1 = new VAOnePole(sampleRate); 69 | LPF2 = new VAOnePole(sampleRate); 70 | LPF3 = new VAOnePole(sampleRate); 71 | LPF4 = new VAOnePole(sampleRate); 72 | 73 | saturation = 1.0; 74 | Q = 3.0; 75 | 76 | SetCutoff(1000.f); 77 | SetResonance(0.1f); 78 | } 79 | 80 | virtual ~OberheimVariationMoog() 81 | { 82 | delete LPF1; 83 | delete LPF2; 84 | delete LPF3; 85 | delete LPF4; 86 | } 87 | 88 | virtual void Process(float * samples, uint32_t n) noexcept override 89 | { 90 | for (int s = 0; s < n; ++s) 91 | { 92 | float input = samples[s]; 93 | 94 | double sigma = 95 | LPF1->GetFeedbackOutput() + 96 | LPF2->GetFeedbackOutput() + 97 | LPF3->GetFeedbackOutput() + 98 | LPF4->GetFeedbackOutput(); 99 | 100 | input *= 1.0 + K; 101 | 102 | // calculate input to first filter 103 | double u = (input - K * sigma) * alpha0; 104 | 105 | u = tanh(saturation * u); 106 | 107 | double stage1 = LPF1->Tick(u); 108 | double stage2 = LPF2->Tick(stage1); 109 | double stage3 = LPF3->Tick(stage2); 110 | double stage4 = LPF4->Tick(stage3); 111 | 112 | // Oberheim variations 113 | samples[s] = 114 | oberheimCoefs[0] * u + 115 | oberheimCoefs[1] * stage1 + 116 | oberheimCoefs[2] * stage2 + 117 | oberheimCoefs[3] * stage3 + 118 | oberheimCoefs[4] * stage4; 119 | } 120 | } 121 | 122 | virtual void SetResonance(float r) override 123 | { 124 | // this maps resonance = 1->10 to K = 0 -> 4 125 | K = (4.0) * (r - 1.0)/(10.0 - 1.0); 126 | } 127 | 128 | virtual void SetCutoff(float c) override 129 | { 130 | cutoff = c; 131 | 132 | // prewarp for BZT 133 | double wd = 2.0 * MOOG_PI * cutoff; 134 | double T = 1.0 / sampleRate; 135 | double wa = (2.0 / T) * tan(wd * T / 2.0); 136 | double g = wa * T / 2.0; 137 | 138 | // Feedforward coeff 139 | double G = g / (1.0 + g); 140 | 141 | LPF1->SetAlpha(G); 142 | LPF2->SetAlpha(G); 143 | LPF3->SetAlpha(G); 144 | LPF4->SetAlpha(G); 145 | 146 | LPF1->SetBeta(G*G*G / (1.0 + g)); 147 | LPF2->SetBeta(G*G / (1.0 + g)); 148 | LPF3->SetBeta(G / (1.0 + g)); 149 | LPF4->SetBeta(1.0 / (1.0 + g)); 150 | 151 | gamma = G*G*G*G; 152 | alpha0 = 1.0 / (1.0 + K * gamma); 153 | 154 | // Oberheim variations / LPF4 155 | oberheimCoefs[0] = 0.0; 156 | oberheimCoefs[1] = 0.0; 157 | oberheimCoefs[2] = 0.0; 158 | oberheimCoefs[3] = 0.0; 159 | oberheimCoefs[4] = 1.0; 160 | } 161 | 162 | private: 163 | 164 | VAOnePole * LPF1; 165 | VAOnePole * LPF2; 166 | VAOnePole * LPF3; 167 | VAOnePole * LPF4; 168 | 169 | double K; 170 | double gamma; 171 | double alpha0; 172 | double Q; 173 | double saturation; 174 | 175 | double oberheimCoefs[5]; 176 | }; 177 | 178 | #endif 179 | -------------------------------------------------------------------------------- /src/SimplifiedModel.h: -------------------------------------------------------------------------------- 1 | /* ------------------------------------------------------------------------- 2 | * This source code is provided without any warranties as published in 3 | * DAFX book 2nd edition, copyright Wiley & Sons 2011, available at 4 | * http://www.dafx.de. It may be used for educational purposes and not 5 | * for commercial applications without further permission. 6 | * ------------------------------------------------------------------------- 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifndef SIMPLIFIED_LADDER_H 12 | #define SIMPLIFIED_LADDER_H 13 | 14 | #include "LadderFilterBase.h" 15 | 16 | /* 17 | The simplified nonlinear Moog filter is based on the full Huovilainen model, 18 | with five nonlinear (tanh) functions (4 first-order sections and a feedback). 19 | Like the original, this model needs an oversampling factor of at least two when 20 | these nonlinear functions are used to reduce possible aliasing. This model 21 | maintains the ability to self oscillate when the feedback gain is >= 1.0. 22 | 23 | References: DAFX - Zolzer (ed) (2nd ed) 24 | Original implementation: Valimaki, Bilbao, Smith, Abel, Pakarinen, Berners (DAFX) 25 | This is a transliteration into C++ of the original matlab source (moogvcf.m) 26 | 27 | Considerations for oversampling: 28 | http://music.columbia.edu/pipermail/music-dsp/2005-February/062778.html 29 | http://www.synthmaker.co.uk/dokuwiki/doku.php?id=tutorials:oversampling 30 | */ 31 | 32 | class SimplifiedMoog : public LadderFilterBase 33 | { 34 | public: 35 | 36 | SimplifiedMoog(float sampleRate) : LadderFilterBase(sampleRate) 37 | { 38 | // To keep the overall level approximately constant, comp should be set 39 | // to 0.5 resulting in a 6 dB passband gain decrease at the maximum resonance 40 | // (compared to a 12 dB decrease in the original Moog model 41 | gainCompensation = 0.5; 42 | 43 | memset(stage, 0, sizeof(stage)); 44 | memset(stageZ1, 0, sizeof(stageZ1)); 45 | memset(stageTanh, 0, sizeof(stageTanh)); 46 | 47 | SetCutoff(1000.0f); 48 | SetResonance(0.10f); 49 | } 50 | 51 | virtual ~SimplifiedMoog() 52 | { 53 | 54 | } 55 | 56 | // This system is nonlinear so we are probably going to create a signal with components that exceed nyquist. 57 | // To prevent aliasing distortion, we need to oversample this processing chunk. Where do these extra samples 58 | // come from? Todo! We can use polynomial interpolation to generate the extra samples, but this is expensive. 59 | // The cheap solution is to zero-stuff the incoming sample buffer. 60 | // With resampling, numSamples should be 2x the frame size of the existing sample rate. 61 | // The output of this filter needs to be run through a decimator to return to the original samplerate. 62 | virtual void Process(float * samples, uint32_t n) override 63 | { 64 | // Processing still happens at sample rate... 65 | for (int s = 0; s < n; ++s) 66 | { 67 | for (int stageIdx = 0; stageIdx < 4; ++stageIdx) 68 | { 69 | if (stageIdx) 70 | { 71 | input = stage[stageIdx-1]; 72 | stageTanh[stageIdx-1] = tanh(input); 73 | stage[stageIdx] = (h * stageZ1[stageIdx] + h0 * stageTanh[stageIdx-1]) + (1.0 - g) * (stageIdx != 3 ? stageTanh[stageIdx] : tanh(stageZ1[stageIdx])); 74 | } 75 | else 76 | { 77 | input = samples[s] - ((4.0 * resonance) * (output - gainCompensation * samples[s])); 78 | stage[stageIdx] = (h * tanh(input) + h0 * stageZ1[stageIdx]) + (1.0 - g) * stageTanh[stageIdx]; 79 | } 80 | 81 | stageZ1[stageIdx] = stage[stageIdx]; 82 | } 83 | 84 | output = stage[3]; 85 | SNAP_TO_ZERO(output); 86 | samples[s] = output; 87 | } 88 | } 89 | 90 | virtual void SetResonance(float r) override 91 | { 92 | resonance = r; 93 | } 94 | 95 | virtual void SetCutoff(float c) override 96 | { 97 | cutoff = c; 98 | 99 | // Not being oversampled at the moment... * 2 when functional 100 | float fs2 = sampleRate; 101 | 102 | // Normalized cutoff [0, 1] in radians: ((2*pi) * cutoff / samplerate) 103 | g = (2 * MOOG_PI) * cutoff / fs2; // feedback coefficient at fs*2 because of doublesampling 104 | g *= MOOG_PI / 1.3; // correction factor that allows _cutoff to be supplied Hertz 105 | 106 | // FIR part with gain g 107 | h = g / 1.3; 108 | h0 = g * 0.3 / 1.3; 109 | } 110 | 111 | private: 112 | 113 | double output; 114 | double lastStage; 115 | 116 | double stage[4]; 117 | double stageZ1[4]; 118 | double stageTanh[3]; 119 | 120 | double input; 121 | double h; 122 | double h0; 123 | double g; 124 | 125 | float gainCompensation; 126 | }; 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /src/RKSimulationModel.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Miller Puckette. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #pragma once 27 | 28 | #ifndef RK_SIMULATION_LADDER_H 29 | #define RK_SIMULATION_LADDER_H 30 | 31 | #include "LadderFilterBase.h" 32 | #include "Util.h" 33 | 34 | /* 35 | Imitates a Moog resonant filter by Runge-Kutte numerical integration of 36 | a differential equation approximately describing the dynamics of the circuit. 37 | 38 | Useful references: 39 | 40 | * Tim Stilson 41 | "Analyzing the Moog VCF with Considerations for Digital Implementation" 42 | Sections 1 and 2 are a reasonably good introduction but the 43 | model they use is highly idealized. 44 | 45 | * Timothy E. Stinchcombe 46 | "Analysis of the Moog Transistor Ladder and Derivative Filters" 47 | Long, but a very thorough description of how the filter works including 48 | its nonlinearities 49 | 50 | * Antti Huovilainen 51 | "Non-linear digital implementation of the moog ladder filter" 52 | Comes close to giving a differential equation for a reasonably realistic 53 | model of the filter 54 | 55 | The differential equations are: 56 | 57 | y1' = k * (S(x - r * y4) - S(y1)) 58 | y2' = k * (S(y1) - S(y2)) 59 | y3' = k * (S(y2) - S(y3)) 60 | y4' = k * (S(y3) - S(y4)) 61 | 62 | where k controls the cutoff frequency, r is feedback (<= 4 for stability), and S(x) is a saturation function. 63 | */ 64 | 65 | class RKSimulationMoog : public LadderFilterBase 66 | { 67 | 68 | public: 69 | 70 | RKSimulationMoog(float sampleRate) : LadderFilterBase(sampleRate) 71 | { 72 | memset(state, 0, sizeof(state)); 73 | 74 | saturation = 3.0; 75 | saturationInv = 1.0 / saturation; 76 | 77 | oversampleFactor = 1; 78 | 79 | stepSize = 1.0 / (oversampleFactor * sampleRate); 80 | 81 | SetCutoff(1000.f); 82 | SetResonance(1.0f); 83 | } 84 | 85 | virtual ~RKSimulationMoog() 86 | { 87 | } 88 | 89 | virtual void Process(float * samples, uint32_t n) override 90 | { 91 | for (int s = 0; s < n; ++s) 92 | { 93 | for (int j = 0; j < oversampleFactor; j++) 94 | { 95 | rungekutteSolver(samples[s], state); 96 | } 97 | 98 | samples[s] = state[3]; 99 | } 100 | } 101 | 102 | virtual void SetResonance(float r) override 103 | { 104 | // 0 to 10 105 | resonance = r; 106 | } 107 | 108 | virtual void SetCutoff(float c) override 109 | { 110 | cutoff = (2.0 * MOOG_PI * c); 111 | } 112 | 113 | private: 114 | 115 | void calculateDerivatives(float input, double * dstate, double * state) 116 | { 117 | double satstate0 = clip(state[0], saturation, saturationInv); 118 | double satstate1 = clip(state[1], saturation, saturationInv); 119 | double satstate2 = clip(state[2], saturation, saturationInv); 120 | 121 | dstate[0] = cutoff * (clip(input - resonance * state[3], saturation, saturationInv) - satstate0); 122 | dstate[1] = cutoff * (satstate0 - satstate1); 123 | dstate[2] = cutoff * (satstate1 - satstate2); 124 | dstate[3] = cutoff * (satstate2 - clip(state[3], saturation, saturationInv)); 125 | } 126 | 127 | void rungekutteSolver(float input, double * state) 128 | { 129 | int i; 130 | double deriv1[4], deriv2[4], deriv3[4], deriv4[4], tempState[4]; 131 | 132 | calculateDerivatives(input, deriv1, state); 133 | 134 | for (i = 0; i < 4; i++) 135 | tempState[i] = state[i] + 0.5 * stepSize * deriv1[i]; 136 | 137 | calculateDerivatives(input, deriv2, tempState); 138 | 139 | for (i = 0; i < 4; i++) 140 | tempState[i] = state[i] + 0.5 * stepSize * deriv2[i]; 141 | 142 | calculateDerivatives(input, deriv3, tempState); 143 | 144 | for (i = 0; i < 4; i++) 145 | tempState[i] = state[i] + stepSize * deriv3[i]; 146 | 147 | calculateDerivatives(input, deriv4, tempState); 148 | 149 | for (i = 0; i < 4; i++) 150 | state[i] += (1.0 / 6.0) * stepSize * (deriv1[i] + 2.0 * deriv2[i] + 2.0 * deriv3[i] + deriv4[i]); 151 | } 152 | 153 | double state[4]; 154 | double saturation, saturationInv; 155 | int oversampleFactor; 156 | double stepSize; 157 | 158 | }; 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /src/StilsonModel.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2003, David Lowenfels 2 | // Released as the moog~ pd extern. 3 | 4 | // This code is Unlicensed (i.e. public domain); in an email exchange on 5 | // 6.14.2019 David Lowenfels stated "You're welcome to use the Moog~ code, 6 | // license it free as in beer or whatever :)" 7 | 8 | #pragma once 9 | 10 | #ifndef STILSON_LADDER_H 11 | #define STILSON_LADDER_H 12 | 13 | #include "LadderFilterBase.h" 14 | 15 | /* 16 | A digital model of the classic Moog filter was presented first by Stilson and 17 | Smith. This model uses a cascade of one-pole IIR filters in series with a global 18 | feedback to produce resonance. A digital realization of this filter introduces a 19 | unit delay, effectively making it a fifth-order filter. Unfortunately, this 20 | delay also has the effect of coupling the cutoff and resonance parameters, 21 | uncharacteristic of the uncoupled control of the original Moog ladder. As a 22 | compromise, a zero can be inserted at z = -0.3 inside each one pole section to 23 | minimize the coupling the parameters (humans are not particularly sensitive to 24 | variations in Q factor). Although fast coefficient updates can be achieved since 25 | the nonlinearities of the Moog are not considered, the filter becomes unstable 26 | with very large resonance values and does not enter self-oscillation. 27 | 28 | References: Stilson and Smith (1996), DAFX - Zolzer (ed) (2nd ed) 29 | Original implementation: Tim Stilson, David Lowenfels 30 | */ 31 | 32 | static float S_STILSON_GAINTABLE[199] = 33 | { 34 | 0.999969, 0.990082, 0.980347, 0.970764, 0.961304, 0.951996, 0.94281, 0.933777, 0.924866, 0.916077, 35 | 0.90741, 0.898865, 0.890442, 0.882141 , 0.873962, 0.865906, 0.857941, 0.850067, 0.842346, 0.834686, 36 | 0.827148, 0.819733, 0.812378, 0.805145, 0.798004, 0.790955, 0.783997, 0.77713, 0.770355, 0.763672, 37 | 0.75708 , 0.75058, 0.744141, 0.737793, 0.731537, 0.725342, 0.719238, 0.713196, 0.707245, 0.701355, 38 | 0.695557, 0.689819, 0.684174, 0.678558, 0.673035, 0.667572, 0.66217, 0.65686, 0.651581, 0.646393, 39 | 0.641235, 0.636169, 0.631134, 0.62619, 0.621277, 0.616425, 0.611633, 0.606903, 0.602234, 0.597626, 40 | 0.593048, 0.588531, 0.584045, 0.579651, 0.575287 , 0.570953, 0.566681, 0.562469, 0.558289, 0.554169, 41 | 0.550079, 0.546051, 0.542053, 0.538116, 0.53421, 0.530334, 0.52652, 0.522736, 0.518982, 0.515289, 42 | 0.511627, 0.507996 , 0.504425, 0.500885, 0.497375, 0.493896, 0.490448, 0.487061, 0.483704, 0.480377, 43 | 0.477081, 0.473816, 0.470581, 0.467377, 0.464203, 0.46109, 0.457977, 0.454926, 0.451874, 0.448883, 44 | 0.445892, 0.442932, 0.440033, 0.437134, 0.434265, 0.431427, 0.428619, 0.425842, 0.423096, 0.42038, 45 | 0.417664, 0.415009, 0.412354, 0.409729, 0.407135, 0.404572, 0.402008, 0.399506, 0.397003, 0.394501, 46 | 0.392059, 0.389618, 0.387207, 0.384827, 0.382477, 0.380127, 0.377808, 0.375488, 0.37323, 0.370972, 47 | 0.368713, 0.366516, 0.364319, 0.362122, 0.359985, 0.357849, 0.355713, 0.353607, 0.351532, 0.349457, 48 | 0.347412, 0.345398, 0.343384, 0.34137, 0.339417, 0.337463, 0.33551, 0.333588, 0.331665, 0.329773, 49 | 0.327911, 0.32605, 0.324188, 0.322357, 0.320557, 0.318756, 0.316986, 0.315216, 0.313446, 0.311707, 50 | 0.309998, 0.308289, 0.30658, 0.304901, 0.303223, 0.301575, 0.299927, 0.298309, 0.296692, 0.295074, 51 | 0.293488, 0.291931, 0.290375, 0.288818, 0.287262, 0.285736, 0.284241, 0.282715, 0.28125, 0.279755, 52 | 0.27829, 0.276825, 0.275391, 0.273956, 0.272552, 0.271118, 0.269745, 0.268341, 0.266968, 0.265594, 53 | 0.264252, 0.262909, 0.261566, 0.260223, 0.258911, 0.257599, 0.256317, 0.255035, 0.25375 54 | }; 55 | 56 | class StilsonMoog : public LadderFilterBase 57 | { 58 | public: 59 | 60 | StilsonMoog(float sampleRate) : LadderFilterBase(sampleRate) 61 | { 62 | memset(state, 0, sizeof(state)); 63 | SetCutoff(1000.0f); 64 | SetResonance(0.10f); 65 | } 66 | 67 | virtual ~StilsonMoog() 68 | { 69 | 70 | } 71 | 72 | virtual void Process(float * samples, uint32_t n) override 73 | { 74 | float localState; 75 | 76 | for (int s = 0; s < n; ++s) 77 | { 78 | // Scale by arbitrary value on account of our saturation function 79 | const float input = samples[s] * 0.65f; 80 | 81 | // Negative Feedback 82 | output = 0.25 * (input - output); 83 | 84 | for (int pole = 0; pole < 4; ++pole) 85 | { 86 | localState = state[pole]; 87 | output = moog_saturate(output + p * (output - localState)); 88 | state[pole] = output; 89 | output = moog_saturate(output + localState); 90 | } 91 | 92 | SNAP_TO_ZERO(output); 93 | samples[s] = output; 94 | output *= Q; // Scale stateful output by Q 95 | } 96 | } 97 | 98 | virtual void SetResonance(float r) override 99 | { 100 | r = moog_min(r, 1); 101 | resonance = r; 102 | 103 | double ix; 104 | double ixfrac; 105 | int ixint; 106 | 107 | ix = p * 99; 108 | ixint = floor(ix); 109 | ixfrac = ix - ixint; 110 | 111 | Q = r * moog_lerp(ixfrac, S_STILSON_GAINTABLE[ixint + 99], S_STILSON_GAINTABLE[ixint + 100]); 112 | } 113 | 114 | virtual void SetCutoff(float c) override 115 | { 116 | cutoff = c; 117 | 118 | // Normalized cutoff between [0, 1] 119 | double fc = (cutoff) / sampleRate; 120 | double x2 = fc * fc; 121 | double x3 = fc * fc * fc; 122 | 123 | // Frequency & amplitude correction (Cubic Fit) 124 | p = -0.69346 * x3 - 0.59515 * x2 + 3.2937 * fc - 1.0072; 125 | 126 | SetResonance(resonance); 127 | } 128 | 129 | private: 130 | 131 | double p; 132 | double Q; 133 | double state[4]; 134 | double output; 135 | }; 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /src/Filters.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef FILTERS_H 4 | #define FILTERS_H 5 | 6 | #include 7 | #include 8 | 9 | #include "Util.h" 10 | 11 | class BiQuadBase 12 | { 13 | public: 14 | 15 | BiQuadBase() 16 | { 17 | bCoef = {{0.0f, 0.0f, 0.0f}}; 18 | aCoef = {{0.0f, 0.0f}}; 19 | w = {{0.0f, 0.0f}}; 20 | } 21 | 22 | ~BiQuadBase() 23 | { 24 | 25 | } 26 | 27 | // DF-II impl 28 | void Process(float * samples, const uint32_t n) 29 | { 30 | float out = 0; 31 | for (int s = 0; s < n; ++s) 32 | { 33 | out = bCoef[0] * samples[s] + w[0]; 34 | w[0] = bCoef[1] * samples[s] - aCoef[0] * out + w[1]; 35 | w[1] = bCoef[2] * samples[s] - aCoef[1] * out; 36 | samples[s] = out; 37 | } 38 | } 39 | 40 | float Tick(float s) 41 | { 42 | float out = bCoef[0] * s + w[0]; 43 | w[0] = bCoef[1] * s - aCoef[0] * out + w[1]; 44 | w[1] = bCoef[2] * s - aCoef[1] * out; 45 | return out; 46 | } 47 | 48 | void SetBiquadCoefs(std::array b, std::array a) 49 | { 50 | bCoef = b; 51 | aCoef = a; 52 | } 53 | 54 | protected: 55 | std::array bCoef; // b0, b1, b2 56 | std::array aCoef; // a1, a2 57 | std::array w; // delays 58 | }; 59 | 60 | class RBJFilter : public BiQuadBase 61 | { 62 | public: 63 | 64 | enum FilterType 65 | { 66 | LOWPASS, 67 | HIGHPASS, 68 | BANDPASS, 69 | ALLPASS, 70 | NOTCH, 71 | PEAK, 72 | LOW_SHELF, 73 | HIGH_SHELF 74 | }; 75 | 76 | RBJFilter(FilterType type = FilterType::LOWPASS, float cutoff = 1, float sampleRate = 44100) : sampleRate(sampleRate), t(type) 77 | { 78 | Q = 1; 79 | A = 1; 80 | 81 | a = {{0.0f, 0.0f, 0.0f}}; 82 | b = {{0.0f, 0.0f, 0.0f}}; 83 | 84 | SetCutoff(cutoff); 85 | } 86 | 87 | ~RBJFilter() 88 | { 89 | 90 | } 91 | 92 | void UpdateCoefficients() 93 | { 94 | cosOmega = cos(omega); 95 | sinOmega = sin(omega); 96 | 97 | switch (t) 98 | { 99 | case LOWPASS: 100 | { 101 | alpha = sinOmega / (2.0 * Q); 102 | b[0] = (1 - cosOmega) / 2; 103 | b[1] = 1 - cosOmega; 104 | b[2] = b[0]; 105 | a[0] = 1 + alpha; 106 | a[1] = -2 * cosOmega; 107 | a[2] = 1 - alpha; 108 | } break; 109 | 110 | case HIGHPASS: 111 | { 112 | alpha = sinOmega / (2.0 * Q); 113 | b[0] = (1 + cosOmega) / 2; 114 | b[1] = -(1 + cosOmega); 115 | b[2] = b[0]; 116 | a[0] = 1 + alpha; 117 | a[1] = -2 * cosOmega; 118 | a[2] = 1 - alpha; 119 | } break; 120 | 121 | case BANDPASS: 122 | { 123 | alpha = sinOmega * sinhf(logf(2.0) / 2.0 * Q * omega/sinOmega); 124 | b[0] = sinOmega / 2; 125 | b[1] = 0; 126 | b[2] = -b[0]; 127 | a[0] = 1 + alpha; 128 | a[1] = -2 * cosOmega; 129 | a[2] = 1 - alpha; 130 | } break; 131 | 132 | case ALLPASS: 133 | { 134 | alpha = sinOmega / (2.0 * Q); 135 | b[0] = 1 - alpha; 136 | b[1] = -2 * cosOmega; 137 | b[2] = 1 + alpha; 138 | a[0] = b[2]; 139 | a[1] = b[1]; 140 | a[2] = b[0]; 141 | } break; 142 | 143 | case NOTCH: 144 | { 145 | alpha = sinOmega * sinhf(logf(2.0) / 2.0 * Q * omega/sinOmega); 146 | b[0] = 1; 147 | b[1] = -2 * cosOmega; 148 | b[2] = 1; 149 | a[0] = 1 + alpha; 150 | a[1] = b[1]; 151 | a[2] = 1 - alpha; 152 | } break; 153 | 154 | case PEAK: 155 | { 156 | alpha = sinOmega * sinhf(logf(2.0) / 2.0 * Q * omega/sinOmega); 157 | b[0] = 1 + (alpha * A); 158 | b[1] = -2 * cosOmega; 159 | b[2] = 1 - (alpha * A); 160 | a[0] = 1 + (alpha / A); 161 | a[1] = b[1]; 162 | a[2] = 1 - (alpha / A); 163 | } break; 164 | 165 | case LOW_SHELF: 166 | { 167 | alpha = sinOmega / 2.0 * sqrt( (A + 1.0 / A) * (1.0 / Q - 1.0) + 2.0); 168 | b[0] = A * ((A + 1) - ((A - 1) * cosOmega) + (2 * sqrtf(A) * alpha)); 169 | b[1] = 2 * A * ((A - 1) - ((A + 1) * cosOmega)); 170 | b[2] = A * ((A + 1) - ((A - 1) * cosOmega) - (2 * sqrtf(A) * alpha)); 171 | a[0] = ((A + 1) + ((A - 1) * cosOmega) + (2 * sqrtf(A) * alpha)); 172 | a[1] = -2 * ((A - 1) + ((A + 1) * cosOmega)); 173 | a[2] = ((A + 1) + ((A - 1) * cosOmega) - (2 * sqrtf(A) * alpha)); 174 | } break; 175 | 176 | case HIGH_SHELF: 177 | { 178 | alpha = sinOmega / 2.0 * sqrt( (A + 1.0 / A) * (1.0 / Q - 1.0) + 2.0); 179 | b[0] = A * ((A + 1) + ((A - 1) * cosOmega) + (2 * sqrtf(A) * alpha)); 180 | b[1] = -2 * A * ((A - 1) + ((A + 1) * cosOmega)); 181 | b[2] = A * ((A + 1) + ((A - 1) * cosOmega) - (2 * sqrtf(A) * alpha)); 182 | a[0] = ((A + 1) - ((A - 1) * cosOmega) + (2 * sqrtf(A) * alpha)); 183 | a[1] = 2 * ((A - 1) - ((A + 1) * cosOmega)); 184 | a[2] = ((A + 1) - ((A - 1) * cosOmega) - (2 * sqrtf(A) * alpha)); 185 | } break; 186 | } 187 | 188 | // Normalize filter coefficients 189 | float factor = 1.0f / a[0]; 190 | 191 | std::array aNorm; 192 | std::array bNorm; 193 | 194 | aNorm[0] = a[1] * factor; 195 | aNorm[1] = a[2] * factor; 196 | 197 | bNorm[0] = b[0] * factor; 198 | bNorm[1] = b[1] * factor; 199 | bNorm[2] = b[2] * factor; 200 | 201 | SetBiquadCoefs(bNorm, aNorm); 202 | } 203 | 204 | // In Hertz, 0 to Nyquist 205 | void SetCutoff(float c) 206 | { 207 | omega = HZ_TO_RAD(c) / sampleRate; 208 | UpdateCoefficients(); 209 | } 210 | 211 | float GetCutoff() 212 | { 213 | return omega; 214 | } 215 | 216 | // Arbitrary, from 0.01f to ~20 217 | void SetQValue(float q) 218 | { 219 | Q = q; 220 | UpdateCoefficients(); 221 | } 222 | 223 | float GetQValue() 224 | { 225 | return Q; 226 | } 227 | 228 | void SetType(FilterType newType) 229 | { 230 | t = newType; 231 | UpdateCoefficients(); 232 | } 233 | 234 | FilterType GetType() 235 | { 236 | return t; 237 | } 238 | 239 | private: 240 | 241 | float sampleRate; 242 | 243 | float omega; 244 | float cosOmega; 245 | float sinOmega; 246 | 247 | float Q; 248 | float alpha; 249 | float A; 250 | 251 | std::array a; 252 | std::array b; 253 | 254 | FilterType t; 255 | }; 256 | 257 | // +/-0.05dB above 9.2Hz @ 44,100Hz 258 | class PinkingFilter 259 | { 260 | double b0, b1, b2, b3, b4, b5, b6; 261 | public: 262 | PinkingFilter() : b0(0), b1(0), b2(0), b3(0), b4(0), b5(0), b6(0) {} 263 | float process(const float s) 264 | { 265 | b0 = 0.99886 * b0 + s * 0.0555179; 266 | b1 = 0.99332 * b1 + s * 0.0750759; 267 | b2 = 0.96900 * b2 + s * 0.1538520; 268 | b3 = 0.86650 * b3 + s * 0.3104856; 269 | b4 = 0.55000 * b4 + s * 0.5329522; 270 | b5 = -0.7616 * b5 - s * 0.0168980; 271 | const double pink = (b0 + b1 + b2 + b3 + b4 + b5 + b6 + (s * 0.5362)) * 0.11; 272 | b6 = s * 0.115926; 273 | return pink; 274 | } 275 | }; 276 | 277 | class BrowningFilter 278 | { 279 | float l; 280 | public: 281 | BrowningFilter() : l(0) {} 282 | float process(const float s) 283 | { 284 | float brown = (l + (0.02f * s)) / 1.02f; 285 | l = brown; 286 | return brown * 3.5f; // compensate for gain 287 | } 288 | }; 289 | 290 | #endif 291 | -------------------------------------------------------------------------------- /src/RingBuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, The Cinder Project 3 | 4 | This code is intended to be used with the Cinder C++ library, http://libcinder.org 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that 7 | the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this list of conditions and 10 | the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 | the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 15 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 16 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 18 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 21 | POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | //! \brief Ringbuffer (aka circular buffer) data structure for use in concurrent audio scenarios. 25 | //! 26 | //! Other than minor modifications, this ringbuffer is a copy of Tim Blechmann's fine work, found as the base 27 | //! structure of boost::lockfree::spsc_queue (ringbuffer_base). Whereas the boost::lockfree data structures 28 | //! are meant for a wide range of applications / archs, this version specifically caters to audio processing. 29 | //! 30 | //! The implementation remains lock-free and thread-safe within a single write thread / single read thread context. 31 | 32 | #pragma once 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | template 40 | class RingBufferT { 41 | public: 42 | //! Constructs a RingBufferT with size = 0 43 | RingBufferT() : mData( nullptr ), mAllocatedSize( 0 ), mWriteIndex( 0 ), mReadIndex( 0 ) {} 44 | //! Constructs a RingBufferT with \a count maximum elements. 45 | RingBufferT( size_t count ) : mAllocatedSize( 0 ) 46 | { 47 | resize( count ); 48 | } 49 | 50 | RingBufferT( RingBufferT &&other ) 51 | : mData( other.mData ), mAllocatedSize( other.mAllocatedSize ), mWriteIndex( 0 ), mReadIndex( 0 ) 52 | { 53 | other.mData = nullptr; 54 | other.mAllocatedSize = 0; 55 | } 56 | 57 | ~RingBufferT() 58 | { 59 | if( mData ) 60 | free( mData ); 61 | } 62 | //! Resizes the container to contain \a count maximum elements. Invalidates the internal buffer and resets read / write indices to 0. \note Must be synchronized with both read and write threads. 63 | void resize( size_t count ) 64 | { 65 | size_t allocatedSize = count + 1; // one bin is used to distinguish between the read and write indices when full. 66 | 67 | if( mAllocatedSize ) 68 | mData = (T *)::realloc( mData, allocatedSize * sizeof( T ) ); 69 | else 70 | mData = (T *)::calloc( allocatedSize, sizeof( T ) ); 71 | 72 | assert( mData ); 73 | 74 | mAllocatedSize = allocatedSize; 75 | clear(); 76 | } 77 | //! Invalidates the internal buffer and resets read / write indices to 0. \note Must be synchronized with both read and write threads. 78 | void clear() 79 | { 80 | mWriteIndex = 0; 81 | mReadIndex = 0; 82 | } 83 | //! Returns the maximum number of elements. 84 | size_t getSize() const 85 | { 86 | return mAllocatedSize - 1; 87 | } 88 | //! Returns the number of elements available for wrtiing. \note Only safe to call from the write thread. 89 | size_t getAvailableWrite() const 90 | { 91 | return getAvailableWrite( mWriteIndex, mReadIndex ); 92 | } 93 | //! Returns the number of elements available for wrtiing. \note Only safe to call from the read thread. 94 | size_t getAvailableRead() const 95 | { 96 | return getAvailableRead( mWriteIndex, mReadIndex ); 97 | } 98 | 99 | //! \brief Writes \a count elements into the internal buffer from \a array. \return `true` if all elements were successfully written, or `false` otherwise. 100 | //! 101 | //! \note only safe to call from the write thread. 102 | //! TODO: consider renaming this to writeAll / readAll, and having generic read / write that just does as much as it can 103 | bool write( const T *array, size_t count ) 104 | { 105 | const size_t writeIndex = mWriteIndex.load( std::memory_order_relaxed ); 106 | const size_t readIndex = mReadIndex.load( std::memory_order_acquire ); 107 | 108 | if( count > getAvailableWrite( writeIndex, readIndex ) ) 109 | return false; 110 | 111 | size_t writeIndexAfter = writeIndex + count; 112 | 113 | if( writeIndex + count > mAllocatedSize ) { 114 | size_t countA = mAllocatedSize - writeIndex; 115 | size_t countB = count - countA; 116 | 117 | memcpy( mData + writeIndex, array, countA * sizeof( T ) ); 118 | memcpy( mData, array + countA, countB * sizeof( T ) ); 119 | writeIndexAfter -= mAllocatedSize; 120 | } 121 | else { 122 | memcpy( mData + writeIndex, array, count * sizeof( T ) ); 123 | if( writeIndexAfter == mAllocatedSize ) 124 | writeIndexAfter = 0; 125 | } 126 | 127 | mWriteIndex.store( writeIndexAfter, std::memory_order_release ); 128 | return true; 129 | } 130 | //! \brief Reads \a count elements from the internal buffer into \a array. \return `true` if all elements were successfully read, or `false` otherwise. 131 | //! 132 | //! \note only safe to call from the read thread. 133 | bool read( T *array, size_t count ) 134 | { 135 | const size_t writeIndex = mWriteIndex.load( std::memory_order_acquire ); 136 | const size_t readIndex = mReadIndex.load( std::memory_order_relaxed ); 137 | 138 | if( count > getAvailableRead( writeIndex, readIndex ) ) 139 | return false; 140 | 141 | size_t readIndexAfter = readIndex + count; 142 | 143 | if( readIndex + count > mAllocatedSize ) { 144 | size_t countA = mAllocatedSize - readIndex; 145 | size_t countB = count - countA; 146 | 147 | memcpy( array, mData + readIndex, countA * sizeof( T ) ); 148 | memcpy( array + countA, mData, countB * sizeof( T ) ); 149 | 150 | readIndexAfter -= mAllocatedSize; 151 | } 152 | else { 153 | memcpy( array, mData + readIndex, count * sizeof( T ) ); 154 | if( readIndexAfter == mAllocatedSize ) 155 | readIndexAfter = 0; 156 | } 157 | 158 | mReadIndex.store( readIndexAfter, std::memory_order_release ); 159 | return true; 160 | } 161 | 162 | private: 163 | 164 | size_t getAvailableWrite( size_t writeIndex, size_t readIndex ) const 165 | { 166 | size_t result = readIndex - writeIndex - 1; 167 | if( writeIndex >= readIndex ) 168 | result += mAllocatedSize; 169 | 170 | return result; 171 | } 172 | 173 | size_t getAvailableRead( size_t writeIndex, size_t readIndex ) const 174 | { 175 | if( writeIndex >= readIndex ) 176 | return writeIndex - readIndex; 177 | 178 | return writeIndex + mAllocatedSize - readIndex; 179 | } 180 | 181 | T *mData; 182 | size_t mAllocatedSize; 183 | std::atomic mWriteIndex, mReadIndex; 184 | }; 185 | 186 | typedef RingBufferT RingBuffer; 187 | -------------------------------------------------------------------------------- /MoogLadders.vcxproj/MoogLadders.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | {992E85A7-B590-477B-A1B2-8A04AAAD0E10} 46 | Win32Proj 47 | MoogLadders 48 | 10.0.16299.0 49 | 50 | 51 | 52 | Application 53 | true 54 | v141 55 | Unicode 56 | 57 | 58 | Application 59 | true 60 | v141 61 | Unicode 62 | 63 | 64 | Application 65 | false 66 | v141 67 | true 68 | Unicode 69 | 70 | 71 | Application 72 | false 73 | v141 74 | true 75 | Unicode 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | true 95 | 96 | 97 | true 98 | 99 | 100 | false 101 | 102 | 103 | false 104 | 105 | 106 | 107 | 108 | 109 | Level3 110 | Disabled 111 | WIN32;__WINDOWS_DS__;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 112 | $(ProjectDir)../third_party;$(ProjectDir)../src;%(AdditionalIncludeDirectories) 113 | 114 | 115 | Console 116 | true 117 | 118 | 119 | 120 | 121 | 122 | 123 | Level3 124 | Disabled 125 | WIN32;__WINDOWS_DS__;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 126 | $(ProjectDir)../third_party;$(ProjectDir)../src;%(AdditionalIncludeDirectories) 127 | 128 | 129 | Console 130 | true 131 | 132 | 133 | 134 | 135 | Level3 136 | 137 | 138 | MaxSpeed 139 | true 140 | true 141 | WIN32;__WINDOWS_DS__;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 142 | $(ProjectDir)../third_party;$(ProjectDir)../src;%(AdditionalIncludeDirectories) 143 | 144 | 145 | Console 146 | true 147 | true 148 | true 149 | 150 | 151 | 152 | 153 | Level3 154 | 155 | 156 | MaxSpeed 157 | true 158 | true 159 | WIN32;__WINDOWS_DS__;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 160 | $(ProjectDir)../third_party;$(ProjectDir)../src;%(AdditionalIncludeDirectories) 161 | 162 | 163 | Console 164 | true 165 | true 166 | true 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /third_party/rtaudio/RtAudio.h: -------------------------------------------------------------------------------- 1 | /************************************************************************/ 2 | /*! \class RtAudio 3 | \brief Realtime audio i/o C++ classes. 4 | 5 | RtAudio provides a common API (Application Programming Interface) 6 | for realtime audio input/output across Linux (native ALSA, Jack, 7 | and OSS), Macintosh OS X (CoreAudio and Jack), and Windows 8 | (DirectSound, ASIO and WASAPI) operating systems. 9 | 10 | RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ 11 | 12 | RtAudio: realtime audio i/o C++ classes 13 | Copyright (c) 2001-2014 Gary P. Scavone 14 | 15 | Permission is hereby granted, free of charge, to any person 16 | obtaining a copy of this software and associated documentation files 17 | (the "Software"), to deal in the Software without restriction, 18 | including without limitation the rights to use, copy, modify, merge, 19 | publish, distribute, sublicense, and/or sell copies of the Software, 20 | and to permit persons to whom the Software is furnished to do so, 21 | subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be 24 | included in all copies or substantial portions of the Software. 25 | 26 | Any person wishing to distribute modifications to the Software is 27 | asked to send the modifications to the original developer so that 28 | they can be incorporated into the canonical version. This is, 29 | however, not a binding provision of this license. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 32 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 33 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 34 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 35 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 36 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 37 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 38 | */ 39 | /************************************************************************/ 40 | 41 | /*! 42 | \file RtAudio.h 43 | */ 44 | 45 | #ifndef __RTAUDIO_H 46 | #define __RTAUDIO_H 47 | 48 | #define RTAUDIO_VERSION "4.1.1" 49 | 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | /*! \typedef typedef unsigned long RtAudioFormat; 56 | \brief RtAudio data format type. 57 | 58 | Support for signed integers and floats. Audio data fed to/from an 59 | RtAudio stream is assumed to ALWAYS be in host byte order. The 60 | internal routines will automatically take care of any necessary 61 | byte-swapping between the host format and the soundcard. Thus, 62 | endian-ness is not a concern in the following format definitions. 63 | 64 | - \e RTAUDIO_SINT8: 8-bit signed integer. 65 | - \e RTAUDIO_SINT16: 16-bit signed integer. 66 | - \e RTAUDIO_SINT24: 24-bit signed integer. 67 | - \e RTAUDIO_SINT32: 32-bit signed integer. 68 | - \e RTAUDIO_FLOAT32: Normalized between plus/minus 1.0. 69 | - \e RTAUDIO_FLOAT64: Normalized between plus/minus 1.0. 70 | */ 71 | typedef unsigned long RtAudioFormat; 72 | static const RtAudioFormat RTAUDIO_SINT8 = 0x1; // 8-bit signed integer. 73 | static const RtAudioFormat RTAUDIO_SINT16 = 0x2; // 16-bit signed integer. 74 | static const RtAudioFormat RTAUDIO_SINT24 = 0x4; // 24-bit signed integer. 75 | static const RtAudioFormat RTAUDIO_SINT32 = 0x8; // 32-bit signed integer. 76 | static const RtAudioFormat RTAUDIO_FLOAT32 = 0x10; // Normalized between plus/minus 1.0. 77 | static const RtAudioFormat RTAUDIO_FLOAT64 = 0x20; // Normalized between plus/minus 1.0. 78 | 79 | /*! \typedef typedef unsigned long RtAudioStreamFlags; 80 | \brief RtAudio stream option flags. 81 | 82 | The following flags can be OR'ed together to allow a client to 83 | make changes to the default stream behavior: 84 | 85 | - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved). 86 | - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency. 87 | - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use. 88 | - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only). 89 | 90 | By default, RtAudio streams pass and receive audio data from the 91 | client in an interleaved format. By passing the 92 | RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio 93 | data will instead be presented in non-interleaved buffers. In 94 | this case, each buffer argument in the RtAudioCallback function 95 | will point to a single array of data, with \c nFrames samples for 96 | each channel concatenated back-to-back. For example, the first 97 | sample of data for the second channel would be located at index \c 98 | nFrames (assuming the \c buffer pointer was recast to the correct 99 | data type for the stream). 100 | 101 | Certain audio APIs offer a number of parameters that influence the 102 | I/O latency of a stream. By default, RtAudio will attempt to set 103 | these parameters internally for robust (glitch-free) performance 104 | (though some APIs, like Windows Direct Sound, make this difficult). 105 | By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream() 106 | function, internal stream settings will be influenced in an attempt 107 | to minimize stream latency, though possibly at the expense of stream 108 | performance. 109 | 110 | If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to 111 | open the input and/or output stream device(s) for exclusive use. 112 | Note that this is not possible with all supported audio APIs. 113 | 114 | If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt 115 | to select realtime scheduling (round-robin) for the callback thread. 116 | 117 | If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to 118 | open the "default" PCM device when using the ALSA API. Note that this 119 | will override any specified input or output device id. 120 | */ 121 | typedef unsigned int RtAudioStreamFlags; 122 | static const RtAudioStreamFlags RTAUDIO_NONINTERLEAVED = 0x1; // Use non-interleaved buffers (default = interleaved). 123 | static const RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY = 0x2; // Attempt to set stream parameters for lowest possible latency. 124 | static const RtAudioStreamFlags RTAUDIO_HOG_DEVICE = 0x4; // Attempt grab device and prevent use by others. 125 | static const RtAudioStreamFlags RTAUDIO_SCHEDULE_REALTIME = 0x8; // Try to select realtime scheduling for callback thread. 126 | static const RtAudioStreamFlags RTAUDIO_ALSA_USE_DEFAULT = 0x10; // Use the "default" PCM device (ALSA only). 127 | 128 | /*! \typedef typedef unsigned long RtAudioStreamStatus; 129 | \brief RtAudio stream status (over- or underflow) flags. 130 | 131 | Notification of a stream over- or underflow is indicated by a 132 | non-zero stream \c status argument in the RtAudioCallback function. 133 | The stream status can be one of the following two options, 134 | depending on whether the stream is open for output and/or input: 135 | 136 | - \e RTAUDIO_INPUT_OVERFLOW: Input data was discarded because of an overflow condition at the driver. 137 | - \e RTAUDIO_OUTPUT_UNDERFLOW: The output buffer ran low, likely producing a break in the output sound. 138 | */ 139 | typedef unsigned int RtAudioStreamStatus; 140 | static const RtAudioStreamStatus RTAUDIO_INPUT_OVERFLOW = 0x1; // Input data was discarded because of an overflow condition at the driver. 141 | static const RtAudioStreamStatus RTAUDIO_OUTPUT_UNDERFLOW = 0x2; // The output buffer ran low, likely causing a gap in the output sound. 142 | 143 | //! RtAudio callback function prototype. 144 | /*! 145 | All RtAudio clients must create a function of type RtAudioCallback 146 | to read and/or write data from/to the audio stream. When the 147 | underlying audio system is ready for new input or output data, this 148 | function will be invoked. 149 | 150 | \param outputBuffer For output (or duplex) streams, the client 151 | should write \c nFrames of audio sample frames into this 152 | buffer. This argument should be recast to the datatype 153 | specified when the stream was opened. For input-only 154 | streams, this argument will be NULL. 155 | 156 | \param inputBuffer For input (or duplex) streams, this buffer will 157 | hold \c nFrames of input audio sample frames. This 158 | argument should be recast to the datatype specified when the 159 | stream was opened. For output-only streams, this argument 160 | will be NULL. 161 | 162 | \param nFrames The number of sample frames of input or output 163 | data in the buffers. The actual buffer size in bytes is 164 | dependent on the data type and number of channels in use. 165 | 166 | \param streamTime The number of seconds that have elapsed since the 167 | stream was started. 168 | 169 | \param status If non-zero, this argument indicates a data overflow 170 | or underflow condition for the stream. The particular 171 | condition can be determined by comparison with the 172 | RtAudioStreamStatus flags. 173 | 174 | \param userData A pointer to optional data provided by the client 175 | when opening the stream (default = NULL). 176 | 177 | To continue normal stream operation, the RtAudioCallback function 178 | should return a value of zero. To stop the stream and drain the 179 | output buffer, the function should return a value of one. To abort 180 | the stream immediately, the client should return a value of two. 181 | */ 182 | typedef int (*RtAudioCallback)( void *outputBuffer, void *inputBuffer, 183 | unsigned int nFrames, 184 | double streamTime, 185 | RtAudioStreamStatus status, 186 | void *userData ); 187 | 188 | /************************************************************************/ 189 | /*! \class RtAudioError 190 | \brief Exception handling class for RtAudio. 191 | 192 | The RtAudioError class is quite simple but it does allow errors to be 193 | "caught" by RtAudioError::Type. See the RtAudio documentation to know 194 | which methods can throw an RtAudioError. 195 | */ 196 | /************************************************************************/ 197 | 198 | class RtAudioError : public std::exception 199 | { 200 | public: 201 | //! Defined RtAudioError types. 202 | enum Type { 203 | WARNING, /*!< A non-critical error. */ 204 | DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ 205 | UNSPECIFIED, /*!< The default, unspecified error type. */ 206 | NO_DEVICES_FOUND, /*!< No devices found on system. */ 207 | INVALID_DEVICE, /*!< An invalid device ID was specified. */ 208 | MEMORY_ERROR, /*!< An error occured during memory allocation. */ 209 | INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ 210 | INVALID_USE, /*!< The function was called incorrectly. */ 211 | DRIVER_ERROR, /*!< A system driver error occured. */ 212 | SYSTEM_ERROR, /*!< A system error occured. */ 213 | THREAD_ERROR /*!< A thread error occured. */ 214 | }; 215 | 216 | //! The constructor. 217 | RtAudioError( const std::string& message, Type type = RtAudioError::UNSPECIFIED ) throw() : message_(message), type_(type) {} 218 | 219 | //! The destructor. 220 | virtual ~RtAudioError( void ) throw() {} 221 | 222 | //! Prints thrown error message to stderr. 223 | virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; } 224 | 225 | //! Returns the thrown error message type. 226 | virtual const Type& getType(void) const throw() { return type_; } 227 | 228 | //! Returns the thrown error message string. 229 | virtual const std::string& getMessage(void) const throw() { return message_; } 230 | 231 | //! Returns the thrown error message as a c-style string. 232 | virtual const char* what( void ) const throw() { return message_.c_str(); } 233 | 234 | protected: 235 | std::string message_; 236 | Type type_; 237 | }; 238 | 239 | //! RtAudio error callback function prototype. 240 | /*! 241 | \param type Type of error. 242 | \param errorText Error description. 243 | */ 244 | typedef void (*RtAudioErrorCallback)( RtAudioError::Type type, const std::string &errorText ); 245 | 246 | // **************************************************************** // 247 | // 248 | // RtAudio class declaration. 249 | // 250 | // RtAudio is a "controller" used to select an available audio i/o 251 | // interface. It presents a common API for the user to call but all 252 | // functionality is implemented by the class RtApi and its 253 | // subclasses. RtAudio creates an instance of an RtApi subclass 254 | // based on the user's API choice. If no choice is made, RtAudio 255 | // attempts to make a "logical" API selection. 256 | // 257 | // **************************************************************** // 258 | 259 | class RtApi; 260 | 261 | class RtAudio 262 | { 263 | public: 264 | 265 | //! Audio API specifier arguments. 266 | enum Api { 267 | UNSPECIFIED, /*!< Search for a working compiled API. */ 268 | LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ 269 | LINUX_PULSE, /*!< The Linux PulseAudio API. */ 270 | LINUX_OSS, /*!< The Linux Open Sound System API. */ 271 | UNIX_JACK, /*!< The Jack Low-Latency Audio Server API. */ 272 | MACOSX_CORE, /*!< Macintosh OS-X Core Audio API. */ 273 | WINDOWS_WASAPI, /*!< The Microsoft WASAPI API. */ 274 | WINDOWS_ASIO, /*!< The Steinberg Audio Stream I/O API. */ 275 | WINDOWS_DS, /*!< The Microsoft Direct Sound API. */ 276 | RTAUDIO_DUMMY /*!< A compilable but non-functional API. */ 277 | }; 278 | 279 | //! The public device information structure for returning queried values. 280 | struct DeviceInfo { 281 | bool probed; /*!< true if the device capabilities were successfully probed. */ 282 | std::string name; /*!< Character string device identifier. */ 283 | unsigned int outputChannels; /*!< Maximum output channels supported by device. */ 284 | unsigned int inputChannels; /*!< Maximum input channels supported by device. */ 285 | unsigned int duplexChannels; /*!< Maximum simultaneous input/output channels supported by device. */ 286 | bool isDefaultOutput; /*!< true if this is the default output device. */ 287 | bool isDefaultInput; /*!< true if this is the default input device. */ 288 | std::vector sampleRates; /*!< Supported sample rates (queried from list of standard rates). */ 289 | unsigned int preferredSampleRate; /*!< Preferred sample rate, eg. for WASAPI the system sample rate. */ 290 | RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */ 291 | 292 | // Default constructor. 293 | DeviceInfo() 294 | :probed(false), outputChannels(0), inputChannels(0), duplexChannels(0), 295 | isDefaultOutput(false), isDefaultInput(false), preferredSampleRate(0), nativeFormats(0) {} 296 | }; 297 | 298 | //! The structure for specifying input or ouput stream parameters. 299 | struct StreamParameters { 300 | unsigned int deviceId; /*!< Device index (0 to getDeviceCount() - 1). */ 301 | unsigned int nChannels; /*!< Number of channels. */ 302 | unsigned int firstChannel; /*!< First channel index on device (default = 0). */ 303 | 304 | // Default constructor. 305 | StreamParameters() 306 | : deviceId(0), nChannels(0), firstChannel(0) {} 307 | }; 308 | 309 | //! The structure for specifying stream options. 310 | /*! 311 | The following flags can be OR'ed together to allow a client to 312 | make changes to the default stream behavior: 313 | 314 | - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved). 315 | - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency. 316 | - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use. 317 | - \e RTAUDIO_SCHEDULE_REALTIME: Attempt to select realtime scheduling for callback thread. 318 | - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only). 319 | 320 | By default, RtAudio streams pass and receive audio data from the 321 | client in an interleaved format. By passing the 322 | RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio 323 | data will instead be presented in non-interleaved buffers. In 324 | this case, each buffer argument in the RtAudioCallback function 325 | will point to a single array of data, with \c nFrames samples for 326 | each channel concatenated back-to-back. For example, the first 327 | sample of data for the second channel would be located at index \c 328 | nFrames (assuming the \c buffer pointer was recast to the correct 329 | data type for the stream). 330 | 331 | Certain audio APIs offer a number of parameters that influence the 332 | I/O latency of a stream. By default, RtAudio will attempt to set 333 | these parameters internally for robust (glitch-free) performance 334 | (though some APIs, like Windows Direct Sound, make this difficult). 335 | By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream() 336 | function, internal stream settings will be influenced in an attempt 337 | to minimize stream latency, though possibly at the expense of stream 338 | performance. 339 | 340 | If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to 341 | open the input and/or output stream device(s) for exclusive use. 342 | Note that this is not possible with all supported audio APIs. 343 | 344 | If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt 345 | to select realtime scheduling (round-robin) for the callback thread. 346 | The \c priority parameter will only be used if the RTAUDIO_SCHEDULE_REALTIME 347 | flag is set. It defines the thread's realtime priority. 348 | 349 | If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to 350 | open the "default" PCM device when using the ALSA API. Note that this 351 | will override any specified input or output device id. 352 | 353 | The \c numberOfBuffers parameter can be used to control stream 354 | latency in the Windows DirectSound, Linux OSS, and Linux Alsa APIs 355 | only. A value of two is usually the smallest allowed. Larger 356 | numbers can potentially result in more robust stream performance, 357 | though likely at the cost of stream latency. The value set by the 358 | user is replaced during execution of the RtAudio::openStream() 359 | function by the value actually used by the system. 360 | 361 | The \c streamName parameter can be used to set the client name 362 | when using the Jack API. By default, the client name is set to 363 | RtApiJack. However, if you wish to create multiple instances of 364 | RtAudio with Jack, each instance must have a unique client name. 365 | */ 366 | struct StreamOptions { 367 | RtAudioStreamFlags flags; /*!< A bit-mask of stream flags (RTAUDIO_NONINTERLEAVED, RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE, RTAUDIO_ALSA_USE_DEFAULT). */ 368 | unsigned int numberOfBuffers; /*!< Number of stream buffers. */ 369 | std::string streamName; /*!< A stream name (currently used only in Jack). */ 370 | int priority; /*!< Scheduling priority of callback thread (only used with flag RTAUDIO_SCHEDULE_REALTIME). */ 371 | 372 | // Default constructor. 373 | StreamOptions() 374 | : flags(0), numberOfBuffers(0), priority(0) {} 375 | }; 376 | 377 | //! A static function to determine the current RtAudio version. 378 | static std::string getVersion( void ) throw(); 379 | 380 | //! A static function to determine the available compiled audio APIs. 381 | /*! 382 | The values returned in the std::vector can be compared against 383 | the enumerated list values. Note that there can be more than one 384 | API compiled for certain operating systems. 385 | */ 386 | static void getCompiledApi( std::vector &apis ) throw(); 387 | 388 | //! The class constructor. 389 | /*! 390 | The constructor performs minor initialization tasks. An exception 391 | can be thrown if no API support is compiled. 392 | 393 | If no API argument is specified and multiple API support has been 394 | compiled, the default order of use is JACK, ALSA, OSS (Linux 395 | systems) and ASIO, DS (Windows systems). 396 | */ 397 | RtAudio( RtAudio::Api api=UNSPECIFIED ); 398 | 399 | //! The destructor. 400 | /*! 401 | If a stream is running or open, it will be stopped and closed 402 | automatically. 403 | */ 404 | ~RtAudio() throw(); 405 | 406 | //! Returns the audio API specifier for the current instance of RtAudio. 407 | RtAudio::Api getCurrentApi( void ) throw(); 408 | 409 | //! A public function that queries for the number of audio devices available. 410 | /*! 411 | This function performs a system query of available devices each time it 412 | is called, thus supporting devices connected \e after instantiation. If 413 | a system error occurs during processing, a warning will be issued. 414 | */ 415 | unsigned int getDeviceCount( void ) throw(); 416 | 417 | //! Return an RtAudio::DeviceInfo structure for a specified device number. 418 | /*! 419 | 420 | Any device integer between 0 and getDeviceCount() - 1 is valid. 421 | If an invalid argument is provided, an RtAudioError (type = INVALID_USE) 422 | will be thrown. If a device is busy or otherwise unavailable, the 423 | structure member "probed" will have a value of "false" and all 424 | other members are undefined. If the specified device is the 425 | current default input or output device, the corresponding 426 | "isDefault" member will have a value of "true". 427 | */ 428 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 429 | 430 | //! A function that returns the index of the default output device. 431 | /*! 432 | If the underlying audio API does not provide a "default 433 | device", or if no devices are available, the return value will be 434 | 0. Note that this is a valid device identifier and it is the 435 | client's responsibility to verify that a device is available 436 | before attempting to open a stream. 437 | */ 438 | unsigned int getDefaultOutputDevice( void ) throw(); 439 | 440 | //! A function that returns the index of the default input device. 441 | /*! 442 | If the underlying audio API does not provide a "default 443 | device", or if no devices are available, the return value will be 444 | 0. Note that this is a valid device identifier and it is the 445 | client's responsibility to verify that a device is available 446 | before attempting to open a stream. 447 | */ 448 | unsigned int getDefaultInputDevice( void ) throw(); 449 | 450 | //! A public function for opening a stream with the specified parameters. 451 | /*! 452 | An RtAudioError (type = SYSTEM_ERROR) is thrown if a stream cannot be 453 | opened with the specified parameters or an error occurs during 454 | processing. An RtAudioError (type = INVALID_USE) is thrown if any 455 | invalid device ID or channel number parameters are specified. 456 | 457 | \param outputParameters Specifies output stream parameters to use 458 | when opening a stream, including a device ID, number of channels, 459 | and starting channel number. For input-only streams, this 460 | argument should be NULL. The device ID is an index value between 461 | 0 and getDeviceCount() - 1. 462 | \param inputParameters Specifies input stream parameters to use 463 | when opening a stream, including a device ID, number of channels, 464 | and starting channel number. For output-only streams, this 465 | argument should be NULL. The device ID is an index value between 466 | 0 and getDeviceCount() - 1. 467 | \param format An RtAudioFormat specifying the desired sample data format. 468 | \param sampleRate The desired sample rate (sample frames per second). 469 | \param *bufferFrames A pointer to a value indicating the desired 470 | internal buffer size in sample frames. The actual value 471 | used by the device is returned via the same pointer. A 472 | value of zero can be specified, in which case the lowest 473 | allowable value is determined. 474 | \param callback A client-defined function that will be invoked 475 | when input data is available and/or output data is needed. 476 | \param userData An optional pointer to data that can be accessed 477 | from within the callback function. 478 | \param options An optional pointer to a structure containing various 479 | global stream options, including a list of OR'ed RtAudioStreamFlags 480 | and a suggested number of stream buffers that can be used to 481 | control stream latency. More buffers typically result in more 482 | robust performance, though at a cost of greater latency. If a 483 | value of zero is specified, a system-specific median value is 484 | chosen. If the RTAUDIO_MINIMIZE_LATENCY flag bit is set, the 485 | lowest allowable value is used. The actual value used is 486 | returned via the structure argument. The parameter is API dependent. 487 | \param errorCallback A client-defined function that will be invoked 488 | when an error has occured. 489 | */ 490 | void openStream( RtAudio::StreamParameters *outputParameters, 491 | RtAudio::StreamParameters *inputParameters, 492 | RtAudioFormat format, unsigned int sampleRate, 493 | unsigned int *bufferFrames, RtAudioCallback callback, 494 | void *userData = NULL, RtAudio::StreamOptions *options = NULL, RtAudioErrorCallback errorCallback = NULL ); 495 | 496 | //! A function that closes a stream and frees any associated stream memory. 497 | /*! 498 | If a stream is not open, this function issues a warning and 499 | returns (no exception is thrown). 500 | */ 501 | void closeStream( void ) throw(); 502 | 503 | //! A function that starts a stream. 504 | /*! 505 | An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs 506 | during processing. An RtAudioError (type = INVALID_USE) is thrown if a 507 | stream is not open. A warning is issued if the stream is already 508 | running. 509 | */ 510 | void startStream( void ); 511 | 512 | //! Stop a stream, allowing any samples remaining in the output queue to be played. 513 | /*! 514 | An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs 515 | during processing. An RtAudioError (type = INVALID_USE) is thrown if a 516 | stream is not open. A warning is issued if the stream is already 517 | stopped. 518 | */ 519 | void stopStream( void ); 520 | 521 | //! Stop a stream, discarding any samples remaining in the input/output queue. 522 | /*! 523 | An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs 524 | during processing. An RtAudioError (type = INVALID_USE) is thrown if a 525 | stream is not open. A warning is issued if the stream is already 526 | stopped. 527 | */ 528 | void abortStream( void ); 529 | 530 | //! Returns true if a stream is open and false if not. 531 | bool isStreamOpen( void ) const throw(); 532 | 533 | //! Returns true if the stream is running and false if it is stopped or not open. 534 | bool isStreamRunning( void ) const throw(); 535 | 536 | //! Returns the number of elapsed seconds since the stream was started. 537 | /*! 538 | If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown. 539 | */ 540 | double getStreamTime( void ); 541 | 542 | //! Set the stream time to a time in seconds greater than or equal to 0.0. 543 | /*! 544 | If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown. 545 | */ 546 | void setStreamTime( double time ); 547 | 548 | //! Returns the internal stream latency in sample frames. 549 | /*! 550 | The stream latency refers to delay in audio input and/or output 551 | caused by internal buffering by the audio system and/or hardware. 552 | For duplex streams, the returned value will represent the sum of 553 | the input and output latencies. If a stream is not open, an 554 | RtAudioError (type = INVALID_USE) will be thrown. If the API does not 555 | report latency, the return value will be zero. 556 | */ 557 | long getStreamLatency( void ); 558 | 559 | //! Returns actual sample rate in use by the stream. 560 | /*! 561 | On some systems, the sample rate used may be slightly different 562 | than that specified in the stream parameters. If a stream is not 563 | open, an RtAudioError (type = INVALID_USE) will be thrown. 564 | */ 565 | unsigned int getStreamSampleRate( void ); 566 | 567 | //! Specify whether warning messages should be printed to stderr. 568 | void showWarnings( bool value = true ) throw(); 569 | 570 | protected: 571 | 572 | void openRtApi( RtAudio::Api api ); 573 | RtApi *rtapi_; 574 | }; 575 | 576 | // Operating system dependent thread functionality. 577 | #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__) 578 | 579 | #ifndef NOMINMAX 580 | #define NOMINMAX 581 | #endif 582 | #include 583 | #include 584 | 585 | typedef uintptr_t ThreadHandle; 586 | typedef CRITICAL_SECTION StreamMutex; 587 | 588 | #elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__) 589 | // Using pthread library for various flavors of unix. 590 | #include 591 | 592 | typedef pthread_t ThreadHandle; 593 | typedef pthread_mutex_t StreamMutex; 594 | 595 | #else // Setup for "dummy" behavior 596 | 597 | #define __RTAUDIO_DUMMY__ 598 | typedef int ThreadHandle; 599 | typedef int StreamMutex; 600 | 601 | #endif 602 | 603 | // This global structure type is used to pass callback information 604 | // between the private RtAudio stream structure and global callback 605 | // handling functions. 606 | struct CallbackInfo { 607 | void *object; // Used as a "this" pointer. 608 | ThreadHandle thread; 609 | void *callback; 610 | void *userData; 611 | void *errorCallback; 612 | void *apiInfo; // void pointer for API specific callback information 613 | bool isRunning; 614 | bool doRealtime; 615 | int priority; 616 | 617 | // Default constructor. 618 | CallbackInfo() 619 | :object(0), callback(0), userData(0), errorCallback(0), apiInfo(0), isRunning(false), doRealtime(false) {} 620 | }; 621 | 622 | // **************************************************************** // 623 | // 624 | // RtApi class declaration. 625 | // 626 | // Subclasses of RtApi contain all API- and OS-specific code necessary 627 | // to fully implement the RtAudio API. 628 | // 629 | // Note that RtApi is an abstract base class and cannot be 630 | // explicitly instantiated. The class RtAudio will create an 631 | // instance of an RtApi subclass (RtApiOss, RtApiAlsa, 632 | // RtApiJack, RtApiCore, RtApiDs, or RtApiAsio). 633 | // 634 | // **************************************************************** // 635 | 636 | #pragma pack(push, 1) 637 | class S24 { 638 | 639 | protected: 640 | unsigned char c3[3]; 641 | 642 | public: 643 | S24() {} 644 | 645 | S24& operator = ( const int& i ) { 646 | c3[0] = (i & 0x000000ff); 647 | c3[1] = (i & 0x0000ff00) >> 8; 648 | c3[2] = (i & 0x00ff0000) >> 16; 649 | return *this; 650 | } 651 | 652 | S24( const S24& v ) { *this = v; } 653 | S24( const double& d ) { *this = (int) d; } 654 | S24( const float& f ) { *this = (int) f; } 655 | S24( const signed short& s ) { *this = (int) s; } 656 | S24( const char& c ) { *this = (int) c; } 657 | 658 | int asInt() { 659 | int i = c3[0] | (c3[1] << 8) | (c3[2] << 16); 660 | if (i & 0x800000) i |= ~0xffffff; 661 | return i; 662 | } 663 | }; 664 | #pragma pack(pop) 665 | 666 | #if defined( HAVE_GETTIMEOFDAY ) 667 | #include 668 | #endif 669 | 670 | #include 671 | 672 | class RtApi 673 | { 674 | public: 675 | 676 | RtApi(); 677 | virtual ~RtApi(); 678 | virtual RtAudio::Api getCurrentApi( void ) = 0; 679 | virtual unsigned int getDeviceCount( void ) = 0; 680 | virtual RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) = 0; 681 | virtual unsigned int getDefaultInputDevice( void ); 682 | virtual unsigned int getDefaultOutputDevice( void ); 683 | void openStream( RtAudio::StreamParameters *outputParameters, 684 | RtAudio::StreamParameters *inputParameters, 685 | RtAudioFormat format, unsigned int sampleRate, 686 | unsigned int *bufferFrames, RtAudioCallback callback, 687 | void *userData, RtAudio::StreamOptions *options, 688 | RtAudioErrorCallback errorCallback ); 689 | virtual void closeStream( void ); 690 | virtual void startStream( void ) = 0; 691 | virtual void stopStream( void ) = 0; 692 | virtual void abortStream( void ) = 0; 693 | long getStreamLatency( void ); 694 | unsigned int getStreamSampleRate( void ); 695 | virtual double getStreamTime( void ); 696 | virtual void setStreamTime( double time ); 697 | bool isStreamOpen( void ) const { return stream_.state != STREAM_CLOSED; } 698 | bool isStreamRunning( void ) const { return stream_.state == STREAM_RUNNING; } 699 | void showWarnings( bool value ) { showWarnings_ = value; } 700 | 701 | 702 | protected: 703 | 704 | static const unsigned int MAX_SAMPLE_RATES; 705 | static const unsigned int SAMPLE_RATES[]; 706 | 707 | enum { FAILURE, SUCCESS }; 708 | 709 | enum StreamState { 710 | STREAM_STOPPED, 711 | STREAM_STOPPING, 712 | STREAM_RUNNING, 713 | STREAM_CLOSED = -50 714 | }; 715 | 716 | enum StreamMode { 717 | OUTPUT, 718 | INPUT, 719 | DUPLEX, 720 | UNINITIALIZED = -75 721 | }; 722 | 723 | // A protected structure used for buffer conversion. 724 | struct ConvertInfo { 725 | int channels; 726 | int inJump, outJump; 727 | RtAudioFormat inFormat, outFormat; 728 | std::vector inOffset; 729 | std::vector outOffset; 730 | }; 731 | 732 | // A protected structure for audio streams. 733 | struct RtApiStream { 734 | unsigned int device[2]; // Playback and record, respectively. 735 | void *apiHandle; // void pointer for API specific stream handle information 736 | StreamMode mode; // OUTPUT, INPUT, or DUPLEX. 737 | StreamState state; // STOPPED, RUNNING, or CLOSED 738 | char *userBuffer[2]; // Playback and record, respectively. 739 | char *deviceBuffer; 740 | bool doConvertBuffer[2]; // Playback and record, respectively. 741 | bool userInterleaved; 742 | bool deviceInterleaved[2]; // Playback and record, respectively. 743 | bool doByteSwap[2]; // Playback and record, respectively. 744 | unsigned int sampleRate; 745 | unsigned int bufferSize; 746 | unsigned int nBuffers; 747 | unsigned int nUserChannels[2]; // Playback and record, respectively. 748 | unsigned int nDeviceChannels[2]; // Playback and record channels, respectively. 749 | unsigned int channelOffset[2]; // Playback and record, respectively. 750 | unsigned long latency[2]; // Playback and record, respectively. 751 | RtAudioFormat userFormat; 752 | RtAudioFormat deviceFormat[2]; // Playback and record, respectively. 753 | StreamMutex mutex; 754 | CallbackInfo callbackInfo; 755 | ConvertInfo convertInfo[2]; 756 | double streamTime; // Number of elapsed seconds since the stream started. 757 | 758 | #if defined(HAVE_GETTIMEOFDAY) 759 | struct timeval lastTickTimestamp; 760 | #endif 761 | 762 | RtApiStream() 763 | :apiHandle(0), deviceBuffer(0) { device[0] = 11111; device[1] = 11111; } 764 | }; 765 | 766 | typedef S24 Int24; 767 | typedef signed short Int16; 768 | typedef signed int Int32; 769 | typedef float Float32; 770 | typedef double Float64; 771 | 772 | std::ostringstream errorStream_; 773 | std::string errorText_; 774 | bool showWarnings_; 775 | RtApiStream stream_; 776 | bool firstErrorOccurred_; 777 | 778 | /*! 779 | Protected, api-specific method that attempts to open a device 780 | with the given parameters. This function MUST be implemented by 781 | all subclasses. If an error is encountered during the probe, a 782 | "warning" message is reported and FAILURE is returned. A 783 | successful probe is indicated by a return value of SUCCESS. 784 | */ 785 | virtual bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 786 | unsigned int firstChannel, unsigned int sampleRate, 787 | RtAudioFormat format, unsigned int *bufferSize, 788 | RtAudio::StreamOptions *options ); 789 | 790 | //! A protected function used to increment the stream time. 791 | void tickStreamTime( void ); 792 | 793 | //! Protected common method to clear an RtApiStream structure. 794 | void clearStreamInfo(); 795 | 796 | /*! 797 | Protected common method that throws an RtAudioError (type = 798 | INVALID_USE) if a stream is not open. 799 | */ 800 | void verifyStream( void ); 801 | 802 | //! Protected common error method to allow global control over error handling. 803 | void error( RtAudioError::Type type ); 804 | 805 | /*! 806 | Protected method used to perform format, channel number, and/or interleaving 807 | conversions between the user and device buffers. 808 | */ 809 | void convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info ); 810 | 811 | //! Protected common method used to perform byte-swapping on buffers. 812 | void byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format ); 813 | 814 | //! Protected common method that returns the number of bytes for a given format. 815 | unsigned int formatBytes( RtAudioFormat format ); 816 | 817 | //! Protected common method that sets up the parameters for buffer conversion. 818 | void setConvertInfo( StreamMode mode, unsigned int firstChannel ); 819 | }; 820 | 821 | // **************************************************************** // 822 | // 823 | // Inline RtAudio definitions. 824 | // 825 | // **************************************************************** // 826 | 827 | inline RtAudio::Api RtAudio :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } 828 | inline unsigned int RtAudio :: getDeviceCount( void ) throw() { return rtapi_->getDeviceCount(); } 829 | inline RtAudio::DeviceInfo RtAudio :: getDeviceInfo( unsigned int device ) { return rtapi_->getDeviceInfo( device ); } 830 | inline unsigned int RtAudio :: getDefaultInputDevice( void ) throw() { return rtapi_->getDefaultInputDevice(); } 831 | inline unsigned int RtAudio :: getDefaultOutputDevice( void ) throw() { return rtapi_->getDefaultOutputDevice(); } 832 | inline void RtAudio :: closeStream( void ) throw() { return rtapi_->closeStream(); } 833 | inline void RtAudio :: startStream( void ) { return rtapi_->startStream(); } 834 | inline void RtAudio :: stopStream( void ) { return rtapi_->stopStream(); } 835 | inline void RtAudio :: abortStream( void ) { return rtapi_->abortStream(); } 836 | inline bool RtAudio :: isStreamOpen( void ) const throw() { return rtapi_->isStreamOpen(); } 837 | inline bool RtAudio :: isStreamRunning( void ) const throw() { return rtapi_->isStreamRunning(); } 838 | inline long RtAudio :: getStreamLatency( void ) { return rtapi_->getStreamLatency(); } 839 | inline unsigned int RtAudio :: getStreamSampleRate( void ) { return rtapi_->getStreamSampleRate(); } 840 | inline double RtAudio :: getStreamTime( void ) { return rtapi_->getStreamTime(); } 841 | inline void RtAudio :: setStreamTime( double time ) { return rtapi_->setStreamTime( time ); } 842 | inline void RtAudio :: showWarnings( bool value ) throw() { rtapi_->showWarnings( value ); } 843 | 844 | // RtApi Subclass prototypes. 845 | 846 | #if defined(__MACOSX_CORE__) 847 | 848 | #include 849 | 850 | class RtApiCore: public RtApi 851 | { 852 | public: 853 | 854 | RtApiCore(); 855 | ~RtApiCore(); 856 | RtAudio::Api getCurrentApi( void ) { return RtAudio::MACOSX_CORE; } 857 | unsigned int getDeviceCount( void ); 858 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 859 | unsigned int getDefaultOutputDevice( void ); 860 | unsigned int getDefaultInputDevice( void ); 861 | void closeStream( void ); 862 | void startStream( void ); 863 | void stopStream( void ); 864 | void abortStream( void ); 865 | long getStreamLatency( void ); 866 | 867 | // This function is intended for internal use only. It must be 868 | // public because it is called by the internal callback handler, 869 | // which is not a member of RtAudio. External use of this function 870 | // will most likely produce highly undesireable results! 871 | bool callbackEvent( AudioDeviceID deviceId, 872 | const AudioBufferList *inBufferList, 873 | const AudioBufferList *outBufferList ); 874 | 875 | private: 876 | 877 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 878 | unsigned int firstChannel, unsigned int sampleRate, 879 | RtAudioFormat format, unsigned int *bufferSize, 880 | RtAudio::StreamOptions *options ); 881 | static const char* getErrorCode( OSStatus code ); 882 | }; 883 | 884 | #endif 885 | 886 | #if defined(__UNIX_JACK__) 887 | 888 | class RtApiJack: public RtApi 889 | { 890 | public: 891 | 892 | RtApiJack(); 893 | ~RtApiJack(); 894 | RtAudio::Api getCurrentApi( void ) { return RtAudio::UNIX_JACK; } 895 | unsigned int getDeviceCount( void ); 896 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 897 | void closeStream( void ); 898 | void startStream( void ); 899 | void stopStream( void ); 900 | void abortStream( void ); 901 | long getStreamLatency( void ); 902 | 903 | // This function is intended for internal use only. It must be 904 | // public because it is called by the internal callback handler, 905 | // which is not a member of RtAudio. External use of this function 906 | // will most likely produce highly undesireable results! 907 | bool callbackEvent( unsigned long nframes ); 908 | 909 | private: 910 | 911 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 912 | unsigned int firstChannel, unsigned int sampleRate, 913 | RtAudioFormat format, unsigned int *bufferSize, 914 | RtAudio::StreamOptions *options ); 915 | }; 916 | 917 | #endif 918 | 919 | #if defined(__WINDOWS_ASIO__) 920 | 921 | class RtApiAsio: public RtApi 922 | { 923 | public: 924 | 925 | RtApiAsio(); 926 | ~RtApiAsio(); 927 | RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_ASIO; } 928 | unsigned int getDeviceCount( void ); 929 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 930 | void closeStream( void ); 931 | void startStream( void ); 932 | void stopStream( void ); 933 | void abortStream( void ); 934 | long getStreamLatency( void ); 935 | 936 | // This function is intended for internal use only. It must be 937 | // public because it is called by the internal callback handler, 938 | // which is not a member of RtAudio. External use of this function 939 | // will most likely produce highly undesireable results! 940 | bool callbackEvent( long bufferIndex ); 941 | 942 | private: 943 | 944 | std::vector devices_; 945 | void saveDeviceInfo( void ); 946 | bool coInitialized_; 947 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 948 | unsigned int firstChannel, unsigned int sampleRate, 949 | RtAudioFormat format, unsigned int *bufferSize, 950 | RtAudio::StreamOptions *options ); 951 | }; 952 | 953 | #endif 954 | 955 | #if defined(__WINDOWS_DS__) 956 | 957 | class RtApiDs: public RtApi 958 | { 959 | public: 960 | 961 | RtApiDs(); 962 | ~RtApiDs(); 963 | RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_DS; } 964 | unsigned int getDeviceCount( void ); 965 | unsigned int getDefaultOutputDevice( void ); 966 | unsigned int getDefaultInputDevice( void ); 967 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 968 | void closeStream( void ); 969 | void startStream( void ); 970 | void stopStream( void ); 971 | void abortStream( void ); 972 | long getStreamLatency( void ); 973 | 974 | // This function is intended for internal use only. It must be 975 | // public because it is called by the internal callback handler, 976 | // which is not a member of RtAudio. External use of this function 977 | // will most likely produce highly undesireable results! 978 | void callbackEvent( void ); 979 | 980 | private: 981 | 982 | bool coInitialized_; 983 | bool buffersRolling; 984 | long duplexPrerollBytes; 985 | std::vector dsDevices; 986 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 987 | unsigned int firstChannel, unsigned int sampleRate, 988 | RtAudioFormat format, unsigned int *bufferSize, 989 | RtAudio::StreamOptions *options ); 990 | }; 991 | 992 | #endif 993 | 994 | #if defined(__WINDOWS_WASAPI__) 995 | 996 | struct IMMDeviceEnumerator; 997 | 998 | class RtApiWasapi : public RtApi 999 | { 1000 | public: 1001 | RtApiWasapi(); 1002 | ~RtApiWasapi(); 1003 | 1004 | RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_WASAPI; } 1005 | unsigned int getDeviceCount( void ); 1006 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 1007 | unsigned int getDefaultOutputDevice( void ); 1008 | unsigned int getDefaultInputDevice( void ); 1009 | void closeStream( void ); 1010 | void startStream( void ); 1011 | void stopStream( void ); 1012 | void abortStream( void ); 1013 | 1014 | private: 1015 | bool coInitialized_; 1016 | IMMDeviceEnumerator* deviceEnumerator_; 1017 | 1018 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 1019 | unsigned int firstChannel, unsigned int sampleRate, 1020 | RtAudioFormat format, unsigned int* bufferSize, 1021 | RtAudio::StreamOptions* options ); 1022 | 1023 | static DWORD WINAPI runWasapiThread( void* wasapiPtr ); 1024 | static DWORD WINAPI stopWasapiThread( void* wasapiPtr ); 1025 | static DWORD WINAPI abortWasapiThread( void* wasapiPtr ); 1026 | void wasapiThread(); 1027 | }; 1028 | 1029 | #endif 1030 | 1031 | #if defined(__LINUX_ALSA__) 1032 | 1033 | class RtApiAlsa: public RtApi 1034 | { 1035 | public: 1036 | 1037 | RtApiAlsa(); 1038 | ~RtApiAlsa(); 1039 | RtAudio::Api getCurrentApi() { return RtAudio::LINUX_ALSA; } 1040 | unsigned int getDeviceCount( void ); 1041 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 1042 | void closeStream( void ); 1043 | void startStream( void ); 1044 | void stopStream( void ); 1045 | void abortStream( void ); 1046 | 1047 | // This function is intended for internal use only. It must be 1048 | // public because it is called by the internal callback handler, 1049 | // which is not a member of RtAudio. External use of this function 1050 | // will most likely produce highly undesireable results! 1051 | void callbackEvent( void ); 1052 | 1053 | private: 1054 | 1055 | std::vector devices_; 1056 | void saveDeviceInfo( void ); 1057 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 1058 | unsigned int firstChannel, unsigned int sampleRate, 1059 | RtAudioFormat format, unsigned int *bufferSize, 1060 | RtAudio::StreamOptions *options ); 1061 | }; 1062 | 1063 | #endif 1064 | 1065 | #if defined(__LINUX_PULSE__) 1066 | 1067 | class RtApiPulse: public RtApi 1068 | { 1069 | public: 1070 | ~RtApiPulse(); 1071 | RtAudio::Api getCurrentApi() { return RtAudio::LINUX_PULSE; } 1072 | unsigned int getDeviceCount( void ); 1073 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 1074 | void closeStream( void ); 1075 | void startStream( void ); 1076 | void stopStream( void ); 1077 | void abortStream( void ); 1078 | 1079 | // This function is intended for internal use only. It must be 1080 | // public because it is called by the internal callback handler, 1081 | // which is not a member of RtAudio. External use of this function 1082 | // will most likely produce highly undesireable results! 1083 | void callbackEvent( void ); 1084 | 1085 | private: 1086 | 1087 | std::vector devices_; 1088 | void saveDeviceInfo( void ); 1089 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 1090 | unsigned int firstChannel, unsigned int sampleRate, 1091 | RtAudioFormat format, unsigned int *bufferSize, 1092 | RtAudio::StreamOptions *options ); 1093 | }; 1094 | 1095 | #endif 1096 | 1097 | #if defined(__LINUX_OSS__) 1098 | 1099 | class RtApiOss: public RtApi 1100 | { 1101 | public: 1102 | 1103 | RtApiOss(); 1104 | ~RtApiOss(); 1105 | RtAudio::Api getCurrentApi() { return RtAudio::LINUX_OSS; } 1106 | unsigned int getDeviceCount( void ); 1107 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 1108 | void closeStream( void ); 1109 | void startStream( void ); 1110 | void stopStream( void ); 1111 | void abortStream( void ); 1112 | 1113 | // This function is intended for internal use only. It must be 1114 | // public because it is called by the internal callback handler, 1115 | // which is not a member of RtAudio. External use of this function 1116 | // will most likely produce highly undesireable results! 1117 | void callbackEvent( void ); 1118 | 1119 | private: 1120 | 1121 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 1122 | unsigned int firstChannel, unsigned int sampleRate, 1123 | RtAudioFormat format, unsigned int *bufferSize, 1124 | RtAudio::StreamOptions *options ); 1125 | }; 1126 | 1127 | #endif 1128 | 1129 | #if defined(__RTAUDIO_DUMMY__) 1130 | 1131 | class RtApiDummy: public RtApi 1132 | { 1133 | public: 1134 | 1135 | RtApiDummy() { errorText_ = "RtApiDummy: This class provides no functionality."; error( RtAudioError::WARNING ); } 1136 | RtAudio::Api getCurrentApi( void ) { return RtAudio::RTAUDIO_DUMMY; } 1137 | unsigned int getDeviceCount( void ) { return 0; } 1138 | RtAudio::DeviceInfo getDeviceInfo( unsigned int /*device*/ ) { RtAudio::DeviceInfo info; return info; } 1139 | void closeStream( void ) {} 1140 | void startStream( void ) {} 1141 | void stopStream( void ) {} 1142 | void abortStream( void ) {} 1143 | 1144 | private: 1145 | 1146 | bool probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/, 1147 | unsigned int /*firstChannel*/, unsigned int /*sampleRate*/, 1148 | RtAudioFormat /*format*/, unsigned int * /*bufferSize*/, 1149 | RtAudio::StreamOptions * /*options*/ ) { return false; } 1150 | }; 1151 | 1152 | #endif 1153 | 1154 | #endif 1155 | 1156 | // Indentation settings for Vim and Emacs 1157 | // 1158 | // Local Variables: 1159 | // c-basic-offset: 2 1160 | // indent-tabs-mode: nil 1161 | // End: 1162 | // 1163 | // vim: et sts=2 sw=2 1164 | --------------------------------------------------------------------------------