Skip to content

Commit

Permalink
Avoid pointer arithmetic with HRTF storage
Browse files Browse the repository at this point in the history
  • Loading branch information
kcat committed Mar 24, 2024
1 parent f7c5237 commit 3e117dc
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 38 deletions.
75 changes: 42 additions & 33 deletions core/hrtf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ std::mutex EnumeratedHrtfLock;
std::vector<HrtfEntry> EnumeratedHrtfs;


/* NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
* To access a memory buffer through the std::istream interface, a custom
* std::streambuf implementation is needed that has to do pointer manipulation
* for seeking. With C++23, we may be able to use std::spanstream instead.
*/
class databuf final : public std::streambuf {
int_type underflow() override
{ return traits_type::eof(); }
Expand Down Expand Up @@ -176,6 +181,7 @@ class databuf final : public std::streambuf {
setg(data.data(), data.data(), al::to_address(data.end()));
}
};
/* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) */

class idstream final : public std::istream {
databuf mStreamBuf;
Expand Down Expand Up @@ -218,7 +224,7 @@ IdxBlend CalcAzIndex(uint azcount, float az)
* and azimuth in radians. The coefficients are normalized.
*/
void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float spread,
HrirArray &coeffs, const al::span<uint,2> delays) const
const HrirSpan coeffs, const al::span<uint,2> delays) const
{
const float dirfact{1.0f - (al::numbers::inv_pi_v<float>/2.0f * spread)};

Expand Down Expand Up @@ -269,17 +275,17 @@ void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float
delays[1] = fastf2u(d * float{1.0f/HrirDelayFracOne});

/* Calculate the blended HRIR coefficients. */
float *coeffout{al::assume_aligned<16>(coeffs[0].data())};
coeffout[0] = PassthruCoeff * (1.0f-dirfact);
coeffout[1] = PassthruCoeff * (1.0f-dirfact);
std::fill_n(coeffout+2, size_t{HrirLength-1}*2, 0.0f);
auto coeffout = coeffs.begin();
coeffout[0][0] = PassthruCoeff * (1.0f-dirfact);
coeffout[0][1] = PassthruCoeff * (1.0f-dirfact);
std::fill_n(coeffout+1, size_t{HrirLength-1}, std::array{0.0f, 0.0f});
for(size_t c{0};c < 4;c++)
{
const float *srccoeffs{al::assume_aligned<16>(mCoeffs[idx[c]][0].data())};
const float mult{blend[c]};
auto blend_coeffs = [mult](const float src, const float coeff) noexcept -> float
{ return src*mult + coeff; };
std::transform(srccoeffs, srccoeffs + HrirLength*2_uz, coeffout, coeffout, blend_coeffs);
auto blend_coeffs = [mult](const float2 &src, const float2 &coeff) noexcept -> float2
{ return float2{{src[0]*mult + coeff[0], src[1]*mult + coeff[1]}}; };
std::transform(mCoeffs[idx[c]].cbegin(), mCoeffs[idx[c]].cend(), coeffout, coeffout,
blend_coeffs);
}
}

Expand Down Expand Up @@ -420,44 +426,47 @@ std::unique_ptr<HrtfStore> CreateHrtfStore(uint rate, uint8_t irSize,
Hrtf->mIrSize = irSize;

/* Set up pointers to storage following the main HRTF struct. */
char *base = reinterpret_cast<char*>(Hrtf.get());
size_t offset{sizeof(HrtfStore)};
auto storage = al::span{reinterpret_cast<char*>(Hrtf.get()), total};
auto base = storage.begin();
ptrdiff_t offset{sizeof(HrtfStore)};

offset = RoundUp(offset, alignof(HrtfStore::Field)); /* Align for field infos */
auto field_ = reinterpret_cast<HrtfStore::Field*>(base + offset);
offset += sizeof(field_[0])*fields.size();
auto field_ = al::span{reinterpret_cast<HrtfStore::Field*>(al::to_address(base + offset)),
fields.size()};
offset += ptrdiff_t(sizeof(field_[0])*fields.size());

offset = RoundUp(offset, alignof(HrtfStore::Elevation)); /* Align for elevation infos */
auto elev_ = reinterpret_cast<HrtfStore::Elevation*>(base + offset);
offset += sizeof(elev_[0])*elevs.size();
auto elev_ = al::span{reinterpret_cast<HrtfStore::Elevation*>(al::to_address(base + offset)),
elevs.size()};
offset += ptrdiff_t(sizeof(elev_[0])*elevs.size());

offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */
auto coeffs_ = reinterpret_cast<HrirArray*>(base + offset);
offset += sizeof(coeffs_[0])*irCount;
auto coeffs_ = al::span{reinterpret_cast<HrirArray*>(al::to_address(base + offset)), irCount};
offset += ptrdiff_t(sizeof(coeffs_[0])*irCount);

auto delays_ = reinterpret_cast<ubyte2*>(base + offset);
offset += sizeof(delays_[0])*irCount;
auto delays_ = al::span{reinterpret_cast<ubyte2*>(al::to_address(base + offset)), irCount};
offset += ptrdiff_t(sizeof(delays_[0])*irCount);

if(offset != total)
if(size_t(offset) != total)
throw std::runtime_error{"HrtfStore allocation size mismatch"};

/* Copy input data to storage. */
std::uninitialized_copy(fields.cbegin(), fields.cend(), field_);
std::uninitialized_copy(elevs.cbegin(), elevs.cend(), elev_);
std::uninitialized_copy_n(coeffs, irCount, coeffs_);
std::uninitialized_copy_n(delays, irCount, delays_);
std::uninitialized_copy(fields.cbegin(), fields.cend(), field_.begin());
std::uninitialized_copy(elevs.cbegin(), elevs.cend(), elev_.begin());
std::uninitialized_copy_n(coeffs, irCount, coeffs_.begin());
std::uninitialized_copy_n(delays, irCount, delays_.begin());

/* Finally, assign the storage pointers. */
Hrtf->mFields = {field_, fields.size()};
Hrtf->mFields = field_;
Hrtf->mElev = elev_;
Hrtf->mCoeffs = coeffs_;
Hrtf->mDelays = delays_;

return Hrtf;
}

void MirrorLeftHrirs(const al::span<const HrtfStore::Elevation> elevs, HrirArray *coeffs,
ubyte2 *delays)
void MirrorLeftHrirs(const al::span<const HrtfStore::Elevation> elevs, al::span<HrirArray> coeffs,
al::span<ubyte2> delays)
{
for(const auto &elev : elevs)
{
Expand Down Expand Up @@ -608,7 +617,7 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data)
}

/* Mirror the left ear responses to the right ear. */
MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data());
MirrorLeftHrirs(elevs, coeffs, delays);

const std::array field{HrtfStore::Field{0.0f, evCount}};
return CreateHrtfStore(rate, static_cast<uint8_t>(irSize), field, elevs, coeffs.data(),
Expand Down Expand Up @@ -679,7 +688,7 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data)
}

/* Mirror the left ear responses to the right ear. */
MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data());
MirrorLeftHrirs(elevs, coeffs, delays);

const std::array field{HrtfStore::Field{0.0f, evCount}};
return CreateHrtfStore(rate, irSize, field, elevs, coeffs.data(), delays.data());
Expand Down Expand Up @@ -756,7 +765,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data)

const size_t ebase{elevs.size()};
elevs.resize(ebase + evCount);
for(auto &elev : al::span<HrtfStore::Elevation>(elevs.data()+ebase, evCount))
for(auto &elev : al::span<HrtfStore::Elevation>{elevs}.subspan(ebase, evCount))
elev.azCount = readle<uint8_t>(data);
if(!data || data.eof())
throw std::runtime_error{"Premature end of file"};
Expand Down Expand Up @@ -818,7 +827,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data)
}

/* Mirror the left ear responses to the right ear. */
MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data());
MirrorLeftHrirs(elevs, coeffs, delays);
}
else if(channelType == ChanType_LeftRight)
{
Expand Down Expand Up @@ -1003,7 +1012,7 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data)

const size_t ebase{elevs.size()};
elevs.resize(ebase + evCount);
for(auto &elev : al::span<HrtfStore::Elevation>(elevs.data()+ebase, evCount))
for(auto &elev : al::span<HrtfStore::Elevation>{elevs}.subspan(ebase, evCount))
elev.azCount = readle<uint8_t>(data);
if(!data || data.eof())
throw std::runtime_error{"Premature end of file"};
Expand Down Expand Up @@ -1054,7 +1063,7 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data)
}

/* Mirror the left ear responses to the right ear. */
MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data());
MirrorLeftHrirs(elevs, coeffs, delays);
}
else if(channelType == ChanType_LeftRight)
{
Expand Down
10 changes: 5 additions & 5 deletions core/hrtf.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ struct alignas(16) HrtfStore {
ushort azCount;
ushort irOffset;
};
Elevation *mElev;
const HrirArray *mCoeffs;
const ubyte2 *mDelays;
al::span<Elevation> mElev;
al::span<const HrirArray> mCoeffs;
al::span<const ubyte2> mDelays;

void getCoeffs(float elevation, float azimuth, float distance, float spread, HrirArray &coeffs,
const al::span<uint,2> delays) const;
void getCoeffs(float elevation, float azimuth, float distance, float spread,
const HrirSpan coeffs, const al::span<uint,2> delays) const;

void add_ref();
void dec_ref();
Expand Down

0 comments on commit 3e117dc

Please sign in to comment.