speechrecorderng 2.25.1 → 3.0.0
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.
- package/esm2020/lib/audio/array_audio_buffer.mjs +101 -0
- package/esm2020/lib/audio/array_audio_buffer_input_stream.mjs +86 -0
- package/esm2020/lib/audio/audio_data_holder.mjs +109 -0
- package/esm2020/lib/audio/audio_display.mjs +5 -5
- package/esm2020/lib/audio/audio_player.mjs +9 -7
- package/esm2020/lib/audio/capture/capture.mjs +6 -1
- package/esm2020/lib/audio/dsp/level_measure.mjs +97 -55
- package/esm2020/lib/audio/io/stream.mjs +3 -3
- package/esm2020/lib/audio/persistor.mjs +12 -6
- package/esm2020/lib/audio/playback/array_audio_buffer_source_node.mjs +265 -0
- package/esm2020/lib/audio/playback/player.mjs +184 -50
- package/esm2020/lib/audio/ui/audio_canvas_layer_comp.mjs +25 -13
- package/esm2020/lib/audio/ui/audio_display_scroll_pane.mjs +2 -2
- package/esm2020/lib/audio/ui/audiosignal.mjs +206 -77
- package/esm2020/lib/audio/ui/container.mjs +14 -14
- package/esm2020/lib/audio/ui/livelevel.mjs +17 -3
- package/esm2020/lib/audio/ui/sonagram.mjs +343 -160
- package/esm2020/lib/io/stream.mjs +77 -1
- package/esm2020/lib/net/uploader.mjs +15 -3
- package/esm2020/lib/speechrecorder/recording.mjs +52 -7
- package/esm2020/lib/speechrecorder/recordings/recordings.service.mjs +99 -7
- package/esm2020/lib/speechrecorder/session/audiorecorder.mjs +131 -117
- package/esm2020/lib/speechrecorder/session/basicrecorder.mjs +36 -12
- package/esm2020/lib/speechrecorder/session/progress.mjs +2 -2
- package/esm2020/lib/speechrecorder/session/prompting.mjs +2 -2
- package/esm2020/lib/speechrecorder/session/recorder_combi_pane.mjs +6 -3
- package/esm2020/lib/speechrecorder/session/recording_file_cache.mjs +182 -0
- package/esm2020/lib/speechrecorder/session/recording_list.mjs +23 -10
- package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-meta.component.mjs +12 -6
- package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.mjs +5 -5
- package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-view.component.mjs +13 -9
- package/esm2020/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +8 -5
- package/esm2020/lib/speechrecorder/session/sessionmanager.mjs +140 -113
- package/esm2020/lib/speechrecorderng.component.mjs +3 -3
- package/esm2020/lib/speechrecorderng.module.mjs +5 -4
- package/esm2020/lib/spr.module.version.mjs +2 -2
- package/esm2020/lib/ui/recordingitem_display.mjs +2 -2
- package/esm2020/lib/utils/wake_lock.mjs +48 -3
- package/fesm2015/speechrecorderng.mjs +2415 -786
- package/fesm2015/speechrecorderng.mjs.map +1 -1
- package/fesm2020/speechrecorderng.mjs +2404 -786
- package/fesm2020/speechrecorderng.mjs.map +1 -1
- package/lib/audio/array_audio_buffer.d.ts +15 -0
- package/lib/audio/array_audio_buffer_input_stream.d.ts +14 -0
- package/lib/audio/audio_data_holder.d.ts +29 -0
- package/lib/audio/audio_display.d.ts +2 -1
- package/lib/audio/audio_player.d.ts +2 -1
- package/lib/audio/capture/capture.d.ts +2 -0
- package/lib/audio/dsp/level_measure.d.ts +2 -1
- package/lib/audio/persistor.d.ts +8 -3
- package/lib/audio/playback/array_audio_buffer_source_node.d.ts +24 -0
- package/lib/audio/playback/player.d.ts +6 -0
- package/lib/audio/ui/audio_canvas_layer_comp.d.ts +4 -1
- package/lib/audio/ui/audio_display_scroll_pane.d.ts +2 -1
- package/lib/audio/ui/audiosignal.d.ts +3 -2
- package/lib/audio/ui/container.d.ts +3 -2
- package/lib/audio/ui/livelevel.d.ts +4 -2
- package/lib/audio/ui/sonagram.d.ts +3 -2
- package/lib/io/stream.d.ts +17 -0
- package/lib/net/uploader.d.ts +6 -1
- package/lib/speechrecorder/recording.d.ts +15 -3
- package/lib/speechrecorder/recordings/recordings.service.d.ts +2 -0
- package/lib/speechrecorder/session/audiorecorder.d.ts +6 -10
- package/lib/speechrecorder/session/basicrecorder.d.ts +10 -5
- package/lib/speechrecorder/session/progress.d.ts +1 -1
- package/lib/speechrecorder/session/prompting.d.ts +1 -1
- package/lib/speechrecorder/session/recorder_combi_pane.d.ts +3 -1
- package/lib/speechrecorder/session/recording_file_cache.d.ts +30 -0
- package/lib/speechrecorder/session/recording_list.d.ts +3 -1
- package/lib/speechrecorder/session/recordingfile/recording-file-meta.component.d.ts +2 -1
- package/lib/speechrecorder/session/recordingfile/recording-file-view.component.d.ts +1 -0
- package/lib/speechrecorder/session/sessionmanager.d.ts +13 -18
- package/lib/speechrecorderng.module.d.ts +2 -1
- package/lib/spr.module.version.d.ts +1 -1
- package/lib/utils/wake_lock.d.ts +12 -0
- package/package.json +1 -1
|
@@ -36,6 +36,7 @@ import * as i2$2 from '@angular/material/table';
|
|
|
36
36
|
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
|
|
37
37
|
import { MatSelectModule } from '@angular/material/select';
|
|
38
38
|
import { MatInputModule } from '@angular/material/input';
|
|
39
|
+
import { MatMenuModule } from '@angular/material/menu';
|
|
39
40
|
|
|
40
41
|
class ActionEvent {
|
|
41
42
|
constructor(_value = null) {
|
|
@@ -109,6 +110,514 @@ class Action {
|
|
|
109
110
|
}
|
|
110
111
|
}
|
|
111
112
|
|
|
113
|
+
class ArrayAudioBufferInputStream {
|
|
114
|
+
constructor(arrayAudioBuffer) {
|
|
115
|
+
this.arrayAudioBuffer = arrayAudioBuffer;
|
|
116
|
+
this._framePos = 0;
|
|
117
|
+
this.chunkFramePos = 0;
|
|
118
|
+
this.chunkIdx = 0;
|
|
119
|
+
this.eod = false;
|
|
120
|
+
//console.debug("Array audio input stream array audio buffer frames: "+arrayAudioBuffer.frameLen);
|
|
121
|
+
}
|
|
122
|
+
close() {
|
|
123
|
+
}
|
|
124
|
+
skipFrames(n) {
|
|
125
|
+
let toSkip = n;
|
|
126
|
+
if (this.chunkIdx >= this.arrayAudioBuffer.chunkCount) {
|
|
127
|
+
this.eod = true;
|
|
128
|
+
if (toSkip > 0) {
|
|
129
|
+
throw Error('Skip out of bounds: Cannot skip ' + toSkip + ' frames.');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
let chunkBuf0 = this.arrayAudioBuffer.data[0][this.chunkIdx];
|
|
134
|
+
let chunkBufsLen = chunkBuf0.length;
|
|
135
|
+
let currBufSkippable = chunkBufsLen - this.chunkFramePos;
|
|
136
|
+
if (n >= currBufSkippable) {
|
|
137
|
+
this._framePos += currBufSkippable;
|
|
138
|
+
toSkip -= currBufSkippable;
|
|
139
|
+
this.chunkIdx++;
|
|
140
|
+
this.chunkFramePos = 0;
|
|
141
|
+
if (this.chunkIdx < this.arrayAudioBuffer.chunkCount) {
|
|
142
|
+
this.skipFrames(toSkip);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
if (toSkip > 0) {
|
|
146
|
+
throw Error('Skip out of bounds: Cannot skip ' + toSkip + ' frames.');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
toSkip -= n;
|
|
152
|
+
this.chunkFramePos += n;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
read(buffers) {
|
|
157
|
+
let read = 0;
|
|
158
|
+
let toRead = buffers[0].length;
|
|
159
|
+
while (read < toRead && !this.eod) {
|
|
160
|
+
//console.debug("Chunk "+this.chunkIdx+" of "+this.arrayAudioBuffer.chunkCount+" chunks.")
|
|
161
|
+
if (this.chunkIdx >= this.arrayAudioBuffer.chunkCount) {
|
|
162
|
+
this.eod = true;
|
|
163
|
+
//console.debug("Array audio input stream end of data read frames: "+this.framePos);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
let chunkBuf0 = this.arrayAudioBuffer.data[0][this.chunkIdx];
|
|
167
|
+
let chunkBufsLen = chunkBuf0.length;
|
|
168
|
+
let chunkBufAvail = chunkBufsLen - this.chunkFramePos;
|
|
169
|
+
let r = chunkBufAvail;
|
|
170
|
+
if (r > toRead - read) {
|
|
171
|
+
r = toRead - read;
|
|
172
|
+
}
|
|
173
|
+
for (let ch = 0; ch < buffers.length; ch++) {
|
|
174
|
+
let chChunkBuf = this.arrayAudioBuffer.data[ch][this.chunkIdx];
|
|
175
|
+
for (let bi = 0; bi < r; bi++) {
|
|
176
|
+
buffers[ch][read + bi] = chChunkBuf[this.chunkFramePos + bi];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
read += r;
|
|
180
|
+
this.chunkFramePos += r;
|
|
181
|
+
if (this.chunkFramePos >= chunkBufsLen) {
|
|
182
|
+
this.chunkIdx++;
|
|
183
|
+
this.chunkFramePos = 0;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
this._framePos += read;
|
|
188
|
+
//console.debug("Read: "+read+", frame pos: "+this.framePos)
|
|
189
|
+
if (this._framePos > this.arrayAudioBuffer.frameLen) {
|
|
190
|
+
console.error("Array audio input stream frame pos: " + this._framePos + " greater then frame length: " + this.arrayAudioBuffer.frameLen);
|
|
191
|
+
}
|
|
192
|
+
return read;
|
|
193
|
+
}
|
|
194
|
+
get framePos() {
|
|
195
|
+
return this._framePos;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
class EditFloat32ArrayInputStream {
|
|
200
|
+
constructor(_srcInputStream, offset = 0, length) {
|
|
201
|
+
this._srcInputStream = _srcInputStream;
|
|
202
|
+
this.offset = offset;
|
|
203
|
+
this.length = length;
|
|
204
|
+
this.framePos = 0;
|
|
205
|
+
this.readFrames = 0;
|
|
206
|
+
if (this.offset < 0) {
|
|
207
|
+
throw Error('Parameter offset must be undefined or greater or equal zero.');
|
|
208
|
+
}
|
|
209
|
+
if (this.length !== undefined && this.length < 0) {
|
|
210
|
+
throw Error('Parameter length must be undefined or greater or equal zero.');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
skipToOffset() {
|
|
214
|
+
if (this.framePos == 0 && this.offset > 0) {
|
|
215
|
+
this._srcInputStream.skipFrames(this.offset);
|
|
216
|
+
this.framePos += this.offset;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
read(buffers) {
|
|
220
|
+
this.skipToOffset();
|
|
221
|
+
let read = 0;
|
|
222
|
+
if (this.length === undefined) {
|
|
223
|
+
read = this._srcInputStream.read(buffers);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
if (buffers.length > 0) {
|
|
227
|
+
let bufsCh0 = buffers[0];
|
|
228
|
+
let bufsLen = bufsCh0.length;
|
|
229
|
+
let avail = this.length - this.readFrames;
|
|
230
|
+
if (avail > 0) {
|
|
231
|
+
if (avail > bufsLen) {
|
|
232
|
+
read = this._srcInputStream.read(buffers);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
// temporary buffers required
|
|
236
|
+
let tmpBufs = new Array(buffers.length);
|
|
237
|
+
for (let ch = 0; ch < buffers.length; ch++) {
|
|
238
|
+
tmpBufs[ch] = new Float32Array(avail);
|
|
239
|
+
}
|
|
240
|
+
read = this._srcInputStream.read(tmpBufs);
|
|
241
|
+
for (let ch = 0; ch < buffers.length; ch++) {
|
|
242
|
+
buffers[ch].set(tmpBufs[ch]);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
this.readFrames += read;
|
|
249
|
+
this.framePos += read;
|
|
250
|
+
return read;
|
|
251
|
+
}
|
|
252
|
+
skipFrames(n) {
|
|
253
|
+
this.skipToOffset();
|
|
254
|
+
if (this.length === undefined) {
|
|
255
|
+
this._srcInputStream.skipFrames(n);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
let avail = this.length - this.readFrames;
|
|
259
|
+
if (avail > 0) {
|
|
260
|
+
if (avail >= n) {
|
|
261
|
+
this._srcInputStream.skipFrames(n);
|
|
262
|
+
this.readFrames += n;
|
|
263
|
+
this.framePos += n;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
throw Error('Tried to skip out of bounds.');
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
close() {
|
|
272
|
+
this._srcInputStream.close();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
class Float32ArrayChunkerOutStream {
|
|
276
|
+
constructor(outStream) {
|
|
277
|
+
this.outStream = outStream;
|
|
278
|
+
this.bufs = new Array();
|
|
279
|
+
this._channels = 0;
|
|
280
|
+
this._chunkSize = 0;
|
|
281
|
+
this.filled = 0;
|
|
282
|
+
this.receivedFrames = 0;
|
|
283
|
+
this.sentFrames = 0;
|
|
284
|
+
}
|
|
285
|
+
createBuffers() {
|
|
286
|
+
this.bufs = new Array(this._channels);
|
|
287
|
+
for (let ch = 0; ch < this._channels; ch++) {
|
|
288
|
+
this.bufs[ch] = new Float32Array(this._chunkSize);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
set chunkSize(chunkSize) {
|
|
292
|
+
this._chunkSize = chunkSize;
|
|
293
|
+
this.createBuffers();
|
|
294
|
+
}
|
|
295
|
+
get chunkSize() {
|
|
296
|
+
return this._chunkSize;
|
|
297
|
+
}
|
|
298
|
+
set channels(channels) {
|
|
299
|
+
this._channels = channels;
|
|
300
|
+
this.createBuffers();
|
|
301
|
+
}
|
|
302
|
+
get channels() {
|
|
303
|
+
return this._channels;
|
|
304
|
+
}
|
|
305
|
+
available() {
|
|
306
|
+
return this._chunkSize - this.filled;
|
|
307
|
+
}
|
|
308
|
+
write(buffers) {
|
|
309
|
+
let copied = 0;
|
|
310
|
+
if (buffers.length > 0) {
|
|
311
|
+
let buffersLen = buffers[0].length;
|
|
312
|
+
this.receivedFrames += buffersLen;
|
|
313
|
+
let avail = buffersLen;
|
|
314
|
+
// Fill out buffers until all values copied
|
|
315
|
+
while (avail > 0) {
|
|
316
|
+
let toFill = this._chunkSize - this.filled;
|
|
317
|
+
if (toFill > avail) {
|
|
318
|
+
toFill = avail;
|
|
319
|
+
}
|
|
320
|
+
let sliceEnd = copied + toFill;
|
|
321
|
+
// Firefox on Android sends only the first channel
|
|
322
|
+
for (let ch = 0; ch < buffersLen; ch++) {
|
|
323
|
+
if (buffers[ch]) {
|
|
324
|
+
let cpPrt = buffers[ch].slice(copied, sliceEnd);
|
|
325
|
+
let buf = this.bufs[ch];
|
|
326
|
+
buf.set(cpPrt, this.filled);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
copied += toFill;
|
|
330
|
+
avail -= toFill;
|
|
331
|
+
this.filled += toFill;
|
|
332
|
+
if (this.filled == this._chunkSize) {
|
|
333
|
+
this.outStream.write(this.bufs);
|
|
334
|
+
this.sentFrames += this.filled;
|
|
335
|
+
this.filled = 0;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return copied;
|
|
340
|
+
}
|
|
341
|
+
flush() {
|
|
342
|
+
if (this.filled > 0) {
|
|
343
|
+
let restBufs = new Array(this._channels);
|
|
344
|
+
for (let ch = 0; ch < this._channels; ch++) {
|
|
345
|
+
restBufs[ch] = this.bufs[ch].slice(0, this.filled);
|
|
346
|
+
}
|
|
347
|
+
this.outStream.write(restBufs);
|
|
348
|
+
this.outStream.flush();
|
|
349
|
+
this.sentFrames += this.filled;
|
|
350
|
+
this.filled = 0;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
close() {
|
|
354
|
+
this.outStream.close();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Code from audio_source_worklet.js as string constant to be loaded as module
|
|
359
|
+
// Changes in audio_source_worklet.js must be copied and pasted to this string constant
|
|
360
|
+
const aswpStr = "\n" +
|
|
361
|
+
"// Important note: Changes in audio_source_worklet.js must be copied and pasted to the string constant aswpStr in array_audio_buffer_source_node.ts\n" +
|
|
362
|
+
"\n" +
|
|
363
|
+
"\n" +
|
|
364
|
+
"class AudioSourceProcessor extends AudioWorkletProcessor{\n" +
|
|
365
|
+
"\n" +
|
|
366
|
+
" filledFrames=0;\n" +
|
|
367
|
+
" audioBuffers=new Array();\n" +
|
|
368
|
+
" currentAudioBuffer=null;\n" +
|
|
369
|
+
" currentAudioBufferFramePos=0;\n" +
|
|
370
|
+
" currentAudioBufferAvail=0;\n" +
|
|
371
|
+
" running=false;\n" +
|
|
372
|
+
" ended=false;\n" +
|
|
373
|
+
"\n" +
|
|
374
|
+
" constructor() {\n" +
|
|
375
|
+
" super({numberOfInputs:0,numberOfOutputs:1});\n" +
|
|
376
|
+
" this.port.onmessage=(msgEv)=>{\n" +
|
|
377
|
+
" // received audio playback data from application\n" +
|
|
378
|
+
" //console.debug(\"Audio source worklet msg: Received.\");\n" +
|
|
379
|
+
"\n" +
|
|
380
|
+
" if(msgEv.data.cmd){\n" +
|
|
381
|
+
" if('data'===msgEv.data.cmd) {\n" +
|
|
382
|
+
" let chs = msgEv.data.chs;\n" +
|
|
383
|
+
"\n" +
|
|
384
|
+
" let audioData = new Array(chs);\n" +
|
|
385
|
+
" for (let ch = 0; ch < chs; ch++) {\n" +
|
|
386
|
+
" audioData[ch] = new Float32Array(msgEv.data.audioData[ch]);\n" +
|
|
387
|
+
" }\n" +
|
|
388
|
+
" let msgChBufLen=audioData[0].length;\n" +
|
|
389
|
+
" this.audioBuffers.push(audioData);\n" +
|
|
390
|
+
" this.filledFrames += msgChBufLen;\n" +
|
|
391
|
+
" //console.debug(\"Audio source worklet msg: Filled \" + this.filledFrames+ \" in \"+this.audioBuffers.length+\" buffers.\");\n" +
|
|
392
|
+
"\n" +
|
|
393
|
+
" }else if('start'===msgEv.data.cmd){\n" +
|
|
394
|
+
" this.running=true;\n" +
|
|
395
|
+
" }else if('stop'===msgEv.data.cmd){\n" +
|
|
396
|
+
" //console.debug(\"Stop...\");\n" +
|
|
397
|
+
" this.running=false;\n" +
|
|
398
|
+
" // clear buffers\n" +
|
|
399
|
+
" this.filledFrames=0;\n" +
|
|
400
|
+
" while(this.audioBuffers.length > 0) {\n" +
|
|
401
|
+
" this.audioBuffers.pop();\n" +
|
|
402
|
+
" }\n" +
|
|
403
|
+
" this.currentAudioBuffer=new Float32Array(0);\n" +
|
|
404
|
+
" }\n" +
|
|
405
|
+
" }\n" +
|
|
406
|
+
" }\n" +
|
|
407
|
+
" }\n" +
|
|
408
|
+
"\n" +
|
|
409
|
+
" process(\n" +
|
|
410
|
+
" inputs,\n" +
|
|
411
|
+
" outputs,\n" +
|
|
412
|
+
" parameters\n" +
|
|
413
|
+
" ){\n" +
|
|
414
|
+
" //console.debug(\"Audio source worklet: process \"+outputs.length+ \" output buffers.\");\n" +
|
|
415
|
+
" // copy ring buffer data to outputs\n" +
|
|
416
|
+
" if(!this.running){\n" +
|
|
417
|
+
" return !this.ended;\n" +
|
|
418
|
+
" }\n" +
|
|
419
|
+
"\n" +
|
|
420
|
+
" let output=outputs[0];\n" +
|
|
421
|
+
" let chs=output.length;\n" +
|
|
422
|
+
" //console.debug(\"Audio source worklet: Output channels: \"+chs);\n" +
|
|
423
|
+
" if(chs>0) {\n" +
|
|
424
|
+
"\n" +
|
|
425
|
+
" let outCh0 = output[0];\n" +
|
|
426
|
+
" let outChLen = outCh0.length;\n" +
|
|
427
|
+
"\n" +
|
|
428
|
+
" if(!this.currentAudioBuffer){\n" +
|
|
429
|
+
" // get first buffer\n" +
|
|
430
|
+
" let nxtBuff=this.audioBuffers.shift();\n" +
|
|
431
|
+
" if(nxtBuff) {\n" +
|
|
432
|
+
" this.currentAudioBuffer = nxtBuff;\n" +
|
|
433
|
+
" this.currentAudioBufferFramePos=0;\n" +
|
|
434
|
+
" this.currentAudioBufferAvail=this.currentAudioBuffer[0].length;\n" +
|
|
435
|
+
" }else{\n" +
|
|
436
|
+
" return true;\n" +
|
|
437
|
+
" }\n" +
|
|
438
|
+
" }\n" +
|
|
439
|
+
"\n" +
|
|
440
|
+
" let copied=0;\n" +
|
|
441
|
+
" do{\n" +
|
|
442
|
+
" if(this.currentAudioBufferAvail==0){\n" +
|
|
443
|
+
" let nxtBuff=this.audioBuffers.shift();\n" +
|
|
444
|
+
" if(nxtBuff){\n" +
|
|
445
|
+
" this.currentAudioBuffer=nxtBuff;\n" +
|
|
446
|
+
" this.currentAudioBufferFramePos=0;\n" +
|
|
447
|
+
" this.currentAudioBufferAvail=this.currentAudioBuffer[0].length;\n" +
|
|
448
|
+
" //console.debug(\"Next buffer with \"+this.currentAudioBufferAvail+ \" frames\");\n" +
|
|
449
|
+
" this.port.postMessage({eventType:'bufferNotification',filledFrames:this.filledFrames});\n" +
|
|
450
|
+
" }else{\n" +
|
|
451
|
+
" this.ended=true;\n" +
|
|
452
|
+
" this.port.postMessage({eventType:'ended'});\n" +
|
|
453
|
+
" //console.debug(\"Stream ended\");\n" +
|
|
454
|
+
" break;\n" +
|
|
455
|
+
" }\n" +
|
|
456
|
+
" }\n" +
|
|
457
|
+
" //console.debug(\"outChLen: \"+outChLen+\", copied: \"+copied+\", current avail: \"+this.currentAudioBufferAvail);\n" +
|
|
458
|
+
" let toCopy=outChLen-copied;\n" +
|
|
459
|
+
" if(toCopy>this.currentAudioBufferAvail){\n" +
|
|
460
|
+
" toCopy=this.currentAudioBufferAvail;\n" +
|
|
461
|
+
" }\n" +
|
|
462
|
+
" //console.debug(\"Copy \"+toCopy+\" frames...\");\n" +
|
|
463
|
+
" for(let ch=0;ch<chs;ch++) {\n" +
|
|
464
|
+
" let outCh=output[ch];\n" +
|
|
465
|
+
" for (let i = 0; i < toCopy; i++) {\n" +
|
|
466
|
+
" outCh[copied+i]=this.currentAudioBuffer[ch][this.currentAudioBufferFramePos+i];\n" +
|
|
467
|
+
" }\n" +
|
|
468
|
+
" }\n" +
|
|
469
|
+
" copied+=toCopy;\n" +
|
|
470
|
+
" this.currentAudioBufferFramePos+=toCopy;\n" +
|
|
471
|
+
" this.currentAudioBufferAvail-=toCopy;\n" +
|
|
472
|
+
"\n" +
|
|
473
|
+
" }while(copied<outChLen);\n" +
|
|
474
|
+
" this.filledFrames-=copied;\n" +
|
|
475
|
+
" //console.debug(\"Copied \"+copied+\" frames.\");\n" +
|
|
476
|
+
" }\n" +
|
|
477
|
+
" return !this.ended;\n" +
|
|
478
|
+
" }\n" +
|
|
479
|
+
"}\n" +
|
|
480
|
+
"\n" +
|
|
481
|
+
"registerProcessor('audio-source-worklet',AudioSourceProcessor);\n";
|
|
482
|
+
class ArrayAudioBufferSourceNode extends AudioWorkletNode {
|
|
483
|
+
constructor(context) {
|
|
484
|
+
super(context, 'audio-source-worklet');
|
|
485
|
+
this._bufferFillSeconds = ArrayAudioBufferSourceNode.DEFAULT_BUFFER_FILL_SECONDS;
|
|
486
|
+
this.bufferFillFrames = 0;
|
|
487
|
+
this._arrayAudioBuffer = null;
|
|
488
|
+
this._audioInputStream = null;
|
|
489
|
+
this._aisBufs = null;
|
|
490
|
+
this.filledFrames = 0;
|
|
491
|
+
this.onended = null;
|
|
492
|
+
this.channelCountMode = 'explicit';
|
|
493
|
+
this.port.onmessage = (msgEv) => {
|
|
494
|
+
if (msgEv.data) {
|
|
495
|
+
let evType = msgEv.data.eventType;
|
|
496
|
+
if (evType) {
|
|
497
|
+
if ('bufferNotification' === evType) {
|
|
498
|
+
this.filledFrames = msgEv.data.filledFrames;
|
|
499
|
+
//console.debug("Buffer notification: filled frames: " + this.filledFrames);
|
|
500
|
+
this.fillBuffer();
|
|
501
|
+
}
|
|
502
|
+
else if ('ended' === evType) {
|
|
503
|
+
let drainTime = 0;
|
|
504
|
+
if (this._arrayAudioBuffer?.sampleRate) {
|
|
505
|
+
drainTime = ArrayAudioBufferSourceNode.QUANTUM_FRAME_LEN / this._arrayAudioBuffer.sampleRate;
|
|
506
|
+
}
|
|
507
|
+
//let dstAny:any=this.context.destination;
|
|
508
|
+
//console.debug('Destination node tail-time: '+dstAny['tail-time']);
|
|
509
|
+
window.setTimeout(() => {
|
|
510
|
+
this.onended?.call(this);
|
|
511
|
+
}, drainTime * 1000);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
fillBuffer(frameOffset) {
|
|
518
|
+
if (this._arrayAudioBuffer && this._audioInputStream && this._aisBufs) {
|
|
519
|
+
let filled = this.filledFrames;
|
|
520
|
+
let bufLen = 0;
|
|
521
|
+
while (filled < this.bufferFillFrames) {
|
|
522
|
+
let read = this._audioInputStream.read(this._aisBufs);
|
|
523
|
+
//console.log("ArrayAudioBufferSourceNode: read: "+read)
|
|
524
|
+
if (read) {
|
|
525
|
+
let trBuffers = new Array(this.channelCount);
|
|
526
|
+
for (let ch = 0; ch < this.channelCount; ch++) {
|
|
527
|
+
let adCh = this._aisBufs[ch];
|
|
528
|
+
let adChCopy = new Float32Array(adCh.length);
|
|
529
|
+
bufLen = adChCopy.length;
|
|
530
|
+
adChCopy.set(adCh);
|
|
531
|
+
trBuffers[ch] = adChCopy.buffer;
|
|
532
|
+
}
|
|
533
|
+
this.port.postMessage({
|
|
534
|
+
cmd: 'data',
|
|
535
|
+
chs: this.channelCount,
|
|
536
|
+
audioData: trBuffers
|
|
537
|
+
}, trBuffers);
|
|
538
|
+
filled += read;
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
get arrayAudioBuffer() {
|
|
547
|
+
return this._arrayAudioBuffer;
|
|
548
|
+
}
|
|
549
|
+
set arrayAudioBuffer(value) {
|
|
550
|
+
this._arrayAudioBuffer = value;
|
|
551
|
+
if (this._arrayAudioBuffer?.channelCount) {
|
|
552
|
+
this.channelCount = this._arrayAudioBuffer?.channelCount;
|
|
553
|
+
this.bufferFillFrames = this._arrayAudioBuffer.sampleRate * this._bufferFillSeconds;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
get bufferFillSeconds() {
|
|
557
|
+
return this._bufferFillSeconds;
|
|
558
|
+
}
|
|
559
|
+
set bufferFillSeconds(value) {
|
|
560
|
+
this._bufferFillSeconds = value;
|
|
561
|
+
}
|
|
562
|
+
start(when, offset, duration) {
|
|
563
|
+
if (when) {
|
|
564
|
+
throw Error("when, offest,duration parameters currently not supported by ArrayAudioBufferSourceNode class");
|
|
565
|
+
}
|
|
566
|
+
if (this._arrayAudioBuffer) {
|
|
567
|
+
let arrAis = new ArrayAudioBufferInputStream(this._arrayAudioBuffer);
|
|
568
|
+
if (offset === undefined && duration === undefined) {
|
|
569
|
+
this._audioInputStream = arrAis;
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
let offsetFrames = 0;
|
|
573
|
+
let durationFrames = undefined;
|
|
574
|
+
if (offset !== undefined) {
|
|
575
|
+
offsetFrames = Math.floor(offset * this._arrayAudioBuffer.sampleRate);
|
|
576
|
+
}
|
|
577
|
+
if (duration !== undefined) {
|
|
578
|
+
durationFrames = Math.floor(duration * this._arrayAudioBuffer.sampleRate);
|
|
579
|
+
}
|
|
580
|
+
this._audioInputStream = new EditFloat32ArrayInputStream(arrAis, offsetFrames, durationFrames);
|
|
581
|
+
}
|
|
582
|
+
let chs = this._arrayAudioBuffer.channelCount;
|
|
583
|
+
this._aisBufs = new Array(chs);
|
|
584
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
585
|
+
this._aisBufs[ch] = new Float32Array(1024);
|
|
586
|
+
}
|
|
587
|
+
this.fillBuffer();
|
|
588
|
+
this.port.postMessage({ cmd: 'start' });
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
stop() {
|
|
592
|
+
this.port.postMessage({ cmd: 'stop' });
|
|
593
|
+
this.onended?.call(this);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
ArrayAudioBufferSourceNode.QUANTUM_FRAME_LEN = 128;
|
|
597
|
+
ArrayAudioBufferSourceNode.DEFAULT_BUFFER_FILL_SECONDS = 10;
|
|
598
|
+
class AudioSourceWorkletModuleLoader {
|
|
599
|
+
static loadModule(context) {
|
|
600
|
+
return new Promise((resolve, reject) => {
|
|
601
|
+
if (AudioSourceWorkletModuleLoader.moduleLoaded) {
|
|
602
|
+
resolve.call(self);
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
let audioWorkletModuleBlob = new Blob([aswpStr], { type: 'text/javascript' });
|
|
606
|
+
let audioWorkletModuleBlobUrl = window.URL.createObjectURL(audioWorkletModuleBlob);
|
|
607
|
+
context.resume();
|
|
608
|
+
context.audioWorklet.addModule(audioWorkletModuleBlobUrl).then(() => {
|
|
609
|
+
AudioSourceWorkletModuleLoader.moduleLoaded = true;
|
|
610
|
+
resolve.call(self);
|
|
611
|
+
}).catch((reason) => {
|
|
612
|
+
console.error(reason);
|
|
613
|
+
reject.call(reason);
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
AudioSourceWorkletModuleLoader.moduleLoaded = false;
|
|
620
|
+
|
|
112
621
|
var EventType;
|
|
113
622
|
(function (EventType) {
|
|
114
623
|
EventType[EventType["CLOSED"] = 0] = "CLOSED";
|
|
@@ -135,10 +644,17 @@ class AudioPlayer {
|
|
|
135
644
|
this.running = false;
|
|
136
645
|
this._audioClip = null;
|
|
137
646
|
this._audioBuffer = null;
|
|
647
|
+
this._arrayAudioBuffer = null;
|
|
138
648
|
this.sourceBufferNode = null;
|
|
649
|
+
this.sourceAudioWorkletNode = null;
|
|
139
650
|
this.playStartTime = null;
|
|
140
651
|
this.timerVar = null;
|
|
141
652
|
this.context = context;
|
|
653
|
+
// AudioSourceWorkletModuleLoader.loadModule(this.context).then(()=>{
|
|
654
|
+
// console.debug("Audio source worklet module loaded.");
|
|
655
|
+
// }).catch((error: any)=>{
|
|
656
|
+
// console.error('Could not add module '+error);
|
|
657
|
+
// });
|
|
142
658
|
this.listener = listener;
|
|
143
659
|
this.bufSize = AudioPlayer.DEFAULT_BUFSIZE;
|
|
144
660
|
this.n = navigator;
|
|
@@ -169,17 +685,23 @@ class AudioPlayer {
|
|
|
169
685
|
return this._stopAction;
|
|
170
686
|
}
|
|
171
687
|
set audioClip(audioClip) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (audioClip && audioClip.
|
|
175
|
-
|
|
688
|
+
let length = 0;
|
|
689
|
+
let chs = 0;
|
|
690
|
+
if (audioClip && audioClip.audioDataHolder) {
|
|
691
|
+
let audioDataHolder = audioClip.audioDataHolder;
|
|
692
|
+
chs = audioDataHolder.numberOfChannels;
|
|
176
693
|
if (chs > 0) {
|
|
177
|
-
length =
|
|
694
|
+
length = audioDataHolder.frameLen;
|
|
178
695
|
if (chs > this.context.destination.maxChannelCount) {
|
|
179
696
|
// TODO exception
|
|
180
697
|
}
|
|
181
698
|
}
|
|
182
|
-
|
|
699
|
+
if (audioDataHolder.buffer) {
|
|
700
|
+
this.audioBuffer = audioDataHolder.buffer;
|
|
701
|
+
}
|
|
702
|
+
if (audioDataHolder.arrayBuffer) {
|
|
703
|
+
this.arrayAudioBuffer = audioDataHolder.arrayBuffer;
|
|
704
|
+
}
|
|
183
705
|
audioClip.addSelectionObserver((ac) => {
|
|
184
706
|
this._startSelectionAction.disabled = this.startSelectionDisabled();
|
|
185
707
|
if (!this.startSelectionAction.disabled && this._autoPlayOnSelectToggleAction.value) {
|
|
@@ -189,12 +711,14 @@ class AudioPlayer {
|
|
|
189
711
|
}
|
|
190
712
|
else {
|
|
191
713
|
this.audioBuffer = null;
|
|
714
|
+
//this.arrayAudioBuffer=null;
|
|
192
715
|
}
|
|
193
716
|
this._audioClip = audioClip;
|
|
194
717
|
}
|
|
195
718
|
set audioBuffer(audioBuffer) {
|
|
196
719
|
this.stop();
|
|
197
720
|
this._audioBuffer = audioBuffer;
|
|
721
|
+
this._arrayAudioBuffer = null;
|
|
198
722
|
if (audioBuffer && this.context) {
|
|
199
723
|
this._startAction.disabled = false;
|
|
200
724
|
this._startSelectionAction.disabled = this.startSelectionDisabled();
|
|
@@ -213,23 +737,86 @@ class AudioPlayer {
|
|
|
213
737
|
get audioBuffer() {
|
|
214
738
|
return this._audioBuffer;
|
|
215
739
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
740
|
+
set arrayAudioBuffer(arrayAudioBuffer) {
|
|
741
|
+
this.stop();
|
|
742
|
+
this._audioBuffer = null;
|
|
743
|
+
this._arrayAudioBuffer = arrayAudioBuffer;
|
|
744
|
+
if (arrayAudioBuffer && this.context) {
|
|
745
|
+
AudioSourceWorkletModuleLoader.loadModule(this.context).then(() => {
|
|
746
|
+
this._startAction.disabled = false;
|
|
747
|
+
this._startSelectionAction.disabled = this.startSelectionDisabled();
|
|
748
|
+
if (this.listener) {
|
|
749
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.READY));
|
|
750
|
+
}
|
|
751
|
+
}).catch((error) => {
|
|
752
|
+
this._startAction.disabled = true;
|
|
753
|
+
this._startSelectionAction.disabled = true;
|
|
754
|
+
if (this.listener) {
|
|
755
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.CLOSED));
|
|
756
|
+
}
|
|
757
|
+
console.error('Could not add module ' + error);
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
227
761
|
this._startAction.disabled = true;
|
|
228
762
|
this._startSelectionAction.disabled = true;
|
|
229
|
-
this._stopAction.disabled = false;
|
|
230
|
-
//this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
|
|
231
763
|
if (this.listener) {
|
|
232
|
-
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.
|
|
764
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.CLOSED));
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
get arrayAudioBuffer() {
|
|
769
|
+
return this._arrayAudioBuffer;
|
|
770
|
+
}
|
|
771
|
+
start() {
|
|
772
|
+
if (!this._startAction.disabled && !this.running) {
|
|
773
|
+
this.context.resume();
|
|
774
|
+
if (this._audioBuffer) {
|
|
775
|
+
this.sourceBufferNode = this.context.createBufferSource();
|
|
776
|
+
this.sourceBufferNode.buffer = this._audioBuffer;
|
|
777
|
+
this.sourceBufferNode.connect(this.context.destination);
|
|
778
|
+
this.sourceBufferNode.onended = () => this.onended();
|
|
779
|
+
this.playStartTime = this.context.currentTime;
|
|
780
|
+
this.running = true;
|
|
781
|
+
this.sourceBufferNode.start();
|
|
782
|
+
this.playStartTime = this.context.currentTime;
|
|
783
|
+
this._startAction.disabled = true;
|
|
784
|
+
this._startSelectionAction.disabled = true;
|
|
785
|
+
this._stopAction.disabled = false;
|
|
786
|
+
//this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
|
|
787
|
+
if (this.listener) {
|
|
788
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
else if (this._arrayAudioBuffer) {
|
|
792
|
+
if (this._arrayAudioBuffer) {
|
|
793
|
+
this.sourceAudioWorkletNode = new ArrayAudioBufferSourceNode(this.context);
|
|
794
|
+
this.sourceAudioWorkletNode.onprocessorerror = (ev) => {
|
|
795
|
+
let msg = 'Unknwon error';
|
|
796
|
+
if (ev instanceof ErrorEvent) {
|
|
797
|
+
msg = ev.message;
|
|
798
|
+
}
|
|
799
|
+
console.error("Audio source worklet error: " + msg);
|
|
800
|
+
if (this.listener) {
|
|
801
|
+
// TODO
|
|
802
|
+
// this.listener.error(msg);
|
|
803
|
+
// this.listener.audioPlayerUpdate(new AudioPlayerEvent());
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
this.sourceAudioWorkletNode.arrayAudioBuffer = this._arrayAudioBuffer;
|
|
807
|
+
this.sourceAudioWorkletNode.connect(this.context.destination); // this already starts playing
|
|
808
|
+
this.sourceAudioWorkletNode.onended = () => this.onended();
|
|
809
|
+
this.playStartTime = this.context.currentTime;
|
|
810
|
+
this.running = true;
|
|
811
|
+
this.sourceAudioWorkletNode.start();
|
|
812
|
+
this.playStartTime = this.context.currentTime;
|
|
813
|
+
this._startAction.disabled = true;
|
|
814
|
+
this._startSelectionAction.disabled = true;
|
|
815
|
+
this._stopAction.disabled = false;
|
|
816
|
+
if (this.listener) {
|
|
817
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
|
|
818
|
+
}
|
|
819
|
+
}
|
|
233
820
|
}
|
|
234
821
|
}
|
|
235
822
|
}
|
|
@@ -239,34 +826,79 @@ class AudioPlayer {
|
|
|
239
826
|
startSelected() {
|
|
240
827
|
if (!this._startAction.disabled && !this.running) {
|
|
241
828
|
this.context.resume();
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
this.listener
|
|
829
|
+
if (this._audioBuffer) {
|
|
830
|
+
this.sourceBufferNode = this.context.createBufferSource();
|
|
831
|
+
this.sourceBufferNode.buffer = this._audioBuffer;
|
|
832
|
+
this.sourceBufferNode.connect(this.context.destination);
|
|
833
|
+
this.sourceBufferNode.onended = () => this.onended();
|
|
834
|
+
this.playStartTime = this.context.currentTime;
|
|
835
|
+
this.running = true;
|
|
836
|
+
// unfortunately Web Audio API uses time values not frames
|
|
837
|
+
let ac = this._audioClip;
|
|
838
|
+
let offset = 0;
|
|
839
|
+
if (ac && ac.selection) {
|
|
840
|
+
let s = ac.selection;
|
|
841
|
+
let sr = ac.audioDataHolder.sampleRate;
|
|
842
|
+
offset = s.leftFrame / sr;
|
|
843
|
+
let stopPosInsecs = s.rightFrame / sr;
|
|
844
|
+
let dur = stopPosInsecs - offset;
|
|
845
|
+
// TODO check valid values
|
|
846
|
+
this.sourceBufferNode.start(0, offset, dur);
|
|
847
|
+
}
|
|
848
|
+
else {
|
|
849
|
+
this.sourceBufferNode.start();
|
|
850
|
+
}
|
|
851
|
+
this.playStartTime = this.context.currentTime - offset;
|
|
852
|
+
this._startAction.disabled = true;
|
|
853
|
+
this._startSelectionAction.disabled = true;
|
|
854
|
+
this._stopAction.disabled = false;
|
|
855
|
+
//this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
|
|
856
|
+
if (this.listener) {
|
|
857
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
else if (this._arrayAudioBuffer) {
|
|
861
|
+
if (this._arrayAudioBuffer) {
|
|
862
|
+
let aabsn = new ArrayAudioBufferSourceNode(this.context);
|
|
863
|
+
this.sourceAudioWorkletNode = aabsn;
|
|
864
|
+
aabsn.onprocessorerror = (ev) => {
|
|
865
|
+
let msg = 'Unknwon error';
|
|
866
|
+
if (ev instanceof ErrorEvent) {
|
|
867
|
+
msg = ev.message;
|
|
868
|
+
}
|
|
869
|
+
console.error("Audio source worklet error: " + msg);
|
|
870
|
+
if (this.listener) {
|
|
871
|
+
// TODO
|
|
872
|
+
// this.listener.error(msg);
|
|
873
|
+
// this.listener.audioPlayerUpdate(new AudioPlayerEvent());
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
aabsn.arrayAudioBuffer = this._arrayAudioBuffer;
|
|
877
|
+
aabsn.connect(this.context.destination); // this already starts playing
|
|
878
|
+
aabsn.onended = () => this.onended();
|
|
879
|
+
this.playStartTime = this.context.currentTime;
|
|
880
|
+
this.running = true;
|
|
881
|
+
let ac = this._audioClip;
|
|
882
|
+
let offset = 0;
|
|
883
|
+
if (ac && ac.selection) {
|
|
884
|
+
let s = ac.selection;
|
|
885
|
+
let sr = ac.audioDataHolder.sampleRate;
|
|
886
|
+
offset = s.leftFrame / sr;
|
|
887
|
+
let stopPosInsecs = s.rightFrame / sr;
|
|
888
|
+
let dur = stopPosInsecs - offset;
|
|
889
|
+
aabsn.start(0, offset, dur);
|
|
890
|
+
}
|
|
891
|
+
else {
|
|
892
|
+
aabsn.start();
|
|
893
|
+
}
|
|
894
|
+
this.playStartTime = this.context.currentTime - offset;
|
|
895
|
+
this._startAction.disabled = true;
|
|
896
|
+
this._startSelectionAction.disabled = true;
|
|
897
|
+
this._stopAction.disabled = false;
|
|
898
|
+
if (this.listener) {
|
|
899
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
|
|
900
|
+
}
|
|
901
|
+
}
|
|
270
902
|
}
|
|
271
903
|
}
|
|
272
904
|
}
|
|
@@ -275,6 +907,9 @@ class AudioPlayer {
|
|
|
275
907
|
if (this.sourceBufferNode) {
|
|
276
908
|
this.sourceBufferNode.stop();
|
|
277
909
|
}
|
|
910
|
+
if (this.sourceAudioWorkletNode) {
|
|
911
|
+
this.sourceAudioWorkletNode.stop();
|
|
912
|
+
}
|
|
278
913
|
if (this.timerVar !== null) {
|
|
279
914
|
window.clearInterval(this.timerVar);
|
|
280
915
|
}
|
|
@@ -288,7 +923,7 @@ class AudioPlayer {
|
|
|
288
923
|
if (this.timerVar != null) {
|
|
289
924
|
window.clearInterval(this.timerVar);
|
|
290
925
|
}
|
|
291
|
-
this._startAction.disabled = !(this.audioBuffer);
|
|
926
|
+
this._startAction.disabled = !(this.audioBuffer || this.arrayAudioBuffer);
|
|
292
927
|
this._startSelectionAction.disabled = this.startSelectionDisabled();
|
|
293
928
|
this._stopAction.disabled = true;
|
|
294
929
|
this.running = false;
|
|
@@ -305,10 +940,17 @@ class AudioPlayer {
|
|
|
305
940
|
}
|
|
306
941
|
get playPositionFrames() {
|
|
307
942
|
let ppFrs = null;
|
|
943
|
+
let sr = null;
|
|
308
944
|
if (this._audioBuffer) {
|
|
945
|
+
sr = this._audioBuffer.sampleRate;
|
|
946
|
+
}
|
|
947
|
+
else if (this._arrayAudioBuffer) {
|
|
948
|
+
sr = this._arrayAudioBuffer.sampleRate;
|
|
949
|
+
}
|
|
950
|
+
if (sr) {
|
|
309
951
|
let ppTime = this.playPositionTime;
|
|
310
952
|
if (ppTime !== null) {
|
|
311
|
-
ppFrs =
|
|
953
|
+
ppFrs = sr * ppTime;
|
|
312
954
|
}
|
|
313
955
|
}
|
|
314
956
|
return ppFrs;
|
|
@@ -522,6 +1164,107 @@ class ProjectUtil {
|
|
|
522
1164
|
}
|
|
523
1165
|
ProjectUtil.DEFAULT_AUDIO_CHANNEL_COUNT = 2;
|
|
524
1166
|
|
|
1167
|
+
class ArrayAudioBuffer {
|
|
1168
|
+
constructor(_channelCount, _sampleRate, _data) {
|
|
1169
|
+
this._channelCount = _channelCount;
|
|
1170
|
+
this._sampleRate = _sampleRate;
|
|
1171
|
+
this._data = _data;
|
|
1172
|
+
this._chunkCount = 0;
|
|
1173
|
+
this._frameLen = 0;
|
|
1174
|
+
if (this._data.length > 0) {
|
|
1175
|
+
let ch0Data = this.data[0];
|
|
1176
|
+
for (let ch0Chk of ch0Data) {
|
|
1177
|
+
this._chunkCount++;
|
|
1178
|
+
this._frameLen += ch0Chk.length;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
static fromAudioBuffer(audioBuffer, chunkFrameSize = 8192) {
|
|
1183
|
+
let aab;
|
|
1184
|
+
let chs = audioBuffer.numberOfChannels;
|
|
1185
|
+
let frameLength = audioBuffer.length;
|
|
1186
|
+
//let chunksSize=Math.ceil(frameLength/chunkFrameSize);
|
|
1187
|
+
let framePos = 0;
|
|
1188
|
+
let data = new Array(chs);
|
|
1189
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
1190
|
+
data[ch] = new Array();
|
|
1191
|
+
}
|
|
1192
|
+
let toCopy = frameLength - framePos;
|
|
1193
|
+
while (toCopy > 0) {
|
|
1194
|
+
let toCopyChunk = chunkFrameSize;
|
|
1195
|
+
if (toCopyChunk > toCopy) {
|
|
1196
|
+
// last chunk, the rest
|
|
1197
|
+
toCopyChunk = toCopy;
|
|
1198
|
+
}
|
|
1199
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
1200
|
+
data[ch].push(audioBuffer.getChannelData(ch).slice(framePos, framePos + toCopyChunk));
|
|
1201
|
+
}
|
|
1202
|
+
framePos += toCopyChunk;
|
|
1203
|
+
toCopy -= toCopyChunk;
|
|
1204
|
+
}
|
|
1205
|
+
aab = new ArrayAudioBuffer(chs, audioBuffer.sampleRate, data);
|
|
1206
|
+
return aab;
|
|
1207
|
+
}
|
|
1208
|
+
get channelCount() {
|
|
1209
|
+
return this._channelCount;
|
|
1210
|
+
}
|
|
1211
|
+
frames(framePos, frameLen, bufs) {
|
|
1212
|
+
let ccFramePos = 0;
|
|
1213
|
+
let trgFramePos = framePos;
|
|
1214
|
+
let ch0Data = this.data[0];
|
|
1215
|
+
let cPos = 0;
|
|
1216
|
+
let filled = 0;
|
|
1217
|
+
let ci = 0;
|
|
1218
|
+
while (filled < frameLen && ci < this._chunkCount) {
|
|
1219
|
+
// Current chunk
|
|
1220
|
+
let cc0 = ch0Data[ci];
|
|
1221
|
+
let ccLen = cc0.length;
|
|
1222
|
+
let ccFrameEndPos = ccFramePos + ccLen;
|
|
1223
|
+
if (trgFramePos >= ccFramePos && trgFramePos < ccFrameEndPos) {
|
|
1224
|
+
let toCp = frameLen - filled;
|
|
1225
|
+
cPos = trgFramePos - ccFramePos;
|
|
1226
|
+
if (cPos + toCp > ccLen) {
|
|
1227
|
+
toCp = ccLen - cPos;
|
|
1228
|
+
}
|
|
1229
|
+
for (let ch = 0; ch < bufs.length; ch++) {
|
|
1230
|
+
let cc = this.data[ch][ci];
|
|
1231
|
+
for (let i = 0; i < toCp; i++) {
|
|
1232
|
+
bufs[ch][filled + i] = cc[cPos + i];
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
filled += toCp;
|
|
1236
|
+
trgFramePos += toCp;
|
|
1237
|
+
cPos += toCp;
|
|
1238
|
+
ccFramePos += toCp;
|
|
1239
|
+
if (cPos >= ccLen) {
|
|
1240
|
+
ccFramePos = ccFrameEndPos;
|
|
1241
|
+
cPos = 0;
|
|
1242
|
+
ci++;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
else {
|
|
1246
|
+
// next chunk
|
|
1247
|
+
ccFramePos = ccFrameEndPos;
|
|
1248
|
+
cPos = 0;
|
|
1249
|
+
ci++;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
return filled;
|
|
1253
|
+
}
|
|
1254
|
+
get sampleRate() {
|
|
1255
|
+
return this._sampleRate;
|
|
1256
|
+
}
|
|
1257
|
+
get frameLen() {
|
|
1258
|
+
return this._frameLen;
|
|
1259
|
+
}
|
|
1260
|
+
get chunkCount() {
|
|
1261
|
+
return this._chunkCount;
|
|
1262
|
+
}
|
|
1263
|
+
get data() {
|
|
1264
|
+
return this._data;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
525
1268
|
const CHROME_ACTIVATE_ECHO_CANCELLATION_WITH_AGC = false;
|
|
526
1269
|
const DEBUG_TRACE_LEVEL = 0;
|
|
527
1270
|
const ENABLE_AUDIO_WORKLET = true;
|
|
@@ -1092,6 +1835,10 @@ class AudioCapture {
|
|
|
1092
1835
|
}
|
|
1093
1836
|
return ab;
|
|
1094
1837
|
}
|
|
1838
|
+
audioBufferArray() {
|
|
1839
|
+
let aba = new ArrayAudioBuffer(this.channelCount, this.currentSampleRate, this.data);
|
|
1840
|
+
return aba;
|
|
1841
|
+
}
|
|
1095
1842
|
}
|
|
1096
1843
|
AudioCapture.BUFFER_SIZE = 8192;
|
|
1097
1844
|
|
|
@@ -1471,21 +2218,28 @@ class RecordingFileDescriptorImpl {
|
|
|
1471
2218
|
constructor() { }
|
|
1472
2219
|
}
|
|
1473
2220
|
class RecordingFile {
|
|
1474
|
-
constructor(uuid, sessionId,
|
|
2221
|
+
constructor(uuid, sessionId, audioDataHolder) {
|
|
1475
2222
|
this.recordingFileId = null;
|
|
1476
2223
|
this.uuid = null;
|
|
2224
|
+
this.serverPersisted = false;
|
|
2225
|
+
this.keepAudioDataCache = false;
|
|
1477
2226
|
this.date = null;
|
|
1478
2227
|
this._dateAsDateObj = null;
|
|
1479
2228
|
this.startedDate = null;
|
|
1480
2229
|
this._startedAsDateObj = null;
|
|
1481
|
-
this.
|
|
2230
|
+
this.audioDataHolder = null;
|
|
1482
2231
|
this.session = null;
|
|
1483
2232
|
this.frames = null;
|
|
2233
|
+
this.timeLength = null;
|
|
1484
2234
|
this.editSampleRate = null;
|
|
1485
2235
|
this.editStartFrame = null;
|
|
1486
2236
|
this.editEndFrame = null;
|
|
1487
2237
|
this.session = sessionId;
|
|
1488
|
-
this.
|
|
2238
|
+
this.audioDataHolder = audioDataHolder;
|
|
2239
|
+
if (audioDataHolder) {
|
|
2240
|
+
this.frames = audioDataHolder.frameLen;
|
|
2241
|
+
this.timeLength = audioDataHolder.duration;
|
|
2242
|
+
}
|
|
1489
2243
|
this.uuid = uuid;
|
|
1490
2244
|
}
|
|
1491
2245
|
filenameString() {
|
|
@@ -1497,14 +2251,27 @@ class RecordingFile {
|
|
|
1497
2251
|
fns += this.uuid;
|
|
1498
2252
|
return fns;
|
|
1499
2253
|
}
|
|
2254
|
+
equals(otherRecordingFile) {
|
|
2255
|
+
if (otherRecordingFile !== null) {
|
|
2256
|
+
if (otherRecordingFile === this) {
|
|
2257
|
+
return true;
|
|
2258
|
+
}
|
|
2259
|
+
if (otherRecordingFile.uuid === this.uuid) {
|
|
2260
|
+
return true;
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
return false;
|
|
2264
|
+
}
|
|
2265
|
+
toString() {
|
|
2266
|
+
return 'Recording file: UUID: ' + this.uuid + ', session: ' + this.session;
|
|
2267
|
+
}
|
|
1500
2268
|
}
|
|
1501
2269
|
class SprRecordingFile extends RecordingFile {
|
|
1502
|
-
constructor(sessionId, itemcode, version,
|
|
1503
|
-
super(UUID.generate(), sessionId,
|
|
2270
|
+
constructor(sessionId, itemcode, version, audioDataHolder) {
|
|
2271
|
+
super(UUID.generate(), sessionId, audioDataHolder);
|
|
1504
2272
|
this.session = sessionId;
|
|
1505
2273
|
this.itemCode = itemcode;
|
|
1506
2274
|
this.version = version;
|
|
1507
|
-
this.audioBuffer = audioBuffer;
|
|
1508
2275
|
}
|
|
1509
2276
|
filenameString() {
|
|
1510
2277
|
let fns = '';
|
|
@@ -1519,6 +2286,31 @@ class SprRecordingFile extends RecordingFile {
|
|
|
1519
2286
|
fns += this.uuid;
|
|
1520
2287
|
return fns;
|
|
1521
2288
|
}
|
|
2289
|
+
toString() {
|
|
2290
|
+
return 'Recording file: UUID: ' + this.uuid + ', session: ' + this.session + ', itemcode: ' + this.itemCode + ', version: ' + this.version + ', UUID: ' + this.uuid;
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
class RecordingFileUtils {
|
|
2294
|
+
static setAudioData(rf, audioDataHolder) {
|
|
2295
|
+
rf.audioDataHolder = audioDataHolder;
|
|
2296
|
+
if (audioDataHolder) {
|
|
2297
|
+
rf.frames = audioDataHolder.frameLen;
|
|
2298
|
+
rf.timeLength = audioDataHolder.duration;
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
static sampleCount(rf) {
|
|
2302
|
+
if (rf.audioDataHolder) {
|
|
2303
|
+
return rf.audioDataHolder.sampleCounts();
|
|
2304
|
+
}
|
|
2305
|
+
else {
|
|
2306
|
+
return 0;
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
static expireAudioData(rf) {
|
|
2310
|
+
let rv = rf.audioDataHolder;
|
|
2311
|
+
rf.audioDataHolder = null;
|
|
2312
|
+
return rv;
|
|
2313
|
+
}
|
|
1522
2314
|
}
|
|
1523
2315
|
|
|
1524
2316
|
// state of an upload
|
|
@@ -1569,7 +2361,8 @@ class UploaderStatusChangeEvent {
|
|
|
1569
2361
|
}
|
|
1570
2362
|
}
|
|
1571
2363
|
class Upload {
|
|
1572
|
-
constructor(blob, url) {
|
|
2364
|
+
constructor(blob, url, serverPersistable = null) {
|
|
2365
|
+
this.serverPersistable = serverPersistable;
|
|
1573
2366
|
this.toString = () => {
|
|
1574
2367
|
let s = `Upload: Status: ${this.status}, URL: ${this._url}`;
|
|
1575
2368
|
if (this._data instanceof Blob) {
|
|
@@ -1590,6 +2383,17 @@ class Upload {
|
|
|
1590
2383
|
get data() {
|
|
1591
2384
|
return this._data;
|
|
1592
2385
|
}
|
|
2386
|
+
done() {
|
|
2387
|
+
this.status = UploadStatus$1.DONE;
|
|
2388
|
+
//console.debug("Single upload done.");
|
|
2389
|
+
if (this.serverPersistable) {
|
|
2390
|
+
this.serverPersistable.serverPersisted = true;
|
|
2391
|
+
//console.debug("Single upload set server persisted: "+this.serverPersistable);
|
|
2392
|
+
}
|
|
2393
|
+
else {
|
|
2394
|
+
//console.debug("Server persistable not set.");
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
1593
2397
|
}
|
|
1594
2398
|
class Uploader {
|
|
1595
2399
|
constructor(http, withCredentials = false) {
|
|
@@ -1628,7 +2432,7 @@ class Uploader {
|
|
|
1628
2432
|
return si;
|
|
1629
2433
|
}
|
|
1630
2434
|
uploadDone(ul) {
|
|
1631
|
-
ul.
|
|
2435
|
+
ul.done();
|
|
1632
2436
|
// remove upload from queue
|
|
1633
2437
|
for (let i = 0; i < this.que.length; i++) {
|
|
1634
2438
|
if (this.que[i] === ul) {
|
|
@@ -2059,15 +2863,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
2059
2863
|
}], ctorParameters: function () { return []; } });
|
|
2060
2864
|
|
|
2061
2865
|
class AudioClip {
|
|
2062
|
-
constructor(
|
|
2866
|
+
constructor(_audioDataHolder) {
|
|
2867
|
+
this._audioDataHolder = _audioDataHolder;
|
|
2063
2868
|
this._selection = null;
|
|
2869
|
+
this._levelInfos = null;
|
|
2064
2870
|
this.selectionObservers = new Array();
|
|
2065
|
-
this._buffer = buffer;
|
|
2066
2871
|
}
|
|
2067
|
-
get
|
|
2068
|
-
return this.
|
|
2872
|
+
get audioDataHolder() {
|
|
2873
|
+
return this._audioDataHolder;
|
|
2069
2874
|
}
|
|
2070
|
-
;
|
|
2071
2875
|
get selection() {
|
|
2072
2876
|
return this._selection;
|
|
2073
2877
|
}
|
|
@@ -2082,6 +2886,12 @@ class AudioClip {
|
|
|
2082
2886
|
selObs(this);
|
|
2083
2887
|
}
|
|
2084
2888
|
}
|
|
2889
|
+
get levelInfos() {
|
|
2890
|
+
return this._levelInfos;
|
|
2891
|
+
}
|
|
2892
|
+
set levelInfos(value) {
|
|
2893
|
+
this._levelInfos = value;
|
|
2894
|
+
}
|
|
2085
2895
|
addSelectionObserver(selectionObserver, init = false) {
|
|
2086
2896
|
let obsAlreadyInList = this.selectionObservers.find((obs) => (obs === selectionObserver));
|
|
2087
2897
|
if (!obsAlreadyInList) {
|
|
@@ -2164,19 +2974,31 @@ class ViewSelection {
|
|
|
2164
2974
|
class BasicAudioCanvasLayerComponent extends CanvasLayerComponent {
|
|
2165
2975
|
constructor() {
|
|
2166
2976
|
super(...arguments);
|
|
2167
|
-
|
|
2977
|
+
//protected _audioData: AudioBuffer|null=null;
|
|
2978
|
+
//protected _arrayAudioData: ArrayAudioBuffer|null=null;
|
|
2979
|
+
this._audioDataHolder = null;
|
|
2168
2980
|
this._bgColor = 'white';
|
|
2169
2981
|
this._selectColor = 'rgba(0%,0%,100%,25%)';
|
|
2170
2982
|
}
|
|
2983
|
+
frameLength() {
|
|
2984
|
+
let frameLength = null;
|
|
2985
|
+
// if (this._audioData && this._audioData.numberOfChannels > 0) {
|
|
2986
|
+
// let ch0 = this._audioData.getChannelData(0);
|
|
2987
|
+
// frameLength = ch0.length;
|
|
2988
|
+
//
|
|
2989
|
+
// }else if(this._arrayAudioData){
|
|
2990
|
+
// frameLength=this._arrayAudioData.frameLen;
|
|
2991
|
+
// }
|
|
2992
|
+
return frameLength;
|
|
2993
|
+
}
|
|
2171
2994
|
/**
|
|
2172
2995
|
* Returns pixel position depending on current zoom setting.
|
|
2173
2996
|
* @param framePos audio frame (sample) position
|
|
2174
2997
|
*/
|
|
2175
2998
|
frameToXPixelPosition(framePos) {
|
|
2176
2999
|
let pixelPos = null;
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
let frameLength = ch0.length;
|
|
3000
|
+
let frameLength = this._audioDataHolder?.frameLen;
|
|
3001
|
+
if (frameLength !== undefined) {
|
|
2180
3002
|
let vw;
|
|
2181
3003
|
if (this.bounds) {
|
|
2182
3004
|
vw = this.bounds.dimension.width;
|
|
@@ -2206,9 +3028,8 @@ class BasicAudioCanvasLayerComponent extends CanvasLayerComponent {
|
|
|
2206
3028
|
}
|
|
2207
3029
|
viewPortXPixelToFramePosition(xViewPortPixelPos) {
|
|
2208
3030
|
let vpXramePos = null;
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
let frameLength = ch0.length;
|
|
3031
|
+
let frameLength = this._audioDataHolder?.frameLen;
|
|
3032
|
+
if (frameLength !== undefined) {
|
|
2212
3033
|
let vw;
|
|
2213
3034
|
if (this.bounds) {
|
|
2214
3035
|
vw = this.bounds.dimension.width;
|
|
@@ -2385,8 +3206,8 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
|
|
|
2385
3206
|
if (viewSel) {
|
|
2386
3207
|
let frameStart = this.viewPortXPixelToFramePosition(viewSel.startX);
|
|
2387
3208
|
let frameEnd = this.viewPortXPixelToFramePosition(viewSel.endX);
|
|
2388
|
-
if (this.
|
|
2389
|
-
ns = new Selection(this.
|
|
3209
|
+
if (this._audioDataHolder && frameStart != null && frameEnd != null) {
|
|
3210
|
+
ns = new Selection(this._audioDataHolder.sampleRate, frameStart, frameEnd);
|
|
2390
3211
|
}
|
|
2391
3212
|
}
|
|
2392
3213
|
this.selectingEventEmitter.emit(ns);
|
|
@@ -2396,8 +3217,8 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
|
|
|
2396
3217
|
if (viewSel) {
|
|
2397
3218
|
let frameStart = this.viewPortXPixelToFramePosition(viewSel.startX);
|
|
2398
3219
|
let frameEnd = this.viewPortXPixelToFramePosition(viewSel.endX);
|
|
2399
|
-
if (this.
|
|
2400
|
-
ns = new Selection(this.
|
|
3220
|
+
if (this._audioDataHolder && frameStart != null && frameEnd != null) {
|
|
3221
|
+
ns = new Selection(this._audioDataHolder.sampleRate, frameStart, frameEnd);
|
|
2401
3222
|
}
|
|
2402
3223
|
}
|
|
2403
3224
|
this.selectedEventEmitter.emit(ns);
|
|
@@ -2478,7 +3299,7 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
|
|
|
2478
3299
|
g.lineTo(xViewPortPixelpos, h);
|
|
2479
3300
|
g.closePath();
|
|
2480
3301
|
g.stroke();
|
|
2481
|
-
if (this.
|
|
3302
|
+
if (this._audioDataHolder) {
|
|
2482
3303
|
g.font = '14px sans-serif';
|
|
2483
3304
|
g.fillStyle = 'yellow';
|
|
2484
3305
|
g.fillText(framePos.toString(), xViewPortPixelpos + 2, 50);
|
|
@@ -2490,6 +3311,7 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
|
|
|
2490
3311
|
}
|
|
2491
3312
|
}
|
|
2492
3313
|
}
|
|
3314
|
+
AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD = 10 * 60 * 48000; // Use streaming/chunking if audio clip has more than this number of samples
|
|
2493
3315
|
AudioCanvasLayerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioCanvasLayerComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
2494
3316
|
AudioCanvasLayerComponent.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.11", type: AudioCanvasLayerComponent, inputs: { pointerPosition: "pointerPosition", selecting: "selecting", selection: "selection" }, outputs: { pointerPositionEventEmitter: "pointerPositionEventEmitter", selectingEventEmitter: "selectingEventEmitter", selectedEventEmitter: "selectedEventEmitter" }, host: { listeners: { "document:mouseup": "onMouseup($event)" } }, viewQueries: [{ propertyName: "bgCanvasRef", first: true, predicate: ["bg"], descendants: true, static: true }, { propertyName: "cursorCanvasRef", first: true, predicate: ["cursor"], descendants: true, static: true }], usesInheritance: true, ngImport: i0 });
|
|
2495
3317
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioCanvasLayerComponent, decorators: [{
|
|
@@ -2524,7 +3346,7 @@ class AudioSignal extends AudioCanvasLayerComponent {
|
|
|
2524
3346
|
this._playFramePosition = null;
|
|
2525
3347
|
this.worker = null;
|
|
2526
3348
|
this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
|
|
2527
|
-
this.
|
|
3349
|
+
this._audioDataHolder = null;
|
|
2528
3350
|
this._bgColor = 'black';
|
|
2529
3351
|
this._selectColor = 'rgba(255,255,0,0.8)';
|
|
2530
3352
|
}
|
|
@@ -2577,25 +3399,26 @@ class AudioSignal extends AudioCanvasLayerComponent {
|
|
|
2577
3399
|
*/
|
|
2578
3400
|
workerFunction() {
|
|
2579
3401
|
addEventListener('message', ({ data }) => {
|
|
2580
|
-
let audioData = data.audioData;
|
|
2581
|
-
let
|
|
2582
|
-
let
|
|
2583
|
-
let
|
|
2584
|
-
let vw = data.vw;
|
|
2585
|
-
let chs = data.chs;
|
|
2586
|
-
let
|
|
3402
|
+
let audioData = data.audioData; // audio data part required to render view port
|
|
3403
|
+
let auOffset = data.audioDataOffset;
|
|
3404
|
+
let l = data.l; // left pixel position of view port
|
|
3405
|
+
let w = data.w; // width of viewport
|
|
3406
|
+
let vw = data.vw; // total width of (virtual) audio view (not viewport width)
|
|
3407
|
+
let chs = data.chs; // number of channels
|
|
3408
|
+
let dataFrameLength = data.audioDataFrameLength; // frame length of audio data part required for view port
|
|
3409
|
+
let frameLength = data.frameLength; // total frame length (of audio clip)
|
|
3410
|
+
//console.debug("W: left: "+l+", w:"+w+", vw: "+vw+", chs: "+chs+", frameLength: "+frameLength);
|
|
2587
3411
|
let psMinMax = new Float32Array(0);
|
|
2588
|
-
if (audioData && w >= 0 && vw > 0) {
|
|
3412
|
+
if (audioData && audioData.length > 0 && w >= 0 && vw > 0) {
|
|
2589
3413
|
let framesPerPixel = frameLength / vw;
|
|
2590
|
-
let y = 0;
|
|
2591
3414
|
let pointsLen = w * chs;
|
|
2592
3415
|
// one for min one for max
|
|
2593
3416
|
let arrLen = pointsLen * 2;
|
|
2594
3417
|
psMinMax = new Float32Array(arrLen);
|
|
2595
3418
|
let chFramePos = 0;
|
|
3419
|
+
let chFrameLength = audioData.length / chs;
|
|
2596
3420
|
for (let ch = 0; ch < chs; ch++) {
|
|
2597
|
-
|
|
2598
|
-
chFramePos = ch * frameLength;
|
|
3421
|
+
chFramePos = ch * chFrameLength;
|
|
2599
3422
|
for (let pii = 0; pii < w; pii++) {
|
|
2600
3423
|
let virtPii = l + pii;
|
|
2601
3424
|
let pMin = Infinity;
|
|
@@ -2605,24 +3428,28 @@ class AudioSignal extends AudioCanvasLayerComponent {
|
|
|
2605
3428
|
for (let ai = 0; ai < framesPerPixel; ai++) {
|
|
2606
3429
|
let framePos = pixelFramePos + ai;
|
|
2607
3430
|
let a = 0;
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
3431
|
+
let bufPos = framePos - auOffset;
|
|
3432
|
+
//let bufPos=framePos;
|
|
3433
|
+
if (bufPos >= 0 && bufPos < audioData.length) {
|
|
3434
|
+
a = audioData[bufPos];
|
|
3435
|
+
//console.debug("W: ch: "+ch+", pixelFramePos: "+pixelFramePos+", framePos: "+framePos+", auOffset: "+auOffset+", bufPos: "+bufPos+", audioData.length: "+audioData.length+", a: "+a);
|
|
3436
|
+
if (a < pMin) {
|
|
3437
|
+
pMin = a;
|
|
3438
|
+
}
|
|
3439
|
+
if (a > pMax) {
|
|
3440
|
+
pMax = a;
|
|
3441
|
+
}
|
|
2616
3442
|
}
|
|
2617
3443
|
}
|
|
2618
3444
|
let psMinPos = ch * w + pii;
|
|
2619
3445
|
psMinMax[psMinPos] = pMin;
|
|
2620
3446
|
let psMaxPos = pointsLen + psMinPos;
|
|
2621
3447
|
psMinMax[psMaxPos] = pMax;
|
|
3448
|
+
//console.debug("psMinMax["+psMinPos+"]="+pMin+",psMinMax["+psMaxPos+"]="+pMax);
|
|
2622
3449
|
}
|
|
2623
3450
|
}
|
|
2624
3451
|
}
|
|
2625
|
-
postMessage({ psMinMax: psMinMax, l: data.l,
|
|
3452
|
+
postMessage({ psMinMax: psMinMax, l: data.l, w: data.w, chs: data.chs, eod: data.eod }, [psMinMax.buffer]);
|
|
2626
3453
|
});
|
|
2627
3454
|
}
|
|
2628
3455
|
startDraw(clear = true) {
|
|
@@ -2650,41 +3477,143 @@ class AudioSignal extends AudioCanvasLayerComponent {
|
|
|
2650
3477
|
if (this.bounds && this.bounds.dimension) {
|
|
2651
3478
|
let w = Math.round(this.bounds.dimension.width);
|
|
2652
3479
|
let h = Math.round(this.bounds.dimension.height);
|
|
2653
|
-
if (this.
|
|
3480
|
+
if (this._audioDataHolder && w > 0 && h > 0) {
|
|
2654
3481
|
//this.wo = new Worker('./audiosignal.worker.js',{type: 'module'});
|
|
2655
3482
|
this.worker = new Worker(this.workerURL);
|
|
2656
3483
|
//this.wo = new Worker('worker/audiosignal.worker.ts');
|
|
2657
3484
|
//let Worker = require('worker!../../../workers/uploader/main');
|
|
2658
|
-
let chs = this.
|
|
2659
|
-
let
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
let
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
//let
|
|
3485
|
+
let chs = this._audioDataHolder.numberOfChannels;
|
|
3486
|
+
let leftPos = Math.round(this.bounds.position.left);
|
|
3487
|
+
let renderPos = leftPos;
|
|
3488
|
+
let vw = Math.round(this.virtualDimension.width);
|
|
3489
|
+
let frameLength = this._audioDataHolder.frameLen;
|
|
3490
|
+
let framesPerPixel = Math.ceil(frameLength / vw);
|
|
3491
|
+
let audioBuffer = this._audioDataHolder.buffer;
|
|
3492
|
+
//let arrayAudioBuffer=this._audioDataHolder.arrayBuffer;
|
|
3493
|
+
let arrAbBuf;
|
|
3494
|
+
//let ais:ArrayAudioBufferInputStream|null=null;
|
|
3495
|
+
//let aisBuf:Float32Array[]|null=null;
|
|
3496
|
+
let psMinMax = null;
|
|
2668
3497
|
if (this.worker) {
|
|
2669
3498
|
this.worker.onmessage = (me) => {
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
3499
|
+
if (me.data.eod === true) {
|
|
3500
|
+
let psMinMaxTmp;
|
|
3501
|
+
if (psMinMax) {
|
|
3502
|
+
psMinMaxTmp = psMinMax;
|
|
3503
|
+
}
|
|
3504
|
+
else {
|
|
3505
|
+
psMinMaxTmp = me.data.psMinMax;
|
|
3506
|
+
}
|
|
3507
|
+
this.drawRendered(leftPos, w, h, chs, psMinMaxTmp);
|
|
3508
|
+
if (this.worker) {
|
|
3509
|
+
this.worker.terminate();
|
|
3510
|
+
}
|
|
3511
|
+
this.worker = null;
|
|
3512
|
+
}
|
|
3513
|
+
else if (this._audioDataHolder && arrAbBuf) {
|
|
3514
|
+
let rw = me.data.w;
|
|
3515
|
+
let rPointsLen = chs * rw;
|
|
3516
|
+
let pointsLen = chs * w;
|
|
3517
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
3518
|
+
if (psMinMax) {
|
|
3519
|
+
let rBasePos = ch * rw;
|
|
3520
|
+
let basePos = ch * w;
|
|
3521
|
+
let rPosMin = rBasePos;
|
|
3522
|
+
let rPosMax = rPointsLen + rPosMin;
|
|
3523
|
+
let posMin = basePos + (renderPos - leftPos);
|
|
3524
|
+
let posMax = pointsLen + posMin;
|
|
3525
|
+
psMinMax[posMin] = me.data.psMinMax[rPosMin];
|
|
3526
|
+
//console.debug('Min: ('+pos+'): '+me.data.psMinMax[0]);
|
|
3527
|
+
psMinMax[posMax] = me.data.psMinMax[rPosMax];
|
|
3528
|
+
// console.debug('Max: ('+(pointsLen+pos)+'): '+me.data.psMinMax[1]);
|
|
3529
|
+
//console.debug("psMinMax["+posMin+"]="+me.data.psMinMax[rPosMin]+" (rPosMin="+rPosMin+"),psMinMax["+posMax+"]="+me.data.psMinMax[rPosMax]);
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
let eod = false;
|
|
3533
|
+
renderPos++;
|
|
3534
|
+
let ad;
|
|
3535
|
+
let leftFramePos = Math.floor(frameLength * renderPos / vw);
|
|
3536
|
+
if (renderPos < leftPos + w) {
|
|
3537
|
+
let read = this._audioDataHolder.frames(leftFramePos, framesPerPixel, arrAbBuf);
|
|
3538
|
+
//console.debug("First read frame: "+arrAbBuf[0][0]);
|
|
3539
|
+
ad = new Float32Array(chs * framesPerPixel);
|
|
3540
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
3541
|
+
ad.set(arrAbBuf[ch], ch * framesPerPixel);
|
|
3542
|
+
}
|
|
3543
|
+
eod = (read <= 0);
|
|
3544
|
+
}
|
|
3545
|
+
else {
|
|
3546
|
+
ad = new Float32Array();
|
|
3547
|
+
eod = true;
|
|
3548
|
+
}
|
|
3549
|
+
let adBuf = ad.buffer;
|
|
3550
|
+
if (this.worker) {
|
|
3551
|
+
this.worker.postMessage({
|
|
3552
|
+
l: renderPos,
|
|
3553
|
+
w: me.data.w,
|
|
3554
|
+
h: h,
|
|
3555
|
+
vw: vw,
|
|
3556
|
+
chs: chs,
|
|
3557
|
+
frameLength: frameLength,
|
|
3558
|
+
audioData: ad,
|
|
3559
|
+
audioDataOffset: leftFramePos,
|
|
3560
|
+
eod: eod
|
|
3561
|
+
}, [adBuf]);
|
|
3562
|
+
}
|
|
2674
3563
|
}
|
|
2675
|
-
this.worker = null;
|
|
2676
3564
|
};
|
|
2677
3565
|
}
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
3566
|
+
if (audioBuffer && audioBuffer.length * audioBuffer.numberOfChannels < AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD) {
|
|
3567
|
+
// Render whole clip at once
|
|
3568
|
+
arrAbBuf = null;
|
|
3569
|
+
psMinMax = null;
|
|
3570
|
+
let ad = new Float32Array(chs * frameLength);
|
|
3571
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
3572
|
+
ad.set(audioBuffer.getChannelData(ch), ch * frameLength);
|
|
3573
|
+
}
|
|
3574
|
+
this.worker.postMessage({
|
|
3575
|
+
l: leftPos,
|
|
3576
|
+
w: w,
|
|
3577
|
+
vw: vw,
|
|
3578
|
+
chs: chs,
|
|
3579
|
+
frameLength: frameLength,
|
|
3580
|
+
audioData: ad,
|
|
3581
|
+
audioDataOffset: 0,
|
|
3582
|
+
eod: true
|
|
3583
|
+
}, [ad.buffer]);
|
|
3584
|
+
}
|
|
3585
|
+
else {
|
|
3586
|
+
// Render pixel by pixel
|
|
3587
|
+
if (w > 0) {
|
|
3588
|
+
if (framesPerPixel > 0) {
|
|
3589
|
+
let rw = 1;
|
|
3590
|
+
//ais = new ArrayAudioBufferInputStream(arrayAudioBuffer);
|
|
3591
|
+
arrAbBuf = new Array(chs);
|
|
3592
|
+
psMinMax = new Float32Array(chs * w * 2);
|
|
3593
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
3594
|
+
arrAbBuf[ch] = new Float32Array(framesPerPixel);
|
|
3595
|
+
}
|
|
3596
|
+
let leftFramePos = Math.floor(frameLength * renderPos / vw);
|
|
3597
|
+
let auOffset = leftFramePos; // should always be 0
|
|
3598
|
+
//let read=arrayAudioBuffer.frames(leftFramePos,framesPerPixel,arrAbBuf);
|
|
3599
|
+
let read = this._audioDataHolder.frames(leftFramePos, framesPerPixel, arrAbBuf);
|
|
3600
|
+
let ad = new Float32Array(chs * framesPerPixel);
|
|
3601
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
3602
|
+
ad.set(arrAbBuf[ch], ch * framesPerPixel);
|
|
3603
|
+
}
|
|
3604
|
+
this.worker.postMessage({
|
|
3605
|
+
l: renderPos,
|
|
3606
|
+
w: rw,
|
|
3607
|
+
vw: vw,
|
|
3608
|
+
chs: chs,
|
|
3609
|
+
frameLength: frameLength,
|
|
3610
|
+
audioData: ad,
|
|
3611
|
+
audioDataOffset: auOffset,
|
|
3612
|
+
eod: (read <= 0)
|
|
3613
|
+
}, [ad.buffer]);
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
2688
3617
|
}
|
|
2689
3618
|
else {
|
|
2690
3619
|
let g = this.signalCanvas.getContext("2d");
|
|
@@ -2694,42 +3623,42 @@ class AudioSignal extends AudioCanvasLayerComponent {
|
|
|
2694
3623
|
}
|
|
2695
3624
|
}
|
|
2696
3625
|
}
|
|
2697
|
-
drawRendered(
|
|
3626
|
+
drawRendered(left, w, h, chs, psMinMax) {
|
|
2698
3627
|
this.drawBg();
|
|
2699
|
-
this.signalCanvas.style.left =
|
|
2700
|
-
this.signalCanvas.width =
|
|
2701
|
-
this.signalCanvas.height =
|
|
3628
|
+
this.signalCanvas.style.left = left.toString() + 'px';
|
|
3629
|
+
this.signalCanvas.width = w;
|
|
3630
|
+
this.signalCanvas.height = h;
|
|
2702
3631
|
let g = this.signalCanvas.getContext("2d");
|
|
2703
3632
|
if (g) {
|
|
2704
|
-
g.clearRect(0, 0,
|
|
3633
|
+
g.clearRect(0, 0, w, h);
|
|
2705
3634
|
//g.fillStyle = "black";
|
|
2706
3635
|
//g.fillRect(0, 0, me.data.w, me.data.h);
|
|
2707
|
-
let pointsLen =
|
|
3636
|
+
let pointsLen = w * chs;
|
|
2708
3637
|
// one for min one for max
|
|
2709
3638
|
let arrLen = pointsLen * 2;
|
|
2710
|
-
if (this.
|
|
3639
|
+
if (this._audioDataHolder) {
|
|
2711
3640
|
let std = Date.now();
|
|
2712
|
-
let chH =
|
|
3641
|
+
let chH = h / chs;
|
|
2713
3642
|
let y = 0;
|
|
2714
|
-
for (let ch = 0; ch <
|
|
3643
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
2715
3644
|
let x = 0;
|
|
2716
|
-
let psMinPos = ch *
|
|
3645
|
+
let psMinPos = ch * w;
|
|
2717
3646
|
let psMaxPos = pointsLen + psMinPos;
|
|
2718
3647
|
g.fillStyle = 'green';
|
|
2719
3648
|
g.strokeStyle = 'green';
|
|
2720
3649
|
// draw audio signal as single polygon
|
|
2721
3650
|
g.beginPath();
|
|
2722
|
-
g.moveTo(0, y + (chH / 2) +
|
|
2723
|
-
for (let pii = 0; pii <
|
|
2724
|
-
let psMax =
|
|
3651
|
+
g.moveTo(0, y + (chH / 2) + psMinMax[psMaxPos] * chH / 2);
|
|
3652
|
+
for (let pii = 0; pii < w; pii++) {
|
|
3653
|
+
let psMax = psMinMax[psMaxPos + pii];
|
|
2725
3654
|
let pv = psMax * chH / 2;
|
|
2726
3655
|
let yd = y + (chH / 2) - pv;
|
|
2727
3656
|
//console.log("LineTo: : "+pii+" "+yd)
|
|
2728
3657
|
g.lineTo(pii, yd);
|
|
2729
3658
|
}
|
|
2730
|
-
let revPixelStart =
|
|
3659
|
+
let revPixelStart = w - 1;
|
|
2731
3660
|
for (let pii = revPixelStart; pii >= 0; pii--) {
|
|
2732
|
-
let psMin =
|
|
3661
|
+
let psMin = psMinMax[psMinPos + pii];
|
|
2733
3662
|
let pv = psMin * chH / 2;
|
|
2734
3663
|
let yd = y + (chH / 2) - pv;
|
|
2735
3664
|
//console.log("LineTo: : "+pii+" "+yd)
|
|
@@ -2753,13 +3682,23 @@ class AudioSignal extends AudioCanvasLayerComponent {
|
|
|
2753
3682
|
g.clearRect(0, 0, w, h);
|
|
2754
3683
|
//g.fillStyle = "black";
|
|
2755
3684
|
//g.fillRect(0, 0, w, h);
|
|
2756
|
-
if (this.
|
|
3685
|
+
if (this._audioDataHolder) {
|
|
2757
3686
|
let std = Date.now();
|
|
2758
|
-
let chs = this.
|
|
3687
|
+
let chs = this._audioDataHolder.numberOfChannels;
|
|
2759
3688
|
let chH = h / chs;
|
|
2760
|
-
let frameLength = this.
|
|
3689
|
+
let frameLength = this._audioDataHolder.frameLen;
|
|
2761
3690
|
let framesPerPixel = frameLength / w;
|
|
2762
3691
|
let y = 0;
|
|
3692
|
+
let ais = null;
|
|
3693
|
+
let audioBuffer = this._audioDataHolder.buffer;
|
|
3694
|
+
let aisBuffer = null;
|
|
3695
|
+
if (!audioBuffer) {
|
|
3696
|
+
ais = this._audioDataHolder.audioInputStream();
|
|
3697
|
+
aisBuffer = new Array(chs);
|
|
3698
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
3699
|
+
aisBuffer[ch] = new Float32Array(framesPerPixel);
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
2763
3702
|
for (let ch = 0; ch < chs; ch++) {
|
|
2764
3703
|
let x = 0;
|
|
2765
3704
|
let psMin = new Float32Array(w);
|
|
@@ -2768,20 +3707,33 @@ class AudioSignal extends AudioCanvasLayerComponent {
|
|
|
2768
3707
|
for (let pii = 0; pii < w; pii++) {
|
|
2769
3708
|
let pMin = 0;
|
|
2770
3709
|
let pMax = 0;
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
pMin
|
|
3710
|
+
if (audioBuffer) {
|
|
3711
|
+
// calculate pixel min/max amplitude
|
|
3712
|
+
for (let ai = 0; ai < framesPerPixel; ai++) {
|
|
3713
|
+
//let framePos=(pii*framesPerPixel)+ai;
|
|
3714
|
+
let a = audioBuffer.getChannelData(ch)[framePos++];
|
|
3715
|
+
if (a < pMin) {
|
|
3716
|
+
pMin = a;
|
|
3717
|
+
}
|
|
3718
|
+
if (a > pMax) {
|
|
3719
|
+
pMax = a;
|
|
3720
|
+
}
|
|
2777
3721
|
}
|
|
2778
|
-
|
|
2779
|
-
|
|
3722
|
+
}
|
|
3723
|
+
else if (ais && aisBuffer) {
|
|
3724
|
+
let r = ais.read(aisBuffer);
|
|
3725
|
+
for (let ai = 0; ai < r; ai++) {
|
|
3726
|
+
let a = aisBuffer[ch][ai];
|
|
3727
|
+
if (a < pMin) {
|
|
3728
|
+
pMin = a;
|
|
3729
|
+
}
|
|
3730
|
+
if (a > pMax) {
|
|
3731
|
+
pMax = a;
|
|
3732
|
+
}
|
|
2780
3733
|
}
|
|
2781
3734
|
}
|
|
2782
3735
|
psMin[pii] = pMin;
|
|
2783
3736
|
psMax[pii] = pMax;
|
|
2784
|
-
//console.log("Min: ", pMin, " max: ", pMax);
|
|
2785
3737
|
}
|
|
2786
3738
|
g.fillStyle = 'green';
|
|
2787
3739
|
g.strokeStyle = 'green';
|
|
@@ -2806,12 +3758,11 @@ class AudioSignal extends AudioCanvasLayerComponent {
|
|
|
2806
3758
|
g.stroke();
|
|
2807
3759
|
y += chH;
|
|
2808
3760
|
}
|
|
2809
|
-
//this.drawPlayPosition();
|
|
2810
3761
|
}
|
|
2811
3762
|
}
|
|
2812
3763
|
}
|
|
2813
3764
|
setData(audioData) {
|
|
2814
|
-
this.
|
|
3765
|
+
this._audioDataHolder = audioData;
|
|
2815
3766
|
this.playFramePosition = 0;
|
|
2816
3767
|
}
|
|
2817
3768
|
}
|
|
@@ -3180,7 +4131,7 @@ class Sonagram extends AudioCanvasLayerComponent {
|
|
|
3180
4131
|
this._playFramePosition = null;
|
|
3181
4132
|
this.dftSize = DEFAULT_DFT_SIZE;
|
|
3182
4133
|
this.worker = null;
|
|
3183
|
-
this.
|
|
4134
|
+
this._audioDataHolder = null;
|
|
3184
4135
|
this.markers = new Array();
|
|
3185
4136
|
this.dft = new DFTFloat32(this.dftSize);
|
|
3186
4137
|
this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
|
|
@@ -3233,7 +4184,7 @@ class Sonagram extends AudioCanvasLayerComponent {
|
|
|
3233
4184
|
g.lineTo(xViewPortPixelpos, h);
|
|
3234
4185
|
g.closePath();
|
|
3235
4186
|
g.stroke();
|
|
3236
|
-
if (this.
|
|
4187
|
+
if (this._audioDataHolder) {
|
|
3237
4188
|
let framePosRound = this.viewPortXPixelToFramePosition(xViewPortPixelpos);
|
|
3238
4189
|
if (framePosRound != null) {
|
|
3239
4190
|
g.font = '14px sans-serif';
|
|
@@ -3497,11 +4448,21 @@ class Sonagram extends AudioCanvasLayerComponent {
|
|
|
3497
4448
|
}
|
|
3498
4449
|
GaussianWindow.DEFAULT_SIGMA = 0.3;
|
|
3499
4450
|
self.onmessage = function (msg) {
|
|
4451
|
+
//console.debug("Sonagram render thread");
|
|
3500
4452
|
let l = msg.data.l;
|
|
3501
4453
|
let w = msg.data.w;
|
|
3502
4454
|
let h = msg.data.h;
|
|
3503
4455
|
let vw = msg.data.vw;
|
|
3504
4456
|
let chs = msg.data.chs;
|
|
4457
|
+
let audioDataOffset = 0;
|
|
4458
|
+
let adOffset = msg.data.audioDataOffset;
|
|
4459
|
+
if (adOffset) {
|
|
4460
|
+
audioDataOffset = adOffset;
|
|
4461
|
+
}
|
|
4462
|
+
let maxPsd = null;
|
|
4463
|
+
if (msg.data.maxPsd !== undefined) {
|
|
4464
|
+
maxPsd = msg.data.maxPsd;
|
|
4465
|
+
}
|
|
3505
4466
|
let audioData = new Array(chs);
|
|
3506
4467
|
for (let ch = 0; ch < chs; ch++) {
|
|
3507
4468
|
audioData[ch] = new Float32Array(msg.data['audioData'][ch]);
|
|
@@ -3517,16 +4478,17 @@ class Sonagram extends AudioCanvasLayerComponent {
|
|
|
3517
4478
|
}
|
|
3518
4479
|
let imgData = new Uint8ClampedArray(arrSize);
|
|
3519
4480
|
//console.log("Render method:");
|
|
3520
|
-
|
|
4481
|
+
//console.debug("Created imgData arrSize: "+arrSize+" ", w, "x", h);
|
|
4482
|
+
let calcMaxPsd = -Infinity;
|
|
4483
|
+
if (arrSize > 0) {
|
|
3521
4484
|
let chH = Math.round(h / chs);
|
|
3522
4485
|
let framesPerPixel = frameLength / vw;
|
|
3523
|
-
//console.
|
|
4486
|
+
//console.debug("Render: ", w, "x", h);
|
|
3524
4487
|
let b = new Float32Array(dftSize);
|
|
3525
4488
|
let sona = new Array(chs);
|
|
3526
|
-
let
|
|
3527
|
-
let p = 0;
|
|
4489
|
+
//let p = 0;
|
|
3528
4490
|
for (let ch = 0; ch < chs; ch++) {
|
|
3529
|
-
p = ch * frameLength;
|
|
4491
|
+
//p = ch * frameLength;
|
|
3530
4492
|
let chDataLen = audioData[ch].length;
|
|
3531
4493
|
let x = 0;
|
|
3532
4494
|
// initialize DFT array buffer
|
|
@@ -3543,8 +4505,13 @@ class Sonagram extends AudioCanvasLayerComponent {
|
|
|
3543
4505
|
// initialize for negative sample positions and out of bounds positions
|
|
3544
4506
|
let chDat = 0;
|
|
3545
4507
|
// Set audio sample if available
|
|
3546
|
-
|
|
3547
|
-
|
|
4508
|
+
let adp = samplePos - audioDataOffset;
|
|
4509
|
+
if (adp >= 0 && adp < chDataLen) {
|
|
4510
|
+
chDat = audioData[ch][adp];
|
|
4511
|
+
//console.debug("Audio data: "+chDat);
|
|
4512
|
+
}
|
|
4513
|
+
else {
|
|
4514
|
+
//console.debug("Sample buf pos oob: adp: "+adp+", chDataLen: "+chDataLen+", samplePos: "+samplePos+", i: "+i);
|
|
3548
4515
|
}
|
|
3549
4516
|
// apply Window
|
|
3550
4517
|
b[i] = chDat * wf.getScale(i);
|
|
@@ -3554,63 +4521,78 @@ class Sonagram extends AudioCanvasLayerComponent {
|
|
|
3554
4521
|
// Get maximum value of spectral energy
|
|
3555
4522
|
for (let s = 0; s < dftBands; s++) {
|
|
3556
4523
|
let psd = (2 * Math.pow(spectr[s], 2)) / dftBands;
|
|
3557
|
-
if (psd >
|
|
3558
|
-
|
|
4524
|
+
if (psd > calcMaxPsd) {
|
|
4525
|
+
calcMaxPsd = psd;
|
|
3559
4526
|
}
|
|
3560
4527
|
}
|
|
3561
4528
|
// Set render model data for this pixel
|
|
3562
4529
|
sona[ch][pii] = spectr;
|
|
3563
4530
|
}
|
|
3564
4531
|
}
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
scaledVal =
|
|
3585
|
-
|
|
3586
|
-
scaledVal
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
rgbVal =
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
4532
|
+
if (!msg.data.norender) {
|
|
4533
|
+
if (!maxPsd) {
|
|
4534
|
+
maxPsd = calcMaxPsd;
|
|
4535
|
+
}
|
|
4536
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
4537
|
+
for (let pii = 0; pii < w; pii++) {
|
|
4538
|
+
let allBlack = true;
|
|
4539
|
+
for (let y = 0; y < chH; y++) {
|
|
4540
|
+
let freqIdx = Math.round(y * dftBands / chH);
|
|
4541
|
+
// calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
|
|
4542
|
+
// PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
|
|
4543
|
+
let val = sona[ch][pii][freqIdx];
|
|
4544
|
+
let psd = (2 * Math.pow(val, 2)) / dftBands;
|
|
4545
|
+
// Calculate logarithmic value
|
|
4546
|
+
//let psdLog = ips.dsp.DSPUtils.toLevelInDB(psd / maxPsd);
|
|
4547
|
+
let linearLevel = psd / maxPsd;
|
|
4548
|
+
let psdLog = 10 * Math.log(linearLevel) / Math.log(10);
|
|
4549
|
+
// Fixed dynamic Range value of 70dB for now
|
|
4550
|
+
let dynRangeInDb = 70;
|
|
4551
|
+
let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
|
|
4552
|
+
// are the following checks necessary for clamped array ?
|
|
4553
|
+
if (scaledVal > 1.0)
|
|
4554
|
+
scaledVal = 1;
|
|
4555
|
+
if (scaledVal < 0.0) {
|
|
4556
|
+
scaledVal = 0;
|
|
4557
|
+
}
|
|
4558
|
+
let rgbVal = Math.round(255 * scaledVal);
|
|
4559
|
+
if (rgbVal < 0) {
|
|
4560
|
+
// System.out.println("Neg RGB val: "+rgbVal);
|
|
4561
|
+
rgbVal = 0;
|
|
4562
|
+
}
|
|
4563
|
+
if (rgbVal > 255) {
|
|
4564
|
+
rgbVal = 255;
|
|
4565
|
+
}
|
|
4566
|
+
rgbVal = 255 - rgbVal;
|
|
4567
|
+
if (rgbVal > 0) {
|
|
4568
|
+
allBlack = false;
|
|
4569
|
+
}
|
|
4570
|
+
let py = chH - y;
|
|
4571
|
+
let dataPos = ((((ch * chH) + py) * w) + pii) * 4;
|
|
4572
|
+
imgData[dataPos + 0] = rgbVal; //R
|
|
4573
|
+
imgData[dataPos + 1] = rgbVal; //G
|
|
4574
|
+
imgData[dataPos + 2] = rgbVal; //B
|
|
4575
|
+
imgData[dataPos + 3] = 255; //A (alpha: fully opaque)
|
|
4576
|
+
//console.debug("Rendered: py: "+py+", rgbval: "+rgbVal);
|
|
4577
|
+
// example 1x1, 2chs
|
|
4578
|
+
// ch0x0y0R,ch0x0y0G,ch0x0y0B,ch0x0y0A,
|
|
4579
|
+
// ch0x1y0R,ch0x1y0G,ch0x1y0B,ch0x1y0A,
|
|
4580
|
+
// ch0x0y0R,ch0x0y0G,ch0x0y0B,ch0x0y0A,
|
|
4581
|
+
// ch0x1y1R,ch0x1y1G,ch0x1y1B,ch0x1y1A,
|
|
4582
|
+
// ch1x0y0R,ch1x0y0G,ch1x0y0B,ch1x0y0A,
|
|
4583
|
+
// ch1x1y0R,ch1x1y0G,ch1x1y0B,ch1x1y0A,
|
|
4584
|
+
// ch1x0y0R,ch1x0y0G,ch1x0y0B,ch1x0y0A,
|
|
4585
|
+
// ch1x1y1R,ch1x1y1G,ch1x1y1B,ch1x1y1A
|
|
3599
4586
|
}
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
imgData[dataPos + 1] = rgbVal; //G
|
|
3604
|
-
imgData[dataPos + 2] = rgbVal; //B
|
|
3605
|
-
imgData[dataPos + 3] = 255; //A (alpha: fully opaque)
|
|
4587
|
+
// if (allBlack) {
|
|
4588
|
+
// console.log("Black: ", pii, " ", ch);
|
|
4589
|
+
// }
|
|
3606
4590
|
}
|
|
3607
|
-
// if (allBlack) {
|
|
3608
|
-
// console.log("Black: ", pii, " ", ch);
|
|
3609
|
-
// }
|
|
3610
4591
|
}
|
|
3611
4592
|
}
|
|
3612
4593
|
}
|
|
3613
|
-
|
|
4594
|
+
//console.debug("Render thread post message imgData: "+imgData.length)
|
|
4595
|
+
postMessage({ imgData: imgData, l: l, w: msg.data.w, h: msg.data.h, vw: vw, maxPsd: calcMaxPsd, terminate: msg.data.terminate }, [imgData.buffer]);
|
|
3614
4596
|
};
|
|
3615
4597
|
}
|
|
3616
4598
|
startDraw(clear = true) {
|
|
@@ -3640,42 +4622,190 @@ class Sonagram extends AudioCanvasLayerComponent {
|
|
|
3640
4622
|
if (this.bounds) {
|
|
3641
4623
|
let w = Math.round(this.bounds.dimension.width);
|
|
3642
4624
|
let h = Math.round(this.bounds.dimension.height);
|
|
3643
|
-
if (this.
|
|
4625
|
+
if (this._audioDataHolder && w > 0 && h > 0) {
|
|
3644
4626
|
this.worker = new Worker(this.workerURL);
|
|
3645
4627
|
//this.wo = new Worker('./worker/sonagram.worker', { type: `module` });
|
|
3646
|
-
let chs = this.
|
|
3647
|
-
let
|
|
3648
|
-
let
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
let
|
|
4628
|
+
let chs = this._audioDataHolder.numberOfChannels;
|
|
4629
|
+
let vw = Math.round(this.virtualDimension.width);
|
|
4630
|
+
let frameLength = this._audioDataHolder.frameLen;
|
|
4631
|
+
let framesPerPixel = Math.ceil(frameLength / vw);
|
|
4632
|
+
let leftPos = Math.round(this.bounds.position.left);
|
|
4633
|
+
let renderPos = leftPos;
|
|
4634
|
+
let audioBuffer = this._audioDataHolder.buffer;
|
|
4635
|
+
//let arrayAudioBuffer=this._audioDataHolder.arrayBuffer;
|
|
4636
|
+
let arrAbBuf;
|
|
4637
|
+
let imgData;
|
|
4638
|
+
let maxPsd = -Infinity;
|
|
4639
|
+
let norender = true;
|
|
3654
4640
|
if (this.worker) {
|
|
3655
4641
|
this.worker.onmessage = (me) => {
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
4642
|
+
if (true === me.data.terminate) {
|
|
4643
|
+
let drawImgData;
|
|
4644
|
+
if (imgData) {
|
|
4645
|
+
drawImgData = imgData;
|
|
4646
|
+
}
|
|
4647
|
+
else {
|
|
4648
|
+
drawImgData = me.data.imgData;
|
|
4649
|
+
}
|
|
4650
|
+
this.drawRendered(w, h, drawImgData);
|
|
4651
|
+
if (this.worker) {
|
|
4652
|
+
this.worker.terminate();
|
|
4653
|
+
}
|
|
4654
|
+
this.worker = null;
|
|
4655
|
+
}
|
|
4656
|
+
else {
|
|
4657
|
+
// set rendered vertical values of one pixel of timescale
|
|
4658
|
+
//let dataPos = renderPos * h * 4;
|
|
4659
|
+
if (norender) {
|
|
4660
|
+
if (me.data.maxPsd > maxPsd) {
|
|
4661
|
+
maxPsd = me.data.maxPsd;
|
|
4662
|
+
//console.debug("new maxPsd: "+maxPsd);
|
|
4663
|
+
}
|
|
4664
|
+
}
|
|
4665
|
+
else {
|
|
4666
|
+
let chH = Math.round(h / chs);
|
|
4667
|
+
let idp = me.data.l - leftPos;
|
|
4668
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
4669
|
+
for (let y = 0; y < chH; y++) {
|
|
4670
|
+
let py = chH - y;
|
|
4671
|
+
let dataPos = ((((ch * chH) + py) * w) + idp) * 4;
|
|
4672
|
+
let mePos = ((ch * chH) + py) * 4;
|
|
4673
|
+
//let lastPos = dataPos + me.data.imgData.length;
|
|
4674
|
+
//if (lastPos < imgData.length) {
|
|
4675
|
+
// set one pixel
|
|
4676
|
+
imgData[dataPos] = me.data.imgData[mePos];
|
|
4677
|
+
imgData[dataPos + 1] = me.data.imgData[mePos + 1];
|
|
4678
|
+
imgData[dataPos + 2] = me.data.imgData[mePos + 2];
|
|
4679
|
+
imgData[dataPos + 3] = me.data.imgData[mePos + 3];
|
|
4680
|
+
//} else {
|
|
4681
|
+
//console.error("Out of range: " + dataPos + "+" + me.data.imgData.length + ">=" + imgData.length);
|
|
4682
|
+
// }
|
|
4683
|
+
}
|
|
4684
|
+
}
|
|
4685
|
+
}
|
|
4686
|
+
if (this._audioDataHolder && arrAbBuf && this.worker) {
|
|
4687
|
+
// proceed with next pixel
|
|
4688
|
+
renderPos++;
|
|
4689
|
+
//console.debug("Render pos: "+renderPos);
|
|
4690
|
+
let terminate = false;
|
|
4691
|
+
let windowEnd = renderPos >= leftPos + w;
|
|
4692
|
+
if (windowEnd) {
|
|
4693
|
+
if (norender) {
|
|
4694
|
+
// phase two: rendering
|
|
4695
|
+
norender = false;
|
|
4696
|
+
// start from beginning
|
|
4697
|
+
renderPos = leftPos;
|
|
4698
|
+
//console.debug("now rendering: maxPsd: "+maxPsd);
|
|
4699
|
+
}
|
|
4700
|
+
else {
|
|
4701
|
+
// terminate render phase
|
|
4702
|
+
terminate = true;
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
let leftFramePos = Math.floor(frameLength * renderPos / vw) - this.dftSize / 2;
|
|
4706
|
+
if (leftFramePos < 0) {
|
|
4707
|
+
leftFramePos = 0;
|
|
4708
|
+
}
|
|
4709
|
+
let ada = new Array(chs);
|
|
4710
|
+
//console.debug("Render pos: "+renderPos+" leftFramePos: "+leftFramePos);
|
|
4711
|
+
if (!terminate) {
|
|
4712
|
+
if (this._audioDataHolder) {
|
|
4713
|
+
let read = this._audioDataHolder.frames(leftFramePos, this.dftSize, arrAbBuf);
|
|
4714
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
4715
|
+
// Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
|
|
4716
|
+
ada[ch] = arrAbBuf[ch].buffer.slice(0);
|
|
4717
|
+
}
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4720
|
+
else {
|
|
4721
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
4722
|
+
ada[ch] = new ArrayBuffer(0);
|
|
4723
|
+
}
|
|
4724
|
+
}
|
|
4725
|
+
this.worker.postMessage({
|
|
4726
|
+
audioData: ada,
|
|
4727
|
+
audioDataOffset: leftFramePos,
|
|
4728
|
+
l: renderPos,
|
|
4729
|
+
w: me.data.w,
|
|
4730
|
+
h: h,
|
|
4731
|
+
vw: vw,
|
|
4732
|
+
chs: chs,
|
|
4733
|
+
frameLength: frameLength,
|
|
4734
|
+
dftSize: this.dftSize,
|
|
4735
|
+
maxPsd: maxPsd,
|
|
4736
|
+
norender: norender,
|
|
4737
|
+
terminate: terminate
|
|
4738
|
+
}, ada);
|
|
4739
|
+
}
|
|
3659
4740
|
}
|
|
3660
|
-
this.worker = null;
|
|
3661
4741
|
};
|
|
3662
4742
|
}
|
|
3663
|
-
if (
|
|
3664
|
-
let
|
|
3665
|
-
|
|
3666
|
-
|
|
4743
|
+
if (audioBuffer && audioBuffer.length * audioBuffer.numberOfChannels < AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD) {
|
|
4744
|
+
let ada = new Array(chs);
|
|
4745
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
4746
|
+
// Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
|
|
4747
|
+
ada[ch] = audioBuffer.getChannelData(ch).buffer.slice(0);
|
|
4748
|
+
}
|
|
4749
|
+
let start = Date.now();
|
|
4750
|
+
if (this.markerCanvas) {
|
|
4751
|
+
let g = this.markerCanvas.getContext("2d");
|
|
4752
|
+
if (g) {
|
|
4753
|
+
g.fillText("Rendering...", 10, 20);
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
this.worker.postMessage({
|
|
4757
|
+
audioData: ada,
|
|
4758
|
+
l: leftPos,
|
|
4759
|
+
w: w,
|
|
4760
|
+
h: h,
|
|
4761
|
+
vw: Math.round(this.virtualDimension.width),
|
|
4762
|
+
chs: chs,
|
|
4763
|
+
frameLength: frameLength,
|
|
4764
|
+
dftSize: this.dftSize,
|
|
4765
|
+
terminate: true
|
|
4766
|
+
}, ada);
|
|
4767
|
+
}
|
|
4768
|
+
else {
|
|
4769
|
+
if (w > 0) {
|
|
4770
|
+
if (framesPerPixel > 0) {
|
|
4771
|
+
let arrSize = w * h * 4;
|
|
4772
|
+
if (arrSize < 0) {
|
|
4773
|
+
arrSize = 0;
|
|
4774
|
+
}
|
|
4775
|
+
imgData = new Uint8ClampedArray(arrSize);
|
|
4776
|
+
let rw = 1;
|
|
4777
|
+
//ais = new ArrayAudioBufferInputStream(arrayAudioBuffer);
|
|
4778
|
+
arrAbBuf = new Array(chs);
|
|
4779
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
4780
|
+
arrAbBuf[ch] = new Float32Array(this.dftSize);
|
|
4781
|
+
}
|
|
4782
|
+
let leftFramePos = Math.floor(frameLength * renderPos / vw) - this.dftSize / 2;
|
|
4783
|
+
let framesToRead = this.dftSize;
|
|
4784
|
+
if (leftFramePos < 0) {
|
|
4785
|
+
//framesToRead=this.dftSize+leftFramePos;
|
|
4786
|
+
leftFramePos = 0;
|
|
4787
|
+
}
|
|
4788
|
+
let read = this._audioDataHolder.frames(leftFramePos, framesToRead, arrAbBuf);
|
|
4789
|
+
let ad = new Float32Array(chs * framesToRead);
|
|
4790
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
4791
|
+
ad.set(arrAbBuf[ch], ch * framesToRead);
|
|
4792
|
+
}
|
|
4793
|
+
this.worker.postMessage({
|
|
4794
|
+
l: renderPos,
|
|
4795
|
+
w: rw,
|
|
4796
|
+
h: h,
|
|
4797
|
+
vw: vw,
|
|
4798
|
+
chs: chs,
|
|
4799
|
+
frameLength: frameLength,
|
|
4800
|
+
audioData: ad,
|
|
4801
|
+
audioDataOffset: leftFramePos,
|
|
4802
|
+
dftSize: this.dftSize,
|
|
4803
|
+
norender: norender,
|
|
4804
|
+
terminate: false
|
|
4805
|
+
}, [ad.buffer]);
|
|
4806
|
+
}
|
|
3667
4807
|
}
|
|
3668
4808
|
}
|
|
3669
|
-
this.worker.postMessage({
|
|
3670
|
-
audioData: ada,
|
|
3671
|
-
l: Math.round(this.bounds.position.left),
|
|
3672
|
-
w: w,
|
|
3673
|
-
h: h,
|
|
3674
|
-
vw: Math.round(this.virtualDimension.width),
|
|
3675
|
-
chs: chs,
|
|
3676
|
-
frameLength: frameLength,
|
|
3677
|
-
dftSize: this.dftSize
|
|
3678
|
-
}, ada);
|
|
3679
4809
|
}
|
|
3680
4810
|
else {
|
|
3681
4811
|
let g = this.sonagramCanvas.getContext("2d");
|
|
@@ -3685,17 +4815,17 @@ class Sonagram extends AudioCanvasLayerComponent {
|
|
|
3685
4815
|
}
|
|
3686
4816
|
}
|
|
3687
4817
|
}
|
|
3688
|
-
drawRendered(
|
|
4818
|
+
drawRendered(w, h, imgData) {
|
|
3689
4819
|
if (this.sonagramCanvas) {
|
|
3690
|
-
this.sonagramCanvas.width =
|
|
3691
|
-
this.sonagramCanvas.height =
|
|
4820
|
+
this.sonagramCanvas.width = w;
|
|
4821
|
+
this.sonagramCanvas.height = h;
|
|
3692
4822
|
let g = this.sonagramCanvas.getContext("2d");
|
|
3693
4823
|
if (g) {
|
|
3694
|
-
let imgDataArr =
|
|
3695
|
-
if (
|
|
3696
|
-
let
|
|
3697
|
-
|
|
3698
|
-
g.putImageData(
|
|
4824
|
+
let imgDataArr = imgData;
|
|
4825
|
+
if (w > 0 && h > 0) {
|
|
4826
|
+
let gImgData = g.createImageData(w, h);
|
|
4827
|
+
gImgData.data.set(imgDataArr);
|
|
4828
|
+
g.putImageData(gImgData, 0, 0);
|
|
3699
4829
|
}
|
|
3700
4830
|
}
|
|
3701
4831
|
}
|
|
@@ -3711,91 +4841,95 @@ class Sonagram extends AudioCanvasLayerComponent {
|
|
|
3711
4841
|
g.clearRect(0, 0, w, h);
|
|
3712
4842
|
g.fillStyle = "white";
|
|
3713
4843
|
g.fillRect(0, 0, w, h);
|
|
3714
|
-
if (this.
|
|
4844
|
+
if (this._audioDataHolder) {
|
|
3715
4845
|
let spectSize = Math.floor(this.dftSize / 2);
|
|
3716
|
-
let chs = this.
|
|
4846
|
+
let chs = this._audioDataHolder.numberOfChannels;
|
|
3717
4847
|
let chH = h / chs;
|
|
3718
|
-
let frameLength = this.
|
|
4848
|
+
let frameLength = this._audioDataHolder.frameLen;
|
|
3719
4849
|
let framesPerPixel = frameLength / w;
|
|
3720
4850
|
let y = 0;
|
|
3721
|
-
|
|
3722
|
-
let
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
let
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
framePos =
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
sona[ch][pii] = spectr;
|
|
3741
|
-
// @ts-ignore
|
|
3742
|
-
let pMax = Math.max.apply(null, spectr);
|
|
3743
|
-
if (pMax > max) {
|
|
3744
|
-
max = pMax;
|
|
3745
|
-
}
|
|
3746
|
-
for (let s = 0; s < spectSize; s++) {
|
|
3747
|
-
let psd = (2 * Math.pow(spectr[s], 2)) / spectSize;
|
|
3748
|
-
if (psd > maxPsd) {
|
|
3749
|
-
maxPsd = psd;
|
|
4851
|
+
let audioBuffer = this._audioDataHolder.buffer;
|
|
4852
|
+
let arrayAudioBuffer = this._audioDataHolder.arrayBuffer;
|
|
4853
|
+
if (audioBuffer) {
|
|
4854
|
+
let b = new Float32Array(this.dftSize);
|
|
4855
|
+
let sona = new Array(chs);
|
|
4856
|
+
let max = 0;
|
|
4857
|
+
let maxPsd = -Infinity;
|
|
4858
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
4859
|
+
let x = 0;
|
|
4860
|
+
sona[ch] = new Array(w);
|
|
4861
|
+
let chData = audioBuffer.getChannelData(ch);
|
|
4862
|
+
// TODO center buffer
|
|
4863
|
+
let framePos = 0;
|
|
4864
|
+
for (let pii = 0; pii < w; pii++) {
|
|
4865
|
+
framePos = Math.round(pii * framesPerPixel);
|
|
4866
|
+
// calculate DFT at pixel position
|
|
4867
|
+
for (let i = 0; i < this.dftSize; i++) {
|
|
4868
|
+
let chDat = chData[framePos + i];
|
|
4869
|
+
b[i] = chDat;
|
|
3750
4870
|
}
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
let framePos = 0;
|
|
3758
|
-
for (let pii = 0; pii < w; pii++) {
|
|
3759
|
-
framePos = pii * framesPerPixel;
|
|
3760
|
-
for (let y = 0; y < h; y++) {
|
|
3761
|
-
let freqIdx = Math.round(y * spectSize / h);
|
|
3762
|
-
// calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
|
|
3763
|
-
// PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
|
|
3764
|
-
let val = sona[ch][pii][freqIdx];
|
|
3765
|
-
let psd = (2 * Math.pow(val, 2)) / spectSize;
|
|
3766
|
-
// Calculate logarithmic
|
|
3767
|
-
let psdLog = DSPUtils.toLevelInDB(psd / maxPsd);
|
|
3768
|
-
let dynRangeInDb = 70;
|
|
3769
|
-
let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
|
|
3770
|
-
if (scaledVal > 1)
|
|
3771
|
-
scaledVal = 1;
|
|
3772
|
-
if (scaledVal < 0) {
|
|
3773
|
-
scaledVal = 0;
|
|
4871
|
+
let spectr = this.dft.processRealMagnitude(b);
|
|
4872
|
+
sona[ch][pii] = spectr;
|
|
4873
|
+
// @ts-ignore
|
|
4874
|
+
let pMax = Math.max.apply(null, spectr);
|
|
4875
|
+
if (pMax > max) {
|
|
4876
|
+
max = pMax;
|
|
3774
4877
|
}
|
|
3775
|
-
let
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
4878
|
+
for (let s = 0; s < spectSize; s++) {
|
|
4879
|
+
let psd = (2 * Math.pow(spectr[s], 2)) / spectSize;
|
|
4880
|
+
if (psd > maxPsd) {
|
|
4881
|
+
maxPsd = psd;
|
|
4882
|
+
}
|
|
3779
4883
|
}
|
|
3780
|
-
|
|
3781
|
-
|
|
4884
|
+
}
|
|
4885
|
+
}
|
|
4886
|
+
//console.log("max: ", max);
|
|
4887
|
+
maxPsd = (2 * Math.pow(max, 2)) / spectSize;
|
|
4888
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
4889
|
+
let framePos = 0;
|
|
4890
|
+
for (let pii = 0; pii < w; pii++) {
|
|
4891
|
+
framePos = pii * framesPerPixel;
|
|
4892
|
+
for (let y = 0; y < h; y++) {
|
|
4893
|
+
let freqIdx = Math.round(y * spectSize / h);
|
|
4894
|
+
// calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
|
|
4895
|
+
// PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
|
|
4896
|
+
let val = sona[ch][pii][freqIdx];
|
|
4897
|
+
let psd = (2 * Math.pow(val, 2)) / spectSize;
|
|
4898
|
+
// Calculate logarithmic
|
|
4899
|
+
let psdLog = DSPUtils.toLevelInDB(psd / maxPsd);
|
|
4900
|
+
let dynRangeInDb = 70;
|
|
4901
|
+
let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
|
|
4902
|
+
if (scaledVal > 1)
|
|
4903
|
+
scaledVal = 1;
|
|
4904
|
+
if (scaledVal < 0) {
|
|
4905
|
+
scaledVal = 0;
|
|
4906
|
+
}
|
|
4907
|
+
let rgbVal = (255 * scaledVal);
|
|
4908
|
+
if (rgbVal < 0) {
|
|
4909
|
+
// System.out.println("Neg RGB val: "+rgbVal);
|
|
4910
|
+
rgbVal = 0;
|
|
4911
|
+
}
|
|
4912
|
+
if (rgbVal > 255) {
|
|
4913
|
+
rgbVal = 255;
|
|
4914
|
+
}
|
|
4915
|
+
rgbVal = 255 - rgbVal;
|
|
4916
|
+
let colorStr = CSSUtils.toColorString(rgbVal, rgbVal, rgbVal);
|
|
4917
|
+
g.fillStyle = colorStr;
|
|
4918
|
+
g.fillRect(pii, chH - y, 1, 1);
|
|
3782
4919
|
}
|
|
3783
|
-
rgbVal = 255 - rgbVal;
|
|
3784
|
-
let colorStr = CSSUtils.toColorString(rgbVal, rgbVal, rgbVal);
|
|
3785
|
-
g.fillStyle = colorStr;
|
|
3786
|
-
g.fillRect(pii, chH - y, 1, 1);
|
|
3787
4920
|
}
|
|
3788
4921
|
}
|
|
4922
|
+
this.drawPlayPosition();
|
|
4923
|
+
}
|
|
4924
|
+
else if (arrayAudioBuffer) {
|
|
4925
|
+
throw Error("Redraw with array audio buffer not supported.");
|
|
3789
4926
|
}
|
|
3790
|
-
this.drawPlayPosition();
|
|
3791
4927
|
}
|
|
3792
4928
|
}
|
|
3793
4929
|
}
|
|
3794
4930
|
setData(audioData) {
|
|
3795
|
-
this.
|
|
4931
|
+
this._audioDataHolder = audioData;
|
|
3796
4932
|
this.playFramePosition = 0;
|
|
3797
|
-
//this.redraw();
|
|
3798
|
-
//this.startRender();
|
|
3799
4933
|
}
|
|
3800
4934
|
}
|
|
3801
4935
|
Sonagram.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: Sonagram, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
@@ -4020,13 +5154,13 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
|
|
|
4020
5154
|
}
|
|
4021
5155
|
currentXZoom() {
|
|
4022
5156
|
let xz = this._xZoom;
|
|
4023
|
-
if (xz == null && this.
|
|
5157
|
+
if (xz == null && this._audioDataHolder) {
|
|
4024
5158
|
let ow = this.ce.offsetWidth;
|
|
4025
5159
|
if (ow < 1) {
|
|
4026
5160
|
// at least one pixel width to avoid x-zoom zero values
|
|
4027
5161
|
ow = 1;
|
|
4028
5162
|
}
|
|
4029
|
-
xz = ow / this.
|
|
5163
|
+
xz = ow / this._audioDataHolder.duration;
|
|
4030
5164
|
}
|
|
4031
5165
|
return xz;
|
|
4032
5166
|
}
|
|
@@ -4107,7 +5241,7 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
|
|
|
4107
5241
|
layout(clear = true) {
|
|
4108
5242
|
if (this.ce && this.dc) {
|
|
4109
5243
|
const clientW = this.ce.clientWidth;
|
|
4110
|
-
if (this.
|
|
5244
|
+
if (this._audioDataHolder) {
|
|
4111
5245
|
if (this._fixFitToPanel) {
|
|
4112
5246
|
// Set the virtual canvas width to the visible width
|
|
4113
5247
|
if (this.ce.style.width != '100%') {
|
|
@@ -4118,7 +5252,7 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
|
|
|
4118
5252
|
else {
|
|
4119
5253
|
if (this._xZoom) {
|
|
4120
5254
|
// Set the virtual canvas width according to the value of the user selected xZoom value
|
|
4121
|
-
const newClW = Math.round(this._xZoom * this.
|
|
5255
|
+
const newClW = Math.round(this._xZoom * this._audioDataHolder?.duration);
|
|
4122
5256
|
this.ce.style.width = newClW + 'px';
|
|
4123
5257
|
}
|
|
4124
5258
|
else {
|
|
@@ -4130,22 +5264,22 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
|
|
|
4130
5264
|
this._layout(clear, true);
|
|
4131
5265
|
}
|
|
4132
5266
|
}
|
|
4133
|
-
set audioData(
|
|
5267
|
+
set audioData(audioDataHolder) {
|
|
4134
5268
|
this._audioClip = null;
|
|
4135
|
-
this.
|
|
4136
|
-
this.as.setData(
|
|
4137
|
-
this.so.setData(
|
|
5269
|
+
this._audioDataHolder = audioDataHolder;
|
|
5270
|
+
this.as.setData(audioDataHolder);
|
|
5271
|
+
this.so.setData(audioDataHolder);
|
|
4138
5272
|
this.layout();
|
|
4139
5273
|
}
|
|
4140
5274
|
get audioData() {
|
|
4141
|
-
return this.
|
|
5275
|
+
return this._audioDataHolder;
|
|
4142
5276
|
}
|
|
4143
5277
|
set audioClip(audioClip) {
|
|
4144
5278
|
this._audioClip = audioClip;
|
|
4145
5279
|
let audioData = null;
|
|
4146
5280
|
let sel = null;
|
|
4147
5281
|
if (audioClip) {
|
|
4148
|
-
audioData = audioClip.
|
|
5282
|
+
audioData = audioClip.audioDataHolder;
|
|
4149
5283
|
if (this._audioClip) {
|
|
4150
5284
|
this._audioClip.addSelectionObserver((clip) => {
|
|
4151
5285
|
this.selection = clip.selection;
|
|
@@ -4153,9 +5287,9 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
|
|
|
4153
5287
|
}
|
|
4154
5288
|
sel = audioClip.selection;
|
|
4155
5289
|
}
|
|
4156
|
-
this.
|
|
4157
|
-
this.as.setData(this.
|
|
4158
|
-
this.so.setData(this.
|
|
5290
|
+
this._audioDataHolder = audioData;
|
|
5291
|
+
this.as.setData(this._audioDataHolder);
|
|
5292
|
+
this.so.setData(this._audioDataHolder);
|
|
4159
5293
|
this.selecting = null;
|
|
4160
5294
|
this.selection = sel;
|
|
4161
5295
|
this.layout();
|
|
@@ -4347,7 +5481,7 @@ class AudioDisplayScrollPane {
|
|
|
4347
5481
|
let audioData = null;
|
|
4348
5482
|
let sel = null;
|
|
4349
5483
|
if (audioClip) {
|
|
4350
|
-
audioData = audioClip.
|
|
5484
|
+
audioData = audioClip.audioDataHolder;
|
|
4351
5485
|
sel = audioClip.selection;
|
|
4352
5486
|
audioClip.addSelectionObserver((clip) => {
|
|
4353
5487
|
this.zoomSelectedAction.disabled = (clip.selection == null);
|
|
@@ -4618,17 +5752,17 @@ class AudioDisplay {
|
|
|
4618
5752
|
console.log("Play started");
|
|
4619
5753
|
this.status = 'Playing...';
|
|
4620
5754
|
}
|
|
4621
|
-
set audioData(
|
|
4622
|
-
this.audioDisplayScrollPane.audioData =
|
|
5755
|
+
set audioData(audioData) {
|
|
5756
|
+
this.audioDisplayScrollPane.audioData = audioData;
|
|
4623
5757
|
if (this.playStartAction) {
|
|
4624
|
-
this.playStartAction.disabled = (
|
|
5758
|
+
this.playStartAction.disabled = (audioData == null);
|
|
4625
5759
|
}
|
|
4626
5760
|
}
|
|
4627
5761
|
set audioClip(audioClip) {
|
|
4628
5762
|
let audioData = null;
|
|
4629
5763
|
let sel = null;
|
|
4630
5764
|
if (audioClip) {
|
|
4631
|
-
audioData = audioClip.buffer;
|
|
5765
|
+
audioData = audioClip.audioDataHolder.buffer;
|
|
4632
5766
|
sel = audioClip.selection;
|
|
4633
5767
|
}
|
|
4634
5768
|
this._audioClip = audioClip;
|
|
@@ -4741,7 +5875,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
4741
5875
|
|
|
4742
5876
|
class Progress {
|
|
4743
5877
|
constructor() {
|
|
4744
|
-
this.items =
|
|
5878
|
+
this.items = undefined;
|
|
4745
5879
|
this.selectedItemIdx = 0;
|
|
4746
5880
|
this.enableDownload = false;
|
|
4747
5881
|
this.onRowSelect = new EventEmitter();
|
|
@@ -5481,7 +6615,7 @@ class Prompting {
|
|
|
5481
6615
|
constructor() {
|
|
5482
6616
|
this.promptItem = null;
|
|
5483
6617
|
this.showPrompt = false;
|
|
5484
|
-
this.items =
|
|
6618
|
+
this.items = undefined;
|
|
5485
6619
|
this.enableDownload = false;
|
|
5486
6620
|
this.audioSignalCollapsed = true;
|
|
5487
6621
|
this.displayAudioClip = null;
|
|
@@ -5792,6 +6926,7 @@ class LevelBar {
|
|
|
5792
6926
|
this._streamingMode = false;
|
|
5793
6927
|
this._staticLevelInfos = null;
|
|
5794
6928
|
this._playFramePosition = null;
|
|
6929
|
+
this._stateLoading = false;
|
|
5795
6930
|
this.warnDBLevel = DEFAULT_WARN_DB_LEVEL$1;
|
|
5796
6931
|
this.dbValues = new Array();
|
|
5797
6932
|
}
|
|
@@ -5825,17 +6960,22 @@ class LevelBar {
|
|
|
5825
6960
|
}
|
|
5826
6961
|
}
|
|
5827
6962
|
set displayLevelInfos(levelInfos) {
|
|
5828
|
-
this._staticLevelInfos = levelInfos;
|
|
5829
6963
|
if (levelInfos) {
|
|
6964
|
+
this._staticLevelInfos = levelInfos;
|
|
5830
6965
|
this.dbValues = levelInfos.bufferLevelInfos.map((li) => {
|
|
5831
6966
|
return li.powerLevelsDB();
|
|
5832
6967
|
});
|
|
5833
6968
|
}
|
|
5834
6969
|
else {
|
|
6970
|
+
this._staticLevelInfos = null;
|
|
5835
6971
|
this.dbValues = new Array();
|
|
5836
6972
|
}
|
|
5837
6973
|
this.layoutStatic();
|
|
5838
6974
|
}
|
|
6975
|
+
set stateLoading(loading) {
|
|
6976
|
+
this._stateLoading = loading;
|
|
6977
|
+
this.layoutStatic();
|
|
6978
|
+
}
|
|
5839
6979
|
set channelCount(channelCount) {
|
|
5840
6980
|
this.reset();
|
|
5841
6981
|
}
|
|
@@ -6016,6 +7156,12 @@ class LevelBar {
|
|
|
6016
7156
|
}
|
|
6017
7157
|
}
|
|
6018
7158
|
}
|
|
7159
|
+
else if (this._stateLoading) {
|
|
7160
|
+
g.strokeStyle = 'white';
|
|
7161
|
+
g.fillStyle = 'white';
|
|
7162
|
+
g.font = '20px sans-serif';
|
|
7163
|
+
g.fillText("Loading...", 10, 25);
|
|
7164
|
+
}
|
|
6019
7165
|
}
|
|
6020
7166
|
}
|
|
6021
7167
|
}
|
|
@@ -6058,7 +7204,7 @@ class LevelBar {
|
|
|
6058
7204
|
}
|
|
6059
7205
|
}
|
|
6060
7206
|
LevelBar.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: LevelBar, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
6061
|
-
LevelBar.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: LevelBar, selector: "audio-levelbar", inputs: { streamingMode: "streamingMode", displayLevelInfos: "displayLevelInfos" }, host: { listeners: { "scroll": "onScroll($event)", "window:resize": "onResize($event)" } }, viewQueries: [{ propertyName: "virtualCanvasRef", first: true, predicate: ["virtualCanvas"], descendants: true, static: true }, { propertyName: "liveLevelCanvasRef", first: true, predicate: ["levelbar"], descendants: true, static: true }, { propertyName: "markerCanvasRef", first: true, predicate: ["markerCanvas"], descendants: true, static: true }], ngImport: i0, template: `
|
|
7207
|
+
LevelBar.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: LevelBar, selector: "audio-levelbar", inputs: { streamingMode: "streamingMode", displayLevelInfos: "displayLevelInfos", stateLoading: "stateLoading" }, host: { listeners: { "scroll": "onScroll($event)", "window:resize": "onResize($event)" } }, viewQueries: [{ propertyName: "virtualCanvasRef", first: true, predicate: ["virtualCanvas"], descendants: true, static: true }, { propertyName: "liveLevelCanvasRef", first: true, predicate: ["levelbar"], descendants: true, static: true }, { propertyName: "markerCanvasRef", first: true, predicate: ["markerCanvas"], descendants: true, static: true }], ngImport: i0, template: `
|
|
6062
7208
|
<div #virtualCanvas>
|
|
6063
7209
|
<canvas #levelbar></canvas>
|
|
6064
7210
|
<canvas #markerCanvas></canvas>
|
|
@@ -6115,6 +7261,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
6115
7261
|
type: Input
|
|
6116
7262
|
}], displayLevelInfos: [{
|
|
6117
7263
|
type: Input
|
|
7264
|
+
}], stateLoading: [{
|
|
7265
|
+
type: Input
|
|
6118
7266
|
}], onResize: [{
|
|
6119
7267
|
type: HostListener,
|
|
6120
7268
|
args: ['window:resize', ['$event']]
|
|
@@ -6705,47 +7853,66 @@ class PeakLevelInterceptor {
|
|
|
6705
7853
|
}
|
|
6706
7854
|
}
|
|
6707
7855
|
class LevelMeasure {
|
|
7856
|
+
//private bufferLevelInfos=new Array<LevelInfo>();
|
|
7857
|
+
//private peakLevelInfo!:LevelInfo;
|
|
6708
7858
|
constructor() {
|
|
6709
7859
|
this.worker = null;
|
|
6710
7860
|
this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
|
|
6711
7861
|
}
|
|
6712
|
-
calcBufferLevelInfos(
|
|
7862
|
+
calcBufferLevelInfos(audioDataHolder, bufferTimeLength) {
|
|
6713
7863
|
return new Promise((resolve) => {
|
|
6714
|
-
let chs =
|
|
6715
|
-
let bufferFrameLength = Math.round(
|
|
6716
|
-
let
|
|
7864
|
+
let chs = audioDataHolder.numberOfChannels;
|
|
7865
|
+
let bufferFrameLength = Math.round(audioDataHolder.sampleRate * bufferTimeLength);
|
|
7866
|
+
let ais = audioDataHolder.audioInputStream();
|
|
7867
|
+
let audioBuffers = new Array(chs);
|
|
7868
|
+
let trBuffers = new Array(chs);
|
|
6717
7869
|
for (let ch = 0; ch < chs; ch++) {
|
|
6718
|
-
|
|
6719
|
-
let adChCopy = new Float32Array(adCh.length);
|
|
6720
|
-
adChCopy.set(adCh);
|
|
6721
|
-
buffers[ch] = adChCopy.buffer;
|
|
7870
|
+
audioBuffers[ch] = new Float32Array(bufferFrameLength);
|
|
6722
7871
|
}
|
|
7872
|
+
let bufferLevelInfos = new Array();
|
|
7873
|
+
let peakLevelInfo = new LevelInfo(chs);
|
|
6723
7874
|
this.worker = new Worker(this.workerURL);
|
|
6724
7875
|
this.worker.onmessage = (me) => {
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
7876
|
+
if (me.data.linLevelBuffers) {
|
|
7877
|
+
let linLevelArrs = new Array(chs);
|
|
7878
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
7879
|
+
linLevelArrs[ch] = new Float32Array(me.data.linLevelBuffers[ch]);
|
|
7880
|
+
}
|
|
7881
|
+
let bufferCount = Math.ceil(me.data.frameLength / me.data.bufferFrameLength);
|
|
7882
|
+
let framePos = 0;
|
|
7883
|
+
for (let bi = 0; bi < bufferCount; bi++) {
|
|
7884
|
+
let minLevels = new Array(chs);
|
|
7885
|
+
let maxLevels = new Array(chs);
|
|
7886
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
7887
|
+
let linLvlArrPos = bi * 2;
|
|
7888
|
+
minLevels[ch] = linLevelArrs[ch][linLvlArrPos];
|
|
7889
|
+
maxLevels[ch] = linLevelArrs[ch][linLvlArrPos + 1];
|
|
7890
|
+
}
|
|
7891
|
+
let bli = new LevelInfo(chs, framePos, me.data.bufferFrameLength, minLevels, maxLevels);
|
|
7892
|
+
bufferLevelInfos.push(bli);
|
|
7893
|
+
peakLevelInfo.merge(bli);
|
|
7894
|
+
}
|
|
7895
|
+
}
|
|
7896
|
+
if (me.data.eod === true) {
|
|
7897
|
+
// end of data, terminate worker and return result
|
|
7898
|
+
this.terminateWorker();
|
|
7899
|
+
resolve(new LevelInfos(bufferLevelInfos, peakLevelInfo));
|
|
7900
|
+
}
|
|
7901
|
+
else {
|
|
7902
|
+
let read = ais.read(audioBuffers);
|
|
6736
7903
|
for (let ch = 0; ch < chs; ch++) {
|
|
6737
|
-
let
|
|
6738
|
-
|
|
6739
|
-
maxLevels[ch] = linLevelArrs[ch][linLvlArrPos + 1];
|
|
7904
|
+
let copy = new Float32Array(audioBuffers[ch]);
|
|
7905
|
+
trBuffers[ch] = copy.buffer;
|
|
6740
7906
|
}
|
|
6741
|
-
|
|
6742
|
-
bufferLevelInfos.push(bli);
|
|
6743
|
-
peakLevelInfo.merge(bli);
|
|
7907
|
+
this.worker?.postMessage({ bufferFrameLength: bufferFrameLength, audioData: trBuffers, chs: chs, len: read }, trBuffers);
|
|
6744
7908
|
}
|
|
6745
|
-
this.terminateWorker();
|
|
6746
|
-
resolve(new LevelInfos(bufferLevelInfos, peakLevelInfo));
|
|
6747
7909
|
};
|
|
6748
|
-
|
|
7910
|
+
let read = ais.read(audioBuffers);
|
|
7911
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
7912
|
+
let copy = new Float32Array(audioBuffers[ch]);
|
|
7913
|
+
trBuffers[ch] = copy.buffer;
|
|
7914
|
+
}
|
|
7915
|
+
this.worker?.postMessage({ bufferFrameLength: bufferFrameLength, audioData: trBuffers, chs: chs, len: read }, trBuffers);
|
|
6749
7916
|
});
|
|
6750
7917
|
}
|
|
6751
7918
|
terminateWorker() {
|
|
@@ -6756,40 +7923,63 @@ class LevelMeasure {
|
|
|
6756
7923
|
*/
|
|
6757
7924
|
workerFunction() {
|
|
6758
7925
|
self.onmessage = function (msg) {
|
|
6759
|
-
let
|
|
7926
|
+
let len = msg.data.len;
|
|
6760
7927
|
let bufferFrameLength = msg.data.bufferFrameLength;
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
7928
|
+
if (len == -1) {
|
|
7929
|
+
// start
|
|
7930
|
+
postMessage({
|
|
7931
|
+
eod: false,
|
|
7932
|
+
bufferFrameLength: bufferFrameLength,
|
|
7933
|
+
frameLength: len
|
|
7934
|
+
});
|
|
6765
7935
|
}
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
7936
|
+
if (len == 0) {
|
|
7937
|
+
postMessage({
|
|
7938
|
+
eod: true,
|
|
7939
|
+
bufferFrameLength: bufferFrameLength,
|
|
7940
|
+
frameLength: len
|
|
7941
|
+
});
|
|
6770
7942
|
}
|
|
6771
|
-
|
|
7943
|
+
else {
|
|
7944
|
+
let chs = msg.data.chs;
|
|
7945
|
+
let audioData = new Array(chs);
|
|
7946
|
+
let linLevels = new Array(chs);
|
|
6772
7947
|
for (let ch = 0; ch < chs; ch++) {
|
|
6773
|
-
|
|
6774
|
-
for (let s = 0; s < frameLength; s++) {
|
|
6775
|
-
let bi = Math.floor(s / bufferFrameLength);
|
|
6776
|
-
let lvlArrPos = bi * 2;
|
|
6777
|
-
//let bs = s % bufferFrameLength;
|
|
6778
|
-
let chSample = chData[s];
|
|
6779
|
-
if (chSample < linLevels[ch][lvlArrPos]) {
|
|
6780
|
-
linLevels[ch][lvlArrPos] = chSample;
|
|
6781
|
-
}
|
|
6782
|
-
lvlArrPos++;
|
|
6783
|
-
if (chSample > linLevels[ch][lvlArrPos]) {
|
|
6784
|
-
linLevels[ch][lvlArrPos] = chSample;
|
|
6785
|
-
}
|
|
6786
|
-
}
|
|
7948
|
+
audioData[ch] = new Float32Array(msg.data.audioData[ch]);
|
|
6787
7949
|
}
|
|
6788
|
-
|
|
7950
|
+
//let frameLength = audioData[0].length;
|
|
7951
|
+
let bufferCount = Math.ceil(len / bufferFrameLength);
|
|
6789
7952
|
for (let ch = 0; ch < chs; ch++) {
|
|
6790
|
-
|
|
7953
|
+
linLevels[ch] = new Float32Array(bufferCount * 2);
|
|
7954
|
+
}
|
|
7955
|
+
if (audioData && chs > 0) {
|
|
7956
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
7957
|
+
let chData = audioData[ch];
|
|
7958
|
+
for (let s = 0; s < len; s++) {
|
|
7959
|
+
let bi = Math.floor(s / bufferFrameLength);
|
|
7960
|
+
let lvlArrPos = bi * 2;
|
|
7961
|
+
//let bs = s % bufferFrameLength;
|
|
7962
|
+
let chSample = chData[s];
|
|
7963
|
+
if (chSample < linLevels[ch][lvlArrPos]) {
|
|
7964
|
+
linLevels[ch][lvlArrPos] = chSample;
|
|
7965
|
+
}
|
|
7966
|
+
lvlArrPos++;
|
|
7967
|
+
if (chSample > linLevels[ch][lvlArrPos]) {
|
|
7968
|
+
linLevels[ch][lvlArrPos] = chSample;
|
|
7969
|
+
}
|
|
7970
|
+
}
|
|
7971
|
+
}
|
|
7972
|
+
const linLevelBufs = new Array(chs);
|
|
7973
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
7974
|
+
linLevelBufs[ch] = linLevels[ch].buffer;
|
|
7975
|
+
}
|
|
7976
|
+
postMessage({
|
|
7977
|
+
eod: false,
|
|
7978
|
+
bufferFrameLength: bufferFrameLength,
|
|
7979
|
+
frameLength: len,
|
|
7980
|
+
linLevelBuffers: linLevelBufs
|
|
7981
|
+
}, linLevelBufs);
|
|
6791
7982
|
}
|
|
6792
|
-
postMessage({ bufferFrameLength: bufferFrameLength, frameLength: frameLength, linLevelBuffers: linLevelBufs }, linLevelBufs);
|
|
6793
7983
|
}
|
|
6794
7984
|
};
|
|
6795
7985
|
}
|
|
@@ -7161,7 +8351,7 @@ RecordingItemDisplay.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", v
|
|
|
7161
8351
|
<audio-levelbar fxFlex="1 0 1" [streamingMode]="streamingMode" [displayLevelInfos]="_displayLevelInfos"></audio-levelbar>
|
|
7162
8352
|
<spr-recordingitemcontrols fxFlex="0 0 0" [audioLoaded]="displayAudioBuffer!==null" [playStartAction]="playStartAction" [playStopAction]="playStopAction" [peakDbLvl]="peakDbLvl" [agc]="_agc" (onShowRecordingDetails)="onShowRecordingDetails.emit()"></spr-recordingitemcontrols>
|
|
7163
8353
|
</div>
|
|
7164
|
-
`, isInline: true, styles: ["div{width:100%;background:darkgray;padding:4px;box-sizing:border-box;flex-wrap:nowrap}\n", "audio-levelbar{box-sizing:border-box}\n"], components: [{ type: LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos"] }, { type: RecordingItemControls, selector: "spr-recordingitemcontrols", inputs: ["audioSignalCollapsed", "enableDownload", "peakDbLvl", "agc", "audioLoaded", "playStartAction", "playStopAction", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }], directives: [{ type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i3$1.DefaultStyleDirective, selector: " [ngStyle], [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl], [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl], [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]", inputs: ["ngStyle", "ngStyle.xs", "ngStyle.sm", "ngStyle.md", "ngStyle.lg", "ngStyle.xl", "ngStyle.lt-sm", "ngStyle.lt-md", "ngStyle.lt-lg", "ngStyle.lt-xl", "ngStyle.gt-xs", "ngStyle.gt-sm", "ngStyle.gt-md", "ngStyle.gt-lg"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i8.DefaultFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }] });
|
|
8354
|
+
`, isInline: true, styles: ["div{width:100%;background:darkgray;padding:4px;box-sizing:border-box;flex-wrap:nowrap}\n", "audio-levelbar{box-sizing:border-box}\n"], components: [{ type: LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos", "stateLoading"] }, { type: RecordingItemControls, selector: "spr-recordingitemcontrols", inputs: ["audioSignalCollapsed", "enableDownload", "peakDbLvl", "agc", "audioLoaded", "playStartAction", "playStopAction", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }], directives: [{ type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i3$1.DefaultStyleDirective, selector: " [ngStyle], [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl], [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl], [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]", inputs: ["ngStyle", "ngStyle.xs", "ngStyle.sm", "ngStyle.md", "ngStyle.lg", "ngStyle.xl", "ngStyle.lt-sm", "ngStyle.lt-md", "ngStyle.lt-lg", "ngStyle.lt-xl", "ngStyle.gt-xs", "ngStyle.gt-sm", "ngStyle.gt-md", "ngStyle.gt-lg"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i8.DefaultFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }] });
|
|
7165
8355
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingItemDisplay, decorators: [{
|
|
7166
8356
|
type: Component,
|
|
7167
8357
|
args: [{
|
|
@@ -7207,89 +8397,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
7207
8397
|
type: Input
|
|
7208
8398
|
}] } });
|
|
7209
8399
|
|
|
7210
|
-
class Float32ArrayChunkerOutStream {
|
|
7211
|
-
constructor(outStream) {
|
|
7212
|
-
this.outStream = outStream;
|
|
7213
|
-
this.bufs = new Array();
|
|
7214
|
-
this._channels = 0;
|
|
7215
|
-
this._chunkSize = 0;
|
|
7216
|
-
this.filled = 0;
|
|
7217
|
-
this.receivedFrames = 0;
|
|
7218
|
-
this.sentFrames = 0;
|
|
7219
|
-
}
|
|
7220
|
-
createBuffers() {
|
|
7221
|
-
this.bufs = new Array(this._channels);
|
|
7222
|
-
for (let ch = 0; ch < this._channels; ch++) {
|
|
7223
|
-
this.bufs[ch] = new Float32Array(this._chunkSize);
|
|
7224
|
-
}
|
|
7225
|
-
}
|
|
7226
|
-
set chunkSize(chunkSize) {
|
|
7227
|
-
this._chunkSize = chunkSize;
|
|
7228
|
-
this.createBuffers();
|
|
7229
|
-
}
|
|
7230
|
-
get chunkSize() {
|
|
7231
|
-
return this._chunkSize;
|
|
7232
|
-
}
|
|
7233
|
-
set channels(channels) {
|
|
7234
|
-
this._channels = channels;
|
|
7235
|
-
this.createBuffers();
|
|
7236
|
-
}
|
|
7237
|
-
get channels() {
|
|
7238
|
-
return this._channels;
|
|
7239
|
-
}
|
|
7240
|
-
available() {
|
|
7241
|
-
return this._chunkSize - this.filled;
|
|
7242
|
-
}
|
|
7243
|
-
write(buffers) {
|
|
7244
|
-
let copied = 0;
|
|
7245
|
-
if (buffers.length > 0) {
|
|
7246
|
-
let buffersLen = buffers[0].length;
|
|
7247
|
-
this.receivedFrames += buffersLen;
|
|
7248
|
-
let avail = buffersLen;
|
|
7249
|
-
// Fill out buffers until all values copied
|
|
7250
|
-
while (avail > 0) {
|
|
7251
|
-
let toFill = this._chunkSize - this.filled;
|
|
7252
|
-
if (toFill > avail) {
|
|
7253
|
-
toFill = avail;
|
|
7254
|
-
}
|
|
7255
|
-
let sliceEnd = copied + toFill;
|
|
7256
|
-
// Firefox on Android sends only the first channel
|
|
7257
|
-
for (let ch = 0; ch < buffersLen; ch++) {
|
|
7258
|
-
if (buffers[ch]) {
|
|
7259
|
-
let cpPrt = buffers[ch].slice(copied, sliceEnd);
|
|
7260
|
-
let buf = this.bufs[ch];
|
|
7261
|
-
buf.set(cpPrt, this.filled);
|
|
7262
|
-
}
|
|
7263
|
-
}
|
|
7264
|
-
copied += toFill;
|
|
7265
|
-
avail -= toFill;
|
|
7266
|
-
this.filled += toFill;
|
|
7267
|
-
if (this.filled == this._chunkSize) {
|
|
7268
|
-
this.outStream.write(this.bufs);
|
|
7269
|
-
this.sentFrames += this.filled;
|
|
7270
|
-
this.filled = 0;
|
|
7271
|
-
}
|
|
7272
|
-
}
|
|
7273
|
-
}
|
|
7274
|
-
return copied;
|
|
7275
|
-
}
|
|
7276
|
-
flush() {
|
|
7277
|
-
if (this.filled > 0) {
|
|
7278
|
-
let restBufs = new Array(this._channels);
|
|
7279
|
-
for (let ch = 0; ch < this._channels; ch++) {
|
|
7280
|
-
restBufs[ch] = this.bufs[ch].slice(0, this.filled);
|
|
7281
|
-
}
|
|
7282
|
-
this.outStream.write(restBufs);
|
|
7283
|
-
this.outStream.flush();
|
|
7284
|
-
this.sentFrames += this.filled;
|
|
7285
|
-
this.filled = 0;
|
|
7286
|
-
}
|
|
7287
|
-
}
|
|
7288
|
-
close() {
|
|
7289
|
-
this.outStream.close();
|
|
7290
|
-
}
|
|
7291
|
-
}
|
|
7292
|
-
|
|
7293
8400
|
class SequenceAudioFloat32ChunkerOutStream extends Float32ArrayChunkerOutStream {
|
|
7294
8401
|
constructor(outStream, chunkDurationSeconds) {
|
|
7295
8402
|
super(outStream);
|
|
@@ -7298,11 +8405,11 @@ class SequenceAudioFloat32ChunkerOutStream extends Float32ArrayChunkerOutStream
|
|
|
7298
8405
|
this.sequenceAudioFloat32OutStream = outStream;
|
|
7299
8406
|
}
|
|
7300
8407
|
setFormat(channels, sampleRate) {
|
|
7301
|
-
console.debug("SequenceAudioFloat32ChunkerOutStream:setFormat(channels: "
|
|
8408
|
+
//console.debug("SequenceAudioFloat32ChunkerOutStream:setFormat(channels: "+channels+",sampleRate: "+sampleRate+")")
|
|
7302
8409
|
this.channels = channels;
|
|
7303
8410
|
this.sampleRate = sampleRate;
|
|
7304
8411
|
this.chunkSize = Math.round(sampleRate * this.chunkDurationSeconds);
|
|
7305
|
-
console.debug("SequenceAudioFloat32ChunkerOutStream: chunkSize: "
|
|
8412
|
+
//console.debug("SequenceAudioFloat32ChunkerOutStream: chunkSize: "+this.chunkSize);
|
|
7306
8413
|
this.sequenceAudioFloat32OutStream.setFormat(channels, sampleRate);
|
|
7307
8414
|
}
|
|
7308
8415
|
nextStream() {
|
|
@@ -7350,12 +8457,24 @@ class SequenceAudioFloat32OutStreamMultiplier {
|
|
|
7350
8457
|
|
|
7351
8458
|
const WAKE_LOCK_VIDEO_MP4_URI = 'data:video/mp4;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAAG31tZGF0AAACrgYF//+q3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE1NSByMjkxNyAwYTg0ZDk4IC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxOCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDExMyBtZT1oZXggc3VibWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MSA4eDhkY3Q9MSBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0tMiB0aHJlYWRzPTIgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTI1IHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAJ2WIhAA7//73Tr8Cm1TCKgOSVwr2yqQmr/tBF6luwQMZUeHtiyA0IQAAAAtBmiRsQ7/+qZYe8N4CAExhdmM1OC41NC4xMDAAQiAIwRg4AAAACEGeQniF/wMzIRAEYIwcIRAEYIwcAAAACAGeYXRCvwQkIRAEYIwcIRAEYIwcAAAACAGeY2pCvwQlIRAEYIwcIRAEYIwcAAAAEUGaaEmo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AAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAEAAAAAChzdHNjAAAAAAAAAAIAAAABAAAAAgAAAAEAAAACAAAAAQAAAAEAAAP8c3RzegAAAAAAAAAAAAAA+gAAAt0AAAAPAAAADAAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABUAAAAOAAAADAAAAAwAAAAVAAAADgAAAAwAAAAMAAAAFQAAAA4AAAAMAAAADAAAABQAAAP0c3RjbwAAAAAAAAD5AAAAMAAAAzMA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AAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAXAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAA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';
|
|
7352
8459
|
|
|
8460
|
+
/**
|
|
8461
|
+
* Utility to prevent devices from screen lock.
|
|
8462
|
+
* If supported uses the HTML5 wake lock API, if not, plays an invisible video.
|
|
8463
|
+
*
|
|
8464
|
+
* Note: I've used the GitHub project richtr/nosleep.js for that. The worked in most cases, but failed sometimes on an
|
|
8465
|
+
* Android smartphone.
|
|
8466
|
+
*/
|
|
7353
8467
|
class WakeLockManager {
|
|
7354
8468
|
constructor() {
|
|
7355
8469
|
this.wakeLockApiSupported = false;
|
|
8470
|
+
this.wakeLockRetryCount = 0;
|
|
8471
|
+
this.randomSeekRequired = false;
|
|
7356
8472
|
this._behaviorSubject = new BehaviorSubject(false);
|
|
8473
|
+
this.VIDEO_LENGTH = 10;
|
|
7357
8474
|
this.wakeLockApiSupported = ('wakeLock' in navigator);
|
|
7358
8475
|
console.debug("Wake lock API supported: " + this.wakeLockApiSupported);
|
|
8476
|
+
this.userAgent = UserAgentBuilder.userAgent();
|
|
8477
|
+
this.randomSeekRequired = (this.userAgent?.detectedBrowser === Browser$1.Safari);
|
|
7359
8478
|
}
|
|
7360
8479
|
get behaviorSubject() {
|
|
7361
8480
|
return this._behaviorSubject;
|
|
@@ -7365,6 +8484,7 @@ class WakeLockManager {
|
|
|
7365
8484
|
//@ts-ignore
|
|
7366
8485
|
navigator.wakeLock.request('screen').then((wls) => {
|
|
7367
8486
|
this.wakeLockSentinel = wls;
|
|
8487
|
+
this.wakeLockRetryCount = 0;
|
|
7368
8488
|
console.debug('Wake lock screen request successful.');
|
|
7369
8489
|
this._behaviorSubject.next(true);
|
|
7370
8490
|
}).catch((reason) => {
|
|
@@ -7375,7 +8495,7 @@ class WakeLockManager {
|
|
|
7375
8495
|
else {
|
|
7376
8496
|
if (!this.mp4VideoElement) {
|
|
7377
8497
|
this.mp4VideoElement = document.createElement('video');
|
|
7378
|
-
this.mp4VideoElement.loop =
|
|
8498
|
+
this.mp4VideoElement.loop = !this.randomSeekRequired;
|
|
7379
8499
|
this.mp4VideoElement.playsInline = true;
|
|
7380
8500
|
this.mp4VideoElement.src = WAKE_LOCK_VIDEO_MP4_URI;
|
|
7381
8501
|
this.mp4VideoElement.addEventListener('play', (ev) => {
|
|
@@ -7390,13 +8510,44 @@ class WakeLockManager {
|
|
|
7390
8510
|
console.debug('Wake lock video pause.');
|
|
7391
8511
|
this._behaviorSubject.next(false);
|
|
7392
8512
|
});
|
|
8513
|
+
if (this.randomSeekRequired) {
|
|
8514
|
+
this.mp4VideoElement.addEventListener('timeupdate', (ev) => {
|
|
8515
|
+
if (this.randomSeekRequired && this.mp4VideoElement) {
|
|
8516
|
+
let thirdVideoLen = this.VIDEO_LENGTH / 3;
|
|
8517
|
+
if (this.mp4VideoElement.currentTime > thirdVideoLen * 2) {
|
|
8518
|
+
let newPos = (thirdVideoLen * Math.random());
|
|
8519
|
+
//console.debug('Wake lock random seek to '+newPos+'s');
|
|
8520
|
+
this.mp4VideoElement.currentTime = newPos;
|
|
8521
|
+
}
|
|
8522
|
+
}
|
|
8523
|
+
});
|
|
8524
|
+
}
|
|
7393
8525
|
this.mp4VideoElement.addEventListener('error', (ev) => {
|
|
7394
8526
|
console.debug('Wake lock video error: ' + ev.message);
|
|
7395
8527
|
this._behaviorSubject.error(ev.error);
|
|
7396
8528
|
});
|
|
7397
8529
|
console.debug('Added listeners to wake lock video.');
|
|
7398
8530
|
}
|
|
7399
|
-
this.
|
|
8531
|
+
this.startWakeLockVideo();
|
|
8532
|
+
}
|
|
8533
|
+
}
|
|
8534
|
+
startWakeLockVideo() {
|
|
8535
|
+
if (this.mp4VideoElement) {
|
|
8536
|
+
this.mp4VideoElement.play().then(() => {
|
|
8537
|
+
this.wakeLockRetryCount = 0;
|
|
8538
|
+
}).catch((err) => {
|
|
8539
|
+
console.debug('Failed starting wake lock video!');
|
|
8540
|
+
if (this.wakeLockRetryCount < 1) {
|
|
8541
|
+
window.setTimeout(() => {
|
|
8542
|
+
this.wakeLockRetryCount++;
|
|
8543
|
+
console.debug('Retry wake lock video #' + this.wakeLockRetryCount);
|
|
8544
|
+
this.startWakeLockVideo();
|
|
8545
|
+
}, 4000);
|
|
8546
|
+
}
|
|
8547
|
+
else {
|
|
8548
|
+
console.debug('Giving up to try to start wake lock video.');
|
|
8549
|
+
}
|
|
8550
|
+
});
|
|
7400
8551
|
}
|
|
7401
8552
|
}
|
|
7402
8553
|
disableWakeLock() {
|
|
@@ -7464,7 +8615,8 @@ class ChunkManager {
|
|
|
7464
8615
|
}
|
|
7465
8616
|
}
|
|
7466
8617
|
let BasicRecorder = class BasicRecorder {
|
|
7467
|
-
constructor(dialog, sessionService, uploader, config) {
|
|
8618
|
+
constructor(changeDetectorRef, dialog, sessionService, uploader, config) {
|
|
8619
|
+
this.changeDetectorRef = changeDetectorRef;
|
|
7468
8620
|
this.dialog = dialog;
|
|
7469
8621
|
this.sessionService = sessionService;
|
|
7470
8622
|
this.uploader = uploader;
|
|
@@ -7479,14 +8631,15 @@ let BasicRecorder = class BasicRecorder {
|
|
|
7479
8631
|
this._selectedDeviceId = undefined;
|
|
7480
8632
|
this._channelCount = 2;
|
|
7481
8633
|
this._session = null;
|
|
8634
|
+
this._recordingFile = null;
|
|
7482
8635
|
this.startedDate = null;
|
|
7483
8636
|
this.uploadProgress = 100;
|
|
7484
8637
|
this.uploadStatus = 'ok';
|
|
7485
8638
|
this.audioSignalCollapsed = true;
|
|
7486
8639
|
this.peakLevelInDb = MIN_DB_LEVEL;
|
|
7487
|
-
this.displayLevelInfos = null;
|
|
7488
8640
|
this.displayAudioClip = null;
|
|
7489
8641
|
this.audioFetchSubscription = null;
|
|
8642
|
+
this.audioFetching = false;
|
|
7490
8643
|
this.destroyed = false;
|
|
7491
8644
|
this.navigationDisabled = true;
|
|
7492
8645
|
// Default: Disabled chunked upload
|
|
@@ -7580,6 +8733,28 @@ let BasicRecorder = class BasicRecorder {
|
|
|
7580
8733
|
this.ac.audioOutStream = outStream;
|
|
7581
8734
|
}
|
|
7582
8735
|
}
|
|
8736
|
+
showRecording() {
|
|
8737
|
+
this._controlAudioPlayer.stop();
|
|
8738
|
+
if (this.displayAudioClip) {
|
|
8739
|
+
let dap = this.displayAudioClip;
|
|
8740
|
+
let adh = dap.audioDataHolder;
|
|
8741
|
+
if (adh) {
|
|
8742
|
+
this.levelMeasure.calcBufferLevelInfos(adh, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
|
|
8743
|
+
dap.levelInfos = levelInfos;
|
|
8744
|
+
this.changeDetectorRef.detectChanges();
|
|
8745
|
+
});
|
|
8746
|
+
}
|
|
8747
|
+
this.playStartAction.disabled = false;
|
|
8748
|
+
}
|
|
8749
|
+
else {
|
|
8750
|
+
this.playStartAction.disabled = true;
|
|
8751
|
+
// Collapse audio signal display if open
|
|
8752
|
+
if (!this.audioSignalCollapsed) {
|
|
8753
|
+
this.audioSignalCollapsed = true;
|
|
8754
|
+
}
|
|
8755
|
+
}
|
|
8756
|
+
this.changeDetectorRef.detectChanges();
|
|
8757
|
+
}
|
|
7583
8758
|
start() {
|
|
7584
8759
|
this.statusAlertType = 'info';
|
|
7585
8760
|
this.statusMsg = 'Starting session...';
|
|
@@ -7788,24 +8963,21 @@ let BasicRecorder = class BasicRecorder {
|
|
|
7788
8963
|
}
|
|
7789
8964
|
startItem() {
|
|
7790
8965
|
this.startedDate = null;
|
|
7791
|
-
|
|
8966
|
+
this.enableWakeLockCond();
|
|
7792
8967
|
this.rfUuid = UUID.generate();
|
|
7793
8968
|
this.transportActions.startAction.disabled = true;
|
|
7794
8969
|
this.transportActions.pauseAction.disabled = true;
|
|
7795
|
-
if (this.readonly) {
|
|
7796
|
-
return;
|
|
7797
|
-
}
|
|
7798
8970
|
}
|
|
7799
8971
|
started() {
|
|
7800
|
-
this.enableWakeLockCond();
|
|
8972
|
+
//this.enableWakeLockCond();
|
|
7801
8973
|
if (!this.startedDate) {
|
|
7802
8974
|
this.startedDate = new Date();
|
|
7803
8975
|
}
|
|
7804
8976
|
this.transportActions.startAction.disabled = true;
|
|
7805
8977
|
}
|
|
7806
|
-
postRecording(wavFile, recUrl) {
|
|
8978
|
+
postRecording(wavFile, recUrl, rf) {
|
|
7807
8979
|
let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
|
|
7808
|
-
let ul = new Upload(wavBlob, recUrl);
|
|
8980
|
+
let ul = new Upload(wavBlob, recUrl, rf);
|
|
7809
8981
|
this.uploader.queueUpload(ul);
|
|
7810
8982
|
}
|
|
7811
8983
|
postAudioStreamStart() {
|
|
@@ -7839,42 +9011,334 @@ let BasicRecorder = class BasicRecorder {
|
|
|
7839
9011
|
if (this.config && this.config.apiEndPoint) {
|
|
7840
9012
|
apiEndPoint = this.config.apiEndPoint;
|
|
7841
9013
|
}
|
|
7842
|
-
if (apiEndPoint !== '') {
|
|
7843
|
-
apiEndPoint = apiEndPoint + '/';
|
|
9014
|
+
if (apiEndPoint !== '') {
|
|
9015
|
+
apiEndPoint = apiEndPoint + '/';
|
|
9016
|
+
}
|
|
9017
|
+
let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
|
|
9018
|
+
let recUrl = sessionsUrl + '/' + this.session?.sessionId + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/concatChunksRequest';
|
|
9019
|
+
let fd = new FormData();
|
|
9020
|
+
fd.set('uuid', this.rfUuid);
|
|
9021
|
+
fd.set('chunkCount', chunkCount.toString());
|
|
9022
|
+
let ul = new Upload(fd, recUrl, this._recordingFile);
|
|
9023
|
+
this.uploader.queueUpload(ul);
|
|
9024
|
+
console.debug("Queued for upload: " + this._recordingFile);
|
|
9025
|
+
}
|
|
9026
|
+
else {
|
|
9027
|
+
console.error("Recording file UUID not set!");
|
|
9028
|
+
}
|
|
9029
|
+
}
|
|
9030
|
+
closed() {
|
|
9031
|
+
this.statusAlertType = 'info';
|
|
9032
|
+
this.statusMsg = 'Session closed.';
|
|
9033
|
+
}
|
|
9034
|
+
error(msg = 'An unknown error occured during recording.', advice = 'Please retry.') {
|
|
9035
|
+
this.statusMsg = 'ERROR: Recording.';
|
|
9036
|
+
this.statusAlertType = 'error';
|
|
9037
|
+
this.dialog.open(MessageDialog, {
|
|
9038
|
+
data: {
|
|
9039
|
+
type: 'error',
|
|
9040
|
+
title: 'Recording error',
|
|
9041
|
+
msg: msg,
|
|
9042
|
+
advice: advice
|
|
9043
|
+
}
|
|
9044
|
+
});
|
|
9045
|
+
}
|
|
9046
|
+
};
|
|
9047
|
+
// Enable only for developemnt/debug purposes of array audio buffers !!
|
|
9048
|
+
BasicRecorder.FORCE_ARRRAY_AUDIO_BUFFER = false;
|
|
9049
|
+
BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS = 30;
|
|
9050
|
+
BasicRecorder = __decorate([
|
|
9051
|
+
__param(4, Inject(SPEECHRECORDER_CONFIG))
|
|
9052
|
+
], BasicRecorder);
|
|
9053
|
+
|
|
9054
|
+
class AudioDataHolder {
|
|
9055
|
+
constructor(_buffer, _arrayBuffer = null) {
|
|
9056
|
+
this._buffer = _buffer;
|
|
9057
|
+
this._arrayBuffer = _arrayBuffer;
|
|
9058
|
+
this._numberOfChannels = 0;
|
|
9059
|
+
this._sampleRate = 0;
|
|
9060
|
+
this._frameLen = 0;
|
|
9061
|
+
this._duration = 0;
|
|
9062
|
+
if (this._buffer && this._arrayBuffer) {
|
|
9063
|
+
throw Error('Only one of either audio buffer or array audio buffer must be set!');
|
|
9064
|
+
}
|
|
9065
|
+
if (this._buffer || this._arrayBuffer) {
|
|
9066
|
+
if (this._buffer) {
|
|
9067
|
+
this._numberOfChannels = this._buffer.numberOfChannels;
|
|
9068
|
+
this._sampleRate = this._buffer.sampleRate;
|
|
9069
|
+
this._frameLen = this._buffer.length;
|
|
9070
|
+
this._duration = this._frameLen / this._sampleRate;
|
|
9071
|
+
}
|
|
9072
|
+
else if (this._arrayBuffer) {
|
|
9073
|
+
this._numberOfChannels = this._arrayBuffer.channelCount;
|
|
9074
|
+
this._sampleRate = this._arrayBuffer.sampleRate;
|
|
9075
|
+
this._frameLen = this._arrayBuffer.frameLen;
|
|
9076
|
+
this._duration = this._frameLen / this._sampleRate;
|
|
9077
|
+
}
|
|
9078
|
+
}
|
|
9079
|
+
else {
|
|
9080
|
+
throw Error(AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG);
|
|
9081
|
+
}
|
|
9082
|
+
}
|
|
9083
|
+
get duration() {
|
|
9084
|
+
return this._duration;
|
|
9085
|
+
}
|
|
9086
|
+
get sampleRate() {
|
|
9087
|
+
return this._sampleRate;
|
|
9088
|
+
}
|
|
9089
|
+
get numberOfChannels() {
|
|
9090
|
+
return this._numberOfChannels;
|
|
9091
|
+
}
|
|
9092
|
+
get frameLen() {
|
|
9093
|
+
return this._frameLen;
|
|
9094
|
+
}
|
|
9095
|
+
sampleCounts() {
|
|
9096
|
+
return this._numberOfChannels * this._frameLen;
|
|
9097
|
+
}
|
|
9098
|
+
frames(framePos, frameLen, bufs) {
|
|
9099
|
+
let read = 0;
|
|
9100
|
+
if (this._buffer) {
|
|
9101
|
+
let toRead = frameLen;
|
|
9102
|
+
if (this._buffer.length < framePos + frameLen) {
|
|
9103
|
+
toRead = this._buffer.length - framePos;
|
|
9104
|
+
}
|
|
9105
|
+
for (let ch = 0; ch < bufs.length; ch++) {
|
|
9106
|
+
let chData = this._buffer.getChannelData(ch);
|
|
9107
|
+
for (let i = 0; i < toRead; i++) {
|
|
9108
|
+
bufs[ch][i] = chData[framePos + i];
|
|
9109
|
+
}
|
|
9110
|
+
}
|
|
9111
|
+
read = toRead;
|
|
9112
|
+
}
|
|
9113
|
+
else if (this._arrayBuffer) {
|
|
9114
|
+
read = this._arrayBuffer.frames(framePos, frameLen, bufs);
|
|
9115
|
+
}
|
|
9116
|
+
return read;
|
|
9117
|
+
}
|
|
9118
|
+
audioInputStream() {
|
|
9119
|
+
if (this._buffer) {
|
|
9120
|
+
return new AudioBufferInputStream(this._buffer);
|
|
9121
|
+
}
|
|
9122
|
+
if (this._arrayBuffer) {
|
|
9123
|
+
return new ArrayAudioBufferInputStream(this._arrayBuffer);
|
|
9124
|
+
}
|
|
9125
|
+
throw Error(AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG);
|
|
9126
|
+
}
|
|
9127
|
+
get buffer() {
|
|
9128
|
+
return this._buffer;
|
|
9129
|
+
}
|
|
9130
|
+
get arrayBuffer() {
|
|
9131
|
+
return this._arrayBuffer;
|
|
9132
|
+
}
|
|
9133
|
+
}
|
|
9134
|
+
AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG = 'One of either audio buffer or array audio buffer must be set!';
|
|
9135
|
+
class AudioBufferInputStream {
|
|
9136
|
+
constructor(audioBuffer) {
|
|
9137
|
+
this.audioBuffer = audioBuffer;
|
|
9138
|
+
this.framePos = 0;
|
|
9139
|
+
}
|
|
9140
|
+
close() {
|
|
9141
|
+
}
|
|
9142
|
+
skipFrames(n) {
|
|
9143
|
+
this.framePos += n;
|
|
9144
|
+
}
|
|
9145
|
+
read(buffers) {
|
|
9146
|
+
let read = 0;
|
|
9147
|
+
let toRead = buffers[0].length;
|
|
9148
|
+
if (this.framePos + toRead > this.audioBuffer.length) {
|
|
9149
|
+
toRead = this.audioBuffer.length - this.framePos;
|
|
9150
|
+
}
|
|
9151
|
+
for (let ch = 0; ch < buffers.length; ch++) {
|
|
9152
|
+
for (let i = 0; i < toRead; i++) {
|
|
9153
|
+
buffers[ch][i] = this.audioBuffer.getChannelData(ch)[this.framePos + i];
|
|
9154
|
+
}
|
|
9155
|
+
}
|
|
9156
|
+
read = toRead;
|
|
9157
|
+
this.framePos += toRead;
|
|
9158
|
+
return read;
|
|
9159
|
+
}
|
|
9160
|
+
}
|
|
9161
|
+
|
|
9162
|
+
class BasicRecFilesCache {
|
|
9163
|
+
constructor() {
|
|
9164
|
+
//public static readonly DEFAULT_MAX_SAMPLES=30*48000; // TEST 30 seconds
|
|
9165
|
+
this._sampleCount = 0;
|
|
9166
|
+
this.maxSampleCount = SprItemsCache.DEFAULT_MAX_SAMPLES;
|
|
9167
|
+
this.currentRecordingFile = null;
|
|
9168
|
+
}
|
|
9169
|
+
}
|
|
9170
|
+
BasicRecFilesCache.DEBUG = false;
|
|
9171
|
+
BasicRecFilesCache.DEFAULT_MAX_SAMPLES = 10 * 60 * 48000; // 20 Minutes mono 48kHz
|
|
9172
|
+
class SprItemsCache extends BasicRecFilesCache {
|
|
9173
|
+
constructor() {
|
|
9174
|
+
super(...arguments);
|
|
9175
|
+
this._items = new Array();
|
|
9176
|
+
}
|
|
9177
|
+
get items() {
|
|
9178
|
+
return this._items;
|
|
9179
|
+
}
|
|
9180
|
+
addItem(item) {
|
|
9181
|
+
this._items.push(item);
|
|
9182
|
+
}
|
|
9183
|
+
getItem(index) {
|
|
9184
|
+
return this._items[index];
|
|
9185
|
+
}
|
|
9186
|
+
length() {
|
|
9187
|
+
return this._items.length;
|
|
9188
|
+
}
|
|
9189
|
+
tryExpire(toBeExpiredRf) {
|
|
9190
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9191
|
+
console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " try expire:");
|
|
9192
|
+
if (!toBeExpiredRf.equals(this.currentRecordingFile)) {
|
|
9193
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9194
|
+
console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " not current file...");
|
|
9195
|
+
if (toBeExpiredRf.serverPersisted) {
|
|
9196
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9197
|
+
console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " is server persisted...");
|
|
9198
|
+
// expire recording files first stored to the cache
|
|
9199
|
+
let expiredAudio = RecordingFileUtils.expireAudioData(toBeExpiredRf);
|
|
9200
|
+
if (expiredAudio) {
|
|
9201
|
+
this._sampleCount -= expiredAudio.sampleCounts();
|
|
9202
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9203
|
+
console.debug("Rec. files cache: Expired: " + toBeExpiredRf.toString() + ". Cache samples now: " + this._sampleCount);
|
|
9204
|
+
}
|
|
9205
|
+
}
|
|
9206
|
+
else {
|
|
9207
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9208
|
+
console.debug("Rec. files cache: #" + toBeExpiredRf.toString() + " not yet persisted on server.");
|
|
9209
|
+
}
|
|
9210
|
+
}
|
|
9211
|
+
else {
|
|
9212
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9213
|
+
console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " is current file.");
|
|
9214
|
+
}
|
|
9215
|
+
}
|
|
9216
|
+
expire() {
|
|
9217
|
+
// expire corrected versions first
|
|
9218
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9219
|
+
console.debug("Rec. files cache: Expire? current: " + this._sampleCount + ", max: " + this.maxSampleCount);
|
|
9220
|
+
if (this._sampleCount > this.maxSampleCount) {
|
|
9221
|
+
// expire older versions of an item first
|
|
9222
|
+
for (let ii = 0; ii < this._items.length; ii++) {
|
|
9223
|
+
if (this._sampleCount <= this.maxSampleCount) {
|
|
9224
|
+
break;
|
|
9225
|
+
}
|
|
9226
|
+
let it = this._items[ii];
|
|
9227
|
+
let itRfs = it.recs;
|
|
9228
|
+
if (itRfs && itRfs.length > 1) {
|
|
9229
|
+
for (let rfi = 0; rfi < itRfs.length - 1; rfi++) {
|
|
9230
|
+
if (this._sampleCount <= this.maxSampleCount) {
|
|
9231
|
+
break;
|
|
9232
|
+
}
|
|
9233
|
+
let toBeExpiredRf = itRfs[rfi];
|
|
9234
|
+
this.tryExpire(toBeExpiredRf);
|
|
9235
|
+
}
|
|
9236
|
+
}
|
|
9237
|
+
}
|
|
9238
|
+
// if that's not sufficient expire audio data of latest versions as well
|
|
9239
|
+
for (let ii = 0; ii < this._items.length; ii++) {
|
|
9240
|
+
if (this._sampleCount <= this.maxSampleCount) {
|
|
9241
|
+
break;
|
|
9242
|
+
}
|
|
9243
|
+
let it = this._items[ii];
|
|
9244
|
+
let itRfs = it.recs;
|
|
9245
|
+
if (itRfs && itRfs.length > 0) {
|
|
9246
|
+
for (let rfi = 0; rfi < itRfs.length; rfi++) {
|
|
9247
|
+
if (this._sampleCount <= this.maxSampleCount) {
|
|
9248
|
+
break;
|
|
9249
|
+
}
|
|
9250
|
+
let toBeExpiredRf = itRfs[rfi];
|
|
9251
|
+
this.tryExpire(toBeExpiredRf);
|
|
9252
|
+
}
|
|
9253
|
+
}
|
|
7844
9254
|
}
|
|
7845
|
-
let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
|
|
7846
|
-
let recUrl = sessionsUrl + '/' + this.session?.sessionId + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/concatChunksRequest';
|
|
7847
|
-
let fd = new FormData();
|
|
7848
|
-
fd.set('uuid', this.rfUuid);
|
|
7849
|
-
fd.set('chunkCount', chunkCount.toString());
|
|
7850
|
-
let ul = new Upload(fd, recUrl);
|
|
7851
|
-
this.uploader.queueUpload(ul);
|
|
7852
9255
|
}
|
|
7853
|
-
|
|
7854
|
-
|
|
9256
|
+
}
|
|
9257
|
+
addSprRecFile(item, sprRecFile) {
|
|
9258
|
+
this.expire();
|
|
9259
|
+
if (!item.recs) {
|
|
9260
|
+
item.recs = new Array();
|
|
7855
9261
|
}
|
|
9262
|
+
item.recs.push(sprRecFile);
|
|
9263
|
+
this._sampleCount += RecordingFileUtils.sampleCount(sprRecFile);
|
|
9264
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9265
|
+
console.debug("Rec. files cache: Added. Cache samples: " + this._sampleCount);
|
|
7856
9266
|
}
|
|
7857
|
-
|
|
7858
|
-
this.
|
|
7859
|
-
|
|
9267
|
+
setSprRecFileAudioData(sprRecFile, adh) {
|
|
9268
|
+
this.expire();
|
|
9269
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9270
|
+
console.debug("Rec. files cache: Set audio data after expire. Cache samples: " + this._sampleCount);
|
|
9271
|
+
let currSampleCnt = RecordingFileUtils.sampleCount(sprRecFile);
|
|
9272
|
+
this._sampleCount -= currSampleCnt;
|
|
9273
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9274
|
+
console.debug("Rec. files cache: Set audio data subtracted curr sample count: " + currSampleCnt + ". Cache samples: " + this._sampleCount);
|
|
9275
|
+
RecordingFileUtils.setAudioData(sprRecFile, adh);
|
|
9276
|
+
let newSampleCnt = RecordingFileUtils.sampleCount(sprRecFile);
|
|
9277
|
+
this._sampleCount += newSampleCnt;
|
|
9278
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9279
|
+
console.debug("Rec. files cache: Set audio data added new sample count: " + newSampleCnt + ". Cache samples: " + this._sampleCount);
|
|
9280
|
+
let fl = adh?.frameLen;
|
|
9281
|
+
if (fl) {
|
|
9282
|
+
sprRecFile.frames = fl;
|
|
9283
|
+
}
|
|
9284
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9285
|
+
console.debug("Rec. files cache: Set audio data. Cache samples: " + this._sampleCount);
|
|
7860
9286
|
}
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
9287
|
+
}
|
|
9288
|
+
class RecFilesCache extends BasicRecFilesCache {
|
|
9289
|
+
constructor() {
|
|
9290
|
+
super(...arguments);
|
|
9291
|
+
this._recFiles = new Array();
|
|
9292
|
+
}
|
|
9293
|
+
get recFiles() {
|
|
9294
|
+
return this._recFiles;
|
|
9295
|
+
}
|
|
9296
|
+
getRecFile(index) {
|
|
9297
|
+
return this._recFiles[index];
|
|
9298
|
+
}
|
|
9299
|
+
length() {
|
|
9300
|
+
return this._recFiles.length;
|
|
9301
|
+
}
|
|
9302
|
+
expire() {
|
|
9303
|
+
if (this._sampleCount > this.maxSampleCount) {
|
|
9304
|
+
// audio recorder list is sorted: lower index is newer
|
|
9305
|
+
for (let rfI = this._recFiles.length - 1; rfI >= 0; rfI--) {
|
|
9306
|
+
if (this._sampleCount <= this.maxSampleCount) {
|
|
9307
|
+
break;
|
|
9308
|
+
}
|
|
9309
|
+
let toBeExpiredRf = this._recFiles[rfI];
|
|
9310
|
+
if (toBeExpiredRf.serverPersisted) {
|
|
9311
|
+
// expire recording files first stored to the cache
|
|
9312
|
+
let expiredAudio = RecordingFileUtils.expireAudioData(toBeExpiredRf);
|
|
9313
|
+
if (expiredAudio) {
|
|
9314
|
+
this._sampleCount -= expiredAudio.sampleCounts();
|
|
9315
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9316
|
+
console.debug("Rec. files cache: Expired #" + rfI + ". Cache samples: " + this._sampleCount);
|
|
9317
|
+
}
|
|
9318
|
+
}
|
|
9319
|
+
else {
|
|
9320
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9321
|
+
console.debug("Rec. files cache: #" + rfI + " not yet server persisted.");
|
|
9322
|
+
}
|
|
7870
9323
|
}
|
|
7871
|
-
}
|
|
9324
|
+
}
|
|
7872
9325
|
}
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
9326
|
+
addRecFile(recFile) {
|
|
9327
|
+
this.expire();
|
|
9328
|
+
this._recFiles.push(recFile);
|
|
9329
|
+
this._sampleCount += RecordingFileUtils.sampleCount(recFile);
|
|
9330
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9331
|
+
console.debug("Rec. files cache: Added. Cache samples: " + this._sampleCount);
|
|
9332
|
+
}
|
|
9333
|
+
setRecFileAudioData(recFile, adh) {
|
|
9334
|
+
this.expire();
|
|
9335
|
+
this._sampleCount -= RecordingFileUtils.sampleCount(recFile);
|
|
9336
|
+
RecordingFileUtils.setAudioData(recFile, adh);
|
|
9337
|
+
this._sampleCount += RecordingFileUtils.sampleCount(recFile);
|
|
9338
|
+
if (BasicRecFilesCache.DEBUG)
|
|
9339
|
+
console.debug("Rec. files cache: Set audio data. Cache samples: " + this._sampleCount);
|
|
9340
|
+
}
|
|
9341
|
+
}
|
|
7878
9342
|
|
|
7879
9343
|
/**
|
|
7880
9344
|
* Created by klausj on 17.06.2017.
|
|
@@ -7967,16 +9431,65 @@ class RecordingService {
|
|
|
7967
9431
|
let recUrl = recFilesUrl + '/' + encItemcode + '/' + version;
|
|
7968
9432
|
return this.audioRequest(recUrl);
|
|
7969
9433
|
}
|
|
9434
|
+
fetchRecordingFileAudioBuffer(aCtx, projectName, recordingFile) {
|
|
9435
|
+
let wobs = new Observable(observer => {
|
|
9436
|
+
let recFileId = recordingFile.recordingFileId;
|
|
9437
|
+
if (!recFileId) {
|
|
9438
|
+
recFileId = recordingFile.uuid;
|
|
9439
|
+
}
|
|
9440
|
+
if (recordingFile.session && recFileId) {
|
|
9441
|
+
let obs = this.fetchAudiofile(projectName, recordingFile.session, recFileId);
|
|
9442
|
+
obs.subscribe({
|
|
9443
|
+
next: resp => {
|
|
9444
|
+
// Do not use Promise version, which does not work with Safari 13 (13.0.5)
|
|
9445
|
+
if (resp.body) {
|
|
9446
|
+
aCtx.decodeAudioData(resp.body, ab => {
|
|
9447
|
+
observer.next(ab);
|
|
9448
|
+
observer.complete();
|
|
9449
|
+
}, error => {
|
|
9450
|
+
observer.error(error);
|
|
9451
|
+
observer.complete();
|
|
9452
|
+
});
|
|
9453
|
+
}
|
|
9454
|
+
else {
|
|
9455
|
+
observer.error('Fetching audio file: response has no body');
|
|
9456
|
+
}
|
|
9457
|
+
},
|
|
9458
|
+
error: (error) => {
|
|
9459
|
+
if (error.status == 404) {
|
|
9460
|
+
// Interpret not as an error, the file ist not recorded yet
|
|
9461
|
+
observer.next(null);
|
|
9462
|
+
observer.complete();
|
|
9463
|
+
}
|
|
9464
|
+
else {
|
|
9465
|
+
// all other states are errors
|
|
9466
|
+
observer.error(error);
|
|
9467
|
+
observer.complete();
|
|
9468
|
+
}
|
|
9469
|
+
}
|
|
9470
|
+
});
|
|
9471
|
+
}
|
|
9472
|
+
else {
|
|
9473
|
+
observer.error();
|
|
9474
|
+
}
|
|
9475
|
+
});
|
|
9476
|
+
return wobs;
|
|
9477
|
+
}
|
|
7970
9478
|
fetchAndApplyRecordingFile(aCtx, projectName, recordingFile) {
|
|
7971
9479
|
let wobs = new Observable(observer => {
|
|
7972
|
-
|
|
7973
|
-
|
|
9480
|
+
let recFileId = recordingFile.recordingFileId;
|
|
9481
|
+
if (!recFileId) {
|
|
9482
|
+
recFileId = recordingFile.uuid;
|
|
9483
|
+
}
|
|
9484
|
+
if (recordingFile.session && recFileId) {
|
|
9485
|
+
let obs = this.fetchAudiofile(projectName, recordingFile.session, recFileId);
|
|
7974
9486
|
obs.subscribe(resp => {
|
|
7975
9487
|
//console.log("Fetched audio file. HTTP response status: "+resp.status+", type: "+resp.type+", byte length: "+ resp.body.byteLength);
|
|
7976
9488
|
// Do not use Promise version, which does not work with Safari 13 (13.0.5)
|
|
7977
9489
|
if (resp.body) {
|
|
7978
9490
|
aCtx.decodeAudioData(resp.body, ab => {
|
|
7979
|
-
|
|
9491
|
+
let adh = new AudioDataHolder(ab, null);
|
|
9492
|
+
RecordingFileUtils.setAudioData(recordingFile, adh);
|
|
7980
9493
|
if (this.debugDelay > 0) {
|
|
7981
9494
|
window.setTimeout(() => {
|
|
7982
9495
|
observer.next(recordingFile);
|
|
@@ -8014,6 +9527,47 @@ class RecordingService {
|
|
|
8014
9527
|
});
|
|
8015
9528
|
return wobs;
|
|
8016
9529
|
}
|
|
9530
|
+
fetchSprRecordingFileAudioBuffer(aCtx, projectName, recordingFile) {
|
|
9531
|
+
let wobs = new Observable(observer => {
|
|
9532
|
+
if (recordingFile.session) {
|
|
9533
|
+
let obs = this.fetchSprAudiofile(projectName, recordingFile.session, recordingFile.itemCode, recordingFile.version);
|
|
9534
|
+
obs.subscribe({
|
|
9535
|
+
next: resp => {
|
|
9536
|
+
// Do not use Promise version, which does not work with Safari 13 (13.0.5)
|
|
9537
|
+
if (resp.body) {
|
|
9538
|
+
aCtx.decodeAudioData(resp.body, ab => {
|
|
9539
|
+
//RecordingFileUtils.setAudioData(recordingFile,new AudioDataHolder(ab,null));
|
|
9540
|
+
observer.next(ab);
|
|
9541
|
+
observer.complete();
|
|
9542
|
+
}, error => {
|
|
9543
|
+
observer.error(error);
|
|
9544
|
+
observer.complete();
|
|
9545
|
+
});
|
|
9546
|
+
}
|
|
9547
|
+
else {
|
|
9548
|
+
observer.error('Fetching audio file: response has no body');
|
|
9549
|
+
}
|
|
9550
|
+
},
|
|
9551
|
+
error: (error) => {
|
|
9552
|
+
if (error.status == 404) {
|
|
9553
|
+
// Interpret not as an error, the file ist not recorded yet
|
|
9554
|
+
observer.next(null);
|
|
9555
|
+
observer.complete();
|
|
9556
|
+
}
|
|
9557
|
+
else {
|
|
9558
|
+
// all other states are errors
|
|
9559
|
+
observer.error(error);
|
|
9560
|
+
observer.complete();
|
|
9561
|
+
}
|
|
9562
|
+
}
|
|
9563
|
+
});
|
|
9564
|
+
}
|
|
9565
|
+
else {
|
|
9566
|
+
observer.error();
|
|
9567
|
+
}
|
|
9568
|
+
});
|
|
9569
|
+
return wobs;
|
|
9570
|
+
}
|
|
8017
9571
|
fetchAndApplySprRecordingFile(aCtx, projectName, recordingFile) {
|
|
8018
9572
|
let wobs = new Observable(observer => {
|
|
8019
9573
|
if (recordingFile.session) {
|
|
@@ -8023,7 +9577,7 @@ class RecordingService {
|
|
|
8023
9577
|
// Do not use Promise version, which does not work with Safari 13 (13.0.5)
|
|
8024
9578
|
if (resp.body) {
|
|
8025
9579
|
aCtx.decodeAudioData(resp.body, ab => {
|
|
8026
|
-
recordingFile
|
|
9580
|
+
RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(ab, null));
|
|
8027
9581
|
if (this.debugDelay > 0) {
|
|
8028
9582
|
window.setTimeout(() => {
|
|
8029
9583
|
observer.next(recordingFile);
|
|
@@ -8068,7 +9622,8 @@ class RecordingService {
|
|
|
8068
9622
|
// Do not use Promise version, which does not work with Safari 13
|
|
8069
9623
|
if (resp.body) {
|
|
8070
9624
|
aCtx.decodeAudioData(resp.body, ab => {
|
|
8071
|
-
let
|
|
9625
|
+
let adh = new AudioDataHolder(ab, null);
|
|
9626
|
+
let rf = new SprRecordingFile(sessId, itemcode, version, adh);
|
|
8072
9627
|
if (this.debugDelay > 0) {
|
|
8073
9628
|
window.setTimeout(() => {
|
|
8074
9629
|
observer.next(rf);
|
|
@@ -8184,14 +9739,9 @@ const DEFAULT_PRE_REC_DELAY = 1000;
|
|
|
8184
9739
|
const DEFAULT_POST_REC_DELAY = 500;
|
|
8185
9740
|
class SessionManager extends BasicRecorder {
|
|
8186
9741
|
constructor(changeDetectorRef, renderer, dialog, sessionService, recFileService, uploader, config) {
|
|
8187
|
-
super(dialog, sessionService, uploader, config);
|
|
8188
|
-
this.changeDetectorRef = changeDetectorRef;
|
|
9742
|
+
super(changeDetectorRef, dialog, sessionService, uploader, config);
|
|
8189
9743
|
this.renderer = renderer;
|
|
8190
|
-
this.dialog = dialog;
|
|
8191
|
-
this.sessionService = sessionService;
|
|
8192
9744
|
this.recFileService = recFileService;
|
|
8193
|
-
this.uploader = uploader;
|
|
8194
|
-
this.config = config;
|
|
8195
9745
|
this.enableUploadRecordings = true;
|
|
8196
9746
|
this.enableDownloadRecordings = false;
|
|
8197
9747
|
this.status = 0 /* BLOCKED */;
|
|
@@ -8225,7 +9775,7 @@ class SessionManager extends BasicRecorder {
|
|
|
8225
9775
|
};
|
|
8226
9776
|
}
|
|
8227
9777
|
ngOnDestroy() {
|
|
8228
|
-
console.debug("Com destroy, disable wake lock.")
|
|
9778
|
+
//console.debug("Com destroy, disable wake lock.")
|
|
8229
9779
|
this.disableWakeLockCond();
|
|
8230
9780
|
this.destroyed = true;
|
|
8231
9781
|
// TODO stop capture /playback
|
|
@@ -8245,7 +9795,7 @@ class SessionManager extends BasicRecorder {
|
|
|
8245
9795
|
context = AudioContextProvider.audioContextInstance();
|
|
8246
9796
|
}
|
|
8247
9797
|
catch (err) {
|
|
8248
|
-
this.status =
|
|
9798
|
+
this.status = 9 /* ERROR */;
|
|
8249
9799
|
let errMsg = 'Unknown error';
|
|
8250
9800
|
if (err instanceof Error) {
|
|
8251
9801
|
errMsg = err.message;
|
|
@@ -8269,7 +9819,7 @@ class SessionManager extends BasicRecorder {
|
|
|
8269
9819
|
console.info("No audio context available!");
|
|
8270
9820
|
}
|
|
8271
9821
|
if (!context || !navigator.mediaDevices) {
|
|
8272
|
-
this.status =
|
|
9822
|
+
this.status = 9 /* ERROR */;
|
|
8273
9823
|
let errMsg = 'Browser does not support Media streams!';
|
|
8274
9824
|
this.statusMsg = 'ERROR: ' + errMsg;
|
|
8275
9825
|
this.statusAlertType = 'error';
|
|
@@ -8356,7 +9906,7 @@ class SessionManager extends BasicRecorder {
|
|
|
8356
9906
|
progressPercentValue() {
|
|
8357
9907
|
let v = 100;
|
|
8358
9908
|
if (this.items) {
|
|
8359
|
-
v = this.promptIndex * 100 / (this.items.length - 1);
|
|
9909
|
+
v = this.promptIndex * 100 / (this.items.length() - 1);
|
|
8360
9910
|
}
|
|
8361
9911
|
return v;
|
|
8362
9912
|
}
|
|
@@ -8370,9 +9920,6 @@ class SessionManager extends BasicRecorder {
|
|
|
8370
9920
|
return ((this._session != null) && (this._session.type === 'TEST_DEF_A') && (this.audioDevices != null) && this.audioDevices.length > 0);
|
|
8371
9921
|
}
|
|
8372
9922
|
set controlAudioPlayer(controlAudioPlayer) {
|
|
8373
|
-
if (this._controlAudioPlayer) {
|
|
8374
|
-
//this._controlAudioPlayer.listener=null;
|
|
8375
|
-
}
|
|
8376
9923
|
this._controlAudioPlayer = controlAudioPlayer;
|
|
8377
9924
|
if (this._controlAudioPlayer) {
|
|
8378
9925
|
this._controlAudioPlayer.listener = this;
|
|
@@ -8440,7 +9987,12 @@ class SessionManager extends BasicRecorder {
|
|
|
8440
9987
|
}
|
|
8441
9988
|
}
|
|
8442
9989
|
startItem() {
|
|
9990
|
+
this.status = 2 /* STARTING */;
|
|
8443
9991
|
super.startItem();
|
|
9992
|
+
if (this.readonly) {
|
|
9993
|
+
this.status = 1 /* IDLE */;
|
|
9994
|
+
return;
|
|
9995
|
+
}
|
|
8444
9996
|
this.transportActions.fwdAction.disabled = true;
|
|
8445
9997
|
this.transportActions.fwdNextAction.disabled = true;
|
|
8446
9998
|
this.transportActions.bwdAction.disabled = true;
|
|
@@ -8452,6 +10004,8 @@ class SessionManager extends BasicRecorder {
|
|
|
8452
10004
|
this.autorecording = true;
|
|
8453
10005
|
}
|
|
8454
10006
|
if (this.ac != null) {
|
|
10007
|
+
// Hide loading hint on livelevel display
|
|
10008
|
+
this.audioFetching = false;
|
|
8455
10009
|
if (!this.ac.opened) {
|
|
8456
10010
|
if (this._selectedDeviceId) {
|
|
8457
10011
|
console.log("Open session with audio device Id: \'" + this._selectedDeviceId + "\' for " + this._channelCount + " channels");
|
|
@@ -8468,7 +10022,7 @@ class SessionManager extends BasicRecorder {
|
|
|
8468
10022
|
}
|
|
8469
10023
|
loadScript() {
|
|
8470
10024
|
this.promptItemCount = 0;
|
|
8471
|
-
this.items = new
|
|
10025
|
+
this.items = new SprItemsCache();
|
|
8472
10026
|
let ln = 0;
|
|
8473
10027
|
//TODO randomize not supported
|
|
8474
10028
|
for (let si = 0; si < this._script.sections.length; si++) {
|
|
@@ -8482,7 +10036,7 @@ class SessionManager extends BasicRecorder {
|
|
|
8482
10036
|
let pi = pis[piSectIdx];
|
|
8483
10037
|
let promptAsStr = PromptitemUtil.toPlainTextString(pi);
|
|
8484
10038
|
let it = new Item$1(promptAsStr, section.training, (!pi.type || pi.type === 'recording'));
|
|
8485
|
-
this.items.
|
|
10039
|
+
this.items.addItem(it);
|
|
8486
10040
|
ln++;
|
|
8487
10041
|
}
|
|
8488
10042
|
}
|
|
@@ -8523,10 +10077,10 @@ class SessionManager extends BasicRecorder {
|
|
|
8523
10077
|
}
|
|
8524
10078
|
downloadRecording() {
|
|
8525
10079
|
if (this.displayRecFile) {
|
|
8526
|
-
let ab = this.displayRecFile.
|
|
10080
|
+
let ab = this.displayRecFile.audioDataHolder;
|
|
8527
10081
|
let ww = new WavWriter();
|
|
8528
|
-
if (ab) {
|
|
8529
|
-
let wavFile = ww.writeAsync(ab, (wavFile) => {
|
|
10082
|
+
if (ab?.buffer) {
|
|
10083
|
+
let wavFile = ww.writeAsync(ab.buffer, (wavFile) => {
|
|
8530
10084
|
let blob = new Blob([wavFile], { type: 'audio/wav' });
|
|
8531
10085
|
let rfUrl = URL.createObjectURL(blob);
|
|
8532
10086
|
let dataDnlLnk = this.renderer.createElement('a');
|
|
@@ -8549,7 +10103,10 @@ class SessionManager extends BasicRecorder {
|
|
|
8549
10103
|
set displayRecFile(displayRecFile) {
|
|
8550
10104
|
this._displayRecFile = displayRecFile;
|
|
8551
10105
|
if (this._displayRecFile) {
|
|
8552
|
-
|
|
10106
|
+
if (this.items) {
|
|
10107
|
+
this.items.currentRecordingFile = this._displayRecFile;
|
|
10108
|
+
}
|
|
10109
|
+
let ab = this._displayRecFile.audioDataHolder;
|
|
8553
10110
|
if (ab) {
|
|
8554
10111
|
this.displayAudioClip = new AudioClip(ab);
|
|
8555
10112
|
this.controlAudioPlayer.audioClip = this.displayAudioClip;
|
|
@@ -8557,27 +10114,46 @@ class SessionManager extends BasicRecorder {
|
|
|
8557
10114
|
else {
|
|
8558
10115
|
// clear for now ...
|
|
8559
10116
|
this.displayAudioClip = null;
|
|
8560
|
-
this.controlAudioPlayer
|
|
10117
|
+
if (this.controlAudioPlayer) {
|
|
10118
|
+
this.controlAudioPlayer.audioClip = null;
|
|
10119
|
+
}
|
|
8561
10120
|
if (this._controlAudioPlayer && this._session) {
|
|
8562
10121
|
//... and try to fetch from server
|
|
8563
|
-
this.
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
10122
|
+
this.audioFetching = true;
|
|
10123
|
+
let rf = this._displayRecFile;
|
|
10124
|
+
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileAudioBuffer(this._controlAudioPlayer.context, this._session.project, rf).subscribe({
|
|
10125
|
+
next: (ab) => {
|
|
10126
|
+
this.audioFetching = false;
|
|
10127
|
+
let fabDh = null;
|
|
10128
|
+
if (ab) {
|
|
10129
|
+
if (rf && this.items) {
|
|
10130
|
+
if (SessionManager.FORCE_ARRRAY_AUDIO_BUFFER) {
|
|
10131
|
+
let aab = ArrayAudioBuffer.fromAudioBuffer(ab);
|
|
10132
|
+
fabDh = new AudioDataHolder(null, aab);
|
|
10133
|
+
}
|
|
10134
|
+
else {
|
|
10135
|
+
fabDh = new AudioDataHolder(ab);
|
|
10136
|
+
}
|
|
10137
|
+
this.items.setSprRecFileAudioData(rf, fabDh);
|
|
10138
|
+
}
|
|
10139
|
+
}
|
|
10140
|
+
else {
|
|
10141
|
+
// Should actually be handled by the error resolver
|
|
10142
|
+
this.statusMsg = 'Recording file could not be loaded.';
|
|
10143
|
+
this.statusAlertType = 'error';
|
|
10144
|
+
}
|
|
10145
|
+
if (fabDh) {
|
|
10146
|
+
// this.displayAudioClip could have been changed meanwhile, but the recorder unsubcribes before changing the item. Therefore there should be no risk to set to wrong item
|
|
10147
|
+
this.displayAudioClip = new AudioClip(fabDh);
|
|
10148
|
+
}
|
|
10149
|
+
this.controlAudioPlayer.audioClip = this.displayAudioClip;
|
|
10150
|
+
this.showRecording();
|
|
10151
|
+
}, error: err => {
|
|
10152
|
+
console.error("Could not load recording file from server: " + err);
|
|
10153
|
+
this.audioFetching = false;
|
|
10154
|
+
this.statusMsg = 'Recording file could not be loaded: ' + err;
|
|
8570
10155
|
this.statusAlertType = 'error';
|
|
8571
10156
|
}
|
|
8572
|
-
if (fab) {
|
|
8573
|
-
this.displayAudioClip = new AudioClip(fab);
|
|
8574
|
-
}
|
|
8575
|
-
this.controlAudioPlayer.audioClip = this.displayAudioClip;
|
|
8576
|
-
this.showRecording();
|
|
8577
|
-
}, err => {
|
|
8578
|
-
console.error("Could not load recording file from server: " + err);
|
|
8579
|
-
this.statusMsg = 'Recording file could not be loaded: ' + err;
|
|
8580
|
-
this.statusAlertType = 'error';
|
|
8581
10157
|
});
|
|
8582
10158
|
}
|
|
8583
10159
|
else {
|
|
@@ -8588,34 +10164,14 @@ class SessionManager extends BasicRecorder {
|
|
|
8588
10164
|
}
|
|
8589
10165
|
else {
|
|
8590
10166
|
this.displayAudioClip = null;
|
|
8591
|
-
this.controlAudioPlayer
|
|
10167
|
+
if (this.controlAudioPlayer) {
|
|
10168
|
+
this.controlAudioPlayer.audioClip = null;
|
|
10169
|
+
}
|
|
8592
10170
|
}
|
|
8593
10171
|
}
|
|
8594
10172
|
get displayRecFile() {
|
|
8595
10173
|
return this._displayRecFile;
|
|
8596
10174
|
}
|
|
8597
|
-
showRecording() {
|
|
8598
|
-
this.controlAudioPlayer.stop();
|
|
8599
|
-
if (this.displayAudioClip) {
|
|
8600
|
-
this.levelMeasure.calcBufferLevelInfos(this.displayAudioClip.buffer, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
|
|
8601
|
-
this.displayLevelInfos = levelInfos;
|
|
8602
|
-
this.changeDetectorRef.detectChanges();
|
|
8603
|
-
});
|
|
8604
|
-
this.playStartAction.disabled = false;
|
|
8605
|
-
}
|
|
8606
|
-
else {
|
|
8607
|
-
// TODO
|
|
8608
|
-
// Setting to null does not trigger a change if it was null before (happens after nextitem() in AUTOPROGRESS mode)
|
|
8609
|
-
// The level bar display does not clear, it shows the last captured stream
|
|
8610
|
-
this.displayLevelInfos = null;
|
|
8611
|
-
this.playStartAction.disabled = true;
|
|
8612
|
-
// Collapse audio signal display if open
|
|
8613
|
-
if (!this.audioSignalCollapsed) {
|
|
8614
|
-
this.audioSignalCollapsed = true;
|
|
8615
|
-
}
|
|
8616
|
-
}
|
|
8617
|
-
this.changeDetectorRef.detectChanges();
|
|
8618
|
-
}
|
|
8619
10175
|
isRecordingItem() {
|
|
8620
10176
|
return (this.promptItem != null && this.promptItem.type !== 'nonrecording');
|
|
8621
10177
|
}
|
|
@@ -8630,17 +10186,20 @@ class SessionManager extends BasicRecorder {
|
|
|
8630
10186
|
if (this.audioFetchSubscription) {
|
|
8631
10187
|
this.audioFetchSubscription.unsubscribe();
|
|
8632
10188
|
}
|
|
10189
|
+
this.audioFetching = false;
|
|
8633
10190
|
this.clearPrompt();
|
|
8634
10191
|
let isNonrecording = (this.promptItem.type === 'nonrecording');
|
|
8635
10192
|
if (isNonrecording || !this.section.promptphase || this.section.promptphase === 'IDLE') {
|
|
8636
10193
|
this.applyPrompt();
|
|
8637
10194
|
}
|
|
8638
10195
|
if (isNonrecording) {
|
|
10196
|
+
this.displayRecFile = null;
|
|
10197
|
+
this.displayRecFileVersion = 0;
|
|
8639
10198
|
this.startStopSignalState = 4 /* OFF */;
|
|
8640
10199
|
}
|
|
8641
10200
|
else {
|
|
8642
10201
|
if (this.items) {
|
|
8643
|
-
let it = this.items
|
|
10202
|
+
let it = this.items.getItem(this.promptIndex);
|
|
8644
10203
|
if (!it.recs) {
|
|
8645
10204
|
it.recs = new Array();
|
|
8646
10205
|
}
|
|
@@ -8657,13 +10216,13 @@ class SessionManager extends BasicRecorder {
|
|
|
8657
10216
|
this.displayRecFileVersion = 0;
|
|
8658
10217
|
}
|
|
8659
10218
|
}
|
|
8660
|
-
if (!temporary) {
|
|
8661
|
-
this.showRecording();
|
|
8662
|
-
}
|
|
8663
10219
|
if (!this.readonly) {
|
|
8664
10220
|
this.startStopSignalState = 0 /* IDLE */;
|
|
8665
10221
|
}
|
|
8666
10222
|
}
|
|
10223
|
+
if (!temporary) {
|
|
10224
|
+
this.showRecording();
|
|
10225
|
+
}
|
|
8667
10226
|
this.updateStartActionDisableState();
|
|
8668
10227
|
this.updateNavigationActions();
|
|
8669
10228
|
}
|
|
@@ -8675,10 +10234,10 @@ class SessionManager extends BasicRecorder {
|
|
|
8675
10234
|
this.enableNavigation();
|
|
8676
10235
|
}
|
|
8677
10236
|
isRecording() {
|
|
8678
|
-
return (this.status ===
|
|
10237
|
+
return (this.status === 3 /* PRE_RECORDING */ || this.status === 4 /* RECORDING */);
|
|
8679
10238
|
}
|
|
8680
10239
|
isActive() {
|
|
8681
|
-
return (!(this.status === 0 /* BLOCKED */ || this.status === 1 /* IDLE */ || this.status ===
|
|
10240
|
+
return (!(this.status === 0 /* BLOCKED */ || this.status === 1 /* IDLE */ || this.status === 9 /* ERROR */) || this.processingRecording || this.sessionService.uploadCount > 0);
|
|
8682
10241
|
}
|
|
8683
10242
|
prevItem() {
|
|
8684
10243
|
let newPrIdx = this._promptIndex;
|
|
@@ -8701,7 +10260,7 @@ class SessionManager extends BasicRecorder {
|
|
|
8701
10260
|
updateNavigationActions() {
|
|
8702
10261
|
let fwdNxtActionDisabled = true;
|
|
8703
10262
|
if (this.items) {
|
|
8704
|
-
let currRecs = this.items
|
|
10263
|
+
let currRecs = this.items.getItem(this._promptIndex).recs;
|
|
8705
10264
|
fwdNxtActionDisabled = !(currRecs != null && currRecs.length > 0);
|
|
8706
10265
|
}
|
|
8707
10266
|
this.transportActions.fwdNextAction.disabled = this.navigationDisabled || fwdNxtActionDisabled;
|
|
@@ -8715,10 +10274,10 @@ class SessionManager extends BasicRecorder {
|
|
|
8715
10274
|
newPrIdx = 0;
|
|
8716
10275
|
}
|
|
8717
10276
|
if (this.items != null) {
|
|
8718
|
-
let itRecs = this.items
|
|
10277
|
+
let itRecs = this.items.getItem(newPrIdx).recs;
|
|
8719
10278
|
while (itRecs != null && (itRecs.length > 0) && newPrIdx < this.promptItemCount) {
|
|
8720
10279
|
newPrIdx++;
|
|
8721
|
-
itRecs = this.items
|
|
10280
|
+
itRecs = this.items.getItem(newPrIdx).recs;
|
|
8722
10281
|
}
|
|
8723
10282
|
if (!itRecs || itRecs.length == 0) {
|
|
8724
10283
|
this.promptIndex = newPrIdx;
|
|
@@ -8739,7 +10298,24 @@ class SessionManager extends BasicRecorder {
|
|
|
8739
10298
|
}
|
|
8740
10299
|
}
|
|
8741
10300
|
started() {
|
|
8742
|
-
|
|
10301
|
+
let ic = this.promptItem.itemcode;
|
|
10302
|
+
let rf = null;
|
|
10303
|
+
if (this._session && ic) {
|
|
10304
|
+
let sessId = this._session.sessionId;
|
|
10305
|
+
let cpIdx = this.promptIndex;
|
|
10306
|
+
if (this.items) {
|
|
10307
|
+
let it = this.items.getItem(cpIdx);
|
|
10308
|
+
if (!it.recs) {
|
|
10309
|
+
it.recs = new Array();
|
|
10310
|
+
}
|
|
10311
|
+
rf = new SprRecordingFile(sessId, ic, it.recs.length, null);
|
|
10312
|
+
//it.recs.push(rf);
|
|
10313
|
+
this.items.addSprRecFile(it, rf);
|
|
10314
|
+
this.items.currentRecordingFile = rf;
|
|
10315
|
+
}
|
|
10316
|
+
}
|
|
10317
|
+
this._recordingFile = rf;
|
|
10318
|
+
this.status = 3 /* PRE_RECORDING */;
|
|
8743
10319
|
super.started();
|
|
8744
10320
|
this.startStopSignalState = 1 /* PRERECORDING */;
|
|
8745
10321
|
if (this._session) {
|
|
@@ -8793,7 +10369,7 @@ class SessionManager extends BasicRecorder {
|
|
|
8793
10369
|
this.maxRecTimerRunning = true;
|
|
8794
10370
|
this.preRecTimerId = window.setTimeout(() => {
|
|
8795
10371
|
this.preRecTimerRunning = false;
|
|
8796
|
-
this.status =
|
|
10372
|
+
this.status = 4 /* RECORDING */;
|
|
8797
10373
|
this.startStopSignalState = 3 /* RECORDING */;
|
|
8798
10374
|
if (this.section.mode === 'AUTORECORDING') {
|
|
8799
10375
|
this.transportActions.nextAction.disabled = false;
|
|
@@ -8812,19 +10388,19 @@ class SessionManager extends BasicRecorder {
|
|
|
8812
10388
|
this.preRecTimerRunning = true;
|
|
8813
10389
|
}
|
|
8814
10390
|
stopItem() {
|
|
8815
|
-
this.status =
|
|
10391
|
+
this.status = 5 /* POST_REC_STOP */;
|
|
8816
10392
|
this.startStopSignalState = 2 /* POSTRECORDING */;
|
|
8817
10393
|
this.transportActions.stopAction.disabled = true;
|
|
8818
10394
|
this.transportActions.nextAction.disabled = true;
|
|
8819
10395
|
this.postRecTimerId = window.setTimeout(() => {
|
|
8820
10396
|
this.postRecTimerRunning = false;
|
|
8821
|
-
this.status =
|
|
10397
|
+
this.status = 7 /* STOPPING_STOP */;
|
|
8822
10398
|
this.stopRecording();
|
|
8823
10399
|
}, this.postDelay);
|
|
8824
10400
|
this.postRecTimerRunning = true;
|
|
8825
10401
|
}
|
|
8826
10402
|
pauseItem() {
|
|
8827
|
-
this.status =
|
|
10403
|
+
this.status = 6 /* POST_REC_PAUSE */;
|
|
8828
10404
|
this.transportActions.pauseAction.disabled = true;
|
|
8829
10405
|
this.startStopSignalState = 2 /* POSTRECORDING */;
|
|
8830
10406
|
this.transportActions.stopAction.disabled = true;
|
|
@@ -8832,7 +10408,7 @@ class SessionManager extends BasicRecorder {
|
|
|
8832
10408
|
this.transportActions.pauseAction.disabled = true;
|
|
8833
10409
|
this.postRecTimerId = window.setTimeout(() => {
|
|
8834
10410
|
this.postRecTimerRunning = false;
|
|
8835
|
-
this.status =
|
|
10411
|
+
this.status = 8 /* STOPPING_PAUSE */;
|
|
8836
10412
|
this.stopRecording();
|
|
8837
10413
|
}, this.postDelay);
|
|
8838
10414
|
this.postRecTimerRunning = true;
|
|
@@ -8856,7 +10432,7 @@ class SessionManager extends BasicRecorder {
|
|
|
8856
10432
|
this.postRecTimerRunning = false;
|
|
8857
10433
|
}
|
|
8858
10434
|
this.maxRecTimerRunning = false;
|
|
8859
|
-
this.status =
|
|
10435
|
+
this.status = 7 /* STOPPING_STOP */;
|
|
8860
10436
|
if (this.ac) {
|
|
8861
10437
|
this.ac.stop();
|
|
8862
10438
|
}
|
|
@@ -8865,13 +10441,14 @@ class SessionManager extends BasicRecorder {
|
|
|
8865
10441
|
if (rfd.recording && rfd.recording.itemcode) {
|
|
8866
10442
|
let prIdx = this.promptIndexByItemcode(rfd.recording.itemcode);
|
|
8867
10443
|
if (this.items != null && prIdx !== null) {
|
|
8868
|
-
let it = this.items
|
|
10444
|
+
let it = this.items.getItem(prIdx);
|
|
8869
10445
|
if (it && this._session) {
|
|
8870
|
-
if (!it.recs) {
|
|
8871
|
-
|
|
8872
|
-
}
|
|
10446
|
+
// if (!it.recs) {
|
|
10447
|
+
// it.recs = new Array<SprRecordingFile>();
|
|
10448
|
+
// }
|
|
8873
10449
|
let rf = new SprRecordingFile(this._session.sessionId, rfd.recording.itemcode, rfd.version, null);
|
|
8874
|
-
|
|
10450
|
+
rf.serverPersisted = true;
|
|
10451
|
+
this.items.addSprRecFile(it, rf);
|
|
8875
10452
|
}
|
|
8876
10453
|
else {
|
|
8877
10454
|
//console.debug("WARN: No recording item with code: \"" +rfd.recording.itemcode+ "\" found.");
|
|
@@ -8893,22 +10470,24 @@ class SessionManager extends BasicRecorder {
|
|
|
8893
10470
|
this.statusMsg = 'Recorded.';
|
|
8894
10471
|
this.startStopSignalState = 0 /* IDLE */;
|
|
8895
10472
|
let ad = null;
|
|
10473
|
+
let ada = null;
|
|
10474
|
+
let adh = null;
|
|
10475
|
+
let frameLen = 0;
|
|
8896
10476
|
if (this.ac != null) {
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
if (this.items) {
|
|
8905
|
-
let it = this.items[cpIdx];
|
|
8906
|
-
if (!it.recs) {
|
|
8907
|
-
it.recs = new Array();
|
|
8908
|
-
}
|
|
8909
|
-
rf = new SprRecordingFile(sessId, ic, it.recs.length, ad);
|
|
8910
|
-
it.recs.push(rf);
|
|
10477
|
+
if (this.uploadChunkSizeSeconds || SessionManager.FORCE_ARRRAY_AUDIO_BUFFER) {
|
|
10478
|
+
ada = this.ac.audioBufferArray();
|
|
10479
|
+
frameLen = ada.frameLen;
|
|
10480
|
+
}
|
|
10481
|
+
else {
|
|
10482
|
+
ad = this.ac.audioBuffer();
|
|
10483
|
+
frameLen = ad.length;
|
|
8911
10484
|
}
|
|
10485
|
+
adh = new AudioDataHolder(ad, ada);
|
|
10486
|
+
}
|
|
10487
|
+
// Use an own reference since the writing of the wave file is asynchronous and this._recordingFile might already contain the next recording
|
|
10488
|
+
let rf = this._recordingFile;
|
|
10489
|
+
if (rf && rf instanceof SprRecordingFile) {
|
|
10490
|
+
this.items?.setSprRecFileAudioData(rf, adh);
|
|
8912
10491
|
if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds) {
|
|
8913
10492
|
// TODO use SpeechRecorderconfig resp. RecfileService
|
|
8914
10493
|
// convert asynchronously to 16-bit integer PCM
|
|
@@ -8916,7 +10495,7 @@ class SessionManager extends BasicRecorder {
|
|
|
8916
10495
|
// TODO duplicate conversion for manual download
|
|
8917
10496
|
//console.log("Build wav writer...");
|
|
8918
10497
|
this.processingRecording = true;
|
|
8919
|
-
if (ad
|
|
10498
|
+
if (ad) {
|
|
8920
10499
|
let ww = new WavWriter();
|
|
8921
10500
|
//new REST API URL
|
|
8922
10501
|
let apiEndPoint = '';
|
|
@@ -8929,7 +10508,7 @@ class SessionManager extends BasicRecorder {
|
|
|
8929
10508
|
let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
|
|
8930
10509
|
let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.itemCode;
|
|
8931
10510
|
ww.writeAsync(ad, (wavFile) => {
|
|
8932
|
-
this.postRecording(wavFile, recUrl);
|
|
10511
|
+
this.postRecording(wavFile, recUrl, rf);
|
|
8933
10512
|
this.processingRecording = false;
|
|
8934
10513
|
this.updateWakeLock();
|
|
8935
10514
|
});
|
|
@@ -8940,15 +10519,15 @@ class SessionManager extends BasicRecorder {
|
|
|
8940
10519
|
let complete = true;
|
|
8941
10520
|
if (this.items) {
|
|
8942
10521
|
// search backwards, to gain faster detection of incomplete state
|
|
8943
|
-
for (let ri = this.items.length - 1; ri >= 0; ri--) {
|
|
8944
|
-
let it = this.items
|
|
10522
|
+
for (let ri = this.items.length() - 1; ri >= 0; ri--) {
|
|
10523
|
+
let it = this.items.getItem(ri);
|
|
8945
10524
|
if (it.recording && !it.training && (!it.recs || it.recs.length == 0)) {
|
|
8946
10525
|
complete = false;
|
|
8947
10526
|
break;
|
|
8948
10527
|
}
|
|
8949
10528
|
}
|
|
8950
10529
|
}
|
|
8951
|
-
let autoStart = (this.status ===
|
|
10530
|
+
let autoStart = (this.status === 7 /* STOPPING_STOP */);
|
|
8952
10531
|
this.status = 1 /* IDLE */;
|
|
8953
10532
|
let startNext = false;
|
|
8954
10533
|
if (complete) {
|
|
@@ -9005,13 +10584,13 @@ class SessionManager extends BasicRecorder {
|
|
|
9005
10584
|
let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
|
|
9006
10585
|
let recUrl = sessionsUrl + '/' + this.session?.sessionId + '/' + RECFILE_API_CTX + '/' + this.promptItem.itemcode + '/' + this.rfUuid + '/' + chunkIdx;
|
|
9007
10586
|
ww.writeAsync(audioBuffer, (wavFile) => {
|
|
9008
|
-
this.postRecording(wavFile, recUrl);
|
|
10587
|
+
this.postRecording(wavFile, recUrl, null);
|
|
9009
10588
|
this.processingRecording = false;
|
|
9010
10589
|
});
|
|
9011
10590
|
}
|
|
9012
|
-
postRecording(wavFile, recUrl) {
|
|
10591
|
+
postRecording(wavFile, recUrl, rf) {
|
|
9013
10592
|
let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
|
|
9014
|
-
let ul = new Upload(wavBlob, recUrl);
|
|
10593
|
+
let ul = new Upload(wavBlob, recUrl, rf);
|
|
9015
10594
|
this.uploader.queueUpload(ul);
|
|
9016
10595
|
}
|
|
9017
10596
|
stop() {
|
|
@@ -9047,7 +10626,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
|
|
|
9047
10626
|
<app-warningbar [show]="isDefaultAudioTestSession()" warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
|
|
9048
10627
|
<app-sprprompting [projectName]="projectName"
|
|
9049
10628
|
[startStopSignalState]="startStopSignalState" [promptItem]="promptItem" [showPrompt]="showPrompt"
|
|
9050
|
-
[items]="items"
|
|
10629
|
+
[items]="items?.items"
|
|
9051
10630
|
[transportActions]="transportActions"
|
|
9052
10631
|
[selectedItemIdx]="promptIndex" (onItemSelect)="itemSelect($event)" (onNextItem)="nextItem()" (onPrevItem)="prevItem()"
|
|
9053
10632
|
[audioSignalCollapsed]="audioSignalCollapsed" [displayAudioClip]="displayAudioClip"
|
|
@@ -9061,10 +10640,10 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
|
|
|
9061
10640
|
|
|
9062
10641
|
|
|
9063
10642
|
<div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}" [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
|
|
9064
|
-
<audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="
|
|
10643
|
+
<audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos" [stateLoading]="audioFetching"></audio-levelbar>
|
|
9065
10644
|
<div fxLayout="row">
|
|
9066
10645
|
<spr-recordingitemcontrols fxFlex="10 0 1"
|
|
9067
|
-
[audioLoaded]="displayAudioClip?.
|
|
10646
|
+
[audioLoaded]="displayAudioClip?.audioDataHolder!==null"
|
|
9068
10647
|
[playStartAction]="controlAudioPlayer?.startAction"
|
|
9069
10648
|
[playStopAction]="controlAudioPlayer?.stopAction"
|
|
9070
10649
|
[peakDbLvl]="peakLevelInDb"
|
|
@@ -9082,7 +10661,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
|
|
|
9082
10661
|
<div fxFlex="1 1 30%" fxLayoutAlign="start center">
|
|
9083
10662
|
<app-sprstatusdisplay fxHide.xs [statusMsg]="statusMsg" [statusAlertType]="statusAlertType" [statusWaiting]="statusWaiting"></app-sprstatusdisplay>
|
|
9084
10663
|
</div>
|
|
9085
|
-
<app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="items
|
|
10664
|
+
<app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="!items || items.length()>1"></app-sprtransport>
|
|
9086
10665
|
<div fxFlex="1 1 30%" fxLayoutAlign="end center" fxLayout="row">
|
|
9087
10666
|
<app-uploadstatus class="ricontrols" fxHide.xs fxLayoutAlign="end center" *ngIf="enableUploadRecordings" [value]="uploadProgress"
|
|
9088
10667
|
[status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
|
|
@@ -9090,7 +10669,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
|
|
|
9090
10669
|
<app-readystateindicator class="ricontrols" fxLayoutAlign="end center" fxHide.xs [ready]="dataSaved && !isActive()"></app-readystateindicator>
|
|
9091
10670
|
</div>
|
|
9092
10671
|
</div>
|
|
9093
|
-
`, isInline: true, styles: [":host{flex:2;background:lightgrey;display:flex;flex-direction:column;margin:0;padding:0;min-height:0px;overflow:hidden}\n", ".ricontrols{padding:4px;box-sizing:border-box;height:100%}\n", ".dark{background:darkgray}\n", ".controlpanel{align-content:center;align-items:center;margin:0;padding:20px;min-height:min-content}\n"], components: [{ type: WarningBar, selector: "app-warningbar", inputs: ["warningText", "show"] }, { type: Prompting, selector: "app-sprprompting", inputs: ["projectName", "startStopSignalState", "promptItem", "showPrompt", "items", "selectedItemIdx", "transportActions", "enableDownload", "audioSignalCollapsed", "displayAudioClip", "playStartAction", "playSelectionAction", "autoPlayOnSelectToggleAction", "playStopAction"], outputs: ["onItemSelect", "onNextItem", "onPrevItem"] }, { type: i7.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { type: LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos"] }, { type: RecordingItemControls, selector: "spr-recordingitemcontrols", inputs: ["audioSignalCollapsed", "enableDownload", "peakDbLvl", "agc", "audioLoaded", "playStartAction", "playStopAction", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }, { type: UploadStatus, selector: "app-uploadstatus", inputs: ["value", "awaitNewUpload", "status"] }, { type: WakeLockIndicator, selector: "app-wakelockindicator", inputs: ["screenLocked"] }, { type: ReadyStateIndicator, selector: "app-readystateindicator", inputs: ["ready"] }, { type: StatusDisplay, selector: "app-sprstatusdisplay", inputs: ["statusAlertType", "statusMsg", "statusWaiting"] }, { type: TransportPanel, selector: "app-sprtransport", inputs: ["readonly", "actions", "navigationEnabled", "pausingEnabled"] }], directives: [{ type: i3$1.DefaultShowHideDirective, selector: " [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]", inputs: ["fxShow", "fxShow.print", "fxShow.xs", "fxShow.sm", "fxShow.md", "fxShow.lg", "fxShow.xl", "fxShow.lt-sm", "fxShow.lt-md", "fxShow.lt-lg", "fxShow.lt-xl", "fxShow.gt-xs", "fxShow.gt-sm", "fxShow.gt-md", "fxShow.gt-lg", "fxHide", "fxHide.print", "fxHide.xs", "fxHide.sm", "fxHide.md", "fxHide.lg", "fxHide.xl", "fxHide.lt-sm", "fxHide.lt-md", "fxHide.lt-lg", "fxHide.lt-xl", "fxHide.gt-xs", "fxHide.gt-sm", "fxHide.gt-md", "fxHide.gt-lg"] }, { type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i3$1.DefaultStyleDirective, selector: " [ngStyle], [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl], [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl], [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]", inputs: ["ngStyle", "ngStyle.xs", "ngStyle.sm", "ngStyle.md", "ngStyle.lg", "ngStyle.xl", "ngStyle.lt-sm", "ngStyle.lt-md", "ngStyle.lt-lg", "ngStyle.lt-xl", "ngStyle.gt-xs", "ngStyle.gt-sm", "ngStyle.gt-md", "ngStyle.gt-lg"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i8.DefaultFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i8.DefaultLayoutAlignDirective, selector: " [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]", inputs: ["fxLayoutAlign", "fxLayoutAlign.xs", "fxLayoutAlign.sm", "fxLayoutAlign.md", "fxLayoutAlign.lg", "fxLayoutAlign.xl", "fxLayoutAlign.lt-sm", "fxLayoutAlign.lt-md", "fxLayoutAlign.lt-lg", "fxLayoutAlign.lt-xl", "fxLayoutAlign.gt-xs", "fxLayoutAlign.gt-sm", "fxLayoutAlign.gt-md", "fxLayoutAlign.gt-lg"] }] });
|
|
10672
|
+
`, isInline: true, styles: [":host{flex:2;background:lightgrey;display:flex;flex-direction:column;margin:0;padding:0;min-height:0px;overflow:hidden}\n", ".ricontrols{padding:4px;box-sizing:border-box;height:100%}\n", ".dark{background:darkgray}\n", ".controlpanel{align-content:center;align-items:center;margin:0;padding:20px;min-height:min-content}\n"], components: [{ type: WarningBar, selector: "app-warningbar", inputs: ["warningText", "show"] }, { type: Prompting, selector: "app-sprprompting", inputs: ["projectName", "startStopSignalState", "promptItem", "showPrompt", "items", "selectedItemIdx", "transportActions", "enableDownload", "audioSignalCollapsed", "displayAudioClip", "playStartAction", "playSelectionAction", "autoPlayOnSelectToggleAction", "playStopAction"], outputs: ["onItemSelect", "onNextItem", "onPrevItem"] }, { type: i7.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { type: LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos", "stateLoading"] }, { type: RecordingItemControls, selector: "spr-recordingitemcontrols", inputs: ["audioSignalCollapsed", "enableDownload", "peakDbLvl", "agc", "audioLoaded", "playStartAction", "playStopAction", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }, { type: UploadStatus, selector: "app-uploadstatus", inputs: ["value", "awaitNewUpload", "status"] }, { type: WakeLockIndicator, selector: "app-wakelockindicator", inputs: ["screenLocked"] }, { type: ReadyStateIndicator, selector: "app-readystateindicator", inputs: ["ready"] }, { type: StatusDisplay, selector: "app-sprstatusdisplay", inputs: ["statusAlertType", "statusMsg", "statusWaiting"] }, { type: TransportPanel, selector: "app-sprtransport", inputs: ["readonly", "actions", "navigationEnabled", "pausingEnabled"] }], directives: [{ type: i3$1.DefaultShowHideDirective, selector: " [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]", inputs: ["fxShow", "fxShow.print", "fxShow.xs", "fxShow.sm", "fxShow.md", "fxShow.lg", "fxShow.xl", "fxShow.lt-sm", "fxShow.lt-md", "fxShow.lt-lg", "fxShow.lt-xl", "fxShow.gt-xs", "fxShow.gt-sm", "fxShow.gt-md", "fxShow.gt-lg", "fxHide", "fxHide.print", "fxHide.xs", "fxHide.sm", "fxHide.md", "fxHide.lg", "fxHide.xl", "fxHide.lt-sm", "fxHide.lt-md", "fxHide.lt-lg", "fxHide.lt-xl", "fxHide.gt-xs", "fxHide.gt-sm", "fxHide.gt-md", "fxHide.gt-lg"] }, { type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i3$1.DefaultStyleDirective, selector: " [ngStyle], [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl], [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl], [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]", inputs: ["ngStyle", "ngStyle.xs", "ngStyle.sm", "ngStyle.md", "ngStyle.lg", "ngStyle.xl", "ngStyle.lt-sm", "ngStyle.lt-md", "ngStyle.lt-lg", "ngStyle.lt-xl", "ngStyle.gt-xs", "ngStyle.gt-sm", "ngStyle.gt-md", "ngStyle.gt-lg"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i8.DefaultFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i8.DefaultLayoutAlignDirective, selector: " [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]", inputs: ["fxLayoutAlign", "fxLayoutAlign.xs", "fxLayoutAlign.sm", "fxLayoutAlign.md", "fxLayoutAlign.lg", "fxLayoutAlign.xl", "fxLayoutAlign.lt-sm", "fxLayoutAlign.lt-md", "fxLayoutAlign.lt-lg", "fxLayoutAlign.lt-xl", "fxLayoutAlign.gt-xs", "fxLayoutAlign.gt-sm", "fxLayoutAlign.gt-md", "fxLayoutAlign.gt-lg"] }] });
|
|
9094
10673
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SessionManager, decorators: [{
|
|
9095
10674
|
type: Component,
|
|
9096
10675
|
args: [{
|
|
@@ -9101,7 +10680,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
9101
10680
|
<app-warningbar [show]="isDefaultAudioTestSession()" warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
|
|
9102
10681
|
<app-sprprompting [projectName]="projectName"
|
|
9103
10682
|
[startStopSignalState]="startStopSignalState" [promptItem]="promptItem" [showPrompt]="showPrompt"
|
|
9104
|
-
[items]="items"
|
|
10683
|
+
[items]="items?.items"
|
|
9105
10684
|
[transportActions]="transportActions"
|
|
9106
10685
|
[selectedItemIdx]="promptIndex" (onItemSelect)="itemSelect($event)" (onNextItem)="nextItem()" (onPrevItem)="prevItem()"
|
|
9107
10686
|
[audioSignalCollapsed]="audioSignalCollapsed" [displayAudioClip]="displayAudioClip"
|
|
@@ -9115,10 +10694,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
9115
10694
|
|
|
9116
10695
|
|
|
9117
10696
|
<div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}" [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
|
|
9118
|
-
<audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="
|
|
10697
|
+
<audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos" [stateLoading]="audioFetching"></audio-levelbar>
|
|
9119
10698
|
<div fxLayout="row">
|
|
9120
10699
|
<spr-recordingitemcontrols fxFlex="10 0 1"
|
|
9121
|
-
[audioLoaded]="displayAudioClip?.
|
|
10700
|
+
[audioLoaded]="displayAudioClip?.audioDataHolder!==null"
|
|
9122
10701
|
[playStartAction]="controlAudioPlayer?.startAction"
|
|
9123
10702
|
[playStopAction]="controlAudioPlayer?.stopAction"
|
|
9124
10703
|
[peakDbLvl]="peakLevelInDb"
|
|
@@ -9136,7 +10715,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
9136
10715
|
<div fxFlex="1 1 30%" fxLayoutAlign="start center">
|
|
9137
10716
|
<app-sprstatusdisplay fxHide.xs [statusMsg]="statusMsg" [statusAlertType]="statusAlertType" [statusWaiting]="statusWaiting"></app-sprstatusdisplay>
|
|
9138
10717
|
</div>
|
|
9139
|
-
<app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="items
|
|
10718
|
+
<app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="!items || items.length()>1"></app-sprtransport>
|
|
9140
10719
|
<div fxFlex="1 1 30%" fxLayoutAlign="end center" fxLayout="row">
|
|
9141
10720
|
<app-uploadstatus class="ricontrols" fxHide.xs fxLayoutAlign="end center" *ngIf="enableUploadRecordings" [value]="uploadProgress"
|
|
9142
10721
|
[status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
|
|
@@ -9280,7 +10859,7 @@ class SpeechrecorderngComponent extends RecorderComponent {
|
|
|
9280
10859
|
ngAfterViewInit() {
|
|
9281
10860
|
// let wakeLockSupp=('wakeLock' in navigator);
|
|
9282
10861
|
// alert('Wake lock API supported: '+wakeLockSupp);
|
|
9283
|
-
if (this.sm.status !==
|
|
10862
|
+
if (this.sm.status !== 9 /* ERROR */) {
|
|
9284
10863
|
let initSuccess = this.init();
|
|
9285
10864
|
if (initSuccess) {
|
|
9286
10865
|
this.route.queryParams.subscribe((params) => {
|
|
@@ -9425,7 +11004,7 @@ class SpeechrecorderngComponent extends RecorderComponent {
|
|
|
9425
11004
|
this.uploadUpdate(ue);
|
|
9426
11005
|
};
|
|
9427
11006
|
window.addEventListener('beforeunload', (e) => {
|
|
9428
|
-
console.debug("Before page unload event");
|
|
11007
|
+
//console.debug("Before page unload event");
|
|
9429
11008
|
if (this.ready()) {
|
|
9430
11009
|
return;
|
|
9431
11010
|
}
|
|
@@ -9744,14 +11323,15 @@ class AudioDisplayPlayer {
|
|
|
9744
11323
|
if (this.aCtx) {
|
|
9745
11324
|
this.aCtx.decodeAudioData(data, (audioBuffer) => {
|
|
9746
11325
|
//console.debug("Audio Buffer Samplerate: ", audioBuffer.sampleRate)
|
|
9747
|
-
|
|
11326
|
+
let adh = new AudioDataHolder(audioBuffer, null);
|
|
11327
|
+
this.audioClip = new AudioClip(adh);
|
|
9748
11328
|
});
|
|
9749
11329
|
}
|
|
9750
11330
|
}
|
|
9751
|
-
set audioData(
|
|
9752
|
-
this.audioDisplayScrollPane.audioData =
|
|
9753
|
-
if (
|
|
9754
|
-
let clip = new AudioClip(
|
|
11331
|
+
set audioData(audioData) {
|
|
11332
|
+
this.audioDisplayScrollPane.audioData = audioData;
|
|
11333
|
+
if (audioData) {
|
|
11334
|
+
let clip = new AudioClip(audioData);
|
|
9755
11335
|
if (this.ap) {
|
|
9756
11336
|
this.ap.audioClip = clip;
|
|
9757
11337
|
this.playStartAction.disabled = false;
|
|
@@ -9773,7 +11353,7 @@ class AudioDisplayPlayer {
|
|
|
9773
11353
|
let audioData = null;
|
|
9774
11354
|
let sel = null;
|
|
9775
11355
|
if (audioClip) {
|
|
9776
|
-
audioData = audioClip.
|
|
11356
|
+
audioData = audioClip.audioDataHolder;
|
|
9777
11357
|
sel = audioClip.selection;
|
|
9778
11358
|
if (this._audioClip) {
|
|
9779
11359
|
this._audioClip.addSelectionObserver((ac) => {
|
|
@@ -10044,7 +11624,7 @@ class RecordingFileService {
|
|
|
10044
11624
|
// Do not use Promise version, which does not work with Safari 13
|
|
10045
11625
|
if (resp.body) {
|
|
10046
11626
|
aCtx.decodeAudioData(resp.body, ab => {
|
|
10047
|
-
recordingFile
|
|
11627
|
+
RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(ab));
|
|
10048
11628
|
if (this.debugDelay > 0) {
|
|
10049
11629
|
window.setTimeout(() => {
|
|
10050
11630
|
observer.next(recordingFile);
|
|
@@ -10094,7 +11674,7 @@ class RecordingFileService {
|
|
|
10094
11674
|
if (resp.body) {
|
|
10095
11675
|
aCtx.decodeAudioData(resp.body, ab => {
|
|
10096
11676
|
if (rf) {
|
|
10097
|
-
rf
|
|
11677
|
+
RecordingFileUtils.setAudioData(rf, new AudioDataHolder(ab));
|
|
10098
11678
|
}
|
|
10099
11679
|
else {
|
|
10100
11680
|
observer.error('Recording file object null');
|
|
@@ -10145,7 +11725,8 @@ class RecordingFileService {
|
|
|
10145
11725
|
if (resp.body) {
|
|
10146
11726
|
aCtx.decodeAudioData(resp.body, ab => {
|
|
10147
11727
|
if (rf) {
|
|
10148
|
-
|
|
11728
|
+
let adh = new AudioDataHolder(ab);
|
|
11729
|
+
RecordingFileUtils.setAudioData(rf, adh);
|
|
10149
11730
|
}
|
|
10150
11731
|
else {
|
|
10151
11732
|
observer.error('Recording file object null');
|
|
@@ -10188,7 +11769,7 @@ class RecordingFileService {
|
|
|
10188
11769
|
// append UUID to make request URL unique to avoid localhost server caching
|
|
10189
11770
|
recUrl = recUrl + '.json?requestUUID=' + UUID.generate();
|
|
10190
11771
|
}
|
|
10191
|
-
console.log("Path request URL: "
|
|
11772
|
+
//console.log("Path request URL: "+recUrl)
|
|
10192
11773
|
return this.http.patch(recUrl, { editSampleRate: editSampleRate, editStartFrame: editStartFrame, editEndFrame: editEndFrame }, { withCredentials: this.withCredentials });
|
|
10193
11774
|
}
|
|
10194
11775
|
}
|
|
@@ -10206,6 +11787,7 @@ class RecordingFileMetaComponent {
|
|
|
10206
11787
|
constructor() {
|
|
10207
11788
|
this.sessionId = null;
|
|
10208
11789
|
this._recordingFile = null;
|
|
11790
|
+
this.stateLoading = false;
|
|
10209
11791
|
this.itemCode = null;
|
|
10210
11792
|
this.uuid = null;
|
|
10211
11793
|
}
|
|
@@ -10221,12 +11803,12 @@ class RecordingFileMetaComponent {
|
|
|
10221
11803
|
}
|
|
10222
11804
|
if (this.itemCode) {
|
|
10223
11805
|
this.uuid = null;
|
|
10224
|
-
console.debug("SprRecordingFile: "
|
|
11806
|
+
//console.debug("SprRecordingFile: "+this.itemCode+ " UUID: "+this.uuid)
|
|
10225
11807
|
}
|
|
10226
11808
|
else {
|
|
10227
11809
|
this.itemCode = null;
|
|
10228
11810
|
this.uuid = this._recordingFile?.uuid;
|
|
10229
|
-
console.debug("RecordingFile: "
|
|
11811
|
+
//console.debug("RecordingFile: "+this.itemCode+ " UUID: "+this.uuid)
|
|
10230
11812
|
}
|
|
10231
11813
|
}
|
|
10232
11814
|
else {
|
|
@@ -10243,10 +11825,11 @@ class RecordingFileMetaComponent {
|
|
|
10243
11825
|
}
|
|
10244
11826
|
}
|
|
10245
11827
|
RecordingFileMetaComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileMetaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10246
|
-
RecordingFileMetaComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: RecordingFileMetaComponent, selector: "app-recording-file-meta", inputs: { sessionId: "sessionId", recordingFile: "recordingFile" }, ngImport: i0, template: `
|
|
11828
|
+
RecordingFileMetaComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: RecordingFileMetaComponent, selector: "app-recording-file-meta", inputs: { sessionId: "sessionId", stateLoading: "stateLoading", recordingFile: "recordingFile" }, ngImport: i0, template: `
|
|
10247
11829
|
<mat-card>
|
|
10248
11830
|
<mat-card-title>Recording file ID: {{recordingFile?.recordingFileId}}</mat-card-title>
|
|
10249
11831
|
<mat-card-content>
|
|
11832
|
+
<mat-progress-spinner *ngIf="stateLoading" mode="indeterminate" [diameter]="20"></mat-progress-spinner>
|
|
10250
11833
|
<table>
|
|
10251
11834
|
<tr *ngIf="itemCode">
|
|
10252
11835
|
<td>Itemcode:</td>
|
|
@@ -10276,7 +11859,7 @@ RecordingFileMetaComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
|
|
|
10276
11859
|
</table>
|
|
10277
11860
|
</mat-card-content>
|
|
10278
11861
|
</mat-card>
|
|
10279
|
-
`, isInline: true, components: [{ type: i1$4.MatCard, selector: "mat-card", exportAs: ["matCard"] }], directives: [{ type: i1$4.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { type: i1$4.MatCardContent, selector: "mat-card-content, [mat-card-content], [matCardContent]" }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
11862
|
+
`, isInline: true, components: [{ type: i1$4.MatCard, selector: "mat-card", exportAs: ["matCard"] }, { type: i1$3.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "diameter", "strokeWidth", "mode", "value"], exportAs: ["matProgressSpinner"] }], directives: [{ type: i1$4.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { type: i1$4.MatCardContent, selector: "mat-card-content, [mat-card-content], [matCardContent]" }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
10280
11863
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileMetaComponent, decorators: [{
|
|
10281
11864
|
type: Component,
|
|
10282
11865
|
args: [{
|
|
@@ -10285,6 +11868,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
10285
11868
|
<mat-card>
|
|
10286
11869
|
<mat-card-title>Recording file ID: {{recordingFile?.recordingFileId}}</mat-card-title>
|
|
10287
11870
|
<mat-card-content>
|
|
11871
|
+
<mat-progress-spinner *ngIf="stateLoading" mode="indeterminate" [diameter]="20"></mat-progress-spinner>
|
|
10288
11872
|
<table>
|
|
10289
11873
|
<tr *ngIf="itemCode">
|
|
10290
11874
|
<td>Itemcode:</td>
|
|
@@ -10319,6 +11903,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
10319
11903
|
}]
|
|
10320
11904
|
}], propDecorators: { sessionId: [{
|
|
10321
11905
|
type: Input
|
|
11906
|
+
}], stateLoading: [{
|
|
11907
|
+
type: Input
|
|
10322
11908
|
}], recordingFile: [{
|
|
10323
11909
|
type: Input
|
|
10324
11910
|
}] } });
|
|
@@ -10461,6 +12047,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
|
|
|
10461
12047
|
this.recordingFileVersion = null;
|
|
10462
12048
|
this.routedByQueryParam = false;
|
|
10463
12049
|
this.posInList = null;
|
|
12050
|
+
this.audioFetching = false;
|
|
10464
12051
|
this.naviInfoLoading = false;
|
|
10465
12052
|
this.parentE = this.eRef.nativeElement;
|
|
10466
12053
|
this.firstAction = new Action('First');
|
|
@@ -10630,18 +12217,20 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
|
|
|
10630
12217
|
this.updateActions();
|
|
10631
12218
|
let audioContext = AudioContextProvider.audioContextInstance();
|
|
10632
12219
|
if (audioContext) {
|
|
12220
|
+
this.audioFetching = true;
|
|
10633
12221
|
this.recordingFileService.fetchSprRecordingFile(audioContext, rfId).subscribe(value => {
|
|
12222
|
+
this.audioFetching = false;
|
|
10634
12223
|
this.status = 'Audio file loaded.';
|
|
10635
12224
|
let clip = null;
|
|
10636
12225
|
this.recordingFile = value;
|
|
10637
12226
|
if (this.recordingFile) {
|
|
10638
|
-
let ab = this.recordingFile.
|
|
12227
|
+
let ab = this.recordingFile.audioDataHolder;
|
|
10639
12228
|
if (ab) {
|
|
10640
12229
|
clip = new AudioClip(ab);
|
|
10641
12230
|
let esffsr = null;
|
|
10642
12231
|
let eeffsr = null;
|
|
10643
12232
|
let esr = null;
|
|
10644
|
-
if (clip.
|
|
12233
|
+
if (clip.audioDataHolder != null) {
|
|
10645
12234
|
esr = ab.sampleRate;
|
|
10646
12235
|
if (esr != null) {
|
|
10647
12236
|
esffsr = RecordingFileUtil.editStartFrameForSampleRate(this.recordingFile, esr);
|
|
@@ -10653,8 +12242,8 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
|
|
|
10653
12242
|
sel = new Selection(ab.sampleRate, esffsr, eeffsr);
|
|
10654
12243
|
}
|
|
10655
12244
|
else {
|
|
10656
|
-
let ch0 = ab.getChannelData(0);
|
|
10657
|
-
let frameLength =
|
|
12245
|
+
//let ch0 = ab.getChannelData(0);
|
|
12246
|
+
let frameLength = ab.frameLen;
|
|
10658
12247
|
sel = new Selection(esr, esffsr, frameLength);
|
|
10659
12248
|
}
|
|
10660
12249
|
}
|
|
@@ -10668,6 +12257,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
|
|
|
10668
12257
|
this.audioClip = clip;
|
|
10669
12258
|
this.loadedRecfile();
|
|
10670
12259
|
}, error1 => {
|
|
12260
|
+
this.audioFetching = false;
|
|
10671
12261
|
this.status = 'Error loading audio file!';
|
|
10672
12262
|
});
|
|
10673
12263
|
}
|
|
@@ -10777,7 +12367,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
|
|
|
10777
12367
|
for (let avRfV of avRf) {
|
|
10778
12368
|
os += avRfV.version + '/';
|
|
10779
12369
|
}
|
|
10780
|
-
console.debug(os);
|
|
12370
|
+
//console.debug(os);
|
|
10781
12371
|
}
|
|
10782
12372
|
}
|
|
10783
12373
|
this.updatePos();
|
|
@@ -10793,7 +12383,7 @@ RecordingFileViewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
|
|
|
10793
12383
|
|
|
10794
12384
|
<audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
|
|
10795
12385
|
<div class="ctrlview">
|
|
10796
|
-
<app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
|
|
12386
|
+
<app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
|
|
10797
12387
|
|
|
10798
12388
|
<audio-display-control [audioClip]="audioClip"
|
|
10799
12389
|
[playStartAction]="playStartAction"
|
|
@@ -10806,7 +12396,7 @@ RecordingFileViewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
|
|
|
10806
12396
|
[zoomFitToPanelAction]="zoomFitToPanelAction"></audio-display-control>
|
|
10807
12397
|
<app-recording-file-navi [items]="availRecFiles?.length" [itemPos]="posInList" [version]="recordingFileVersion" [versions]="versions" [firstAction]="firstAction" [prevAction]="prevAction" [nextAction]="nextAction" [lastAction]="lastAction" [selectVersion]="toVersionAction" [naviInfoLoading]="naviInfoLoading"></app-recording-file-navi>
|
|
10808
12398
|
</div>
|
|
10809
|
-
`, isInline: true, styles: [":host{flex:2;display:flex;flex-direction:column;min-height:0;overflow:hidden;padding:20px;z-index:5;box-sizing:border-box;background-color:#fff}\n", ".ctrlview{display:flex;flex-direction:row}\n", "audio-display-control{flex:3}\n"], components: [{ type: AudioDisplayScrollPane, selector: "audio-display-scroll-pane", inputs: ["audioClip"], outputs: ["zoomInAction", "zoomOutAction", "zoomSelectedAction", "zoomFitToPanelAction"] }, { type: RecordingFileMetaComponent, selector: "app-recording-file-meta", inputs: ["sessionId", "recordingFile"] }, { type: AudioDisplayControl, selector: "audio-display-control", inputs: ["audioClip", "playStartAction", "playSelectionAction", "playStopAction", "zoomInAction", "zoomOutAction", "zoomFitToPanelAction", "zoomSelectedAction", "autoPlayOnSelectToggleAction"] }, { type: RecordingFileNaviComponent, selector: "app-recording-file-navi", inputs: ["firstAction", "prevAction", "nextAction", "lastAction", "items", "itemPos", "selectVersion", "versions", "version", "naviInfoLoading"] }] });
|
|
12399
|
+
`, isInline: true, styles: [":host{flex:2;display:flex;flex-direction:column;min-height:0;overflow:hidden;padding:20px;z-index:5;box-sizing:border-box;background-color:#fff}\n", ".ctrlview{display:flex;flex-direction:row}\n", "audio-display-control{flex:3}\n"], components: [{ type: AudioDisplayScrollPane, selector: "audio-display-scroll-pane", inputs: ["audioClip"], outputs: ["zoomInAction", "zoomOutAction", "zoomSelectedAction", "zoomFitToPanelAction"] }, { type: RecordingFileMetaComponent, selector: "app-recording-file-meta", inputs: ["sessionId", "stateLoading", "recordingFile"] }, { type: AudioDisplayControl, selector: "audio-display-control", inputs: ["audioClip", "playStartAction", "playSelectionAction", "playStopAction", "zoomInAction", "zoomOutAction", "zoomFitToPanelAction", "zoomSelectedAction", "autoPlayOnSelectToggleAction"] }, { type: RecordingFileNaviComponent, selector: "app-recording-file-navi", inputs: ["firstAction", "prevAction", "nextAction", "lastAction", "items", "itemPos", "selectVersion", "versions", "version", "naviInfoLoading"] }] });
|
|
10810
12400
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileViewComponent, decorators: [{
|
|
10811
12401
|
type: Component,
|
|
10812
12402
|
args: [{
|
|
@@ -10815,7 +12405,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
10815
12405
|
|
|
10816
12406
|
<audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
|
|
10817
12407
|
<div class="ctrlview">
|
|
10818
|
-
<app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
|
|
12408
|
+
<app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
|
|
10819
12409
|
|
|
10820
12410
|
<audio-display-control [audioClip]="audioClip"
|
|
10821
12411
|
[playStartAction]="playStartAction"
|
|
@@ -10908,7 +12498,7 @@ class RecordingFileUI extends RecordingFileViewComponent {
|
|
|
10908
12498
|
}
|
|
10909
12499
|
applySelection() {
|
|
10910
12500
|
if (this.audioClip) {
|
|
10911
|
-
let ab = this.audioClip.
|
|
12501
|
+
let ab = this.audioClip.audioDataHolder;
|
|
10912
12502
|
let s = this.audioClip.selection;
|
|
10913
12503
|
if (ab && this.recordingFile?.recordingFileId) {
|
|
10914
12504
|
let sr = null;
|
|
@@ -10946,7 +12536,7 @@ RecordingFileUI.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", versio
|
|
|
10946
12536
|
|
|
10947
12537
|
<audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
|
|
10948
12538
|
<div class="ctrlview">
|
|
10949
|
-
<app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
|
|
12539
|
+
<app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
|
|
10950
12540
|
<audio-display-control [audioClip]="audioClip"
|
|
10951
12541
|
[playStartAction]="playStartAction"
|
|
10952
12542
|
[playSelectionAction]="playSelectionAction"
|
|
@@ -10960,7 +12550,7 @@ RecordingFileUI.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", versio
|
|
|
10960
12550
|
</div>
|
|
10961
12551
|
|
|
10962
12552
|
<button mat-raised-button color="accent" (click)="applySelection()" [disabled]="editSaved">{{this.applyButtonText()}}</button>
|
|
10963
|
-
`, isInline: true, styles: [":host{flex:2;display:flex;flex-direction:column;min-height:0;overflow:hidden;padding:20px;z-index:5;box-sizing:border-box;background-color:#fff}\n", ".ctrlview{display:flex;flex-direction:row}\n", "audio-display-control{flex:3}\n"], components: [{ type: AudioDisplayScrollPane, selector: "audio-display-scroll-pane", inputs: ["audioClip"], outputs: ["zoomInAction", "zoomOutAction", "zoomSelectedAction", "zoomFitToPanelAction"] }, { type: RecordingFileMetaComponent, selector: "app-recording-file-meta", inputs: ["sessionId", "recordingFile"] }, { type: AudioDisplayControl, selector: "audio-display-control", inputs: ["audioClip", "playStartAction", "playSelectionAction", "playStopAction", "zoomInAction", "zoomOutAction", "zoomFitToPanelAction", "zoomSelectedAction", "autoPlayOnSelectToggleAction"] }, { type: RecordingFileNaviComponent, selector: "app-recording-file-navi", inputs: ["firstAction", "prevAction", "nextAction", "lastAction", "items", "itemPos", "selectVersion", "versions", "version", "naviInfoLoading"] }, { type: i3$2.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }] });
|
|
12553
|
+
`, isInline: true, styles: [":host{flex:2;display:flex;flex-direction:column;min-height:0;overflow:hidden;padding:20px;z-index:5;box-sizing:border-box;background-color:#fff}\n", ".ctrlview{display:flex;flex-direction:row}\n", "audio-display-control{flex:3}\n"], components: [{ type: AudioDisplayScrollPane, selector: "audio-display-scroll-pane", inputs: ["audioClip"], outputs: ["zoomInAction", "zoomOutAction", "zoomSelectedAction", "zoomFitToPanelAction"] }, { type: RecordingFileMetaComponent, selector: "app-recording-file-meta", inputs: ["sessionId", "stateLoading", "recordingFile"] }, { type: AudioDisplayControl, selector: "audio-display-control", inputs: ["audioClip", "playStartAction", "playSelectionAction", "playStopAction", "zoomInAction", "zoomOutAction", "zoomFitToPanelAction", "zoomSelectedAction", "autoPlayOnSelectToggleAction"] }, { type: RecordingFileNaviComponent, selector: "app-recording-file-navi", inputs: ["firstAction", "prevAction", "nextAction", "lastAction", "items", "itemPos", "selectVersion", "versions", "version", "naviInfoLoading"] }, { type: i3$2.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }] });
|
|
10964
12554
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileUI, decorators: [{
|
|
10965
12555
|
type: Component,
|
|
10966
12556
|
args: [{
|
|
@@ -10971,7 +12561,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
10971
12561
|
|
|
10972
12562
|
<audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
|
|
10973
12563
|
<div class="ctrlview">
|
|
10974
|
-
<app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
|
|
12564
|
+
<app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
|
|
10975
12565
|
<audio-display-control [audioClip]="audioClip"
|
|
10976
12566
|
[playStartAction]="playStartAction"
|
|
10977
12567
|
[playSelectionAction]="playSelectionAction"
|
|
@@ -11028,7 +12618,8 @@ class MediaUtils {
|
|
|
11028
12618
|
|
|
11029
12619
|
class RecordingList {
|
|
11030
12620
|
constructor() {
|
|
11031
|
-
|
|
12621
|
+
//private recordingList:Array<RecordingFile>=new Array<RecordingFile>();
|
|
12622
|
+
this.recordingList = new RecFilesCache();
|
|
11032
12623
|
//cols=['index','length','samples','samplerate','action'];
|
|
11033
12624
|
this.cols = ['index', 'startedDate', 'length', 'action'];
|
|
11034
12625
|
this.selectDisabled = false;
|
|
@@ -11040,7 +12631,7 @@ class RecordingList {
|
|
|
11040
12631
|
this.buildDataSource();
|
|
11041
12632
|
}
|
|
11042
12633
|
buildDataSource() {
|
|
11043
|
-
this.recordingList.sort((a, b) => {
|
|
12634
|
+
this.recordingList.recFiles.sort((a, b) => {
|
|
11044
12635
|
let cmp = 0;
|
|
11045
12636
|
let aD = null;
|
|
11046
12637
|
let bD = null;
|
|
@@ -11061,24 +12652,35 @@ class RecordingList {
|
|
|
11061
12652
|
}
|
|
11062
12653
|
return cmp;
|
|
11063
12654
|
});
|
|
11064
|
-
this.recordingListDataSource.data = this.recordingList;
|
|
12655
|
+
this.recordingListDataSource.data = this.recordingList.recFiles;
|
|
11065
12656
|
}
|
|
11066
|
-
|
|
11067
|
-
this.recordingList.
|
|
12657
|
+
addRecFile(rf) {
|
|
12658
|
+
this.recordingList.addRecFile(rf);
|
|
11068
12659
|
this.buildDataSource();
|
|
11069
12660
|
}
|
|
12661
|
+
setRecFileAudioData(recFile, adh) {
|
|
12662
|
+
this.recordingList.setRecFileAudioData(recFile, adh);
|
|
12663
|
+
}
|
|
11070
12664
|
selectRecordingFile(rf) {
|
|
12665
|
+
this.recordingList.currentRecordingFile = rf;
|
|
11071
12666
|
this.selectedRecordingFileChanged.emit(rf);
|
|
11072
12667
|
}
|
|
11073
12668
|
selectTop() {
|
|
11074
|
-
if (this.recordingList.length > 0) {
|
|
11075
|
-
this.selectRecordingFile(this.recordingList[0]);
|
|
12669
|
+
if (this.recordingList.recFiles.length > 0) {
|
|
12670
|
+
this.selectRecordingFile(this.recordingList.recFiles[0]);
|
|
11076
12671
|
}
|
|
11077
12672
|
}
|
|
11078
12673
|
lengthTimeFormatted(rf) {
|
|
11079
12674
|
let str = '--:--:--';
|
|
11080
|
-
|
|
11081
|
-
|
|
12675
|
+
let tl = null;
|
|
12676
|
+
if (rf.timeLength) {
|
|
12677
|
+
tl = rf.timeLength;
|
|
12678
|
+
}
|
|
12679
|
+
else if (rf.frames && rf.audioDataHolder) {
|
|
12680
|
+
tl = rf.frames / rf.audioDataHolder?.sampleRate;
|
|
12681
|
+
}
|
|
12682
|
+
if (tl) {
|
|
12683
|
+
str = MediaUtils.toMediaTime(tl);
|
|
11082
12684
|
}
|
|
11083
12685
|
return str;
|
|
11084
12686
|
}
|
|
@@ -11164,8 +12766,11 @@ class RecorderCombiPane {
|
|
|
11164
12766
|
}
|
|
11165
12767
|
ngAfterViewInit() {
|
|
11166
12768
|
}
|
|
11167
|
-
|
|
11168
|
-
this.recordingListComp.
|
|
12769
|
+
addRecFile(rf) {
|
|
12770
|
+
this.recordingListComp.addRecFile(rf);
|
|
12771
|
+
}
|
|
12772
|
+
setRecFileAudioData(recFile, adh) {
|
|
12773
|
+
this.recordingListComp.setRecFileAudioData(recFile, adh);
|
|
11169
12774
|
}
|
|
11170
12775
|
selectRecordingFile(rf) {
|
|
11171
12776
|
this.selectedRecordingFileChanged.emit(rf);
|
|
@@ -11238,15 +12843,11 @@ class Item {
|
|
|
11238
12843
|
}
|
|
11239
12844
|
class AudioRecorder extends BasicRecorder {
|
|
11240
12845
|
constructor(changeDetectorRef, renderer, route, dialog, sessionService, recFileService, uploader, config) {
|
|
11241
|
-
super(dialog, sessionService, uploader, config);
|
|
11242
|
-
this.changeDetectorRef = changeDetectorRef;
|
|
12846
|
+
super(changeDetectorRef, dialog, sessionService, uploader, config);
|
|
11243
12847
|
this.renderer = renderer;
|
|
11244
12848
|
this.route = route;
|
|
11245
|
-
this.dialog = dialog;
|
|
11246
|
-
this.sessionService = sessionService;
|
|
11247
12849
|
this.recFileService = recFileService;
|
|
11248
12850
|
this.uploader = uploader;
|
|
11249
|
-
this.config = config;
|
|
11250
12851
|
this._project = null;
|
|
11251
12852
|
this.projectName = null;
|
|
11252
12853
|
this.enableUploadRecordings = true;
|
|
@@ -11299,7 +12900,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11299
12900
|
context = AudioContextProvider.audioContextInstance();
|
|
11300
12901
|
}
|
|
11301
12902
|
catch (err) {
|
|
11302
|
-
this.status =
|
|
12903
|
+
this.status = 5 /* ERROR */;
|
|
11303
12904
|
let errMsg = 'Unknown error';
|
|
11304
12905
|
if (err instanceof Error) {
|
|
11305
12906
|
errMsg = err.message;
|
|
@@ -11323,7 +12924,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11323
12924
|
console.info("No audio context available!");
|
|
11324
12925
|
}
|
|
11325
12926
|
if (!context || !navigator.mediaDevices) {
|
|
11326
|
-
this.status =
|
|
12927
|
+
this.status = 5 /* ERROR */;
|
|
11327
12928
|
let errMsg = 'Browser does not support Media streams!';
|
|
11328
12929
|
this.statusMsg = 'ERROR: ' + errMsg;
|
|
11329
12930
|
this.statusAlertType = 'error';
|
|
@@ -11429,13 +13030,15 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11429
13030
|
if (rfs) {
|
|
11430
13031
|
if (rfs instanceof Array) {
|
|
11431
13032
|
rfs.forEach((rf) => {
|
|
13033
|
+
// the list comes from the server, asssuem all recording files as server persisted
|
|
13034
|
+
rf.serverPersisted = true;
|
|
11432
13035
|
if (rf.startedDate) {
|
|
11433
13036
|
rf._startedAsDateObj = new Date(rf.startedDate);
|
|
11434
13037
|
}
|
|
11435
13038
|
if (rf.date) {
|
|
11436
13039
|
rf._dateAsDateObj = new Date(rf.date);
|
|
11437
13040
|
}
|
|
11438
|
-
this.recorderCombiPane.
|
|
13041
|
+
this.recorderCombiPane.addRecFile(rf);
|
|
11439
13042
|
});
|
|
11440
13043
|
}
|
|
11441
13044
|
else {
|
|
@@ -11489,6 +13092,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11489
13092
|
return this._project;
|
|
11490
13093
|
}
|
|
11491
13094
|
selectRecordingFile(rf) {
|
|
13095
|
+
this.audioFetching = false;
|
|
11492
13096
|
this.displayRecFile = rf;
|
|
11493
13097
|
}
|
|
11494
13098
|
uploadUpdate(ue) {
|
|
@@ -11510,9 +13114,6 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11510
13114
|
this.changeDetectorRef.detectChanges();
|
|
11511
13115
|
}
|
|
11512
13116
|
set controlAudioPlayer(controlAudioPlayer) {
|
|
11513
|
-
if (this._controlAudioPlayer) {
|
|
11514
|
-
//this._controlAudioPlayer.listener=null;
|
|
11515
|
-
}
|
|
11516
13117
|
this._controlAudioPlayer = controlAudioPlayer;
|
|
11517
13118
|
if (this._controlAudioPlayer) {
|
|
11518
13119
|
this._controlAudioPlayer.listener = this;
|
|
@@ -11578,7 +13179,12 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11578
13179
|
}
|
|
11579
13180
|
}
|
|
11580
13181
|
startItem() {
|
|
13182
|
+
this.status = 2 /* STARTING */;
|
|
11581
13183
|
super.startItem();
|
|
13184
|
+
if (this.readonly) {
|
|
13185
|
+
this.status = 1 /* IDLE */;
|
|
13186
|
+
return;
|
|
13187
|
+
}
|
|
11582
13188
|
this.transportActions.fwdAction.disabled = true;
|
|
11583
13189
|
this.transportActions.fwdNextAction.disabled = true;
|
|
11584
13190
|
this.transportActions.bwdAction.disabled = true;
|
|
@@ -11587,6 +13193,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11587
13193
|
this.displayAudioClip = null;
|
|
11588
13194
|
this.showRecording();
|
|
11589
13195
|
if (this.ac) {
|
|
13196
|
+
this.audioFetching = false;
|
|
11590
13197
|
if (!this.ac.opened) {
|
|
11591
13198
|
if (this._selectedDeviceId) {
|
|
11592
13199
|
console.log("Open session with audio device Id: \'" + this._selectedDeviceId + "\' for " + this._channelCount + " channels");
|
|
@@ -11603,10 +13210,10 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11603
13210
|
}
|
|
11604
13211
|
downloadRecording() {
|
|
11605
13212
|
if (this.displayRecFile) {
|
|
11606
|
-
let ab = this.displayRecFile.
|
|
13213
|
+
let ab = this.displayRecFile.audioDataHolder;
|
|
11607
13214
|
let ww = new WavWriter();
|
|
11608
|
-
if (ab) {
|
|
11609
|
-
let wavFile = ww.writeAsync(ab, (wavFile) => {
|
|
13215
|
+
if (ab?.buffer) {
|
|
13216
|
+
let wavFile = ww.writeAsync(ab.buffer, (wavFile) => {
|
|
11610
13217
|
let blob = new Blob([wavFile], { type: 'audio/wav' });
|
|
11611
13218
|
let rfUrl = URL.createObjectURL(blob);
|
|
11612
13219
|
let dataDnlLnk = this.renderer.createElement('a');
|
|
@@ -11629,35 +13236,50 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11629
13236
|
set displayRecFile(displayRecFile) {
|
|
11630
13237
|
this._displayRecFile = displayRecFile;
|
|
11631
13238
|
if (this._displayRecFile) {
|
|
11632
|
-
let
|
|
11633
|
-
if (
|
|
11634
|
-
this.displayAudioClip = new AudioClip(
|
|
13239
|
+
let adh = this._displayRecFile.audioDataHolder;
|
|
13240
|
+
if (adh) {
|
|
13241
|
+
this.displayAudioClip = new AudioClip(adh);
|
|
13242
|
+
console.debug(" set recording file: display audio clip set");
|
|
11635
13243
|
this.controlAudioPlayer.audioClip = this.displayAudioClip;
|
|
11636
13244
|
}
|
|
11637
13245
|
else {
|
|
11638
13246
|
// clear for now ...
|
|
11639
13247
|
this.displayAudioClip = null;
|
|
13248
|
+
console.debug("set recording file: display audio clip null");
|
|
11640
13249
|
this.controlAudioPlayer.audioClip = null;
|
|
11641
13250
|
if (this._controlAudioPlayer && this._session) {
|
|
11642
13251
|
//... and try to fetch from server
|
|
11643
|
-
this.
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
13252
|
+
this.audioFetching = true;
|
|
13253
|
+
let rf = this._displayRecFile;
|
|
13254
|
+
let clip = this.displayAudioClip;
|
|
13255
|
+
this.audioFetchSubscription = this.recFileService.fetchRecordingFileAudioBuffer(this._controlAudioPlayer.context, this._session.project, rf).subscribe({
|
|
13256
|
+
next: ab => {
|
|
13257
|
+
this.audioFetching = false;
|
|
13258
|
+
let fabDh = null;
|
|
13259
|
+
if (ab) {
|
|
13260
|
+
if (rf) {
|
|
13261
|
+
fabDh = new AudioDataHolder(ab);
|
|
13262
|
+
this.recorderCombiPane.setRecFileAudioData(rf, fabDh);
|
|
13263
|
+
}
|
|
13264
|
+
}
|
|
13265
|
+
else {
|
|
13266
|
+
console.error('Recording file could not be loaded.');
|
|
13267
|
+
this.statusMsg = 'Recording file could not be loaded.';
|
|
13268
|
+
this.statusAlertType = 'error';
|
|
13269
|
+
}
|
|
13270
|
+
if (fabDh) {
|
|
13271
|
+
// this.displayAudioClip could have been changed meanwhile, but the recorder unsubcribes before changing the item. Therefore there should be no risk to set to wrong item
|
|
13272
|
+
this.displayAudioClip = new AudioClip(fabDh);
|
|
13273
|
+
//console.debug("set recording file: display audio clip from fetched audio buffer");
|
|
13274
|
+
}
|
|
13275
|
+
this.controlAudioPlayer.audioClip = this.displayAudioClip;
|
|
13276
|
+
this.showRecording();
|
|
13277
|
+
}, error: err => {
|
|
13278
|
+
console.error("Could not load recording file from server: " + err);
|
|
13279
|
+
this.audioFetching = false;
|
|
13280
|
+
this.statusMsg = 'Recording file could not be loaded: ' + err;
|
|
11650
13281
|
this.statusAlertType = 'error';
|
|
11651
13282
|
}
|
|
11652
|
-
if (fab) {
|
|
11653
|
-
this.displayAudioClip = new AudioClip(fab);
|
|
11654
|
-
}
|
|
11655
|
-
this.controlAudioPlayer.audioClip = this.displayAudioClip;
|
|
11656
|
-
this.showRecording();
|
|
11657
|
-
}, err => {
|
|
11658
|
-
console.error("Could not load recording file from server: " + err);
|
|
11659
|
-
this.statusMsg = 'Recording file could not be loaded: ' + err;
|
|
11660
|
-
this.statusAlertType = 'error';
|
|
11661
13283
|
});
|
|
11662
13284
|
}
|
|
11663
13285
|
else {
|
|
@@ -11667,6 +13289,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11667
13289
|
}
|
|
11668
13290
|
}
|
|
11669
13291
|
else {
|
|
13292
|
+
console.debug("recording file null");
|
|
11670
13293
|
this.displayAudioClip = null;
|
|
11671
13294
|
this.controlAudioPlayer.audioClip = null;
|
|
11672
13295
|
}
|
|
@@ -11675,28 +13298,6 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11675
13298
|
get displayRecFile() {
|
|
11676
13299
|
return this._displayRecFile;
|
|
11677
13300
|
}
|
|
11678
|
-
showRecording() {
|
|
11679
|
-
this.controlAudioPlayer.stop();
|
|
11680
|
-
if (this.displayAudioClip) {
|
|
11681
|
-
this.levelMeasure.calcBufferLevelInfos(this.displayAudioClip.buffer, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
|
|
11682
|
-
this.displayLevelInfos = levelInfos;
|
|
11683
|
-
this.changeDetectorRef.detectChanges();
|
|
11684
|
-
});
|
|
11685
|
-
this.playStartAction.disabled = false;
|
|
11686
|
-
}
|
|
11687
|
-
else {
|
|
11688
|
-
// TODO
|
|
11689
|
-
// Setting to null does not trigger a change if it was null before (happens after nextitem() in AUTOPROGRESS mode)
|
|
11690
|
-
// The level bar display does not clear, it shows the last captured stream
|
|
11691
|
-
this.displayLevelInfos = null;
|
|
11692
|
-
this.playStartAction.disabled = true;
|
|
11693
|
-
// Collapse audio signal display if open
|
|
11694
|
-
if (!this.audioSignalCollapsed) {
|
|
11695
|
-
this.audioSignalCollapsed = true;
|
|
11696
|
-
}
|
|
11697
|
-
}
|
|
11698
|
-
this.changeDetectorRef.detectChanges();
|
|
11699
|
-
}
|
|
11700
13301
|
updateStartActionDisableState() {
|
|
11701
13302
|
this.transportActions.startAction.disabled = !(this.ac);
|
|
11702
13303
|
}
|
|
@@ -11707,10 +13308,10 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11707
13308
|
this.updateStartActionDisableState();
|
|
11708
13309
|
}
|
|
11709
13310
|
isRecording() {
|
|
11710
|
-
return (this.status ===
|
|
13311
|
+
return (this.status === 3 /* RECORDING */);
|
|
11711
13312
|
}
|
|
11712
13313
|
isActive() {
|
|
11713
|
-
return (!(this.status === 0 /* BLOCKED */ || this.status === 1 /* IDLE */ || this.status ===
|
|
13314
|
+
return (!(this.status === 0 /* BLOCKED */ || this.status === 1 /* IDLE */ || this.status === 5 /* ERROR */) || this.processingRecording || this.sessionService.uploadCount > 0);
|
|
11714
13315
|
}
|
|
11715
13316
|
updateWakeLock(dataSaved = this.dataSaved) {
|
|
11716
13317
|
//console.debug("Update wake lock: dataSaved: "+dataSaved+", not active: "+! this.isActive())
|
|
@@ -11736,10 +13337,23 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11736
13337
|
}
|
|
11737
13338
|
}
|
|
11738
13339
|
started() {
|
|
11739
|
-
this.status =
|
|
13340
|
+
this.status = 3 /* RECORDING */;
|
|
11740
13341
|
super.started();
|
|
11741
13342
|
this.statusAlertType = 'info';
|
|
11742
13343
|
this.statusMsg = 'Recording...';
|
|
13344
|
+
if (!this.rfUuid) {
|
|
13345
|
+
this.rfUuid = UUID.generate();
|
|
13346
|
+
}
|
|
13347
|
+
let sessId = 0;
|
|
13348
|
+
if (this._session) {
|
|
13349
|
+
sessId = this._session.sessionId;
|
|
13350
|
+
}
|
|
13351
|
+
let rf = new RecordingFile(this.rfUuid, sessId, null);
|
|
13352
|
+
rf._startedAsDateObj = this.startedDate;
|
|
13353
|
+
if (rf._startedAsDateObj) {
|
|
13354
|
+
rf.startedDate = rf._startedAsDateObj.toString();
|
|
13355
|
+
}
|
|
13356
|
+
this._recordingFile = rf;
|
|
11743
13357
|
let maxRecordingTimeMs = MAX_RECORDING_TIME_MS;
|
|
11744
13358
|
this.maxRecTimerId = window.setTimeout(() => {
|
|
11745
13359
|
this.stopRecordingMaxRec();
|
|
@@ -11750,7 +13364,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11750
13364
|
stopItem() {
|
|
11751
13365
|
this.transportActions.stopAction.disabled = true;
|
|
11752
13366
|
this.transportActions.nextAction.disabled = true;
|
|
11753
|
-
this.status =
|
|
13367
|
+
this.status = 4 /* STOPPING_STOP */;
|
|
11754
13368
|
this.stopRecording();
|
|
11755
13369
|
}
|
|
11756
13370
|
stopRecording() {
|
|
@@ -11764,7 +13378,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11764
13378
|
}
|
|
11765
13379
|
stopRecordingMaxRec() {
|
|
11766
13380
|
this.maxRecTimerRunning = false;
|
|
11767
|
-
this.status =
|
|
13381
|
+
this.status = 4 /* STOPPING_STOP */;
|
|
11768
13382
|
if (this.ac) {
|
|
11769
13383
|
this.ac.stop();
|
|
11770
13384
|
}
|
|
@@ -11776,72 +13390,75 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11776
13390
|
this.transportActions.pauseAction.disabled = true;
|
|
11777
13391
|
this.statusAlertType = 'info';
|
|
11778
13392
|
this.statusMsg = 'Recorded.';
|
|
11779
|
-
let ad;
|
|
13393
|
+
let ad = null;
|
|
13394
|
+
let ada = null;
|
|
13395
|
+
let adh = null;
|
|
13396
|
+
let frameLen = 0;
|
|
11780
13397
|
if (this.ac) {
|
|
11781
|
-
|
|
13398
|
+
if (this.uploadChunkSizeSeconds || AudioRecorder.FORCE_ARRRAY_AUDIO_BUFFER) {
|
|
13399
|
+
ada = this.ac.audioBufferArray();
|
|
13400
|
+
frameLen = ada.frameLen;
|
|
13401
|
+
}
|
|
13402
|
+
else {
|
|
13403
|
+
ad = this.ac.audioBuffer();
|
|
13404
|
+
frameLen = ad.length;
|
|
13405
|
+
}
|
|
13406
|
+
adh = new AudioDataHolder(ad, ada);
|
|
11782
13407
|
let sessId = 0;
|
|
11783
13408
|
if (this._session) {
|
|
11784
13409
|
sessId = this._session.sessionId;
|
|
11785
13410
|
}
|
|
11786
|
-
if (
|
|
11787
|
-
this.
|
|
11788
|
-
|
|
11789
|
-
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
13411
|
+
if (this._recordingFile) {
|
|
13412
|
+
// Use an own reference since the writing of the wave file is asynchronous and this._recordingFile might already contain the next recording
|
|
13413
|
+
let rf = this._recordingFile;
|
|
13414
|
+
RecordingFileUtils.setAudioData(rf, adh);
|
|
13415
|
+
this.recorderCombiPane.addRecFile(rf);
|
|
13416
|
+
// Upload if upload enabled and not in chunked upload mode
|
|
13417
|
+
if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds && rf != null && ad != null) {
|
|
13418
|
+
let apiEndPoint = '';
|
|
13419
|
+
if (this.config && this.config.apiEndPoint) {
|
|
13420
|
+
apiEndPoint = this.config.apiEndPoint;
|
|
13421
|
+
}
|
|
13422
|
+
if (apiEndPoint !== '') {
|
|
13423
|
+
apiEndPoint = apiEndPoint + '/';
|
|
13424
|
+
}
|
|
13425
|
+
let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
|
|
13426
|
+
let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
|
|
13427
|
+
// convert asynchronously to 16-bit integer PCM
|
|
13428
|
+
// TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
|
|
13429
|
+
// TODO duplicate conversion for manual download
|
|
13430
|
+
this.processingRecording = true;
|
|
13431
|
+
let ww = new WavWriter();
|
|
13432
|
+
ww.writeAsync(ad, (wavFile) => {
|
|
13433
|
+
this.postRecordingMultipart(wavFile, recUrl, rf);
|
|
13434
|
+
this.processingRecording = false;
|
|
13435
|
+
this.updateWakeLock();
|
|
13436
|
+
this.changeDetectorRef.detectChanges();
|
|
13437
|
+
});
|
|
11805
13438
|
}
|
|
11806
|
-
let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
|
|
11807
|
-
let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
|
|
11808
|
-
// convert asynchronously to 16-bit integer PCM
|
|
11809
|
-
// TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
|
|
11810
|
-
// TODO duplicate conversion for manual download
|
|
11811
|
-
this.processingRecording = true;
|
|
11812
|
-
let ww = new WavWriter();
|
|
11813
|
-
ww.writeAsync(ad, (wavFile) => {
|
|
11814
|
-
this.postRecordingMultipart(wavFile, rf.uuid, rf.session, rf._startedAsDateObj, recUrl);
|
|
11815
|
-
this.processingRecording = false;
|
|
11816
|
-
this.updateWakeLock();
|
|
11817
|
-
this.changeDetectorRef.detectChanges();
|
|
11818
|
-
});
|
|
11819
13439
|
}
|
|
11820
13440
|
}
|
|
11821
|
-
|
|
11822
|
-
let complete = true;
|
|
11823
|
-
let autoStart = (this.status === 3 /* STOPPING_STOP */);
|
|
13441
|
+
this.displayRecFile = this._recordingFile;
|
|
11824
13442
|
this.status = 1 /* IDLE */;
|
|
11825
|
-
let startNext = false;
|
|
11826
13443
|
this.navigationDisabled = false;
|
|
11827
13444
|
this.updateNavigationActions();
|
|
11828
13445
|
this.updateWakeLock();
|
|
11829
13446
|
this.changeDetectorRef.detectChanges();
|
|
11830
13447
|
}
|
|
11831
|
-
postRecordingMultipart(wavFile,
|
|
13448
|
+
postRecordingMultipart(wavFile, recUrl, rf) {
|
|
11832
13449
|
let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
|
|
11833
13450
|
let fd = new FormData();
|
|
11834
|
-
if (uuid) {
|
|
11835
|
-
fd.set('uuid', uuid);
|
|
13451
|
+
if (rf.uuid) {
|
|
13452
|
+
fd.set('uuid', rf.uuid);
|
|
11836
13453
|
}
|
|
11837
|
-
if (
|
|
11838
|
-
fd.set('sessionId',
|
|
13454
|
+
if (rf.session !== null) {
|
|
13455
|
+
fd.set('sessionId', rf.session.toString());
|
|
11839
13456
|
}
|
|
11840
|
-
if (
|
|
11841
|
-
fd.set('startedDate',
|
|
13457
|
+
if (rf._startedAsDateObj) {
|
|
13458
|
+
fd.set('startedDate', rf._startedAsDateObj.toJSON());
|
|
11842
13459
|
}
|
|
11843
13460
|
fd.set('audio', wavBlob);
|
|
11844
|
-
let ul = new Upload(fd, recUrl);
|
|
13461
|
+
let ul = new Upload(fd, recUrl, rf);
|
|
11845
13462
|
this.uploader.queueUpload(ul);
|
|
11846
13463
|
}
|
|
11847
13464
|
postChunkAudioBuffer(audioBuffer, chunkIdx) {
|
|
@@ -11857,8 +13474,9 @@ class AudioRecorder extends BasicRecorder {
|
|
|
11857
13474
|
}
|
|
11858
13475
|
let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
|
|
11859
13476
|
let recUrl = sessionsUrl + '/' + this.session?.sessionId + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/' + chunkIdx;
|
|
13477
|
+
let rf = this._recordingFile;
|
|
11860
13478
|
ww.writeAsync(audioBuffer, (wavFile) => {
|
|
11861
|
-
this.postRecording(wavFile, recUrl);
|
|
13479
|
+
this.postRecording(wavFile, recUrl, rf);
|
|
11862
13480
|
this.processingRecording = false;
|
|
11863
13481
|
});
|
|
11864
13482
|
}
|
|
@@ -11907,11 +13525,11 @@ AudioRecorder.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version:
|
|
|
11907
13525
|
|
|
11908
13526
|
<div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}"
|
|
11909
13527
|
[ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
|
|
11910
|
-
<audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()"
|
|
11911
|
-
[displayLevelInfos]="
|
|
13528
|
+
<audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [stateLoading]="audioFetching"
|
|
13529
|
+
[displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
|
|
11912
13530
|
<div fxLayout="row">
|
|
11913
13531
|
<spr-recordingitemcontrols fxFlex="10 0 1"
|
|
11914
|
-
[audioLoaded]="displayAudioClip?.
|
|
13532
|
+
[audioLoaded]="displayAudioClip?.audioDataHolder!==null"
|
|
11915
13533
|
[playStartAction]="controlAudioPlayer?.startAction"
|
|
11916
13534
|
[playStopAction]="controlAudioPlayer?.stopAction"
|
|
11917
13535
|
[peakDbLvl]="peakLevelInDb"
|
|
@@ -11950,7 +13568,7 @@ AudioRecorder.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version:
|
|
|
11950
13568
|
[ready]="dataSaved && !isActive()"></app-readystateindicator>
|
|
11951
13569
|
</div>
|
|
11952
13570
|
</div>
|
|
11953
|
-
`, isInline: true, styles: [":host{flex:2;background:lightgrey;display:flex;flex-direction:column;margin:0;padding:0;height:100%;min-height:0px;overflow:hidden}\n", ".ricontrols{padding:4px;box-sizing:border-box;height:100%}\n", ".dark{background:darkgray}\n", ".controlpanel{align-content:center;align-items:center;margin:0;padding:20px;min-height:min-content}\n", ".startstop{width:100%;text-align:center;align-content:center;align-items:center}\n", ".bigbutton{min-width:70px;min-height:50px;font-size:50px;border-radius:20px}\n"], components: [{ type: WarningBar, selector: "app-warningbar", inputs: ["warningText", "show"] }, { type: RecorderCombiPane, selector: "app-recordercombipane", inputs: ["selectDisabled", "selectedRecordingFile", "audioSignalCollapsed", "displayAudioClip", "playStartAction", "playSelectionAction", "autoPlayOnSelectToggleAction", "playStopAction"], outputs: ["selectedRecordingFileChanged"] }, { type: LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos"] }, { type: RecordingItemControls, selector: "spr-recordingitemcontrols", inputs: ["audioSignalCollapsed", "enableDownload", "peakDbLvl", "agc", "audioLoaded", "playStartAction", "playStopAction", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }, { type: UploadStatus, selector: "app-uploadstatus", inputs: ["value", "awaitNewUpload", "status"] }, { type: WakeLockIndicator, selector: "app-wakelockindicator", inputs: ["screenLocked"] }, { type: ReadyStateIndicator, selector: "app-readystateindicator", inputs: ["ready"] }, { type: StatusDisplay, selector: "app-sprstatusdisplay", inputs: ["statusAlertType", "statusMsg", "statusWaiting"] }, { type: i3$2.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i3$1.DefaultStyleDirective, selector: " [ngStyle], [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl], [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl], [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]", inputs: ["ngStyle", "ngStyle.xs", "ngStyle.sm", "ngStyle.md", "ngStyle.lg", "ngStyle.xl", "ngStyle.lt-sm", "ngStyle.lt-md", "ngStyle.lt-lg", "ngStyle.lt-xl", "ngStyle.gt-xs", "ngStyle.gt-sm", "ngStyle.gt-md", "ngStyle.gt-lg"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i8.DefaultFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3$1.DefaultShowHideDirective, selector: " [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]", inputs: ["fxShow", "fxShow.print", "fxShow.xs", "fxShow.sm", "fxShow.md", "fxShow.lg", "fxShow.xl", "fxShow.lt-sm", "fxShow.lt-md", "fxShow.lt-lg", "fxShow.lt-xl", "fxShow.gt-xs", "fxShow.gt-sm", "fxShow.gt-md", "fxShow.gt-lg", "fxHide", "fxHide.print", "fxHide.xs", "fxHide.sm", "fxHide.md", "fxHide.lg", "fxHide.xl", "fxHide.lt-sm", "fxHide.lt-md", "fxHide.lt-lg", "fxHide.lt-xl", "fxHide.gt-xs", "fxHide.gt-sm", "fxHide.gt-md", "fxHide.gt-lg"] }] });
|
|
13571
|
+
`, isInline: true, styles: [":host{flex:2;background:lightgrey;display:flex;flex-direction:column;margin:0;padding:0;height:100%;min-height:0px;overflow:hidden}\n", ".ricontrols{padding:4px;box-sizing:border-box;height:100%}\n", ".dark{background:darkgray}\n", ".controlpanel{align-content:center;align-items:center;margin:0;padding:20px;min-height:min-content}\n", ".startstop{width:100%;text-align:center;align-content:center;align-items:center}\n", ".bigbutton{min-width:70px;min-height:50px;font-size:50px;border-radius:20px}\n"], components: [{ type: WarningBar, selector: "app-warningbar", inputs: ["warningText", "show"] }, { type: RecorderCombiPane, selector: "app-recordercombipane", inputs: ["selectDisabled", "selectedRecordingFile", "audioSignalCollapsed", "displayAudioClip", "playStartAction", "playSelectionAction", "autoPlayOnSelectToggleAction", "playStopAction"], outputs: ["selectedRecordingFileChanged"] }, { type: LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos", "stateLoading"] }, { type: RecordingItemControls, selector: "spr-recordingitemcontrols", inputs: ["audioSignalCollapsed", "enableDownload", "peakDbLvl", "agc", "audioLoaded", "playStartAction", "playStopAction", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }, { type: UploadStatus, selector: "app-uploadstatus", inputs: ["value", "awaitNewUpload", "status"] }, { type: WakeLockIndicator, selector: "app-wakelockindicator", inputs: ["screenLocked"] }, { type: ReadyStateIndicator, selector: "app-readystateindicator", inputs: ["ready"] }, { type: StatusDisplay, selector: "app-sprstatusdisplay", inputs: ["statusAlertType", "statusMsg", "statusWaiting"] }, { type: i3$2.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i3$1.DefaultStyleDirective, selector: " [ngStyle], [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl], [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl], [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]", inputs: ["ngStyle", "ngStyle.xs", "ngStyle.sm", "ngStyle.md", "ngStyle.lg", "ngStyle.xl", "ngStyle.lt-sm", "ngStyle.lt-md", "ngStyle.lt-lg", "ngStyle.lt-xl", "ngStyle.gt-xs", "ngStyle.gt-sm", "ngStyle.gt-md", "ngStyle.gt-lg"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i8.DefaultFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3$1.DefaultShowHideDirective, selector: " [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]", inputs: ["fxShow", "fxShow.print", "fxShow.xs", "fxShow.sm", "fxShow.md", "fxShow.lg", "fxShow.xl", "fxShow.lt-sm", "fxShow.lt-md", "fxShow.lt-lg", "fxShow.lt-xl", "fxShow.gt-xs", "fxShow.gt-sm", "fxShow.gt-md", "fxShow.gt-lg", "fxHide", "fxHide.print", "fxHide.xs", "fxHide.sm", "fxHide.md", "fxHide.lg", "fxHide.xl", "fxHide.lt-sm", "fxHide.lt-md", "fxHide.lt-lg", "fxHide.lt-xl", "fxHide.gt-xs", "fxHide.gt-sm", "fxHide.gt-md", "fxHide.gt-lg"] }] });
|
|
11954
13572
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioRecorder, decorators: [{
|
|
11955
13573
|
type: Component,
|
|
11956
13574
|
args: [{
|
|
@@ -11973,11 +13591,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
11973
13591
|
|
|
11974
13592
|
<div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}"
|
|
11975
13593
|
[ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
|
|
11976
|
-
<audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()"
|
|
11977
|
-
[displayLevelInfos]="
|
|
13594
|
+
<audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [stateLoading]="audioFetching"
|
|
13595
|
+
[displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
|
|
11978
13596
|
<div fxLayout="row">
|
|
11979
13597
|
<spr-recordingitemcontrols fxFlex="10 0 1"
|
|
11980
|
-
[audioLoaded]="displayAudioClip?.
|
|
13598
|
+
[audioLoaded]="displayAudioClip?.audioDataHolder!==null"
|
|
11981
13599
|
[playStartAction]="controlAudioPlayer?.startAction"
|
|
11982
13600
|
[playStopAction]="controlAudioPlayer?.stopAction"
|
|
11983
13601
|
[peakDbLvl]="peakLevelInDb"
|
|
@@ -12255,20 +13873,20 @@ class SpeechrecorderngModule {
|
|
|
12255
13873
|
}
|
|
12256
13874
|
SpeechrecorderngModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
12257
13875
|
SpeechrecorderngModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, declarations: [AudioSignal, Sonagram, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, Progress, SimpleTrafficLight, Recinstructions, Prompter, PromptContainer, PromptingContainer, Prompting, StatusDisplay,
|
|
12258
|
-
ProgressDisplay, RecordingItemDisplay, RecordingItemControls, UploadStatus, TransportPanel, WakeLockIndicator, ReadyStateIndicator, ControlPanel, WarningBar, AudioRecorder, SessionManager, MessageDialog, SessionFinishedDialog, SpeechrecorderngComponent, AudioRecorderComponent, RecordingFileViewComponent, RecordingFileUI, ScrollIntoViewDirective, RecordingFileNaviComponent, RecordingFileMetaComponent, RecordingList, RecorderCombiPane, AudioRecorder], imports: [i1$1.RouterModule, FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule], exports: [MessageDialog, SpeechrecorderngComponent, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, AudioRecorder] });
|
|
12259
|
-
SpeechrecorderngModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, providers: [SessionService, ProjectService, ScriptService, RecordingService, RecordingFileService, SpeechRecorderUploader], imports: [[RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule]] });
|
|
13876
|
+
ProgressDisplay, RecordingItemDisplay, RecordingItemControls, UploadStatus, TransportPanel, WakeLockIndicator, ReadyStateIndicator, ControlPanel, WarningBar, AudioRecorder, SessionManager, MessageDialog, SessionFinishedDialog, SpeechrecorderngComponent, AudioRecorderComponent, RecordingFileViewComponent, RecordingFileUI, ScrollIntoViewDirective, RecordingFileNaviComponent, RecordingFileMetaComponent, RecordingList, RecorderCombiPane, AudioRecorder], imports: [i1$1.RouterModule, FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule, MatMenuModule], exports: [MessageDialog, SpeechrecorderngComponent, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, AudioRecorder] });
|
|
13877
|
+
SpeechrecorderngModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, providers: [SessionService, ProjectService, ScriptService, RecordingService, RecordingFileService, SpeechRecorderUploader], imports: [[RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule, MatMenuModule]] });
|
|
12260
13878
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, decorators: [{
|
|
12261
13879
|
type: NgModule,
|
|
12262
13880
|
args: [{
|
|
12263
13881
|
declarations: [AudioSignal, Sonagram, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, Progress, SimpleTrafficLight, Recinstructions, Prompter, PromptContainer, PromptingContainer, Prompting, StatusDisplay,
|
|
12264
13882
|
ProgressDisplay, RecordingItemDisplay, RecordingItemControls, UploadStatus, TransportPanel, WakeLockIndicator, ReadyStateIndicator, ControlPanel, WarningBar, AudioRecorder, SessionManager, MessageDialog, SessionFinishedDialog, SpeechrecorderngComponent, AudioRecorderComponent, RecordingFileViewComponent, RecordingFileUI, ScrollIntoViewDirective, RecordingFileNaviComponent, RecordingFileMetaComponent, RecordingList, RecorderCombiPane, AudioRecorder],
|
|
12265
13883
|
exports: [MessageDialog, SpeechrecorderngComponent, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, AudioRecorder],
|
|
12266
|
-
imports: [RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule],
|
|
13884
|
+
imports: [RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule, MatMenuModule],
|
|
12267
13885
|
providers: [SessionService, ProjectService, ScriptService, RecordingService, RecordingFileService, SpeechRecorderUploader]
|
|
12268
13886
|
}]
|
|
12269
13887
|
}] });
|
|
12270
13888
|
|
|
12271
|
-
const VERSION = '
|
|
13889
|
+
const VERSION = '3.0.0';
|
|
12272
13890
|
|
|
12273
13891
|
/*
|
|
12274
13892
|
* Public API Surface of speechrecorderng
|