#include "HrtfData.h" #include "Endianness.h" #include #include #include typedef struct { uint8_t bytes[3]; } sample_int24_t; const double pi = 3.1415926535897932385; template void read_stream(std::istream& stream, T& value) { stream.read(reinterpret_cast(&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 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 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 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 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 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(elevation_scaled0); const size_t elevation_index10 = static_cast(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(azimuth_scaled00) % elevations0[elevation_index00].azimuths.size(); const size_t azimuth_index001 = static_cast(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(azimuth_scaled10) % elevations1[elevation_index10].azimuths.size(); const size_t azimuth_index101 = static_cast(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(azimuth_scaled01) % elevations0[elevation_index01].azimuths.size(); const size_t azimuth_index011 = static_cast(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(azimuth_scaled11) % elevations1[elevation_index11].azimuths.size(); const size_t azimuth_index111 = static_cast(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(elevation_scaled0); const size_t elevation_index10 = static_cast(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(azimuth_scaled00) % elevations0[elevation_index00].azimuths.size(); const size_t azimuth_index001 = static_cast(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(azimuth_scaled10) % elevations1[elevation_index10].azimuths.size(); const size_t azimuth_index101 = static_cast(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(azimuth_scaled01) % elevations0[elevation_index01].azimuths.size(); const size_t azimuth_index011 = static_cast(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(azimuth_scaled11) % elevations1[elevation_index11].azimuths.size(); const size_t azimuth_index111 = static_cast(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); } }