307 static_assert(std::atomic<bool>::is_always_lock_free,
308 "audio-thread stores must not lock");
337 std::memory_order_relaxed);
343 return quality_.load(std::memory_order_relaxed);
368 if (fftSize != 0 && ((fftSize & (fftSize - 1)) != 0
369 || fftSize < 256 || fftSize > (1 << 20)))
372 prepared_.store(
false, std::memory_order_relaxed);
378 fftStandard_ = fftSize != 0 ? fftSize : 2048;
379 fftStudio_ = fftSize != 0 ? fftSize : studioDefaultFrame(spec.
sampleRate);
386 const int widest = std::max(fftStandard_, fftStudio_);
389 while (queueSize_ < capacity) queueSize_ <<= 1;
390 queueMask_ = queueSize_ - 1;
397 engine_.prepare(spec.
sampleRate, numChannels_, fftStandard_,
false,
false,
true,
true);
398 studio_.prepare(spec.
sampleRate, numChannels_, fftStudio_,
false);
400 queue_.assign(
static_cast<size_t>(numChannels_), {});
401 for (
int ch = 0; ch < numChannels_; ++ch)
402 queue_[
static_cast<size_t>(ch)].assign(
static_cast<size_t>(queueSize_), T(0));
403 feed_.assign(
static_cast<size_t>(capacity), T(0));
405 publishEngineParams();
406 prepared_.store(
true, std::memory_order_relaxed);
414 if (!prepared_.load(std::memory_order_relaxed))
return;
415 studioActive_ = quality_.load(std::memory_order_relaxed) ==
Quality::Studio;
420 fftSize_ = fftStudio_;
423 latency_ = fftStudio_ + studio_.lookahead();
424 accumMask_ = studio_.olaMask();
428 fftSize_ = fftStandard_;
434 latency_ = fftStandard_;
435 accumMask_ = engine_.olaMask();
437 synthHop_ = fftSize_ / 4;
438 for (
auto& q : queue_) std::fill(q.begin(), q.end(), T(0));
448 readPos_ = eWriteHead() -
static_cast<int64_t
>(latency_ - fftSize_ + synthHop_);
464 if (!std::isfinite(ratio))
return;
466 std::memory_order_relaxed);
467 publishEngineParams();
480 if (!std::isfinite(pct))
return;
481 const T denom = T(1) + pct / T(100);
491 transientPreserve_.store(on, std::memory_order_relaxed);
492 publishEngineParams();
505 phaseLock_.store(on, std::memory_order_relaxed);
506 publishEngineParams();
512 return timeRatio_.load(std::memory_order_relaxed);
518 return transientPreserve_.load(std::memory_order_relaxed);
524 return phaseLock_.load(std::memory_order_relaxed);
534 return prepared_.load(std::memory_order_relaxed) ? latency_ : 0;
552 if (!prepared_.load(std::memory_order_relaxed) || path_ == Path::Adaptor)
554 return queueSize_ - queued_;
566 if (!prepared_.load(std::memory_order_relaxed) || path_ != Path::Pull)
568 return static_cast<int>(std::max<int64_t>(0, eWriteHead() - readPos_));
587 if (!prepared_.load(std::memory_order_relaxed) || !claimPullPath())
return 0;
590 const int nCh = std::min(in.getNumChannels(), numChannels_);
591 const int count = std::min(std::max(0, in.getNumSamples()), queueSize_ - queued_);
592 int wp = queueWrite_;
593 for (
int k = 0; k < count; ++k)
595 for (
int ch = 0; ch < nCh; ++ch)
596 queue_[
static_cast<size_t>(ch)][
static_cast<size_t>(wp)]
597 = in.getChannel(ch)[k];
598 for (
int ch = nCh; ch < numChannels_; ++ch)
599 queue_[
static_cast<size_t>(ch)][
static_cast<size_t>(wp)] = T(0);
600 wp = (wp + 1) & queueMask_;
624 if (!prepared_.load(std::memory_order_relaxed) || !claimPullPath())
return 0;
627 const int nCh = std::min(out.getNumChannels(), numChannels_);
628 const int count = std::min(std::max(0, out.getNumSamples()),
629 static_cast<int>(std::max<int64_t>(0, eWriteHead() - readPos_)));
632 readSynthesis(out, nCh, 0, count);
638 return std::max(0, count);
654 [[nodiscard]] std::vector<uint8_t>
getState()
const
657 w.
write(
"ratio",
static_cast<float>(timeRatio_.load(std::memory_order_relaxed)));
658 w.
write(
"transient", transientPreserve_.load(std::memory_order_relaxed));
659 w.
write(
"phaselock", phaseLock_.load(std::memory_order_relaxed));
660 w.
write(
"quality",
static_cast<int32_t
>(quality_.load(std::memory_order_relaxed)));
696 if (!prepared_.load(std::memory_order_relaxed))
return;
697 if (path_ == Path::Pull)
return;
698 path_ = Path::Adaptor;
701 const int nCh = std::min(buffer.getNumChannels(), numChannels_);
702 const int nS = buffer.getNumSamples();
716 takeInput(buffer, nCh, taken, i);
718 const int64_t avail = eWriteHead() - readPos_;
721 const int take =
static_cast<int>(
722 std::min<int64_t>(avail,
static_cast<int64_t
>(nS - i)));
723 takeInput(buffer, nCh, taken, i + take);
724 readSynthesis(buffer, nCh, i, take);
735 const int need = eSamplesToNextHop();
738 const int silence = std::min(need - queued_, nS - i);
742 takeInput(buffer, nCh, taken, i + silence);
743 for (
int ch = 0; ch < nCh; ++ch)
744 std::fill(buffer.getChannel(ch) + i, buffer.getChannel(ch) + i + silence, T(0));
749 for (
int ch = 0; ch < nCh; ++ch)
751 dequeueInto(ch, need);
752 ePushInput(ch, feed_.data(), need);
754 eCommitInput(need, nCh);
755 queueRead_ = (queueRead_ + need) & queueMask_;
780 if (!prepared_.load(std::memory_order_relaxed) || inLen <= 0 || inCh <= 0)
782 out.
resize(std::max(0, inCh), std::max(0, inLen));
783 for (
int ch = 0; ch < inCh; ++ch)
788 const int nCh = std::min(inCh, numChannels_);
792 const int outLen =
static_cast<int>(offlineStop_ - offlineSkip_);
793 out.
resize(inCh, std::max(0, outLen));
796 std::vector<T*> ptrs(
static_cast<size_t>(nCh));
797 for (
int ch = 0; ch < nCh; ++ch) ptrs[static_cast<size_t>(ch)] = out.
getChannel(ch);
800 offlineOpen_ =
false;
805 for (
int ch = nCh; ch < inCh; ++ch)
809 const int n = std::min(outLen, inLen);
810 std::copy(src, src + n, dst);
811 std::fill(dst + n, dst + outLen, T(0));
833 offlineOpen_ =
false;
834 if (!prepared_.load(std::memory_order_relaxed))
return;
836 path_ = Path::Adaptor;
837 offlineChannels_ = std::clamp(numChannels, 1, numChannels_);
838 offlineRatio_ = studioActive_ ? studio_.activeRatio() : engine_.activeRatio();
842 offlineSkip_ = studioActive_
843 ? std::llround(studio_.streamPositionOf(0.0)) - readPos_
844 : std::lround(0.5 *
static_cast<double>(fftSize_) * (1.0 + offlineRatio_));
845 offlineStreamPos_ = 0;
846 offlineReceived_ = 0;
847 offlineStop_ = offlineSkip_;
848 offlineFinished_ =
false;
849 offlineStaged_.assign(
static_cast<size_t>(offlineChannels_), std::vector<T> {});
850 offlineStagedHead_ = 0;
851 offlineOut_.assign(
static_cast<size_t>(offlineChannels_), std::vector<T> {});
859 if (!offlineOpen_ || offlineFinished_)
return;
862 for (
int ch = 0; ch < offlineChannels_; ++ch)
864 auto& q = offlineStaged_[
static_cast<size_t>(ch)];
868 q.insert(q.end(),
static_cast<size_t>(n), T(0));
870 offlineReceived_ += n;
878 if (!offlineOpen_ || offlineFinished_)
return;
879 offlineFinished_ =
true;
880 offlineStop_ = offlineSkip_ +
static_cast<int64_t
>(
881 std::lround(
static_cast<double>(offlineReceived_) * offlineRatio_));
888 if (!offlineOpen_ || offlineOut_.empty())
return 0;
889 return static_cast<int>(offlineOut_[0].size() - offlineOutHead_);
900 if (n <= 0)
return 0;
901 for (
int ch = 0; ch < offlineChannels_ && ch < out.
getNumChannels(); ++ch)
903 const T* src = offlineOut_[
static_cast<size_t>(ch)].data() + offlineOutHead_;
906 offlineOutHead_ +=
static_cast<size_t>(n);
907 if (offlineOutHead_ > 65536 && offlineOutHead_ * 2 > offlineOut_[0].size())
909 for (
auto& q : offlineOut_)
910 q.erase(q.begin(), q.begin() +
static_cast<std::ptrdiff_t
>(offlineOutHead_));
929 const int nCh = offlineChannels_;
932 if (offlineFinished_ && offlineStreamPos_ >= offlineStop_)
break;
933 const int64_t avail = eWriteHead() - readPos_;
936 const int need = eSamplesToNextHop();
937 const int64_t staged =
static_cast<int64_t
>(offlineStaged_[0].size() - offlineStagedHead_);
938 if (!offlineFinished_ && staged < need)
break;
940 for (
int ch = 0; ch < nCh; ++ch)
942 const auto& q = offlineStaged_[
static_cast<size_t>(ch)];
943 for (
int k = 0; k < need; ++k)
945 const size_t idx = offlineStagedHead_ +
static_cast<size_t>(k);
946 feed_[
static_cast<size_t>(k)] = (idx < q.size()) ? q[idx] : T(0);
948 ePushInput(ch, feed_.data(), need);
950 eCommitInput(need, nCh);
951 offlineStagedHead_ = std::min(offlineStagedHead_ +
static_cast<size_t>(need),
952 offlineStaged_[0].size());
953 if (offlineStagedHead_ > 65536)
955 for (
auto& q : offlineStaged_)
956 q.erase(q.begin(), q.begin() + static_cast<std::ptrdiff_t>(offlineStagedHead_));
957 offlineStagedHead_ = 0;
962 const int64_t limit = offlineFinished_
964 : offlineSkip_ +
static_cast<int64_t
>(
965 std::lround(
static_cast<double>(offlineReceived_) * offlineRatio_));
966 const int64_t take = std::min(avail, limit - offlineStreamPos_);
967 if (take <= 0)
break;
968 const int64_t from = std::max(offlineStreamPos_, offlineSkip_);
969 const int64_t count = offlineStreamPos_ + take - from;
972 for (
int ch = 0; ch < nCh; ++ch)
974 const T* acc = eOla(ch);
975 auto& q = offlineOut_[
static_cast<size_t>(ch)];
976 const int64_t rp = readPos_ + (from - offlineStreamPos_);
977 for (int64_t k = 0; k < count; ++k)
978 q.push_back(acc[
static_cast<size_t>((rp + k) & accumMask_)]);
982 offlineStreamPos_ += take;
988 void publishEngineParams() noexcept
990 typename detail::PhaseVocoderEngine<T>::Params p;
994 p.targetSemitones = 12.0 * std::log2(
static_cast<double>(
995 timeRatio_.load(std::memory_order_relaxed)));
996 p.transientPreserve = transientPreserve_.load(std::memory_order_relaxed);
997 p.formantPreserve =
false;
998 p.phaseLock = phaseLock_.load(std::memory_order_relaxed);
999 p.percussiveSplit =
false;
1000 engine_.publishParams(p);
1002 typename detail::StudioVocoder<T>::Params q;
1003 q.targetSemitones = p.targetSemitones;
1004 q.transientPreserve = p.transientPreserve;
1005 q.formantPreserve =
false;
1006 studio_.publishParams(q);
1030 void openIntake() noexcept
1032 intakeSpan_ = std::max(1, synthHop_);
1033 const double ratio = studioActive_ ? studio_.activeRatio() : engine_.activeRatio();
1038 intakeWant_ = ratio <= 1.0
1039 ? queueSize_ - queued_
1040 : std::clamp(eSamplesToNextHop() - queued_, 0, queueSize_ - queued_);
1046 [[nodiscard]]
bool acceptOffered() noexcept
1048 if (intakeTaken_ >= intakeWant_)
return false;
1049 if (intakeWant_ < intakeSpan_)
1051 intakeErr_ += intakeWant_;
1052 if (intakeErr_ < intakeSpan_)
return false;
1053 intakeErr_ -= intakeSpan_;
1069 void takeInput(AudioBufferView<T> buffer,
int nCh,
int& taken,
int upTo)
noexcept
1071 if (upTo <= taken)
return;
1072 int wp = queueWrite_;
1074 for (
int k = taken; k < upTo; ++k)
1076 if (!acceptOffered()) { ++discarded_;
continue; }
1077 for (
int ch = 0; ch < nCh; ++ch)
1078 queue_[
static_cast<size_t>(ch)][
static_cast<size_t>(wp)]
1079 = buffer.getChannel(ch)[k];
1080 wp = (wp + 1) & queueMask_;
1090 [[nodiscard]]
bool claimPullPath() noexcept
1092 if (path_ == Path::Pull)
return true;
1093 if (path_ != Path::None)
return false;
1100 readPos_ = studioActive_
1101 ?
static_cast<int64_t
>(std::llround(studio_.streamPositionOf(0.0)))
1102 : eWriteHead() + static_cast<int64_t>(fftSize_ - eSamplesToNextHop());
1108 void runReadyFrames() noexcept
1112 const int need = eSamplesToNextHop();
1113 if (queued_ < need)
return;
1116 if (eWriteHead() +
static_cast<int64_t
>(fftSize_) - readPos_
1117 >
static_cast<int64_t
>(accumMask_) + 1)
1120 for (
int ch = 0; ch < numChannels_; ++ch)
1122 dequeueInto(ch, need);
1123 ePushInput(ch, feed_.data(), need);
1125 eCommitInput(need, numChannels_);
1126 queueRead_ = (queueRead_ + need) & queueMask_;
1134 void dequeueInto(
int ch,
int count)
noexcept
1136 const auto& q = queue_[
static_cast<size_t>(ch)];
1137 int rp = queueRead_;
1138 for (
int k = 0; k < count; ++k)
1140 feed_[
static_cast<size_t>(k)] = q[
static_cast<size_t>(rp)];
1141 rp = (rp + 1) & queueMask_;
1146 void readSynthesis(AudioBufferView<T> buffer,
int nCh,
int offset,
int count)
noexcept
1148 for (
int ch = 0; ch < nCh; ++ch)
1150 const T* acc = eOla(ch);
1151 T* dst = buffer.getChannel(ch) + offset;
1152 const int64_t rp = readPos_;
1153 for (
int k = 0; k < count; ++k)
1154 dst[k] = acc[
static_cast<size_t>((rp + k) & accumMask_)];
1160 [[nodiscard]] int64_t eWriteHead() const noexcept
1162 return studioActive_ ? studio_.writeHead() : engine_.writeHead();
1164 [[nodiscard]]
int eSamplesToNextHop() const noexcept
1166 return studioActive_ ? studio_.samplesToNextHop() : engine_.samplesToNextHop();
1168 void ePushInput(
int ch,
const T* src,
int count)
noexcept
1170 if (studioActive_) studio_.pushInput(ch, src, count);
1171 else engine_.pushInput(ch, src, count);
1173 void eCommitInput(
int count,
int nCh)
noexcept
1175 if (studioActive_) studio_.commitInput(count, nCh);
1176 else engine_.commitInput(count, nCh);
1178 [[nodiscard]]
const T* eOla(
int ch)
const noexcept
1180 return studioActive_ ? studio_.olaData(ch) : engine_.olaData(ch);
1184 [[nodiscard]]
static int studioDefaultFrame(
double sampleRate)
noexcept
1187 while (n < (1 << 16) &&
static_cast<double>(n) * 1.5 < 0.085 * sampleRate) n <<= 1;
1192 int numChannels_ = 0;
1193 std::atomic<bool> prepared_ {
false };
1195 int fftSize_ = 2048;
1196 int synthHop_ = 512;
1197 int latency_ = 2048;
1198 int64_t accumMask_ = 8191;
1200 detail::PhaseVocoderEngine<T> engine_;
1201 detail::StudioVocoder<T> studio_;
1202 bool studioActive_ =
true;
1203 int fftStandard_ = 2048;
1204 int fftStudio_ = 4096;
1206 std::vector<std::vector<T>> queue_;
1207 std::vector<T> feed_;
1210 int queueWrite_ = 0;
1215 enum class Path { None, Adaptor, Pull };
1216 Path path_ = Path::None;
1218 int64_t discarded_ = 0;
1219 int intakeSpan_ = 0;
1220 int intakeWant_ = 0;
1221 int intakeTaken_ = 0;
1224 int64_t readPos_ = 0;
1227 std::atomic<T> timeRatio_ { T(1) };
1228 std::atomic<bool> transientPreserve_ {
true };
1229 std::atomic<bool> phaseLock_ {
true };
1232 bool offlineOpen_ =
false;
1233 bool offlineFinished_ =
false;
1234 int offlineChannels_ = 0;
1235 double offlineRatio_ = 1.0;
1236 int64_t offlineSkip_ = 0;
1237 int64_t offlineStop_ = 0;
1238 int64_t offlineStreamPos_ = 0;
1239 int64_t offlineReceived_ = 0;
1240 std::vector<std::vector<T>> offlineStaged_;
1241 size_t offlineStagedHead_ = 0;
1242 std::vector<std::vector<T>> offlineOut_;
1243 size_t offlineOutHead_ = 0;