22
33#include < algorithm>
44#include < iterator>
5+ #include < ranges>
56#include < utility>
67
78#include " Constants.h"
1112namespace rnexecutorch ::models::speech_to_text::whisper::stream {
1213
1314OnlineASR::OnlineASR (const ASR *asr) : asr_(asr) {
14- // Reserve an expected amount of memory for audio buffer.
1515 audioBuffer_.reserve ((constants::kChunkSize + 1 ) * constants::kSamplingRate );
1616}
1717
1818bool OnlineASR::isReady () const {
19- std::scoped_lock<std::mutex> lock (streamingMutex);
19+ std::scoped_lock lock (streamingMutex);
2020
2121 return audioBuffer_.size () >= constants::kMinChunkSamples ;
2222}
2323
2424void OnlineASR::insertAudioChunk (std::span<const float > audio) {
25- std::scoped_lock<std::mutex> lock (streamingMutex);
25+ std::scoped_lock lock (streamingMutex);
2626
2727 audioBuffer_.insert (audioBuffer_.end (), audio.begin (), audio.end ());
2828
@@ -41,12 +41,15 @@ void OnlineASR::insertAudioChunk(std::span<const float> audio) {
4141}
4242
4343ProcessResult OnlineASR::process (const DecodingOptions &options) {
44+ constexpr float kStreamSafeBufferMaxSamples =
45+ params::kStreamSafeBufferDuration * constants::kSamplingRate ;
46+
4447 std::vector<float > audioCopy;
4548
4649 // Copy the audio buffer to avoid keeping the lock during the entire
4750 // transcription process.
4851 {
49- std::scoped_lock<std::mutex> lock (streamingMutex);
52+ std::scoped_lock lock (streamingMutex);
5053 audioCopy = audioBuffer_;
5154 }
5255
@@ -60,25 +63,23 @@ ProcessResult OnlineASR::process(const DecodingOptions &options) {
6063 std::vector<Segment> transcriptions = asr_->transcribe (input, options);
6164
6265 // Flatten segments into a single word sequence.
63- // This is basically our 'nonCommitted' part for now.
66+ // This is our 'nonCommitted' part for now.
6467 std::vector<Word> words;
6568 for (auto &segment : transcriptions) {
66- std::move (segment.words .begin (), segment.words .end (),
67- std::back_inserter (words));
69+ std::ranges::move (segment.words , std::back_inserter (words));
6870 }
6971
7072 // Aquire lock for the rest of the method (extensive usage of audioBuffer_).
71- std::scoped_lock<std::mutex> lock (streamingMutex);
73+ std::scoped_lock lock (streamingMutex);
7274
7375 // Step 1: examine all previously saved EOS points.
7476 // The idea is to remove entries which have changed or no longer exist
7577 // due to model correcting it's output.
76- for (size_t i = 0 ; i < memory_.eos .size (); i++) {
77- const auto &eos = memory_.eos [i];
78- if (eos.position >= words.size () || !utils::isEos (words[eos.position ]) ||
79- (eos.position > 0 &&
80- eos.preceeding != words[eos.position - 1 ].content )) {
81- memory_.eos .erase (memory_.eos .begin () + i, memory_.eos .end ());
78+ for (auto it = memory_.eos .begin (); it != memory_.eos .end (); it++) {
79+ if (it->position >= words.size () || !utils::isEos (words[it->position ]) ||
80+ (it->position > 0 &&
81+ it->preceeding != words[it->position - 1 ].content )) {
82+ memory_.eos .erase (it, memory_.eos .end ());
8283 break ;
8384 }
8485 }
@@ -92,7 +93,6 @@ ProcessResult OnlineASR::process(const DecodingOptions &options) {
9293 // Because of step 1, we know that if the last EOS exist in eos_,
9394 // then it must be the last entry.
9495 if (memory_.eos .empty () || memory_.eos .back ().position != lastEosIndex) {
95- // Register last EOS entry
9696 std::string preceeding =
9797 lastEosIndex > 0 ? words[lastEosIndex - 1 ].content : " " ;
9898 memory_.eos .emplace_back (lastEosIndex, preceeding, lastEosIt->end );
@@ -115,8 +115,8 @@ ProcessResult OnlineASR::process(const DecodingOptions &options) {
115115 // in a 'good' spot - where it will remove a significant audio chunk, yet
116116 // won't affect most recent, unfinished speech samples.
117117 size_t bufferSize = audioBuffer_.size ();
118- if (bufferSize > static_cast <size_t >(params:: kStreamSafeBufferDuration *
119- constants:: kSamplingRate )) {
118+ if (std::cmp_greater <size_t , size_t >(bufferSize,
119+ kStreamSafeBufferMaxSamples )) {
120120 auto newCommitted = commitAndClean (words);
121121
122122 committed.insert (committed.end (),
@@ -139,7 +139,7 @@ std::vector<Word> OnlineASR::finish(const DecodingOptions &options) {
139139
140140 // Last-tick committed delta + whatever never made it past the commit
141141 // threshold.
142- std::vector<Word> residual = std::move (result.committed );
142+ std::vector<Word> residual{ std::move (result.committed )} ;
143143 residual.insert (residual.end (),
144144 std::make_move_iterator (result.nonCommitted .begin ()),
145145 std::make_move_iterator (result.nonCommitted .end ()));
@@ -150,7 +150,7 @@ std::vector<Word> OnlineASR::finish(const DecodingOptions &options) {
150150}
151151
152152void OnlineASR::reset () {
153- std::scoped_lock<std::mutex> lock (streamingMutex);
153+ std::scoped_lock lock (streamingMutex);
154154
155155 audioBuffer_.clear ();
156156
@@ -161,8 +161,16 @@ void OnlineASR::reset() {
161161}
162162
163163std::vector<Word> OnlineASR::commitAndClean (std::vector<Word> &transcript) {
164+ constexpr float kMidpointAnchorTime = params::kStreamMaxDuration / 2 .0F ;
165+ constexpr size_t kMidpointAnchorSamples =
166+ static_cast <size_t >(kMidpointAnchorTime * constants::kSamplingRate );
167+ constexpr size_t kSafetyMarginSamples = static_cast <size_t >(
168+ params::kStreamSafetyThreshold * constants::kSamplingRate );
169+ constexpr float kMaxSafeEosTime =
170+ params::kStreamSafeBufferDuration - params::kStreamSafetyThreshold ;
171+ constexpr float kMinDurationToCalculateDensity = 0 .1F ;
172+
164173 const size_t bufferSize = audioBuffer_.size ();
165- const float midBufferThreshold = params::kStreamMaxDuration / 2 .0F ;
166174
167175 std::vector<Word> committed;
168176
@@ -184,35 +192,30 @@ std::vector<Word> OnlineASR::commitAndClean(std::vector<Word> &transcript) {
184192 const float eosTimestamp = memory_.eos [0 ].tmstpend ;
185193
186194 const float upperHalfDuration =
187- std::max (0 .0F , eosTimestamp - midBufferThreshold );
195+ std::max (0 .0F , eosTimestamp - kMidpointAnchorTime );
188196 const float wordsPerSecond =
189- upperHalfDuration > 0 . 1F
197+ upperHalfDuration > kMinDurationToCalculateDensity
190198 ? static_cast <float >(transcript.size ()) / upperHalfDuration
191199 : 0 .0F ;
192200
193201 // The EOS sits early enough that cutting up to the safety margin won't
194202 // touch the ongoing (post-EOS) speech.
195- const bool eosSafe = eosTimestamp < params::kStreamSafeBufferDuration -
196- params::kStreamSafetyThreshold ;
203+ const bool eosSafe = eosTimestamp < kMaxSafeEosTime ;
197204
198205 if (!eosSafe && wordsPerSecond < params::kWordsPerSecondLow ) {
199206 // EOS lies past the midpoint, but a low word density implies the spoken
200207 // audio is concentrated in the upper half. Drop the lower half and
201208 // shift the EOS accordingly.
202209 audioBuffer_.erase (audioBuffer_.begin (),
203- audioBuffer_.begin () +
204- static_cast <size_t >(midBufferThreshold *
205- constants::kSamplingRate ));
206- memory_.eos [0 ].tmstpend -= midBufferThreshold;
210+ audioBuffer_.begin () + kMidpointAnchorSamples );
211+ memory_.eos [0 ].tmstpend -= kMidpointAnchorTime ;
207212 } else {
208213 // Cut everything up to and including the sentence — either by the
209214 // safety margin (when EOS is early) or (more aggresively) right at the
210215 // EOS boundary — and commit its words.
211216 const size_t cut =
212217 eosSafe
213- ? bufferSize -
214- static_cast <size_t >(params::kStreamSafetyThreshold *
215- constants::kSamplingRate )
218+ ? bufferSize - kSafetyMarginSamples
216219 : static_cast <size_t >(eosTimestamp * constants::kSamplingRate );
217220
218221 audioBuffer_.erase (audioBuffer_.begin (), audioBuffer_.begin () + cut);
@@ -237,8 +240,6 @@ std::vector<Word> OnlineASR::commitAndClean(std::vector<Word> &transcript) {
237240
238241 audioBuffer_.erase (audioBuffer_.begin (), audioBuffer_.begin () + cut);
239242
240- // Move all words up to the last committed position (inclusive) to the
241- // committed buffer.
242243 committed.insert (
243244 committed.end (), std::make_move_iterator (transcript.begin ()),
244245 std::make_move_iterator (transcript.begin () + lastCommittedPos + 1 ));
0 commit comments