641 lines
27 KiB
C++
641 lines
27 KiB
C++
|
|
#include "HrtfData.h"
|
|
#include "Endianness.h"
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
typedef struct {
|
|
uint8_t bytes[3];
|
|
} sample_int24_t;
|
|
|
|
const double pi = 3.1415926535897932385;
|
|
|
|
template <typename T>
|
|
void read_stream(std::istream& stream, T& value) {
|
|
stream.read(reinterpret_cast<std::istream::char_type*>(&value), sizeof(value));
|
|
from_little_endian_inplace(value);
|
|
}
|
|
|
|
HrtfData::HrtfData(std::istream& stream) {
|
|
const char required_magic00[] = { 'M', 'i', 'n', 'P', 'H', 'R', '0', '0' };
|
|
const char required_magic01[] = { 'M', 'i', 'n', 'P', 'H', 'R', '0', '1' };
|
|
const char required_magic02[] = { 'M', 'i', 'n', 'P', 'H', 'R', '0', '2' };
|
|
const char required_magic03[] = { 'M', 'i', 'n', 'P', 'H', 'R', '0', '3' };
|
|
char actual_magic[sizeof(required_magic03) / sizeof(required_magic03[0])];
|
|
|
|
stream.read(actual_magic, sizeof(actual_magic));
|
|
if(std::equal(std::begin(required_magic03), std::end(required_magic03), std::begin(actual_magic), std::end(actual_magic))) {
|
|
LoadHrtf03(stream);
|
|
} else if(std::equal(std::begin(required_magic02), std::end(required_magic02), std::begin(actual_magic), std::end(actual_magic))) {
|
|
LoadHrtf02(stream);
|
|
} else if(std::equal(std::begin(required_magic01), std::end(required_magic01), std::begin(actual_magic), std::end(actual_magic))) {
|
|
LoadHrtf01(stream);
|
|
} else if(std::equal(std::begin(required_magic00), std::end(required_magic00), std::begin(actual_magic), std::end(actual_magic))) {
|
|
LoadHrtf00(stream);
|
|
} else {
|
|
throw std::logic_error("Bad file format.");
|
|
}
|
|
}
|
|
|
|
void HrtfData::LoadHrtf03(std::istream& stream) {
|
|
// const uint8_t ChanType_LeftOnly{0};
|
|
const uint8_t ChanType_LeftRight{ 1 };
|
|
|
|
uint32_t sample_rate;
|
|
uint8_t channel_type;
|
|
uint8_t impulse_response_length;
|
|
uint8_t distances_count;
|
|
|
|
read_stream(stream, sample_rate);
|
|
read_stream(stream, channel_type);
|
|
read_stream(stream, impulse_response_length);
|
|
read_stream(stream, distances_count);
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
if(channel_type > ChanType_LeftRight) {
|
|
throw std::logic_error("Invalid channel format.");
|
|
}
|
|
|
|
int channel_count = channel_type == ChanType_LeftRight ? 2 : 1;
|
|
|
|
std::vector<DistanceData> distances(distances_count);
|
|
|
|
for(uint8_t i = 0; i < distances_count; i++) {
|
|
uint16_t distance;
|
|
read_stream(stream, distance);
|
|
distances[i].distance = float(distance) / 1000.0f;
|
|
|
|
uint8_t elevations_count;
|
|
read_stream(stream, elevations_count);
|
|
distances[i].elevations.resize(elevations_count);
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
for(uint8_t j = 0; j < elevations_count; j++) {
|
|
uint8_t azimuth_count;
|
|
read_stream(stream, azimuth_count);
|
|
distances[i].elevations[j].azimuths.resize(azimuth_count);
|
|
}
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
}
|
|
|
|
const float normalization_factor = 1.0f / 8388608.0f;
|
|
|
|
for(auto& distance : distances) {
|
|
for(auto& elevation : distance.elevations) {
|
|
for(auto& azimuth : elevation.azimuths) {
|
|
azimuth.impulse_response.resize(impulse_response_length * channel_count);
|
|
for(auto& sample : azimuth.impulse_response) {
|
|
union {
|
|
sample_int24_t sample;
|
|
int32_t sample_int;
|
|
} sample_union;
|
|
sample_union.sample_int = 0;
|
|
read_stream(stream, sample_union.sample);
|
|
sample_union.sample_int <<= 8;
|
|
sample_union.sample_int >>= 8;
|
|
sample = sample_union.sample_int * normalization_factor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
uint8_t longest_delay = 0;
|
|
for(auto& distance : distances) {
|
|
for(auto& elevation : distance.elevations) {
|
|
for(auto& azimuth : elevation.azimuths) {
|
|
uint8_t delay;
|
|
read_stream(stream, delay);
|
|
azimuth.delay = delay;
|
|
longest_delay = std::max(longest_delay, delay);
|
|
if(channel_type == ChanType_LeftRight) {
|
|
read_stream(stream, delay);
|
|
azimuth.delay_right = delay;
|
|
longest_delay = std::max(longest_delay, delay);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
std::sort(distances.begin(), distances.end(),
|
|
[](const DistanceData& lhs, const DistanceData& rhs) noexcept { return lhs.distance > rhs.distance; });
|
|
|
|
m_distances = std::move(distances);
|
|
m_channel_count = channel_count;
|
|
m_response_length = impulse_response_length;
|
|
m_sample_rate = sample_rate;
|
|
m_longest_delay = longest_delay;
|
|
}
|
|
|
|
void HrtfData::LoadHrtf02(std::istream& stream) {
|
|
// const uint8_t SampleType_S16{0};
|
|
const uint8_t SampleType_S24{ 1 };
|
|
// const uint8_t ChanType_LeftOnly{0};
|
|
const uint8_t ChanType_LeftRight{ 1 };
|
|
|
|
uint32_t sample_rate;
|
|
uint8_t sample_type;
|
|
uint8_t channel_type;
|
|
uint8_t impulse_response_length;
|
|
uint8_t distances_count;
|
|
|
|
read_stream(stream, sample_rate);
|
|
read_stream(stream, sample_type);
|
|
read_stream(stream, channel_type);
|
|
read_stream(stream, impulse_response_length);
|
|
read_stream(stream, distances_count);
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
if(sample_type > SampleType_S24) {
|
|
throw std::logic_error("Invalid sample type.");
|
|
}
|
|
|
|
if(channel_type > ChanType_LeftRight) {
|
|
throw std::logic_error("Invalid channel format.");
|
|
}
|
|
|
|
int channel_count = channel_type == ChanType_LeftRight ? 2 : 1;
|
|
|
|
std::vector<DistanceData> distances(distances_count);
|
|
|
|
for(uint8_t i = 0; i < distances_count; i++) {
|
|
uint16_t distance;
|
|
read_stream(stream, distance);
|
|
distances[i].distance = float(distance) / 1000.0f;
|
|
|
|
uint8_t elevations_count;
|
|
read_stream(stream, elevations_count);
|
|
distances[i].elevations.resize(elevations_count);
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
for(uint8_t j = 0; j < elevations_count; j++) {
|
|
uint8_t azimuth_count;
|
|
read_stream(stream, azimuth_count);
|
|
distances[i].elevations[j].azimuths.resize(azimuth_count);
|
|
}
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
}
|
|
|
|
const float normalization_factor = (sample_type == SampleType_S24) ? 1.0f / 8388608.0f : 1.0f / 32768.0f;
|
|
|
|
for(auto& distance : distances) {
|
|
for(auto& elevation : distance.elevations) {
|
|
for(auto& azimuth : elevation.azimuths) {
|
|
azimuth.impulse_response.resize(impulse_response_length * channel_count);
|
|
if(sample_type == SampleType_S24) {
|
|
for(auto& sample : azimuth.impulse_response) {
|
|
union {
|
|
sample_int24_t sample;
|
|
int32_t sample_int;
|
|
} sample_union;
|
|
sample_union.sample_int = 0;
|
|
read_stream(stream, sample_union.sample);
|
|
sample_union.sample_int <<= 8;
|
|
sample_union.sample_int >>= 8;
|
|
sample = sample_union.sample_int * normalization_factor;
|
|
}
|
|
} else {
|
|
for(auto& sample : azimuth.impulse_response) {
|
|
int16_t sample_from_file;
|
|
read_stream(stream, sample_from_file);
|
|
sample = sample_from_file * normalization_factor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
uint8_t longest_delay = 0;
|
|
for(auto& distance : distances) {
|
|
for(auto& elevation : distance.elevations) {
|
|
for(auto& azimuth : elevation.azimuths) {
|
|
uint8_t delay;
|
|
read_stream(stream, delay);
|
|
azimuth.delay = delay;
|
|
longest_delay = std::max(longest_delay, delay);
|
|
if(channel_type == ChanType_LeftRight) {
|
|
read_stream(stream, delay);
|
|
azimuth.delay_right = delay;
|
|
longest_delay = std::max(longest_delay, delay);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
std::sort(distances.begin(), distances.end(),
|
|
[](const DistanceData& lhs, const DistanceData& rhs) noexcept { return lhs.distance > rhs.distance; });
|
|
|
|
m_distances = std::move(distances);
|
|
m_channel_count = channel_count;
|
|
m_response_length = impulse_response_length;
|
|
m_sample_rate = sample_rate;
|
|
m_longest_delay = longest_delay;
|
|
}
|
|
|
|
void HrtfData::LoadHrtf01(std::istream& stream) {
|
|
uint32_t sample_rate;
|
|
uint8_t impulse_response_length;
|
|
|
|
read_stream(stream, sample_rate);
|
|
read_stream(stream, impulse_response_length);
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
std::vector<DistanceData> distances(1);
|
|
|
|
distances[0].distance = 1.0;
|
|
|
|
uint8_t elevations_count;
|
|
read_stream(stream, elevations_count);
|
|
distances[0].elevations.resize(elevations_count);
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
for(uint8_t i = 0; i < elevations_count; i++) {
|
|
uint8_t azimuth_count;
|
|
read_stream(stream, azimuth_count);
|
|
distances[0].elevations[i].azimuths.resize(azimuth_count);
|
|
}
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
const float normalization_factor = 1.0f / 32768.0f;
|
|
|
|
for(auto& elevation : distances[0].elevations) {
|
|
for(auto& azimuth : elevation.azimuths) {
|
|
azimuth.impulse_response.resize(impulse_response_length);
|
|
for(auto& sample : azimuth.impulse_response) {
|
|
int16_t sample_from_file;
|
|
read_stream(stream, sample_from_file);
|
|
sample = sample_from_file * normalization_factor;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
uint8_t longest_delay = 0;
|
|
for(auto& elevation : distances[0].elevations) {
|
|
for(auto& azimuth : elevation.azimuths) {
|
|
uint8_t delay;
|
|
read_stream(stream, delay);
|
|
delay <<= 2;
|
|
azimuth.delay = delay;
|
|
longest_delay = std::max(longest_delay, delay);
|
|
}
|
|
}
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
m_distances = std::move(distances);
|
|
m_channel_count = 1;
|
|
m_response_length = impulse_response_length;
|
|
m_sample_rate = sample_rate;
|
|
m_longest_delay = longest_delay;
|
|
}
|
|
|
|
void HrtfData::LoadHrtf00(std::istream& stream) {
|
|
uint32_t sample_rate;
|
|
uint16_t impulse_response_count;
|
|
uint16_t impulse_response_length;
|
|
|
|
read_stream(stream, sample_rate);
|
|
read_stream(stream, impulse_response_count);
|
|
read_stream(stream, impulse_response_length);
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
std::vector<DistanceData> distances(1);
|
|
|
|
distances[0].distance = 1.0;
|
|
|
|
uint8_t elevations_count;
|
|
read_stream(stream, elevations_count);
|
|
distances[0].elevations.resize(elevations_count);
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
std::vector<uint16_t> irOffsets(elevations_count);
|
|
|
|
for(uint8_t i = 0; i < elevations_count; i++) {
|
|
read_stream(stream, irOffsets[i]);
|
|
}
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
for(size_t i = 1; i < elevations_count; i++) {
|
|
if(irOffsets[i] <= irOffsets[i - 1]) {
|
|
throw std::logic_error("Invalid elevation offset.");
|
|
}
|
|
}
|
|
if(impulse_response_count <= irOffsets[elevations_count - 1]) {
|
|
throw std::logic_error("Invalid elevation offset.");
|
|
}
|
|
|
|
for(size_t i = 1; i < elevations_count; i++) {
|
|
distances[0].elevations[i - 1].azimuths.resize(irOffsets[i] - irOffsets[i - 1]);
|
|
}
|
|
distances[0].elevations[elevations_count - 1].azimuths.resize(impulse_response_count - irOffsets[elevations_count - 1]);
|
|
|
|
const float normalization_factor = 1.0f / 32768.0f;
|
|
|
|
for(auto& elevation : distances[0].elevations) {
|
|
for(auto& azimuth : elevation.azimuths) {
|
|
azimuth.impulse_response.resize(impulse_response_length);
|
|
for(auto& sample : azimuth.impulse_response) {
|
|
int16_t sample_from_file;
|
|
read_stream(stream, sample_from_file);
|
|
sample = sample_from_file * normalization_factor;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
uint8_t longest_delay = 0;
|
|
for(auto& elevation : distances[0].elevations) {
|
|
for(auto& azimuth : elevation.azimuths) {
|
|
uint8_t delay;
|
|
read_stream(stream, delay);
|
|
delay <<= 2;
|
|
azimuth.delay = delay;
|
|
longest_delay = std::max(longest_delay, delay);
|
|
}
|
|
}
|
|
|
|
if(!stream || stream.eof()) {
|
|
throw std::logic_error("Failed reading file.");
|
|
}
|
|
|
|
m_distances = std::move(distances);
|
|
m_channel_count = 1;
|
|
m_response_length = impulse_response_length;
|
|
m_sample_rate = sample_rate;
|
|
m_longest_delay = longest_delay;
|
|
}
|
|
|
|
void HrtfData::get_direction_data(angle_t elevation, angle_t azimuth, distance_t distance, uint32_t channel, DirectionData& ref_data) const {
|
|
assert(elevation >= -angle_t(pi * 0.5));
|
|
assert(elevation <= angle_t(pi * 0.5));
|
|
assert(azimuth >= -angle_t(2.0 * pi));
|
|
assert(azimuth <= angle_t(2.0 * pi));
|
|
|
|
const float azimuth_mod = std::fmod(azimuth + angle_t(pi * 2.0), angle_t(pi * 2.0));
|
|
|
|
size_t distance_index0 = 0;
|
|
while(distance_index0 < m_distances.size() - 1 &&
|
|
m_distances[distance_index0].distance > distance) {
|
|
distance_index0++;
|
|
}
|
|
const size_t distance_index1 = std::min(distance_index0 + 1, m_distances.size() - 1);
|
|
const distance_t distance0 = m_distances[distance_index0].distance;
|
|
const distance_t distance1 = m_distances[distance_index1].distance;
|
|
const distance_t distance_delta = distance0 - distance1;
|
|
const float distance_fractional_part = distance_delta ? (distance - distance1) / distance_delta : 0;
|
|
|
|
const auto& elevations0 = m_distances[distance_index0].elevations;
|
|
const auto& elevations1 = m_distances[distance_index1].elevations;
|
|
|
|
const angle_t elevation_scaled0 = (elevation + angle_t(pi * 0.5)) * (elevations0.size() - 1) / angle_t(pi);
|
|
const angle_t elevation_scaled1 = (elevation + angle_t(pi * 0.5)) * (elevations1.size() - 1) / angle_t(pi);
|
|
const size_t elevation_index00 = static_cast<size_t>(elevation_scaled0);
|
|
const size_t elevation_index10 = static_cast<size_t>(elevation_scaled1);
|
|
const size_t elevation_index01 = std::min(elevation_index00 + 1, elevations0.size() - 1);
|
|
const size_t elevation_index11 = std::min(elevation_index10 + 1, elevations1.size() - 1);
|
|
|
|
const float elevation_fractional_part0 = std::fmod(elevation_scaled0, 1.0);
|
|
const float elevation_fractional_part1 = std::fmod(elevation_scaled1, 1.0);
|
|
|
|
const angle_t azimuth_scaled00 = azimuth_mod * elevations0[elevation_index00].azimuths.size() / angle_t(2 * pi);
|
|
const size_t azimuth_index000 = static_cast<size_t>(azimuth_scaled00) % elevations0[elevation_index00].azimuths.size();
|
|
const size_t azimuth_index001 = static_cast<size_t>(azimuth_scaled00 + 1) % elevations0[elevation_index00].azimuths.size();
|
|
const float azimuth_fractional_part00 = std::fmod(azimuth_scaled00, 1.0);
|
|
|
|
const angle_t azimuth_scaled10 = azimuth_mod * elevations1[elevation_index10].azimuths.size() / angle_t(2 * pi);
|
|
const size_t azimuth_index100 = static_cast<size_t>(azimuth_scaled10) % elevations1[elevation_index10].azimuths.size();
|
|
const size_t azimuth_index101 = static_cast<size_t>(azimuth_scaled10 + 1) % elevations1[elevation_index10].azimuths.size();
|
|
const float azimuth_fractional_part10 = std::fmod(azimuth_scaled10, 1.0);
|
|
|
|
const angle_t azimuth_scaled01 = azimuth_mod * elevations0[elevation_index01].azimuths.size() / angle_t(2 * pi);
|
|
const size_t azimuth_index010 = static_cast<size_t>(azimuth_scaled01) % elevations0[elevation_index01].azimuths.size();
|
|
const size_t azimuth_index011 = static_cast<size_t>(azimuth_scaled01 + 1) % elevations0[elevation_index01].azimuths.size();
|
|
const float azimuth_fractional_part01 = std::fmod(azimuth_scaled01, 1.0);
|
|
|
|
const angle_t azimuth_scaled11 = azimuth_mod * elevations1[elevation_index11].azimuths.size() / angle_t(2 * pi);
|
|
const size_t azimuth_index110 = static_cast<size_t>(azimuth_scaled11) % elevations1[elevation_index11].azimuths.size();
|
|
const size_t azimuth_index111 = static_cast<size_t>(azimuth_scaled11 + 1) % elevations1[elevation_index11].azimuths.size();
|
|
const float azimuth_fractional_part11 = std::fmod(azimuth_scaled11, 1.0);
|
|
|
|
const float blend_factor_000 = (1.0f - elevation_fractional_part0) * (1.0f - azimuth_fractional_part00) * distance_fractional_part;
|
|
const float blend_factor_001 = (1.0f - elevation_fractional_part0) * azimuth_fractional_part00 * distance_fractional_part;
|
|
const float blend_factor_010 = elevation_fractional_part0 * (1.0f - azimuth_fractional_part01) * distance_fractional_part;
|
|
const float blend_factor_011 = elevation_fractional_part0 * azimuth_fractional_part01 * distance_fractional_part;
|
|
|
|
const float blend_factor_100 = (1.0f - elevation_fractional_part1) * (1.0f - azimuth_fractional_part10) * (1.0f - distance_fractional_part);
|
|
const float blend_factor_101 = (1.0f - elevation_fractional_part1) * azimuth_fractional_part10 * (1.0f - distance_fractional_part);
|
|
const float blend_factor_110 = elevation_fractional_part1 * (1.0f - azimuth_fractional_part11) * (1.0f - distance_fractional_part);
|
|
const float blend_factor_111 = elevation_fractional_part1 * azimuth_fractional_part11 * (1.0f - distance_fractional_part);
|
|
|
|
delay_t delay0;
|
|
delay_t delay1;
|
|
|
|
if(channel == 0) {
|
|
delay0 =
|
|
elevations0[elevation_index00].azimuths[azimuth_index000].delay * blend_factor_000 + elevations0[elevation_index00].azimuths[azimuth_index001].delay * blend_factor_001 + elevations0[elevation_index01].azimuths[azimuth_index010].delay * blend_factor_010 + elevations0[elevation_index01].azimuths[azimuth_index011].delay * blend_factor_011;
|
|
|
|
delay1 =
|
|
elevations1[elevation_index10].azimuths[azimuth_index100].delay * blend_factor_100 + elevations1[elevation_index10].azimuths[azimuth_index101].delay * blend_factor_101 + elevations1[elevation_index11].azimuths[azimuth_index110].delay * blend_factor_110 + elevations1[elevation_index11].azimuths[azimuth_index111].delay * blend_factor_111;
|
|
} else {
|
|
delay0 =
|
|
elevations0[elevation_index00].azimuths[azimuth_index000].delay_right * blend_factor_000 + elevations0[elevation_index00].azimuths[azimuth_index001].delay_right * blend_factor_001 + elevations0[elevation_index01].azimuths[azimuth_index010].delay_right * blend_factor_010 + elevations0[elevation_index01].azimuths[azimuth_index011].delay_right * blend_factor_011;
|
|
|
|
delay1 =
|
|
elevations1[elevation_index10].azimuths[azimuth_index100].delay_right * blend_factor_100 + elevations1[elevation_index10].azimuths[azimuth_index101].delay_right * blend_factor_101 + elevations1[elevation_index11].azimuths[azimuth_index110].delay_right * blend_factor_110 + elevations1[elevation_index11].azimuths[azimuth_index111].delay_right * blend_factor_111;
|
|
}
|
|
|
|
ref_data.delay = delay0 + delay1;
|
|
|
|
if(ref_data.impulse_response.size() < m_response_length)
|
|
ref_data.impulse_response.resize(m_response_length);
|
|
|
|
for(size_t i = 0, j = channel; i < m_response_length; i++, j += m_channel_count) {
|
|
float sample0 =
|
|
elevations0[elevation_index00].azimuths[azimuth_index000].impulse_response[j] * blend_factor_000 + elevations0[elevation_index00].azimuths[azimuth_index001].impulse_response[j] * blend_factor_001 + elevations0[elevation_index01].azimuths[azimuth_index010].impulse_response[j] * blend_factor_010 + elevations0[elevation_index01].azimuths[azimuth_index011].impulse_response[j] * blend_factor_011;
|
|
float sample1 =
|
|
elevations1[elevation_index10].azimuths[azimuth_index100].impulse_response[j] * blend_factor_100 + elevations1[elevation_index10].azimuths[azimuth_index101].impulse_response[j] * blend_factor_101 + elevations1[elevation_index11].azimuths[azimuth_index110].impulse_response[j] * blend_factor_110 + elevations1[elevation_index11].azimuths[azimuth_index111].impulse_response[j] * blend_factor_111;
|
|
|
|
ref_data.impulse_response[i] = sample0 + sample1;
|
|
}
|
|
}
|
|
|
|
void HrtfData::get_direction_data(angle_t elevation, angle_t azimuth, distance_t distance, DirectionData& ref_data_left, DirectionData& ref_data_right) const {
|
|
assert(elevation >= -angle_t(pi * 0.5));
|
|
assert(elevation <= angle_t(pi * 0.5));
|
|
assert(azimuth >= -angle_t(2.0 * pi));
|
|
assert(azimuth <= angle_t(2.0 * pi));
|
|
|
|
get_direction_data(elevation, azimuth, distance, 0, ref_data_left);
|
|
if(m_channel_count == 1) {
|
|
get_direction_data(elevation, -azimuth, distance, 0, ref_data_right);
|
|
} else {
|
|
get_direction_data(elevation, azimuth, distance, 1, ref_data_right);
|
|
}
|
|
}
|
|
|
|
void HrtfData::sample_direction(angle_t elevation, angle_t azimuth, distance_t distance, uint32_t sample, uint32_t channel, float& value, float& delay) const {
|
|
assert(elevation >= -angle_t(pi * 0.5));
|
|
assert(elevation <= angle_t(pi * 0.5));
|
|
assert(azimuth >= -angle_t(2.0 * pi));
|
|
assert(azimuth <= angle_t(2.0 * pi));
|
|
|
|
size_t distance_index0 = 0;
|
|
while(distance_index0 < m_distances.size() - 1 &&
|
|
m_distances[distance_index0].distance > distance) {
|
|
distance_index0++;
|
|
}
|
|
const size_t distance_index1 = std::min(distance_index0 + 1, m_distances.size() - 1);
|
|
const distance_t distance0 = m_distances[distance_index0].distance;
|
|
const distance_t distance1 = m_distances[distance_index1].distance;
|
|
const distance_t distance_delta = distance0 - distance1;
|
|
const float distance_fractional_part = distance_delta ? (distance - distance1) / distance_delta : 0;
|
|
|
|
const auto& elevations0 = m_distances[distance_index0].elevations;
|
|
const auto& elevations1 = m_distances[distance_index1].elevations;
|
|
|
|
const float azimuth_mod = std::fmod(azimuth + angle_t(pi * 2.0), angle_t(pi * 2.0));
|
|
|
|
const angle_t elevation_scaled0 = (elevation + angle_t(pi * 0.5)) * (elevations0.size() - 1) / angle_t(pi);
|
|
const angle_t elevation_scaled1 = (elevation + angle_t(pi * 0.5)) * (elevations1.size() - 1) / angle_t(pi);
|
|
const size_t elevation_index00 = static_cast<size_t>(elevation_scaled0);
|
|
const size_t elevation_index10 = static_cast<size_t>(elevation_scaled1);
|
|
const size_t elevation_index01 = std::min(elevation_index00 + 1, elevations0.size() - 1);
|
|
const size_t elevation_index11 = std::min(elevation_index10 + 1, elevations1.size() - 1);
|
|
|
|
const float elevation_fractional_part0 = std::fmod(elevation_scaled0, 1.0);
|
|
const float elevation_fractional_part1 = std::fmod(elevation_scaled1, 1.0);
|
|
|
|
const angle_t azimuth_scaled00 = azimuth_mod * elevations0[elevation_index00].azimuths.size() / angle_t(2 * pi);
|
|
const size_t azimuth_index000 = static_cast<size_t>(azimuth_scaled00) % elevations0[elevation_index00].azimuths.size();
|
|
const size_t azimuth_index001 = static_cast<size_t>(azimuth_scaled00 + 1) % elevations0[elevation_index00].azimuths.size();
|
|
const float azimuth_fractional_part00 = std::fmod(azimuth_scaled00, 1.0);
|
|
|
|
const angle_t azimuth_scaled10 = azimuth_mod * elevations1[elevation_index10].azimuths.size() / angle_t(2 * pi);
|
|
const size_t azimuth_index100 = static_cast<size_t>(azimuth_scaled10) % elevations1[elevation_index10].azimuths.size();
|
|
const size_t azimuth_index101 = static_cast<size_t>(azimuth_scaled10 + 1) % elevations1[elevation_index10].azimuths.size();
|
|
const float azimuth_fractional_part10 = std::fmod(azimuth_scaled10, 1.0);
|
|
|
|
const angle_t azimuth_scaled01 = azimuth_mod * elevations0[elevation_index01].azimuths.size() / angle_t(2 * pi);
|
|
const size_t azimuth_index010 = static_cast<size_t>(azimuth_scaled01) % elevations0[elevation_index01].azimuths.size();
|
|
const size_t azimuth_index011 = static_cast<size_t>(azimuth_scaled01 + 1) % elevations0[elevation_index01].azimuths.size();
|
|
const float azimuth_fractional_part01 = std::fmod(azimuth_scaled01, 1.0);
|
|
|
|
const angle_t azimuth_scaled11 = azimuth_mod * elevations1[elevation_index11].azimuths.size() / angle_t(2 * pi);
|
|
const size_t azimuth_index110 = static_cast<size_t>(azimuth_scaled11) % elevations1[elevation_index11].azimuths.size();
|
|
const size_t azimuth_index111 = static_cast<size_t>(azimuth_scaled11 + 1) % elevations1[elevation_index11].azimuths.size();
|
|
const float azimuth_fractional_part11 = std::fmod(azimuth_scaled11, 1.0);
|
|
|
|
const float blend_factor_000 = (1.0f - elevation_fractional_part0) * (1.0f - azimuth_fractional_part00) * distance_fractional_part;
|
|
const float blend_factor_001 = (1.0f - elevation_fractional_part0) * azimuth_fractional_part00 * distance_fractional_part;
|
|
const float blend_factor_010 = elevation_fractional_part0 * (1.0f - azimuth_fractional_part01) * distance_fractional_part;
|
|
const float blend_factor_011 = elevation_fractional_part0 * azimuth_fractional_part01 * distance_fractional_part;
|
|
|
|
const float blend_factor_100 = (1.0f - elevation_fractional_part1) * (1.0f - azimuth_fractional_part10) * (1.0f - distance_fractional_part);
|
|
const float blend_factor_101 = (1.0f - elevation_fractional_part1) * azimuth_fractional_part10 * (1.0f - distance_fractional_part);
|
|
const float blend_factor_110 = elevation_fractional_part1 * (1.0f - azimuth_fractional_part11) * (1.0f - distance_fractional_part);
|
|
const float blend_factor_111 = elevation_fractional_part1 * azimuth_fractional_part11 * (1.0f - distance_fractional_part);
|
|
|
|
float delay0;
|
|
float delay1;
|
|
|
|
if(channel == 0) {
|
|
delay0 =
|
|
elevations0[elevation_index00].azimuths[azimuth_index000].delay * blend_factor_000 + elevations0[elevation_index00].azimuths[azimuth_index001].delay * blend_factor_001 + elevations0[elevation_index01].azimuths[azimuth_index010].delay * blend_factor_010 + elevations0[elevation_index01].azimuths[azimuth_index011].delay * blend_factor_011;
|
|
|
|
delay1 =
|
|
elevations1[elevation_index10].azimuths[azimuth_index100].delay * blend_factor_100 + elevations1[elevation_index10].azimuths[azimuth_index101].delay * blend_factor_101 + elevations1[elevation_index11].azimuths[azimuth_index110].delay * blend_factor_110 + elevations1[elevation_index11].azimuths[azimuth_index111].delay * blend_factor_111;
|
|
} else {
|
|
delay0 =
|
|
elevations0[elevation_index00].azimuths[azimuth_index000].delay_right * blend_factor_000 + elevations0[elevation_index00].azimuths[azimuth_index001].delay_right * blend_factor_001 + elevations0[elevation_index01].azimuths[azimuth_index010].delay_right * blend_factor_010 + elevations0[elevation_index01].azimuths[azimuth_index011].delay_right * blend_factor_011;
|
|
|
|
delay1 =
|
|
elevations1[elevation_index10].azimuths[azimuth_index100].delay_right * blend_factor_100 + elevations1[elevation_index10].azimuths[azimuth_index101].delay_right * blend_factor_101 + elevations1[elevation_index11].azimuths[azimuth_index110].delay_right * blend_factor_110 + elevations1[elevation_index11].azimuths[azimuth_index111].delay_right * blend_factor_111;
|
|
}
|
|
|
|
delay = delay0 + delay1;
|
|
|
|
sample = sample * m_channel_count + channel;
|
|
|
|
float value0 =
|
|
elevations0[elevation_index00].azimuths[azimuth_index000].impulse_response[sample] * blend_factor_000 + elevations0[elevation_index00].azimuths[azimuth_index001].impulse_response[sample] * blend_factor_001 + elevations0[elevation_index01].azimuths[azimuth_index010].impulse_response[sample] * blend_factor_010 + elevations0[elevation_index01].azimuths[azimuth_index011].impulse_response[sample] * blend_factor_011;
|
|
|
|
float value1 =
|
|
elevations1[elevation_index10].azimuths[azimuth_index100].impulse_response[sample] * blend_factor_100 + elevations1[elevation_index10].azimuths[azimuth_index101].impulse_response[sample] * blend_factor_101 + elevations1[elevation_index11].azimuths[azimuth_index110].impulse_response[sample] * blend_factor_110 + elevations1[elevation_index11].azimuths[azimuth_index111].impulse_response[sample] * blend_factor_111;
|
|
|
|
value = value0 + value1;
|
|
}
|
|
|
|
void HrtfData::sample_direction(angle_t elevation, angle_t azimuth, distance_t distance, uint32_t sample, float& value_left, float& delay_left, float& value_right, float& delay_right) const {
|
|
assert(elevation >= -angle_t(pi * 0.5));
|
|
assert(elevation <= angle_t(pi * 0.5));
|
|
assert(azimuth >= -angle_t(2.0 * pi));
|
|
assert(azimuth <= angle_t(2.0 * pi));
|
|
|
|
sample_direction(elevation, azimuth, distance, sample, 0, value_left, delay_left);
|
|
if(m_channel_count == 1) {
|
|
sample_direction(elevation, -azimuth, distance, sample, 0, value_right, delay_right);
|
|
} else {
|
|
sample_direction(elevation, azimuth, distance, sample, 1, value_right, delay_right);
|
|
}
|
|
}
|