├── .bzrignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── include ├── Ambisonix.h └── ambisonix │ ├── AmbComponent.h │ ├── AmbFormat.h │ ├── AmbSource.h │ └── AmbSpeaker.h └── src ├── AmbComponent.cpp ├── AmbFormat.cpp ├── AmbSource.cpp ├── AmbSpeaker.cpp └── Ambisonix.cpp /.bzrignore: -------------------------------------------------------------------------------- 1 | *.user 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(Ambisonix) 4 | 5 | file(GLOB srcs src/*.cpp) 6 | file(GLOB hdrs include/*.h) 7 | file(GLOB in_hdrs include/ambisonix/*.h) 8 | 9 | include_directories( 10 | ${CMAKE_SOURCE_DIR}/include 11 | ) 12 | 13 | add_library( 14 | ${PROJECT_NAME} 15 | ${srcs} 16 | ${hdrs} 17 | ${in_hdrs} 18 | ) 19 | 20 | install( 21 | TARGETS ${PROJECT_NAME} 22 | DESTINATION lib 23 | ) 24 | 25 | install( 26 | FILES ${hdrs} 27 | DESTINATION include 28 | ) 29 | 30 | install( 31 | FILES ${in_hdrs} 32 | DESTINATION include/ambisonix 33 | ) 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2020, Marcus Tomlinson 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ambisonix 2 | C++ speaker layout-independent 3D positional audio library 3 | 4 | Ambisonix is an object-oriented positional audio library that gives you the ability to create realistic surround sound applications without having to conform to a particular speaker layout. The easy-to-use C++ API allows you to specify any number of sources / speakers and their positions in order to pin-point the best possible audio image for any sound system. 5 | 6 | Ambisonix makes use of 3rd order Ambisonics technology with distance encoding and Doppler effect to deliver a true 3D audio experience. 7 | -------------------------------------------------------------------------------- /include/Ambisonix.h: -------------------------------------------------------------------------------- 1 | #ifndef AMBISONIX_H 2 | #define AMBISONIX_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | //================================================================================================= 10 | 11 | static float DegToRad( float degrees ) 12 | { 13 | return degrees * 3.14159265358979323846f / 180.f; 14 | } 15 | 16 | static float RadToDeg( float radians ) 17 | { 18 | return radians * 180.f / 3.14159265358979323846f; 19 | } 20 | 21 | //================================================================================================= 22 | 23 | class Ambisonix 24 | { 25 | public: 26 | Ambisonix( unsigned short bufferSize = 128, unsigned long sampleRate = 44100 ); 27 | virtual ~Ambisonix(); 28 | 29 | void SetBufferSize( unsigned short bufferSize ); 30 | void SetSampleRate( unsigned long sampleRate ); 31 | 32 | unsigned short GetBufferSize() const; 33 | unsigned long GetSampleRate() const; 34 | 35 | void AddSourceCart( float x, float y, float z ); 36 | void AddSourceSph( float azimuth, float elevation, float distance ); 37 | bool MoveSourceCart( unsigned short sourceIndex, float x, float y, float z ); 38 | bool MoveSourceSph( unsigned short sourceIndex, float azimuth, float elevation, float distance ); 39 | void ClearSources(); 40 | unsigned short GetSourceCount() const; 41 | 42 | void AddSpeakerCart( float x, float y, float z ); 43 | void AddSpeakerSph( float azimuth, float elevation, float distance ); 44 | bool MoveSpeakerCart( unsigned short speakerIndex, float x, float y, float z ); 45 | bool MoveSpeakerSph( unsigned short speakerIndex, float azimuth, float elevation, float distance ); 46 | void ClearSpeakers(); 47 | unsigned short GetSpeakerCount() const; 48 | 49 | bool SetSourceInput( unsigned short sourceIndex, std::vector< float >& sourceInput ); 50 | bool GetSpeakerOutput( unsigned short speakerIndex, std::vector< float >& speakerOutput ); 51 | 52 | bool SetOrder( unsigned char order ); 53 | 54 | private: 55 | std::vector< AmbSource > _sources; 56 | std::vector< AmbSpeaker > _speakers; 57 | 58 | AmbFormat _bFormat; 59 | 60 | unsigned short _bufferSize; 61 | unsigned long _sampleRate; 62 | float _speakerRadius; 63 | 64 | bool _speakerOutputProcessed; 65 | 66 | unsigned char _order; 67 | 68 | static void _CartToSph( float& x_azimuth, float& y_elevation, float& z_distance ); 69 | static void _ClampSph( float& azimuth, float& elevation ); 70 | }; 71 | 72 | //================================================================================================= 73 | 74 | #endif // AMBISONIX_H 75 | -------------------------------------------------------------------------------- /include/ambisonix/AmbComponent.h: -------------------------------------------------------------------------------- 1 | #ifndef AMBCOMPONENT_H 2 | #define AMBCOMPONENT_H 3 | 4 | #include 5 | #include 6 | 7 | //================================================================================================= 8 | 9 | struct PolarCoord 10 | { 11 | float azimuth; 12 | float elevation; 13 | float distance; 14 | }; 15 | 16 | //================================================================================================= 17 | 18 | class AmbComponent 19 | { 20 | public: 21 | AmbComponent(); 22 | virtual ~AmbComponent(); 23 | 24 | virtual void SetPosition( PolarCoord position ); 25 | PolarCoord GetPosition() const; 26 | 27 | void SetSpeakerRadius( float speakerRadius ); 28 | float GetSpeakerRadius() const; 29 | 30 | bool SetOrder( unsigned char order ); 31 | 32 | protected: 33 | float coeffValues_[17]; 34 | float coeffWeights_[17]; 35 | PolarCoord position_; 36 | 37 | unsigned char order_; 38 | unsigned char nCoeff_; 39 | 40 | float _speakerRadius; 41 | }; 42 | 43 | //================================================================================================= 44 | 45 | #endif // AMBCOMPONENT_H 46 | -------------------------------------------------------------------------------- /include/ambisonix/AmbFormat.h: -------------------------------------------------------------------------------- 1 | #ifndef AMBFORMAT_H 2 | #define AMBFORMAT_H 3 | 4 | #include 5 | 6 | //================================================================================================= 7 | 8 | class AmbFormat 9 | { 10 | public: 11 | AmbFormat( unsigned short bufferSize = 128 ); 12 | virtual ~AmbFormat(); 13 | 14 | bool SetBufferSize( unsigned short bufferSize ); 15 | unsigned short GetBufferSize() const; 16 | 17 | void Clear(); 18 | 19 | AmbFormat& operator+( const AmbFormat& rhs ); 20 | AmbFormat& operator+=( const AmbFormat& rhs ); 21 | 22 | std::vector< float > data[17]; 23 | 24 | private: 25 | unsigned short _bufferSize; 26 | }; 27 | 28 | //================================================================================================= 29 | 30 | #endif // AMBFORMAT_H 31 | -------------------------------------------------------------------------------- /include/ambisonix/AmbSource.h: -------------------------------------------------------------------------------- 1 | #ifndef AMBSOURCE_H 2 | #define AMBSOURCE_H 3 | 4 | #include 5 | 6 | //================================================================================================= 7 | 8 | class AmbSource : public AmbComponent 9 | { 10 | public: 11 | AmbSource( unsigned long sampleRate = 44100 ); 12 | virtual ~AmbSource(); 13 | 14 | bool SetSampleRate( unsigned long sampleRate ); 15 | void SetPosition( PolarCoord position ); 16 | 17 | void Process( std::vector< float >& src, AmbFormat& dest ); 18 | 19 | private: 20 | unsigned long _sampleRate; 21 | 22 | float _delayFrac; 23 | unsigned long _delayWhole; 24 | float _newDelayFrac; 25 | unsigned long _newDelayWhole; 26 | unsigned long _delayBufferSize; 27 | std::vector< float > _delayBuffer; 28 | unsigned long _inIndex; 29 | unsigned long _newInIndex; 30 | unsigned long _outAIndex; 31 | unsigned long _outBIndex; 32 | 33 | float _lastDistance; 34 | bool _distanceChanged; 35 | 36 | static void StretchBuffer( std::vector< float >& actAddr, unsigned long actLength, std::vector< float >& strAddr, unsigned long strLength ); 37 | }; 38 | 39 | //================================================================================================= 40 | 41 | #endif // AMBSOURCE_H 42 | -------------------------------------------------------------------------------- /include/ambisonix/AmbSpeaker.h: -------------------------------------------------------------------------------- 1 | #ifndef AMBSPEAKER_H 2 | #define AMBSPEAKER_H 3 | 4 | #include 5 | 6 | //================================================================================================= 7 | 8 | class AmbSpeaker : public AmbComponent 9 | { 10 | public: 11 | AmbSpeaker(); 12 | virtual ~AmbSpeaker(); 13 | 14 | void Process( AmbFormat& src, std::vector< float >& dest ) const; 15 | }; 16 | 17 | //================================================================================================= 18 | 19 | #endif // AMBSPEAKER_H 20 | -------------------------------------------------------------------------------- /src/AmbComponent.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static const float fSqrt3_2 = sqrtf( 3.f ) / 2.f; 5 | static const float fSqrt_3_8 = sqrtf( 3.f / 8.f ); 6 | static const float fSqrt15_2 = sqrtf( 15.f ) / 2.f; 7 | static const float fSqrt_5_8 = sqrtf( 5.f / 8.f ); 8 | 9 | //================================================================================================= 10 | 11 | AmbComponent::AmbComponent() 12 | : _speakerRadius( 1.f ) 13 | { 14 | position_.azimuth = 0.f; 15 | position_.elevation = 0.f; 16 | position_.distance = 0.f; 17 | 18 | coeffWeights_[0] = 1.f / sqrtf( 2.f ); 19 | 20 | coeffWeights_[1] = 1.f; 21 | coeffWeights_[2] = 1.f; 22 | coeffWeights_[3] = 1.f; 23 | 24 | coeffWeights_[4] = 1.f; 25 | coeffWeights_[5] = 2.f / sqrtf( 3.f ); 26 | coeffWeights_[6] = 2.f / sqrtf( 3.f ); 27 | coeffWeights_[7] = 2.f / sqrtf( 3.f ); 28 | coeffWeights_[8] = 2.f / sqrtf( 3.f ); 29 | 30 | coeffWeights_[9] = 1.f; 31 | coeffWeights_[10] = sqrtf( 45.f / 32.f ); 32 | coeffWeights_[11] = sqrtf( 45.f / 32.f ); 33 | coeffWeights_[12] = 3.f / sqrtf( 5.f ); 34 | coeffWeights_[13] = 3.f / sqrtf( 5.f ); 35 | coeffWeights_[14] = sqrtf( 8.f / 5.f ); 36 | coeffWeights_[15] = sqrtf( 8.f / 5.f ); 37 | 38 | coeffWeights_[16] = 1.f; 39 | 40 | SetOrder( 3 ); 41 | SetPosition( position_ ); 42 | } 43 | 44 | //------------------------------------------------------------------------------------------------- 45 | 46 | AmbComponent::~AmbComponent() {} 47 | 48 | //================================================================================================= 49 | 50 | void AmbComponent::SetPosition( PolarCoord position ) 51 | { 52 | position_ = position; 53 | 54 | float hypSphDistance = 0.0; 55 | 56 | if( _speakerRadius != 0 ) 57 | { 58 | if( position_.distance < _speakerRadius ) 59 | { 60 | hypSphDistance = ( position_.distance * 3.14159265358979323846f ) / ( 2.0f * _speakerRadius ); 61 | } 62 | else 63 | { 64 | hypSphDistance = 3.14159265358979323846f / 2.0f; 65 | } 66 | } 67 | else 68 | { 69 | hypSphDistance = 0; 70 | } 71 | 72 | float fSinDist = sinf( hypSphDistance ); 73 | float fCosDist = cosf( hypSphDistance ); 74 | 75 | float fCosAzim = cosf( position_.azimuth ); 76 | float fSinAzim = sinf( position_.azimuth ); 77 | float fCosElev = cosf( position_.elevation ); 78 | float fSinElev = sinf( position_.elevation ); 79 | 80 | float fCos2Azim = cosf( 2.f * position_.azimuth ); 81 | float fSin2Azim = sinf( 2.f * position_.azimuth ); 82 | float fSin2Elev = sinf( 2.f * position_.elevation ); 83 | 84 | float fCos3Azim = cosf( 3.f * position_.azimuth ); 85 | float fSin3Azim = sinf( 3.f * position_.azimuth ); 86 | 87 | float fCosElev2 = powf( fCosElev, 2.f ); 88 | float fCosElev3 = powf( fCosElev, 3.f ); 89 | float fSinElev2 = powf( fSinElev, 2.f ); 90 | 91 | // clear coefficients 92 | memset( coeffValues_, 0, 17 * sizeof( float ) ); 93 | 94 | // Zero Order 95 | coeffValues_[0] = coeffWeights_[0]; 96 | 97 | // First Order 98 | if( order_ >= 1 ) 99 | { 100 | coeffValues_[1] = ( fCosAzim * fCosElev * fSinDist ) * coeffWeights_[1]; 101 | coeffValues_[2] = ( fSinAzim * fCosElev * fSinDist ) * coeffWeights_[2]; 102 | coeffValues_[3] = ( fSinElev * fSinDist ) * coeffWeights_[3]; 103 | } 104 | 105 | // Second Order 106 | if( order_ >= 2 ) 107 | { 108 | coeffValues_[4] = ( 0.5f * ( 3.f * fSinElev2 - 1.f ) * fSinDist ) * coeffWeights_[4]; 109 | coeffValues_[5] = ( fSqrt3_2 * fCosAzim * fSin2Elev * fSinDist ) * coeffWeights_[5]; 110 | coeffValues_[6] = ( fSqrt3_2 * fSinAzim * fSin2Elev * fSinDist ) * coeffWeights_[6]; 111 | coeffValues_[7] = ( fSqrt3_2 * fCos2Azim * fCosElev2 * fSinDist ) * coeffWeights_[7]; 112 | coeffValues_[8] = ( fSqrt3_2 * fSin2Azim * fCosElev2 * fSinDist ) * coeffWeights_[8]; 113 | } 114 | 115 | // Third Order 116 | if( order_ >= 3 ) 117 | { 118 | coeffValues_[9] = ( 0.5f * fSinElev * ( 5.f * fSinElev2 - 3.f ) * fSinDist ) * coeffWeights_[9]; 119 | coeffValues_[10] = ( fSqrt_3_8 * fCosAzim * fCosElev * ( 5.f * fSinElev2 - 1.f ) * fSinDist ) * coeffWeights_[10]; 120 | coeffValues_[11] = ( fSqrt_3_8 * fSinAzim * fCosElev * ( 5.f * fSinElev2 - 1.f ) * fSinDist ) * coeffWeights_[11]; 121 | coeffValues_[12] = ( fSqrt15_2 * fCos2Azim * fSinElev * fCosElev2 * fSinDist ) * coeffWeights_[12]; 122 | coeffValues_[13] = ( fSqrt15_2 * fSin2Azim * fSinElev * fCosElev2 * fSinDist ) * coeffWeights_[13]; 123 | coeffValues_[14] = ( fSqrt_5_8 * fCos3Azim * fCosElev3 * fSinDist ) * coeffWeights_[14]; 124 | coeffValues_[15] = ( fSqrt_5_8 * fSin3Azim * fCosElev3 * fSinDist ) * coeffWeights_[15]; 125 | } 126 | 127 | // Distance Encoding 128 | coeffValues_[16] = fCosDist * coeffWeights_[16]; 129 | } 130 | 131 | //------------------------------------------------------------------------------------------------- 132 | 133 | PolarCoord AmbComponent::GetPosition() const 134 | { 135 | return position_; 136 | } 137 | 138 | //------------------------------------------------------------------------------------------------- 139 | 140 | void AmbComponent::SetSpeakerRadius( float speakerRadius ) 141 | { 142 | _speakerRadius = speakerRadius; 143 | SetPosition( position_ ); 144 | } 145 | 146 | //------------------------------------------------------------------------------------------------- 147 | 148 | float AmbComponent::GetSpeakerRadius() const 149 | { 150 | return _speakerRadius; 151 | } 152 | 153 | //------------------------------------------------------------------------------------------------- 154 | 155 | bool AmbComponent::SetOrder( unsigned char order ) 156 | { 157 | switch( order ) 158 | { 159 | case 0: 160 | nCoeff_ = 1; 161 | break; 162 | 163 | case 1: 164 | nCoeff_ = 4; 165 | break; 166 | 167 | case 2: 168 | nCoeff_ = 9; 169 | break; 170 | 171 | case 3: 172 | nCoeff_ = 16; 173 | break; 174 | 175 | default: 176 | return false; 177 | } 178 | 179 | order_ = order; 180 | 181 | SetPosition( position_ ); 182 | return true; 183 | } 184 | 185 | //================================================================================================= 186 | -------------------------------------------------------------------------------- /src/AmbFormat.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //================================================================================================= 4 | 5 | AmbFormat::AmbFormat( unsigned short bufferSize ) 6 | : _bufferSize( bufferSize ) 7 | { 8 | SetBufferSize( bufferSize ); 9 | } 10 | 11 | //------------------------------------------------------------------------------------------------- 12 | 13 | AmbFormat::~AmbFormat() {} 14 | 15 | //================================================================================================= 16 | 17 | bool AmbFormat::SetBufferSize( unsigned short bufferSize ) 18 | { 19 | _bufferSize = bufferSize; 20 | 21 | for( unsigned short i = 0; i < 17; i++ ) 22 | { 23 | data[i].resize( bufferSize ); 24 | } 25 | 26 | return true; 27 | } 28 | 29 | //------------------------------------------------------------------------------------------------- 30 | 31 | unsigned short AmbFormat::GetBufferSize() const 32 | { 33 | return _bufferSize; 34 | } 35 | 36 | //------------------------------------------------------------------------------------------------- 37 | 38 | void AmbFormat::Clear() 39 | { 40 | for( unsigned short i = 0; i < 17; i++ ) 41 | { 42 | for( unsigned short j = 0; j < _bufferSize; j++ ) 43 | { 44 | data[i][j] = 0; 45 | } 46 | } 47 | } 48 | 49 | //------------------------------------------------------------------------------------------------- 50 | 51 | AmbFormat& AmbFormat::operator+( const AmbFormat& rhs ) 52 | { 53 | for( unsigned short i = 0; i < 17; i++ ) 54 | { 55 | for( unsigned short j = 0; j < _bufferSize; j++ ) 56 | { 57 | data[i][j] += rhs.data[i][j]; 58 | } 59 | } 60 | 61 | return *this; 62 | } 63 | 64 | //------------------------------------------------------------------------------------------------- 65 | 66 | AmbFormat& AmbFormat::operator+=( const AmbFormat& rhs ) 67 | { 68 | return *this + rhs; 69 | } 70 | 71 | //================================================================================================= 72 | -------------------------------------------------------------------------------- /src/AmbSource.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static const unsigned short speedOfSound = 344; 4 | static const unsigned short maxDistance = 150; 5 | 6 | //================================================================================================= 7 | 8 | AmbSource::AmbSource( unsigned long sampleRate ) 9 | : _sampleRate( sampleRate ), 10 | 11 | _delayFrac( 0.f ), 12 | _delayWhole( 0 ), 13 | _newDelayFrac( 0 ), 14 | _newDelayWhole( 0 ), 15 | _delayBufferSize( 0 ), 16 | _inIndex( 0 ), 17 | _newInIndex( 0 ), 18 | _outAIndex( 0 ), 19 | _outBIndex( 0 ), 20 | 21 | _lastDistance( 0.f ), 22 | _distanceChanged( false ) 23 | { 24 | SetSampleRate( _sampleRate ); 25 | } 26 | 27 | //------------------------------------------------------------------------------------------------- 28 | 29 | AmbSource::~AmbSource() {} 30 | 31 | //================================================================================================= 32 | 33 | bool AmbSource::SetSampleRate( unsigned long sampleRate ) 34 | { 35 | _sampleRate = sampleRate; 36 | 37 | _delayBufferSize = ( unsigned long ) ( ( float ) maxDistance / speedOfSound * _sampleRate + 0.5f ); 38 | _delayBuffer.resize( _delayBufferSize ); 39 | _delayBuffer.assign( _delayBufferSize, 0 ); 40 | 41 | _delayFrac = position_.distance / speedOfSound * _sampleRate + 0.5f; 42 | _delayWhole = ( unsigned long ) _delayFrac; 43 | _delayFrac -= _delayWhole; 44 | 45 | _outAIndex = 0; 46 | _outBIndex = 1; 47 | _inIndex = ( _outBIndex + _delayWhole ) % _delayBufferSize; 48 | 49 | return true; 50 | } 51 | 52 | //------------------------------------------------------------------------------------------------- 53 | 54 | void AmbSource::SetPosition( PolarCoord position ) 55 | { 56 | AmbComponent::SetPosition( position ); 57 | 58 | if( !_distanceChanged && position_.distance != _lastDistance ) 59 | { 60 | _distanceChanged = true; 61 | 62 | _lastDistance = position_.distance; 63 | 64 | _newDelayFrac = ( fabs( position_.distance ) / speedOfSound ) * _sampleRate; 65 | _newDelayWhole = ( unsigned long ) _newDelayFrac; 66 | _newDelayFrac -= _newDelayWhole; 67 | 68 | _newInIndex = ( _outBIndex + _newDelayWhole ) % _delayBufferSize; 69 | 70 | if( _newDelayWhole == _delayWhole ) 71 | { 72 | _distanceChanged = false; 73 | _delayFrac = _newDelayFrac; 74 | } 75 | } 76 | } 77 | 78 | //------------------------------------------------------------------------------------------------- 79 | 80 | void AmbSource::Process( std::vector< float >& src, AmbFormat& dest ) 81 | { 82 | unsigned short bufferSize = dest.GetBufferSize(); 83 | 84 | if( src.size() != bufferSize ) 85 | { 86 | src.resize( bufferSize ); 87 | } 88 | 89 | // INPUT 90 | if( _distanceChanged ) 91 | { 92 | // Moves away 93 | // ========== 94 | if( _newDelayWhole > _delayWhole ) 95 | { 96 | unsigned long newBufferSize = bufferSize + ( _newDelayWhole - _delayWhole ); 97 | std::vector< float > stretched; 98 | stretched.resize( newBufferSize ); 99 | StretchBuffer( src, bufferSize, stretched, newBufferSize ); 100 | 101 | for( unsigned long i = 0; i < newBufferSize; i++ ) 102 | { 103 | _delayBuffer[_inIndex] = stretched[i]; 104 | _inIndex = ( _inIndex + 1 ) % _delayBufferSize; 105 | } 106 | 107 | _distanceChanged = false; 108 | _delayFrac = _newDelayFrac; 109 | _delayWhole = _newDelayWhole; 110 | _inIndex = ( _newInIndex + bufferSize ) % _delayBufferSize; 111 | } 112 | 113 | // Moves closer 114 | // ============ 115 | else if( _newDelayWhole < _delayWhole ) 116 | { 117 | long newLength = bufferSize - ( _delayWhole - _newDelayWhole ); 118 | 119 | // > Speed of sound 120 | // ================ 121 | if( newLength < 0 ) 122 | { 123 | // this is where the funky stuff happens 124 | _distanceChanged = false; 125 | _delayFrac = _newDelayFrac; 126 | _delayWhole = _newDelayWhole; 127 | _inIndex = ( _newInIndex + bufferSize ) % _delayBufferSize; 128 | } 129 | 130 | // <= Speed of sound 131 | // ================= 132 | else if( newLength >= 0 ) 133 | { 134 | if( newLength > 0 ) 135 | { 136 | std::vector< float > stretched; 137 | stretched.resize( newLength ); 138 | 139 | StretchBuffer( src, bufferSize, stretched, newLength ); 140 | 141 | for( long i = 0; i < newLength; i++ ) 142 | { 143 | _delayBuffer[_inIndex] = stretched[i]; 144 | _inIndex = ( _inIndex + 1 ) % _delayBufferSize; 145 | } 146 | } 147 | 148 | _distanceChanged = false; 149 | _delayFrac = _newDelayFrac; 150 | _delayWhole = _newDelayWhole; 151 | _inIndex = ( _newInIndex + bufferSize ) % _delayBufferSize; 152 | } 153 | } 154 | } 155 | else 156 | { 157 | for( unsigned short i = 0; i < bufferSize; i++ ) 158 | { 159 | _delayBuffer[_inIndex] = src[i]; 160 | _inIndex = ( _inIndex + 1 ) % _delayBufferSize; 161 | } 162 | } 163 | 164 | // OUTPUT 165 | for( unsigned short i = 0; i < bufferSize; i++ ) 166 | { 167 | float sample = ( _delayBuffer[_outAIndex] * ( 1.f - _delayFrac ) ) + 168 | ( _delayBuffer[_outBIndex] * _delayFrac ); 169 | 170 | //Source is outside speaker array 171 | if( fabs( position_.distance ) > _speakerRadius ) 172 | { 173 | sample *= _speakerRadius / fabs( position_.distance ); 174 | } 175 | 176 | for( unsigned short j = 0; j < nCoeff_; j++ ) 177 | { 178 | dest.data[j][i] += sample * coeffValues_[j]; 179 | } 180 | dest.data[16][i] += sample * coeffValues_[16]; 181 | 182 | _outAIndex = ( _outAIndex + 1 ) % _delayBufferSize; 183 | _outBIndex = ( _outBIndex + 1 ) % _delayBufferSize; 184 | } 185 | } 186 | 187 | //================================================================================================= 188 | 189 | void AmbSource::StretchBuffer( std::vector< float >& actBuffer, unsigned long actLength, std::vector< float >& strBuffer, unsigned long strLength ) 190 | { 191 | if( actLength != strLength ) 192 | { 193 | for( unsigned long i = 0; i < strLength; i++ ) 194 | { 195 | float frac = ( i / ( float ) ( strLength - 1 ) ) * ( actLength - 1 ); 196 | unsigned long whole = ( unsigned long ) frac; 197 | frac -= whole; 198 | 199 | if( whole != actLength - 1 ) 200 | { 201 | strBuffer[i] = ( ( 1.0f - frac ) * actBuffer[ whole ] ) + ( frac * actBuffer[ whole + 1 ] ); 202 | } 203 | else 204 | { 205 | strBuffer[i] = actBuffer[actLength - 1]; 206 | } 207 | } 208 | } 209 | else if( actLength == strLength ) 210 | { 211 | actBuffer = strBuffer; 212 | } 213 | } 214 | 215 | //================================================================================================= 216 | -------------------------------------------------------------------------------- /src/AmbSpeaker.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //================================================================================================= 4 | 5 | AmbSpeaker::AmbSpeaker() {} 6 | 7 | //------------------------------------------------------------------------------------------------- 8 | 9 | AmbSpeaker::~AmbSpeaker() {} 10 | 11 | //================================================================================================= 12 | 13 | void AmbSpeaker::Process( AmbFormat& src, std::vector< float >& dest ) const 14 | { 15 | unsigned short bufferSize = src.GetBufferSize(); 16 | 17 | if( dest.size() != bufferSize ) 18 | { 19 | dest.resize( bufferSize ); 20 | } 21 | 22 | dest.assign( dest.size(), 0 ); 23 | 24 | for( unsigned short i = 0; i < bufferSize; i++ ) 25 | { 26 | for( unsigned short j = 0; j < nCoeff_; j++ ) 27 | { 28 | dest[i] += src.data[j][i] * coeffValues_[j]; 29 | } 30 | dest[i] += src.data[16][i] * coeffValues_[16]; 31 | } 32 | } 33 | 34 | //================================================================================================= 35 | -------------------------------------------------------------------------------- /src/Ambisonix.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //================================================================================================= 4 | 5 | Ambisonix::Ambisonix( unsigned short bufferSize, unsigned long sampleRate ) 6 | : _bufferSize( bufferSize ), 7 | _sampleRate( sampleRate ), 8 | _speakerRadius( 0 ), 9 | _speakerOutputProcessed( false ), 10 | _order( 3 ) {} 11 | 12 | //------------------------------------------------------------------------------------------------- 13 | 14 | Ambisonix::~Ambisonix() {} 15 | 16 | //================================================================================================= 17 | 18 | void Ambisonix::SetBufferSize( unsigned short bufferSize ) 19 | { 20 | _bufferSize = bufferSize; 21 | 22 | _bFormat.SetBufferSize( _bufferSize ); 23 | } 24 | 25 | //------------------------------------------------------------------------------------------------- 26 | 27 | void Ambisonix::SetSampleRate( unsigned long sampleRate ) 28 | { 29 | _sampleRate = sampleRate; 30 | 31 | for( unsigned short i = 0; i < _sources.size(); i++ ) 32 | { 33 | _sources[i].SetSampleRate( sampleRate ); 34 | } 35 | } 36 | 37 | //------------------------------------------------------------------------------------------------- 38 | 39 | unsigned short Ambisonix::GetBufferSize() const 40 | { 41 | return _bufferSize; 42 | } 43 | 44 | //------------------------------------------------------------------------------------------------- 45 | 46 | unsigned long Ambisonix::GetSampleRate() const 47 | { 48 | return _sampleRate; 49 | } 50 | 51 | //------------------------------------------------------------------------------------------------- 52 | 53 | void Ambisonix::AddSourceCart( float x, float y, float z ) 54 | { 55 | _CartToSph( x, y, z ); 56 | AddSourceSph( x, y, z ); 57 | } 58 | 59 | //------------------------------------------------------------------------------------------------- 60 | 61 | void Ambisonix::AddSourceSph( float azimuth, float elevation, float distance ) 62 | { 63 | AmbSource newSource( _sampleRate ); 64 | 65 | _ClampSph( azimuth, elevation ); 66 | 67 | newSource.SetSpeakerRadius( _speakerRadius ); 68 | newSource.SetOrder( _order ); 69 | _sources.push_back( newSource ); 70 | 71 | MoveSourceSph( _sources.size() - 1, azimuth, elevation, distance ); 72 | } 73 | 74 | //------------------------------------------------------------------------------------------------- 75 | 76 | bool Ambisonix::MoveSourceCart( unsigned short sourceIndex, float x, float y, float z ) 77 | { 78 | _CartToSph( x, y, z ); 79 | return MoveSourceSph( sourceIndex, x, y, z ); 80 | } 81 | 82 | //------------------------------------------------------------------------------------------------- 83 | 84 | bool Ambisonix::MoveSourceSph( unsigned short sourceIndex, float azimuth, float elevation, float distance ) 85 | { 86 | if( sourceIndex >= _sources.size() ) 87 | { 88 | return false; 89 | } 90 | 91 | _ClampSph( azimuth, elevation ); 92 | 93 | PolarCoord inputPosition; 94 | 95 | inputPosition.azimuth = DegToRad( azimuth ); 96 | inputPosition.elevation = DegToRad( elevation ); 97 | inputPosition.distance = distance; 98 | 99 | _sources[sourceIndex].SetPosition( inputPosition ); 100 | 101 | return true; 102 | } 103 | 104 | //------------------------------------------------------------------------------------------------- 105 | 106 | void Ambisonix::ClearSources() 107 | { 108 | _sources.clear(); 109 | } 110 | 111 | //------------------------------------------------------------------------------------------------- 112 | 113 | unsigned short Ambisonix::GetSourceCount() const 114 | { 115 | return _sources.size(); 116 | } 117 | 118 | //------------------------------------------------------------------------------------------------- 119 | 120 | void Ambisonix::AddSpeakerCart( float x, float y, float z ) 121 | { 122 | _CartToSph( x, y, z ); 123 | AddSpeakerSph( x, y, z ); 124 | } 125 | 126 | //------------------------------------------------------------------------------------------------- 127 | 128 | void Ambisonix::AddSpeakerSph( float azimuth, float elevation, float distance ) 129 | { 130 | AmbSpeaker newSpeaker; 131 | 132 | _ClampSph( azimuth, elevation ); 133 | 134 | newSpeaker.SetSpeakerRadius( _speakerRadius ); 135 | newSpeaker.SetOrder( _order ); 136 | _speakers.push_back( newSpeaker ); 137 | 138 | MoveSpeakerSph( _speakers.size() - 1, azimuth, elevation, distance ); 139 | } 140 | 141 | //------------------------------------------------------------------------------------------------- 142 | 143 | bool Ambisonix::MoveSpeakerCart( unsigned short speakerIndex, float x, float y, float z ) 144 | { 145 | _CartToSph( x, y, z ); 146 | return MoveSpeakerSph( speakerIndex, x, y, z ); 147 | } 148 | 149 | //------------------------------------------------------------------------------------------------- 150 | 151 | bool Ambisonix::MoveSpeakerSph( unsigned short speakerIndex, float azimuth, float elevation, float distance ) 152 | { 153 | if( speakerIndex >= _speakers.size() ) 154 | { 155 | return false; 156 | } 157 | 158 | _ClampSph( azimuth, elevation ); 159 | 160 | PolarCoord speakerPosition; 161 | 162 | speakerPosition.azimuth = DegToRad( azimuth ); 163 | speakerPosition.elevation = DegToRad( elevation ); 164 | speakerPosition.distance = distance; 165 | 166 | _speakers[speakerIndex].SetPosition( speakerPosition ); 167 | 168 | // update speaker radius 169 | _speakerRadius = 0; 170 | for( unsigned short i = 0; i < _speakers.size(); i++ ) 171 | { 172 | if( _speakers[i].GetPosition().distance > _speakerRadius ) 173 | { 174 | _speakerRadius = _speakers[i].GetPosition().distance; 175 | } 176 | } 177 | 178 | for( unsigned short i = 0; i < _sources.size(); i++ ) 179 | { 180 | _sources[i].SetSpeakerRadius( _speakerRadius ); 181 | } 182 | 183 | for( unsigned short i = 0; i < _speakers.size(); i++ ) 184 | { 185 | _speakers[i].SetSpeakerRadius( _speakerRadius ); 186 | } 187 | 188 | return true; 189 | } 190 | 191 | //------------------------------------------------------------------------------------------------- 192 | 193 | void Ambisonix::ClearSpeakers() 194 | { 195 | _speakers.clear(); 196 | _speakerRadius = 0; 197 | } 198 | 199 | //------------------------------------------------------------------------------------------------- 200 | 201 | unsigned short Ambisonix::GetSpeakerCount() const 202 | { 203 | return _speakers.size(); 204 | } 205 | 206 | //------------------------------------------------------------------------------------------------- 207 | 208 | bool Ambisonix::SetSourceInput( unsigned short sourceIndex, std::vector< float >& sourceInput ) 209 | { 210 | if( sourceIndex >= _sources.size() ) 211 | { 212 | return false; 213 | } 214 | 215 | if( _speakerOutputProcessed ) 216 | { 217 | _bFormat.Clear(); 218 | _speakerOutputProcessed = false; 219 | } 220 | 221 | _sources[sourceIndex].Process( sourceInput, _bFormat ); 222 | 223 | return true; 224 | } 225 | 226 | //------------------------------------------------------------------------------------------------- 227 | 228 | bool Ambisonix::GetSpeakerOutput( unsigned short speakerIndex, std::vector< float >& speakerOutput ) 229 | { 230 | if( speakerIndex >= _speakers.size() ) 231 | { 232 | return false; 233 | } 234 | 235 | _speakers[speakerIndex].Process( _bFormat, speakerOutput ); 236 | _speakerOutputProcessed = true; 237 | 238 | for( unsigned short i = 0; i < speakerOutput.size(); i++ ) 239 | { 240 | speakerOutput[i] /= _speakers.size(); 241 | } 242 | 243 | return true; 244 | } 245 | 246 | //------------------------------------------------------------------------------------------------- 247 | 248 | bool Ambisonix::SetOrder( unsigned char order ) 249 | { 250 | if( order > 3 ) 251 | { 252 | return false; 253 | } 254 | 255 | for( unsigned long i = 0; i < _sources.size(); i++ ) 256 | { 257 | _sources[i].SetOrder( order ); 258 | } 259 | 260 | for( unsigned long i = 0; i < _speakers.size(); i++ ) 261 | { 262 | _speakers[i].SetOrder( order ); 263 | } 264 | 265 | _order = order; 266 | return true; 267 | } 268 | 269 | //================================================================================================= 270 | 271 | void Ambisonix::_CartToSph( float& x_azimuth, float& y_elevation, float& z_distance ) 272 | { 273 | float x, y, z; 274 | x = x_azimuth; 275 | y = y_elevation; 276 | z = z_distance; 277 | 278 | x_azimuth = 90 - RadToDeg( atan2( y, x ) ); 279 | 280 | if( x < 0 && y <= 0 ) //3rd quadrant 281 | x_azimuth -= 360; 282 | 283 | y_elevation = RadToDeg( atan2( z, sqrt( x * x + y * y ) ) ); 284 | z_distance = sqrt( x * x + y * y + z * z ); 285 | } 286 | 287 | //------------------------------------------------------------------------------------------------- 288 | 289 | void Ambisonix::_ClampSph( float& azimuth, float& elevation ) 290 | { 291 | while( azimuth > 180 ) 292 | { 293 | azimuth -= 360; 294 | } 295 | while( azimuth <= -180 ) 296 | { 297 | azimuth += 360; 298 | } 299 | 300 | if( elevation > 90 ) 301 | { 302 | elevation = 90; 303 | } 304 | else if( elevation < -90 ) 305 | { 306 | elevation = -90; 307 | } 308 | } 309 | 310 | //================================================================================================= 311 | --------------------------------------------------------------------------------