Skip to content

Commit d2ef88d

Browse files
author
ruiren_microsoft
committed
update app.js
1 parent 2035462 commit d2ef88d

1 file changed

Lines changed: 48 additions & 15 deletions

File tree

  • samples/js/live-audio-transcription-example

samples/js/live-audio-transcription-example/app.js

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ console.log('Loading model...');
3939
await model.load();
4040
console.log('✓ Model loaded');
4141

42-
// Create live transcription session
42+
// Create live transcription session (same pattern as C# sample).
4343
const audioClient = model.createAudioClient();
4444
const session = audioClient.createLiveTranscriptionSession();
45+
4546
session.settings.sampleRate = 16000; // Default is 16000; shown here for clarity
4647
session.settings.channels = 1;
4748
session.settings.bitsPerSample = 16;
@@ -56,10 +57,12 @@ const readPromise = (async () => {
5657
try {
5758
for await (const result of session.getTranscriptionStream()) {
5859
const text = result.content?.[0]?.text;
60+
if (!text) continue;
61+
62+
// `is_final` is a transcript-state marker only. It should not stop the app.
5963
if (result.is_final) {
60-
console.log();
61-
console.log(` [FINAL] ${text}`);
62-
} else if (text) {
64+
process.stdout.write(`\n [FINAL] ${text}\n`);
65+
} else {
6366
process.stdout.write(text);
6467
}
6568
}
@@ -88,22 +91,52 @@ try {
8891
? portAudio.SampleFormat16Bit
8992
: portAudio.SampleFormat32Bit,
9093
sampleRate: session.settings.sampleRate,
91-
framesPerBuffer: 1600, // 100ms chunks
92-
maxQueue: 15 // buffer during event-loop blocks from sync FFI calls
94+
// Larger chunk size lowers callback frequency and reduces overflow risk.
95+
framesPerBuffer: 3200,
96+
// Allow deeper native queue during occasional event-loop stalls.
97+
maxQueue: 64
9398
}
9499
});
95100

96-
let appendPending = false;
101+
const appendQueue = [];
102+
let pumping = false;
103+
let warnedQueueDrop = false;
104+
105+
const pumpAudio = async () => {
106+
if (pumping) return;
107+
pumping = true;
108+
try {
109+
while (appendQueue.length > 0) {
110+
const pcm = appendQueue.shift();
111+
await session.append(pcm);
112+
}
113+
} catch (err) {
114+
console.error('append error:', err.message);
115+
} finally {
116+
pumping = false;
117+
// Handle race where new data arrived after loop exit.
118+
if (appendQueue.length > 0) {
119+
void pumpAudio();
120+
}
121+
}
122+
};
123+
97124
audioInput.on('data', (buffer) => {
98-
if (appendPending) return; // drop frame while backpressured
99125
const pcm = new Uint8Array(buffer);
100-
appendPending = true;
101-
session.append(pcm).then(() => {
102-
appendPending = false;
103-
}).catch((err) => {
104-
appendPending = false;
105-
console.error('append error:', err.message);
106-
});
126+
const copy = new Uint8Array(pcm.length);
127+
copy.set(pcm);
128+
129+
// Keep a bounded queue to avoid unbounded memory growth.
130+
if (appendQueue.length >= 100) {
131+
appendQueue.shift();
132+
if (!warnedQueueDrop) {
133+
warnedQueueDrop = true;
134+
console.warn('Audio append queue overflow; dropping oldest chunk to keep stream alive.');
135+
}
136+
}
137+
138+
appendQueue.push(copy);
139+
void pumpAudio();
107140
});
108141

109142
console.log();

0 commit comments

Comments
 (0)