whisper.rn 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,6 +15,7 @@ import android.media.AudioRecord;
15
15
  import android.media.MediaRecorder.AudioSource;
16
16
 
17
17
  import java.util.Random;
18
+ import java.util.ArrayList;
18
19
  import java.lang.StringBuilder;
19
20
  import java.io.File;
20
21
  import java.io.BufferedReader;
@@ -41,19 +42,24 @@ public class WhisperContext {
41
42
  private int id;
42
43
  private ReactApplicationContext reactContext;
43
44
  private long context;
44
-
45
- private DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter;
46
-
47
45
  private int jobId = -1;
46
+ private DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter;
47
+
48
48
  private AudioRecord recorder = null;
49
49
  private int bufferSize;
50
- private short[] buffer16;
51
- private int nSamples = 0;
52
50
  private int nSamplesTranscribing = 0;
51
+ private ArrayList<short[]> shortBufferSlices;
52
+ // Remember number of samples in each slice
53
+ private ArrayList<Integer> sliceNSamples;
54
+ // Current buffer slice index
55
+ private int sliceIndex = 0;
56
+ // Current transcribing slice index
57
+ private int transcribeSliceIndex = 0;
58
+ private boolean isUseSlices = false;
59
+ private boolean isRealtime = false;
53
60
  private boolean isCapturing = false;
54
61
  private boolean isStoppedByAction = false;
55
62
  private boolean isTranscribing = false;
56
- private boolean isRealtime = false;
57
63
  private Thread fullHandler = null;
58
64
 
59
65
  public WhisperContext(int id, ReactApplicationContext reactContext, long context) {
@@ -64,6 +70,19 @@ public class WhisperContext {
64
70
  bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT);
65
71
  }
66
72
 
73
+ private void resetRealtimeTranscribe() {
74
+ shortBufferSlices = null;
75
+ sliceNSamples = null;
76
+ sliceIndex = 0;
77
+ transcribeSliceIndex = 0;
78
+ isUseSlices = false;
79
+ isRealtime = false;
80
+ isCapturing = false;
81
+ isStoppedByAction = false;
82
+ isTranscribing = false;
83
+ fullHandler = null;
84
+ }
85
+
67
86
  public int startRealtimeTranscribe(int jobId, ReadableMap options) {
68
87
  if (isCapturing || isTranscribing) {
69
88
  return -100;
@@ -76,20 +95,25 @@ public class WhisperContext {
76
95
  recorder.release();
77
96
  return state;
78
97
  }
79
-
80
- int realtimeAudioSec = options.hasKey("realtimeAudioSec") ? options.getInt("realtimeAudioSec") : 0;
81
- final int maxAudioSec = realtimeAudioSec > 0 ? realtimeAudioSec : DEFAULT_MAX_AUDIO_SEC;
82
98
 
83
- buffer16 = new short[maxAudioSec * SAMPLE_RATE * Short.BYTES];
99
+ resetRealtimeTranscribe();
84
100
 
85
101
  this.jobId = jobId;
86
- isCapturing = true;
87
- isStoppedByAction = false;
88
- isRealtime = true;
89
- nSamples = 0;
90
- nSamplesTranscribing = 0;
91
- fullHandler = null;
102
+
103
+ int realtimeAudioSec = options.hasKey("realtimeAudioSec") ? options.getInt("realtimeAudioSec") : 0;
104
+ final int audioSec = realtimeAudioSec > 0 ? realtimeAudioSec : DEFAULT_MAX_AUDIO_SEC;
105
+
106
+ int realtimeAudioSliceSec = options.hasKey("realtimeAudioSliceSec") ? options.getInt("realtimeAudioSliceSec") : 0;
107
+ final int audioSliceSec = realtimeAudioSliceSec > 0 && realtimeAudioSliceSec < audioSec ? realtimeAudioSliceSec : audioSec;
108
+
109
+ isUseSlices = audioSliceSec < audioSec;
110
+
111
+ shortBufferSlices = new ArrayList<short[]>();
112
+ shortBufferSlices.add(new short[audioSliceSec * SAMPLE_RATE]);
113
+ sliceNSamples = new ArrayList<Integer>();
114
+ sliceNSamples.add(0);
92
115
 
116
+ isCapturing = true;
93
117
  recorder.startRecording();
94
118
 
95
119
  new Thread(new Runnable() {
@@ -102,26 +126,56 @@ public class WhisperContext {
102
126
  int n = recorder.read(buffer, 0, bufferSize);
103
127
  if (n == 0) continue;
104
128
 
105
- if (nSamples + n > maxAudioSec * SAMPLE_RATE) {
129
+ int totalNSamples = 0;
130
+ for (int i = 0; i < sliceNSamples.size(); i++) {
131
+ totalNSamples += sliceNSamples.get(i);
132
+ }
133
+
134
+ int nSamples = sliceNSamples.get(sliceIndex);
135
+ if (totalNSamples + n > audioSec * SAMPLE_RATE) {
106
136
  // Full, stop capturing
107
137
  isCapturing = false;
108
- if (!isTranscribing && nSamples == nSamplesTranscribing) {
138
+ if (
139
+ !isTranscribing &&
140
+ nSamples == nSamplesTranscribing &&
141
+ sliceIndex == transcribeSliceIndex
142
+ ) {
109
143
  emitTranscribeEvent("@RNWhisper_onRealtimeTranscribeEnd", Arguments.createMap());
110
- } else {
111
- // wait previous handler to finish
112
- fullHandler.join();
144
+ } else if (!isTranscribing) {
145
+ isTranscribing = true;
113
146
  fullTranscribeSamples(options, true);
114
147
  }
115
148
  break;
116
149
  }
117
150
 
118
151
  // Append to buffer
119
- nSamples += n;
120
- for (int i = 0; i < n; i++) {
121
- buffer16[nSamples + i] = buffer[i];
152
+ short[] shortBuffer = shortBufferSlices.get(sliceIndex);
153
+ if (nSamples + n > audioSliceSec * SAMPLE_RATE) {
154
+ Log.d(NAME, "next slice");
155
+
156
+ sliceIndex++;
157
+ nSamples = 0;
158
+ shortBuffer = new short[audioSliceSec * SAMPLE_RATE];
159
+ shortBufferSlices.add(shortBuffer);
160
+ sliceNSamples.add(0);
122
161
  }
123
162
 
124
- fullTranscribeSamples(options, false);
163
+ for (int i = 0; i < n; i++) {
164
+ shortBuffer[nSamples + i] = buffer[i];
165
+ }
166
+ nSamples += n;
167
+ sliceNSamples.set(sliceIndex, nSamples);
168
+
169
+ if (!isTranscribing && nSamples > SAMPLE_RATE / 2) {
170
+ isTranscribing = true;
171
+ fullHandler = new Thread(new Runnable() {
172
+ @Override
173
+ public void run() {
174
+ fullTranscribeSamples(options, false);
175
+ }
176
+ });
177
+ fullHandler.start();
178
+ }
125
179
  } catch (Exception e) {
126
180
  Log.e(NAME, "Error transcribing realtime: " + e.getMessage());
127
181
  }
@@ -132,6 +186,8 @@ public class WhisperContext {
132
186
  if (fullHandler != null) {
133
187
  fullHandler.join(); // Wait for full transcribe to finish
134
188
  }
189
+ // Cleanup
190
+ resetRealtimeTranscribe();
135
191
  recorder.stop();
136
192
  } catch (Exception e) {
137
193
  e.printStackTrace();
@@ -145,55 +201,73 @@ public class WhisperContext {
145
201
  }
146
202
 
147
203
  private void fullTranscribeSamples(ReadableMap options, boolean skipCapturingCheck) {
148
- if (!isTranscribing && nSamples > SAMPLE_RATE / 2) {
149
- isTranscribing = true;
150
- fullHandler = new Thread(new Runnable() {
151
- @Override
152
- public void run() {
153
- if (!isCapturing && !skipCapturingCheck) return;
154
-
155
- nSamplesTranscribing = nSamples;
156
-
157
- // convert I16 to F32
158
- float[] nSamplesBuffer32 = new float[nSamplesTranscribing];
159
- for (int i = 0; i < nSamplesTranscribing; i++) {
160
- nSamplesBuffer32[i] = buffer16[i] / 32768.0f;
161
- }
204
+ int nSamplesOfIndex = sliceNSamples.get(transcribeSliceIndex);
162
205
 
163
- Log.d(NAME, "Start transcribing realtime: " + nSamplesTranscribing);
206
+ if (!isCapturing && !skipCapturingCheck) return;
164
207
 
165
- int timeStart = (int) System.currentTimeMillis();
166
- int code = full(jobId, options, nSamplesBuffer32, nSamplesTranscribing);
167
- int timeEnd = (int) System.currentTimeMillis();
168
- int timeRecording = (int) (nSamplesTranscribing / SAMPLE_RATE * 1000);
208
+ short[] shortBuffer = shortBufferSlices.get(transcribeSliceIndex);
209
+ int nSamples = sliceNSamples.get(transcribeSliceIndex);
169
210
 
170
- WritableMap payload = Arguments.createMap();
171
- payload.putInt("code", code);
172
- payload.putInt("processTime", timeEnd - timeStart);
173
- payload.putInt("recordingTime", timeRecording);
211
+ nSamplesTranscribing = nSamplesOfIndex;
174
212
 
175
- if (code == 0) {
176
- payload.putMap("data", getTextSegments());
177
- } else {
178
- payload.putString("error", "Transcribe failed with code " + code);
179
- }
213
+ // convert I16 to F32
214
+ float[] nSamplesBuffer32 = new float[nSamplesTranscribing];
215
+ for (int i = 0; i < nSamplesTranscribing; i++) {
216
+ nSamplesBuffer32[i] = shortBuffer[i] / 32768.0f;
217
+ }
180
218
 
181
- if (isStoppedByAction || !isCapturing && nSamplesTranscribing == nSamples) {
182
- payload.putBoolean("isCapturing", false);
183
- payload.putBoolean("isStoppedByAction", isStoppedByAction);
184
- emitTranscribeEvent("@RNWhisper_onRealtimeTranscribeEnd", payload);
185
- } else if (code == 0) {
186
- payload.putBoolean("isCapturing", true);
187
- emitTranscribeEvent("@RNWhisper_onRealtimeTranscribe", payload);
188
- } else {
189
- payload.putBoolean("isCapturing", true);
190
- emitTranscribeEvent("@RNWhisper_onRealtimeTranscribe", payload);
191
- }
192
- isTranscribing = false;
193
- }
194
- });
195
- fullHandler.start();
219
+ Log.d(NAME, "Start transcribing realtime: " + nSamplesTranscribing);
220
+
221
+ int timeStart = (int) System.currentTimeMillis();
222
+ int code = full(jobId, options, nSamplesBuffer32, nSamplesTranscribing);
223
+ int timeEnd = (int) System.currentTimeMillis();
224
+ int timeRecording = (int) (nSamplesTranscribing / SAMPLE_RATE * 1000);
225
+
226
+ WritableMap payload = Arguments.createMap();
227
+ payload.putInt("code", code);
228
+ payload.putInt("processTime", timeEnd - timeStart);
229
+ payload.putInt("recordingTime", timeRecording);
230
+ payload.putBoolean("isUseSlices", isUseSlices);
231
+ payload.putInt("sliceIndex", transcribeSliceIndex);
232
+
233
+ if (code == 0) {
234
+ payload.putMap("data", getTextSegments());
235
+ } else {
236
+ payload.putString("error", "Transcribe failed with code " + code);
237
+ }
238
+
239
+ nSamplesOfIndex = sliceNSamples.get(transcribeSliceIndex);
240
+ if (
241
+ isStoppedByAction ||
242
+ !isCapturing &&
243
+ nSamplesTranscribing == nSamplesOfIndex &&
244
+ sliceIndex == transcribeSliceIndex
245
+ ) {
246
+ payload.putBoolean("isCapturing", false);
247
+ payload.putBoolean("isStoppedByAction", isStoppedByAction);
248
+ emitTranscribeEvent("@RNWhisper_onRealtimeTranscribeEnd", payload);
249
+ } else if (code == 0) {
250
+ payload.putBoolean("isCapturing", true);
251
+ emitTranscribeEvent("@RNWhisper_onRealtimeTranscribe", payload);
252
+ } else {
253
+ payload.putBoolean("isCapturing", true);
254
+ emitTranscribeEvent("@RNWhisper_onRealtimeTranscribe", payload);
255
+ }
256
+
257
+ if (
258
+ // If no more samples on current slice, move to next slice
259
+ nSamplesTranscribing == sliceNSamples.get(transcribeSliceIndex) &&
260
+ transcribeSliceIndex != sliceIndex
261
+ ) {
262
+ transcribeSliceIndex++;
263
+ nSamplesTranscribing = 0;
196
264
  }
265
+
266
+ if (!isCapturing && nSamplesTranscribing != nSamplesOfIndex) {
267
+ // If no more capturing, continue transcribing until all slices are transcribed
268
+ fullTranscribeSamples(options, true);
269
+ }
270
+ isTranscribing = false;
197
271
  }
198
272
 
199
273
  private void emitTranscribeEvent(final String eventName, final WritableMap payload) {
@@ -221,8 +295,6 @@ public class WhisperContext {
221
295
  return fullTranscribe(
222
296
  jobId,
223
297
  context,
224
- // jboolean realtime,
225
- isRealtime,
226
298
  // float[] audio_data,
227
299
  audioData,
228
300
  // jint audio_data_len,
@@ -273,6 +345,7 @@ public class WhisperContext {
273
345
  builder.append(text);
274
346
 
275
347
  WritableMap segment = Arguments.createMap();
348
+ Log.d(NAME, "getTextSegments: " + text + " " + transcribeSliceIndex);
276
349
  segment.putString("text", text);
277
350
  segment.putInt("t0", getTextSegmentT0(context, i));
278
351
  segment.putInt("t1", getTextSegmentT1(context, i));
@@ -399,7 +472,6 @@ public class WhisperContext {
399
472
  protected static native int fullTranscribe(
400
473
  int job_id,
401
474
  long context,
402
- boolean realtime,
403
475
  float[] audio_data,
404
476
  int audio_data_len,
405
477
  int n_threads,
@@ -39,7 +39,6 @@ Java_com_rnwhisper_WhisperContext_fullTranscribe(
39
39
  jobject thiz,
40
40
  jint job_id,
41
41
  jlong context_ptr,
42
- jboolean realtime,
43
42
  jfloatArray audio_data,
44
43
  jint audio_data_len,
45
44
  jint n_threads,
@@ -84,7 +83,7 @@ Java_com_rnwhisper_WhisperContext_fullTranscribe(
84
83
  params.speed_up = speed_up;
85
84
  params.offset_ms = 0;
86
85
  params.no_context = true;
87
- params.single_segment = realtime;
86
+ params.single_segment = false;
88
87
 
89
88
  if (max_len > -1) {
90
89
  params.max_len = max_len;
@@ -20,10 +20,13 @@ typedef struct {
20
20
  bool isCapturing;
21
21
  bool isStoppedByAction;
22
22
  int maxAudioSec;
23
- int nSamples;
24
23
  int nSamplesTranscribing;
25
- int16_t* audioBufferI16;
26
- float* audioBufferF32;
24
+ NSMutableArray<NSValue *> *shortBufferSlices;
25
+ NSMutableArray<NSNumber *> *sliceNSamples;
26
+ bool isUseSlices;
27
+ int sliceIndex;
28
+ int transcribeSliceIndex;
29
+ int audioSliceSec;
27
30
 
28
31
  AudioQueueRef queue;
29
32
  AudioStreamBasicDescription dataFormat;
@@ -27,13 +27,28 @@
27
27
  self->recordState.dataFormat.mReserved = 0;
28
28
  self->recordState.dataFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
29
29
 
30
- self->recordState.nSamples = 0;
31
-
32
30
  int maxAudioSecOpt = options[@"realtimeAudioSec"] != nil ? [options[@"realtimeAudioSec"] intValue] : 0;
33
31
  int maxAudioSec = maxAudioSecOpt > 0 ? maxAudioSecOpt : DEFAULT_MAX_AUDIO_SEC;
34
32
  self->recordState.maxAudioSec = maxAudioSec;
35
- self->recordState.audioBufferI16 = (int16_t *) malloc(maxAudioSec * WHISPER_SAMPLE_RATE * sizeof(int16_t));
36
- self->recordState.audioBufferF32 = (float *) malloc(maxAudioSec * WHISPER_SAMPLE_RATE * sizeof(float));
33
+
34
+ int realtimeAudioSliceSec = options[@"realtimeAudioSliceSec"] != nil ? [options[@"realtimeAudioSliceSec"] intValue] : 0;
35
+ int audioSliceSec = realtimeAudioSliceSec > 0 && realtimeAudioSliceSec < maxAudioSec ? realtimeAudioSliceSec : maxAudioSec;
36
+
37
+ self->recordState.audioSliceSec = audioSliceSec;
38
+ self->recordState.isUseSlices = audioSliceSec < maxAudioSec;
39
+
40
+ self->recordState.sliceIndex = 0;
41
+ self->recordState.transcribeSliceIndex = 0;
42
+ self->recordState.nSamplesTranscribing = 0;
43
+
44
+ [self freeBufferIfNeeded];
45
+ self->recordState.shortBufferSlices = [NSMutableArray new];
46
+
47
+ int16_t *audioBufferI16 = (int16_t *) malloc(audioSliceSec * WHISPER_SAMPLE_RATE * sizeof(int16_t));
48
+ [self->recordState.shortBufferSlices addObject:[NSValue valueWithPointer:audioBufferI16]];
49
+
50
+ self->recordState.sliceNSamples = [NSMutableArray new];
51
+ [self->recordState.sliceNSamples addObject:[NSNumber numberWithInt:0]];
37
52
 
38
53
  self->recordState.isRealtime = true;
39
54
  self->recordState.isTranscribing = false;
@@ -42,6 +57,16 @@
42
57
  self->recordState.mSelf = self;
43
58
  }
44
59
 
60
+ - (void)freeBufferIfNeeded {
61
+ if (self->recordState.shortBufferSlices != nil) {
62
+ for (int i = 0; i < [self->recordState.shortBufferSlices count]; i++) {
63
+ int16_t *audioBufferI16 = (int16_t *) [self->recordState.shortBufferSlices[i] pointerValue];
64
+ free(audioBufferI16);
65
+ }
66
+ self->recordState.shortBufferSlices = nil;
67
+ }
68
+ }
69
+
45
70
  void AudioInputCallback(void * inUserData,
46
71
  AudioQueueRef inAQ,
47
72
  AudioQueueBufferRef inBuffer,
@@ -59,16 +84,29 @@ void AudioInputCallback(void * inUserData,
59
84
  return;
60
85
  }
61
86
 
87
+ int totalNSamples = 0;
88
+ for (int i = 0; i < [state->sliceNSamples count]; i++) {
89
+ totalNSamples += [[state->sliceNSamples objectAtIndex:i] intValue];
90
+ }
91
+
62
92
  const int n = inBuffer->mAudioDataByteSize / 2;
63
- NSLog(@"[RNWhisper] Captured %d new samples", n);
64
93
 
65
- if (state->nSamples + n > state->maxAudioSec * WHISPER_SAMPLE_RATE) {
94
+ int nSamples = [state->sliceNSamples[state->sliceIndex] intValue];
95
+
96
+ if (totalNSamples + n > state->maxAudioSec * WHISPER_SAMPLE_RATE) {
66
97
  NSLog(@"[RNWhisper] Audio buffer is full, stop capturing");
67
98
  state->isCapturing = false;
68
99
  [state->mSelf stopAudio];
69
- if (!state->isTranscribing && state->nSamples == state->nSamplesTranscribing) {
100
+ if (
101
+ !state->isTranscribing &&
102
+ nSamples == state->nSamplesTranscribing &&
103
+ state->sliceIndex == state->transcribeSliceIndex
104
+ ) {
70
105
  state->transcribeHandler(state->jobId, @"end", @{});
71
- } else if (!state->isTranscribing && state->nSamples != state->nSamplesTranscribing) {
106
+ } else if (
107
+ !state->isTranscribing &&
108
+ nSamples != state->nSamplesTranscribing
109
+ ) {
72
110
  state->isTranscribing = true;
73
111
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
74
112
  [state->mSelf fullTranscribeSamples:state];
@@ -77,10 +115,25 @@ void AudioInputCallback(void * inUserData,
77
115
  return;
78
116
  }
79
117
 
118
+ int audioSliceSec = state->audioSliceSec;
119
+ if (nSamples + n > audioSliceSec * WHISPER_SAMPLE_RATE) {
120
+ // next slice
121
+ state->sliceIndex++;
122
+ nSamples = 0;
123
+ int16_t* audioBufferI16 = (int16_t*) malloc(audioSliceSec * WHISPER_SAMPLE_RATE * sizeof(int16_t));
124
+ [state->shortBufferSlices addObject:[NSValue valueWithPointer:audioBufferI16]];
125
+ [state->sliceNSamples addObject:[NSNumber numberWithInt:0]];
126
+ }
127
+
128
+ // Append to buffer
129
+ NSLog(@"[RNWhisper] Slice %d has %d samples", state->sliceIndex, nSamples);
130
+
131
+ int16_t* audioBufferI16 = (int16_t*) [state->shortBufferSlices[state->sliceIndex] pointerValue];
80
132
  for (int i = 0; i < n; i++) {
81
- state->audioBufferI16[state->nSamples + i] = ((short*)inBuffer->mAudioData)[i];
133
+ audioBufferI16[nSamples + i] = ((short*)inBuffer->mAudioData)[i];
82
134
  }
83
- state->nSamples += n;
135
+ nSamples += n;
136
+ state->sliceNSamples[state->sliceIndex] = [NSNumber numberWithInt:nSamples];
84
137
 
85
138
  AudioQueueEnqueueBuffer(state->queue, inBuffer, 0, NULL);
86
139
 
@@ -93,15 +146,19 @@ void AudioInputCallback(void * inUserData,
93
146
  }
94
147
 
95
148
  - (void)fullTranscribeSamples:(RNWhisperContextRecordState*) state {
96
- state->nSamplesTranscribing = state->nSamples;
149
+ int nSamplesOfIndex = [[state->sliceNSamples objectAtIndex:state->transcribeSliceIndex] intValue];
150
+ state->nSamplesTranscribing = nSamplesOfIndex;
97
151
  NSLog(@"[RNWhisper] Transcribing %d samples", state->nSamplesTranscribing);
98
152
 
153
+ int16_t* audioBufferI16 = (int16_t*) [state->shortBufferSlices[state->transcribeSliceIndex] pointerValue];
154
+ float* audioBufferF32 = (float*) malloc(state->nSamplesTranscribing * sizeof(float));
99
155
  // convert I16 to F32
100
156
  for (int i = 0; i < state->nSamplesTranscribing; i++) {
101
- state->audioBufferF32[i] = (float)state->audioBufferI16[i] / 32768.0f;
157
+ audioBufferF32[i] = (float)audioBufferI16[i] / 32768.0f;
102
158
  }
103
159
  CFTimeInterval timeStart = CACurrentMediaTime();
104
- int code = [state->mSelf fullTranscribe:state->jobId audioData:state->audioBufferF32 audioDataCount:state->nSamplesTranscribing options:state->options];
160
+ int code = [state->mSelf fullTranscribe:state->jobId audioData:audioBufferF32 audioDataCount:state->nSamplesTranscribing options:state->options];
161
+ free(audioBufferF32);
105
162
  CFTimeInterval timeEnd = CACurrentMediaTime();
106
163
  const float timeRecording = (float) state->nSamplesTranscribing / (float) state->dataFormat.mSampleRate;
107
164
 
@@ -109,7 +166,10 @@ void AudioInputCallback(void * inUserData,
109
166
  @"code": [NSNumber numberWithInt:code],
110
167
  @"processTime": [NSNumber numberWithInt:(timeEnd - timeStart) * 1E3],
111
168
  @"recordingTime": [NSNumber numberWithInt:timeRecording * 1E3],
169
+ @"isUseSlices": @(state->isUseSlices),
170
+ @"sliceIndex": @(state->transcribeSliceIndex),
112
171
  };
172
+
113
173
  NSMutableDictionary* result = [base mutableCopy];
114
174
 
115
175
  if (code == 0) {
@@ -118,7 +178,15 @@ void AudioInputCallback(void * inUserData,
118
178
  result[@"error"] = [NSString stringWithFormat:@"Transcribe failed with code %d", code];
119
179
  }
120
180
 
121
- if (state->isStoppedByAction || (!state->isCapturing && state->nSamplesTranscribing == state->nSamples)) {
181
+ nSamplesOfIndex = [[state->sliceNSamples objectAtIndex:state->transcribeSliceIndex] intValue];
182
+ if (
183
+ state->isStoppedByAction ||
184
+ (
185
+ !state->isCapturing &&
186
+ state->nSamplesTranscribing == nSamplesOfIndex &&
187
+ state->sliceIndex == state->transcribeSliceIndex
188
+ )
189
+ ) {
122
190
  NSLog(@"[RNWhisper] Transcribe end");
123
191
  result[@"isStoppedByAction"] = @(state->isStoppedByAction);
124
192
  result[@"isCapturing"] = @(false);
@@ -130,13 +198,25 @@ void AudioInputCallback(void * inUserData,
130
198
  result[@"isCapturing"] = @(true);
131
199
  state->transcribeHandler(state->jobId, @"transcribe", result);
132
200
  }
133
- state->isTranscribing = false;
134
201
 
135
- if (!state->isCapturing && state->nSamplesTranscribing != state->nSamples) {
202
+ if (
203
+ // If no more samples on current slice, move to next slice
204
+ state->nSamplesTranscribing == nSamplesOfIndex &&
205
+ state->transcribeSliceIndex != state->sliceIndex
206
+ ) {
207
+ state->transcribeSliceIndex++;
208
+ state->nSamplesTranscribing = 0;
209
+ }
210
+
211
+ if (
212
+ !state->isCapturing &&
213
+ state->nSamplesTranscribing != nSamplesOfIndex
214
+ ) {
136
215
  state->isTranscribing = true;
137
216
  // Finish transcribing the rest of the samples
138
217
  [self fullTranscribeSamples:state];
139
218
  }
219
+ state->isTranscribing = false;
140
220
  }
141
221
 
142
222
  - (bool)isCapturing {
@@ -154,7 +234,6 @@ void AudioInputCallback(void * inUserData,
154
234
  self->recordState.transcribeHandler = onTranscribe;
155
235
  self->recordState.jobId = jobId;
156
236
  [self prepareRealtime:options];
157
- self->recordState.nSamples = 0;
158
237
 
159
238
  OSStatus status = AudioQueueNewInput(
160
239
  &self->recordState.dataFormat,
@@ -239,7 +318,7 @@ void AudioInputCallback(void * inUserData,
239
318
  params.n_threads = max_threads;
240
319
  params.offset_ms = 0;
241
320
  params.no_context = true;
242
- params.single_segment = self->recordState.isRealtime;
321
+ params.single_segment = false;
243
322
 
244
323
  if (options[@"maxLen"] != nil) {
245
324
  params.max_len = [options[@"maxLen"] intValue];
@@ -321,6 +400,7 @@ void AudioInputCallback(void * inUserData,
321
400
  - (void)invalidate {
322
401
  [self stopCurrentTranscribe];
323
402
  whisper_free(self->ctx);
403
+ [self freeBufferIfNeeded];
324
404
  }
325
405
 
326
406
  @end
@@ -45,6 +45,51 @@ class WhisperContext {
45
45
  const jobId = Math.floor(Math.random() * 10000);
46
46
  await RNWhisper.startRealtimeTranscribe(this.id, jobId, options);
47
47
  let lastTranscribePayload;
48
+ const slices = [];
49
+ let sliceIndex = 0;
50
+ let tOffset = 0;
51
+ const putSlice = payload => {
52
+ if (!payload.isUseSlices) return;
53
+ if (sliceIndex !== payload.sliceIndex) {
54
+ var _slices$sliceIndex, _segments;
55
+ const {
56
+ segments = []
57
+ } = ((_slices$sliceIndex = slices[sliceIndex]) === null || _slices$sliceIndex === void 0 ? void 0 : _slices$sliceIndex.data) || {};
58
+ tOffset = ((_segments = segments[segments.length - 1]) === null || _segments === void 0 ? void 0 : _segments.t1) || 0;
59
+ }
60
+ ({
61
+ sliceIndex
62
+ } = payload);
63
+ slices[sliceIndex] = {
64
+ ...payload,
65
+ data: payload.data ? {
66
+ ...payload.data,
67
+ segments: payload.data.segments.map(segment => ({
68
+ ...segment,
69
+ t0: segment.t0 + tOffset,
70
+ t1: segment.t1 + tOffset
71
+ })) || []
72
+ } : undefined
73
+ };
74
+ };
75
+ const mergeSlicesIfNeeded = payload => {
76
+ if (!payload.isUseSlices) return payload;
77
+ const mergedPayload = {};
78
+ slices.forEach(slice => {
79
+ var _mergedPayload$data, _slice$data, _mergedPayload$data2, _slice$data2;
80
+ mergedPayload.data = {
81
+ result: (((_mergedPayload$data = mergedPayload.data) === null || _mergedPayload$data === void 0 ? void 0 : _mergedPayload$data.result) || '') + (((_slice$data = slice.data) === null || _slice$data === void 0 ? void 0 : _slice$data.result) || ''),
82
+ segments: [...((mergedPayload === null || mergedPayload === void 0 ? void 0 : (_mergedPayload$data2 = mergedPayload.data) === null || _mergedPayload$data2 === void 0 ? void 0 : _mergedPayload$data2.segments) || []), ...(((_slice$data2 = slice.data) === null || _slice$data2 === void 0 ? void 0 : _slice$data2.segments) || [])]
83
+ };
84
+ mergedPayload.processTime = slice.processTime;
85
+ mergedPayload.recordingTime = ((mergedPayload === null || mergedPayload === void 0 ? void 0 : mergedPayload.recordingTime) || 0) + slice.recordingTime;
86
+ });
87
+ return {
88
+ ...payload,
89
+ ...mergedPayload,
90
+ slices
91
+ };
92
+ };
48
93
  return {
49
94
  stop: () => RNWhisper.abortTranscribe(this.id, jobId),
50
95
  subscribe: callback => {
@@ -55,10 +100,11 @@ class WhisperContext {
55
100
  } = evt;
56
101
  if (contextId !== this.id || evt.jobId !== jobId) return;
57
102
  lastTranscribePayload = payload;
103
+ putSlice(payload);
58
104
  callback({
59
105
  contextId,
60
106
  jobId: evt.jobId,
61
- ...payload
107
+ ...mergeSlicesIfNeeded(payload)
62
108
  });
63
109
  });
64
110
  const endListener = EventEmitter.addListener(EVENT_ON_REALTIME_TRANSCRIBE_END, evt => {
@@ -67,11 +113,15 @@ class WhisperContext {
67
113
  payload
68
114
  } = evt;
69
115
  if (contextId !== this.id || evt.jobId !== jobId) return;
116
+ const lastPayload = {
117
+ ...lastTranscribePayload,
118
+ ...payload
119
+ };
120
+ putSlice(lastPayload);
70
121
  callback({
71
122
  contextId,
72
123
  jobId: evt.jobId,
73
- ...lastTranscribePayload,
74
- ...payload,
124
+ ...mergeSlicesIfNeeded(lastPayload),
75
125
  isCapturing: false
76
126
  });
77
127
  transcribeListener.remove();
@@ -1 +1 @@
1
- {"version":3,"names":["_reactNative","require","LINKING_ERROR","Platform","select","ios","default","RNWhisper","NativeModules","Proxy","get","Error","EventEmitter","OS","NativeEventEmitter","DeviceEventEmitter","EVENT_ON_REALTIME_TRANSCRIBE","EVENT_ON_REALTIME_TRANSCRIBE_END","WhisperContext","constructor","id","transcribe","path","options","arguments","length","undefined","jobId","Math","floor","random","stop","abortTranscribe","promise","transcribeFile","transcribeRealtime","startRealtimeTranscribe","lastTranscribePayload","subscribe","callback","transcribeListener","addListener","evt","contextId","payload","endListener","isCapturing","remove","release","releaseContext","initWhisper","filePath","initContext","releaseAllWhisper","releaseAllContexts"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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"}
1
+ {"version":3,"names":["_reactNative","require","LINKING_ERROR","Platform","select","ios","default","RNWhisper","NativeModules","Proxy","get","Error","EventEmitter","OS","NativeEventEmitter","DeviceEventEmitter","EVENT_ON_REALTIME_TRANSCRIBE","EVENT_ON_REALTIME_TRANSCRIBE_END","WhisperContext","constructor","id","transcribe","path","options","arguments","length","undefined","jobId","Math","floor","random","stop","abortTranscribe","promise","transcribeFile","transcribeRealtime","startRealtimeTranscribe","lastTranscribePayload","slices","sliceIndex","tOffset","putSlice","payload","isUseSlices","_slices$sliceIndex","_segments","segments","data","t1","map","segment","t0","mergeSlicesIfNeeded","mergedPayload","forEach","slice","_mergedPayload$data","_slice$data","_mergedPayload$data2","_slice$data2","result","processTime","recordingTime","subscribe","callback","transcribeListener","addListener","evt","contextId","endListener","lastPayload","isCapturing","remove","release","releaseContext","initWhisper","filePath","initContext","releaseAllWhisper","releaseAllContexts"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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"}
@@ -38,6 +38,51 @@ class WhisperContext {
38
38
  const jobId = Math.floor(Math.random() * 10000);
39
39
  await RNWhisper.startRealtimeTranscribe(this.id, jobId, options);
40
40
  let lastTranscribePayload;
41
+ const slices = [];
42
+ let sliceIndex = 0;
43
+ let tOffset = 0;
44
+ const putSlice = payload => {
45
+ if (!payload.isUseSlices) return;
46
+ if (sliceIndex !== payload.sliceIndex) {
47
+ var _slices$sliceIndex, _segments;
48
+ const {
49
+ segments = []
50
+ } = ((_slices$sliceIndex = slices[sliceIndex]) === null || _slices$sliceIndex === void 0 ? void 0 : _slices$sliceIndex.data) || {};
51
+ tOffset = ((_segments = segments[segments.length - 1]) === null || _segments === void 0 ? void 0 : _segments.t1) || 0;
52
+ }
53
+ ({
54
+ sliceIndex
55
+ } = payload);
56
+ slices[sliceIndex] = {
57
+ ...payload,
58
+ data: payload.data ? {
59
+ ...payload.data,
60
+ segments: payload.data.segments.map(segment => ({
61
+ ...segment,
62
+ t0: segment.t0 + tOffset,
63
+ t1: segment.t1 + tOffset
64
+ })) || []
65
+ } : undefined
66
+ };
67
+ };
68
+ const mergeSlicesIfNeeded = payload => {
69
+ if (!payload.isUseSlices) return payload;
70
+ const mergedPayload = {};
71
+ slices.forEach(slice => {
72
+ var _mergedPayload$data, _slice$data, _mergedPayload$data2, _slice$data2;
73
+ mergedPayload.data = {
74
+ result: (((_mergedPayload$data = mergedPayload.data) === null || _mergedPayload$data === void 0 ? void 0 : _mergedPayload$data.result) || '') + (((_slice$data = slice.data) === null || _slice$data === void 0 ? void 0 : _slice$data.result) || ''),
75
+ segments: [...((mergedPayload === null || mergedPayload === void 0 ? void 0 : (_mergedPayload$data2 = mergedPayload.data) === null || _mergedPayload$data2 === void 0 ? void 0 : _mergedPayload$data2.segments) || []), ...(((_slice$data2 = slice.data) === null || _slice$data2 === void 0 ? void 0 : _slice$data2.segments) || [])]
76
+ };
77
+ mergedPayload.processTime = slice.processTime;
78
+ mergedPayload.recordingTime = ((mergedPayload === null || mergedPayload === void 0 ? void 0 : mergedPayload.recordingTime) || 0) + slice.recordingTime;
79
+ });
80
+ return {
81
+ ...payload,
82
+ ...mergedPayload,
83
+ slices
84
+ };
85
+ };
41
86
  return {
42
87
  stop: () => RNWhisper.abortTranscribe(this.id, jobId),
43
88
  subscribe: callback => {
@@ -48,10 +93,11 @@ class WhisperContext {
48
93
  } = evt;
49
94
  if (contextId !== this.id || evt.jobId !== jobId) return;
50
95
  lastTranscribePayload = payload;
96
+ putSlice(payload);
51
97
  callback({
52
98
  contextId,
53
99
  jobId: evt.jobId,
54
- ...payload
100
+ ...mergeSlicesIfNeeded(payload)
55
101
  });
56
102
  });
57
103
  const endListener = EventEmitter.addListener(EVENT_ON_REALTIME_TRANSCRIBE_END, evt => {
@@ -60,11 +106,15 @@ class WhisperContext {
60
106
  payload
61
107
  } = evt;
62
108
  if (contextId !== this.id || evt.jobId !== jobId) return;
109
+ const lastPayload = {
110
+ ...lastTranscribePayload,
111
+ ...payload
112
+ };
113
+ putSlice(lastPayload);
63
114
  callback({
64
115
  contextId,
65
116
  jobId: evt.jobId,
66
- ...lastTranscribePayload,
67
- ...payload,
117
+ ...mergeSlicesIfNeeded(lastPayload),
68
118
  isCapturing: false
69
119
  });
70
120
  transcribeListener.remove();
@@ -1 +1 @@
1
- {"version":3,"names":["NativeEventEmitter","DeviceEventEmitter","NativeModules","Platform","LINKING_ERROR","select","ios","default","RNWhisper","Proxy","get","Error","EventEmitter","OS","EVENT_ON_REALTIME_TRANSCRIBE","EVENT_ON_REALTIME_TRANSCRIBE_END","WhisperContext","constructor","id","transcribe","path","options","arguments","length","undefined","jobId","Math","floor","random","stop","abortTranscribe","promise","transcribeFile","transcribeRealtime","startRealtimeTranscribe","lastTranscribePayload","subscribe","callback","transcribeListener","addListener","evt","contextId","payload","endListener","isCapturing","remove","release","releaseContext","initWhisper","filePath","initContext","releaseAllWhisper","releaseAllContexts"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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"}
1
+ {"version":3,"names":["NativeEventEmitter","DeviceEventEmitter","NativeModules","Platform","LINKING_ERROR","select","ios","default","RNWhisper","Proxy","get","Error","EventEmitter","OS","EVENT_ON_REALTIME_TRANSCRIBE","EVENT_ON_REALTIME_TRANSCRIBE_END","WhisperContext","constructor","id","transcribe","path","options","arguments","length","undefined","jobId","Math","floor","random","stop","abortTranscribe","promise","transcribeFile","transcribeRealtime","startRealtimeTranscribe","lastTranscribePayload","slices","sliceIndex","tOffset","putSlice","payload","isUseSlices","_slices$sliceIndex","_segments","segments","data","t1","map","segment","t0","mergeSlicesIfNeeded","mergedPayload","forEach","slice","_mergedPayload$data","_slice$data","_mergedPayload$data2","_slice$data2","result","processTime","recordingTime","subscribe","callback","transcribeListener","addListener","evt","contextId","endListener","lastPayload","isCapturing","remove","release","releaseContext","initWhisper","filePath","initContext","releaseAllWhisper","releaseAllContexts"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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"}
@@ -36,6 +36,12 @@ export type TranscribeRealtimeOptions = TranscribeOptions & {
36
36
  * the recommended value will be <= 30 seconds. (Default: 30)
37
37
  */
38
38
  realtimeAudioSec?: number;
39
+ /**
40
+ * Optimize audio transcription performance by slicing audio samples when `realtimeAudioSec` > 30.
41
+ * Set `realtimeAudioSliceSec` < 30 so performance improvements can be achieved in the Whisper hard constraint (processes the audio in chunks of 30 seconds).
42
+ * (Default: Equal to `realtimeMaxAudioSec`)
43
+ */
44
+ realtimeAudioSliceSec?: number;
39
45
  };
40
46
  export type TranscribeResult = {
41
47
  result: string;
@@ -52,24 +58,34 @@ export type TranscribeRealtimeEvent = {
52
58
  isCapturing: boolean;
53
59
  isStoppedByAction?: boolean;
54
60
  code: number;
61
+ data?: TranscribeResult;
62
+ error?: string;
55
63
  processTime: number;
56
64
  recordingTime: number;
65
+ slices?: Array<{
66
+ code: number;
67
+ error?: string;
68
+ data?: TranscribeResult;
69
+ processTime: number;
70
+ recordingTime: number;
71
+ }>;
72
+ };
73
+ export type TranscribeRealtimeNativePayload = {
74
+ /** Is capturing audio, when false, the event is the final result */
75
+ isCapturing: boolean;
76
+ isStoppedByAction?: boolean;
77
+ code: number;
78
+ processTime: number;
79
+ recordingTime: number;
80
+ isUseSlices: boolean;
81
+ sliceIndex: number;
57
82
  data?: TranscribeResult;
58
83
  error?: string;
59
84
  };
60
85
  export type TranscribeRealtimeNativeEvent = {
61
86
  contextId: number;
62
87
  jobId: number;
63
- payload: {
64
- /** Is capturing audio, when false, the event is the final result */
65
- isCapturing: boolean;
66
- isStoppedByAction?: boolean;
67
- code: number;
68
- processTime: number;
69
- recordingTime: number;
70
- data?: TranscribeResult;
71
- error?: string;
72
- };
88
+ payload: TranscribeRealtimeNativePayload;
73
89
  };
74
90
  declare class WhisperContext {
75
91
  id: number;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whisper.rn",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "React Native binding of whisper.cpp",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
package/src/index.tsx CHANGED
@@ -71,6 +71,12 @@ export type TranscribeRealtimeOptions = TranscribeOptions & {
71
71
  * the recommended value will be <= 30 seconds. (Default: 30)
72
72
  */
73
73
  realtimeAudioSec?: number,
74
+ /**
75
+ * Optimize audio transcription performance by slicing audio samples when `realtimeAudioSec` > 30.
76
+ * Set `realtimeAudioSliceSec` < 30 so performance improvements can be achieved in the Whisper hard constraint (processes the audio in chunks of 30 seconds).
77
+ * (Default: Equal to `realtimeMaxAudioSec`)
78
+ */
79
+ realtimeAudioSliceSec?: number
74
80
  }
75
81
 
76
82
  export type TranscribeResult = {
@@ -85,12 +91,32 @@ export type TranscribeResult = {
85
91
  export type TranscribeRealtimeEvent = {
86
92
  contextId: number,
87
93
  jobId: number,
94
+ /** Is capturing audio, when false, the event is the final result */
95
+ isCapturing: boolean,
96
+ isStoppedByAction?: boolean,
97
+ code: number,
98
+ data?: TranscribeResult,
99
+ error?: string,
100
+ processTime: number,
101
+ recordingTime: number,
102
+ slices?: Array<{
103
+ code: number,
104
+ error?: string,
105
+ data?: TranscribeResult,
106
+ processTime: number,
107
+ recordingTime: number,
108
+ }>,
109
+ }
110
+
111
+ export type TranscribeRealtimeNativePayload = {
88
112
  /** Is capturing audio, when false, the event is the final result */
89
113
  isCapturing: boolean,
90
114
  isStoppedByAction?: boolean,
91
115
  code: number,
92
116
  processTime: number,
93
117
  recordingTime: number,
118
+ isUseSlices: boolean,
119
+ sliceIndex: number,
94
120
  data?: TranscribeResult,
95
121
  error?: string,
96
122
  }
@@ -98,16 +124,7 @@ export type TranscribeRealtimeEvent = {
98
124
  export type TranscribeRealtimeNativeEvent = {
99
125
  contextId: number,
100
126
  jobId: number,
101
- payload: {
102
- /** Is capturing audio, when false, the event is the final result */
103
- isCapturing: boolean,
104
- isStoppedByAction?: boolean,
105
- code: number,
106
- processTime: number,
107
- recordingTime: number,
108
- data?: TranscribeResult,
109
- error?: string,
110
- },
127
+ payload: TranscribeRealtimeNativePayload,
111
128
  }
112
129
 
113
130
  class WhisperContext {
@@ -140,7 +157,52 @@ class WhisperContext {
140
157
  }> {
141
158
  const jobId: number = Math.floor(Math.random() * 10000)
142
159
  await RNWhisper.startRealtimeTranscribe(this.id, jobId, options)
143
- let lastTranscribePayload: TranscribeRealtimeNativeEvent['payload']
160
+ let lastTranscribePayload: TranscribeRealtimeNativePayload
161
+
162
+ const slices: TranscribeRealtimeNativePayload[] = []
163
+ let sliceIndex: number = 0
164
+ let tOffset: number = 0
165
+
166
+ const putSlice = (payload: TranscribeRealtimeNativePayload) => {
167
+ if (!payload.isUseSlices) return
168
+ if (sliceIndex !== payload.sliceIndex) {
169
+ const { segments = [] } = slices[sliceIndex]?.data || {}
170
+ tOffset = segments[segments.length - 1]?.t1 || 0
171
+ }
172
+ ({ sliceIndex } = payload)
173
+ slices[sliceIndex] = {
174
+ ...payload,
175
+ data: payload.data ? {
176
+ ...payload.data,
177
+ segments: payload.data.segments.map((segment) => ({
178
+ ...segment,
179
+ t0: segment.t0 + tOffset,
180
+ t1: segment.t1 + tOffset,
181
+ })) || [],
182
+ } : undefined,
183
+ }
184
+ }
185
+
186
+ const mergeSlicesIfNeeded = (payload: TranscribeRealtimeNativePayload): TranscribeRealtimeNativePayload => {
187
+ if (!payload.isUseSlices) return payload
188
+
189
+ const mergedPayload: any = {}
190
+ slices.forEach(
191
+ (slice) => {
192
+ mergedPayload.data = {
193
+ result: (mergedPayload.data?.result || '') + (slice.data?.result || ''),
194
+ segments: [
195
+ ...(mergedPayload?.data?.segments || []),
196
+ ...(slice.data?.segments || []),
197
+ ],
198
+ }
199
+ mergedPayload.processTime = slice.processTime
200
+ mergedPayload.recordingTime = (mergedPayload?.recordingTime || 0) + slice.recordingTime
201
+ }
202
+ )
203
+ return { ...payload, ...mergedPayload, slices }
204
+ }
205
+
144
206
  return {
145
207
  stop: () => RNWhisper.abortTranscribe(this.id, jobId),
146
208
  subscribe: (callback: (event: TranscribeRealtimeEvent) => void) => {
@@ -150,7 +212,12 @@ class WhisperContext {
150
212
  const { contextId, payload } = evt
151
213
  if (contextId !== this.id || evt.jobId !== jobId) return
152
214
  lastTranscribePayload = payload
153
- callback({ contextId, jobId: evt.jobId, ...payload })
215
+ putSlice(payload)
216
+ callback({
217
+ contextId,
218
+ jobId: evt.jobId,
219
+ ...mergeSlicesIfNeeded(payload),
220
+ })
154
221
  }
155
222
  )
156
223
  const endListener = EventEmitter.addListener(
@@ -158,11 +225,15 @@ class WhisperContext {
158
225
  (evt: TranscribeRealtimeNativeEvent) => {
159
226
  const { contextId, payload } = evt
160
227
  if (contextId !== this.id || evt.jobId !== jobId) return
228
+ const lastPayload = {
229
+ ...lastTranscribePayload,
230
+ ...payload,
231
+ }
232
+ putSlice(lastPayload)
161
233
  callback({
162
234
  contextId,
163
235
  jobId: evt.jobId,
164
- ...lastTranscribePayload,
165
- ...payload,
236
+ ...mergeSlicesIfNeeded(lastPayload),
166
237
  isCapturing: false
167
238
  })
168
239
  transcribeListener.remove()