source: main/trunk/model-interfaces-dev/atea/korero-maori-asr/src/js/TranscribeModule.js@ 35456

Last change on this file since 35456 was 35454, checked in by cstephen, 3 years ago

Improve translations
Improve translation test return
Begin adding WordTimingSelector

File size: 9.5 KB
Line 
1/**
2 * @file Defines components that are used to interact with the transcription proxy servlet.
3 * @author Carl Stephens
4 * @module
5 */
6
7/* eslint-disable camelcase */
8
9export class TranscriptionMetadata {
10 /**
11 * A metadata object held by a {@link TranscriptionModel}.
12 *
13 * @param {String} char The character.
14 * @param {Number} confidence The confidence that the character is correct. Smaller values are more confident.
15 * @param {Number} start_time The timestamp in the audio at which this character is spoken.
16 */
17 constructor(char, confidence, start_time) {
18 this.char = char;
19 this.confidence = confidence;
20 this.start_time = start_time;
21 }
22}
23
24export class TranscriptionModel {
25 /**
26 * The transcription object returned from our transcription endpoint.
27 *
28 * @param {String} file_name The name of the file that was transcribed.
29 * @param {String} log A note of how the transcription was processed.
30 * @param {TranscriptionMetadata[]} metadata The character metadata.
31 * @param {boolean} success A value indicating if the transcription was successful or not.
32 * @param {String} transcription The transcription.
33 */
34 constructor(file_name, log, metadata, success, transcription) {
35 this.file_name = file_name;
36 this.log = log;
37 this.metadata = metadata;
38 this.success = success;
39 this.transcription = transcription;
40 }
41}
42
43/* eslint-enable camelcase */
44
45export class TranscriptionError extends Error {
46 /**
47 * Initialises a new instance of the {@link TranscriptionError} object.
48 *
49 * @param {Number | undefined} statusCode The HTTP status code of the error.
50 * @param {String | undefined} message The status message.
51 * @param {String | null} fileName The file on which the transcription failed.
52 */
53 constructor(message = undefined, fileName = null, statusCode = -1) {
54 super(message);
55
56 /** @type {String | null} The name of the file that the transcription error occured on. */
57 this.fileName = fileName;
58
59 /** @type {Number | undefined} The status code returned by the API when the erro was generated. */
60 this.statusCode = statusCode;
61 }
62}
63
64/**
65 * A service that uploads audio files in a multipart request to the Korero Maori API Interface servlet, and interprets the result.
66 */
67export default class TranscribeService {
68 constructor() {
69 /* eslint-disable quotes */
70 this.TEST_JSON = '{"file_name":"akl_mi_pk_0002.wav","log":"Took 0.6s to transcribe 8.7s audio file /tmp/transcribe-aw7ujpu8-clean.wav","metadata":[{"char":"k","prob":7.0E-6,"start_time":0.24},{"char":"o","prob":8.3E-5,"start_time":0.26},{"char":"t","prob":0.995578,"start_time":0.42},{"char":"a","prob":0.997089,"start_time":0.44},{"char":"h","prob":3.0E-6,"start_time":0.54},{"char":"i","prob":8.0E-6,"start_time":0.56},{"char":" ","prob":2.2E-5,"start_time":0.6},{"char":"a","prob":0.982987,"start_time":0.64},{"char":"n","prob":5.0E-6,"start_time":0.8},{"char":"ō","prob":1.82E-4,"start_time":0.84},{"char":" ","prob":0.996944,"start_time":1.08},{"char":"t","prob":1.23E-4,"start_time":1.14},{"char":"e","prob":6.3E-5,"start_time":1.16},{"char":" ","prob":0.587595,"start_time":1.28},{"char":"t","prob":4.91E-4,"start_time":1.34},{"char":"u","prob":0.010654,"start_time":1.36},{"char":"p","prob":5.9E-5,"start_time":1.52},{"char":"u","prob":1.26E-4,"start_time":1.54},{"char":"n","prob":0.0,"start_time":1.7},{"char":"a","prob":0.99997,"start_time":1.72},{"char":" ","prob":0.993724,"start_time":2.14},{"char":"o","prob":4.3E-5,"start_time":2.22},{"char":" ","prob":0.99696,"start_time":2.32},{"char":"t","prob":1.4E-5,"start_time":2.36},{"char":"e","prob":3.8E-5,"start_time":2.38},{"char":" ","prob":0.996606,"start_time":2.48},{"char":"t","prob":0.0,"start_time":2.52},{"char":"a","prob":0.994873,"start_time":2.54},{"char":"ŋ","prob":2.9E-5,"start_time":2.64},{"char":"a","prob":0.994639,"start_time":2.66},{"char":"t","prob":3.0E-6,"start_time":2.8},{"char":"a","prob":0.99995,"start_time":2.82},{"char":" ","prob":0.0,"start_time":2.9},{"char":"m","prob":0.999606,"start_time":2.98},{"char":"ā","prob":0.010628,"start_time":3.0},{"char":"o","prob":0.996415,"start_time":3.2},{"char":"r","prob":0.0,"start_time":3.28},{"char":"i","prob":0.999698,"start_time":3.32},{"char":" ","prob":0.997927,"start_time":4.5},{"char":"k","prob":0.0,"start_time":4.58},{"char":"o","prob":3.2E-5,"start_time":4.6},{"char":" ","prob":0.956949,"start_time":4.7},{"char":"r","prob":2.5E-5,"start_time":4.74},{"char":"a","prob":0.997649,"start_time":4.76},{"char":"ŋ","prob":0.0,"start_time":4.9},{"char":"i","prob":1.63E-4,"start_time":4.92},{"char":"n","prob":1.3E-5,"start_time":5.28},{"char":"u","prob":4.0E-6,"start_time":5.3},{"char":"i","prob":0.99997,"start_time":5.42},{"char":" ","prob":0.994305,"start_time":5.78},{"char":"e","prob":3.1E-5,"start_time":5.84},{"char":" ","prob":0.966097,"start_time":6.04},{"char":"t","prob":2.0E-6,"start_time":6.1},{"char":"ū","prob":5.4E-5,"start_time":6.12},{"char":" ","prob":0.837368,"start_time":6.34},{"char":"n","prob":0.0,"start_time":6.38},{"char":"e","prob":0.004238,"start_time":6.4},{"char":"i","prob":0.99966,"start_time":6.5},{"char":" ","prob":0.99728,"start_time":7.28},{"char":"o","prob":6.28E-4,"start_time":7.36},{"char":" ","prob":0.961049,"start_time":7.48},{"char":"p","prob":5.0E-6,"start_time":7.52},{"char":"a","prob":0.984707,"start_time":7.54},{"char":"p","prob":9.0E-6,"start_time":7.66},{"char":"a","prob":0.998754,"start_time":7.68},{"char":"t","prob":0.002356,"start_time":7.88},{"char":"ū","prob":1.24E-4,"start_time":7.9},{"char":"ā","prob":0.883958,"start_time":8.0},{"char":"n","prob":0.985871,"start_time":8.24},{"char":"u","prob":0.907369,"start_time":8.26},{"char":"k","prob":0.999045,"start_time":8.38},{"char":"u","prob":0.995632,"start_time":8.4}],"success":true,"transcription":"kotahi anō te tupuna o te tangata māori ko ranginui e tū nei o papatūānuku"}';
71 /* eslint-enable quotes */
72
73 /** @type {String} The URL to which query POST requests should be made. */
74 this.queryUrl = "//localhost:8383/gs3-koreromaori/transcribe";
75
76 /** @type {Number} The maximum number of files which can be transcribed in one request to the API. */
77 this.MAX_BATCH_COUNT = 3;
78
79 /** @type {Number} The soft upper limit on how many bytes can be submitted in one request. */
80 this.BATCH_BYTE_LIMIT = 5242880; // 5 MiB
81 }
82
83 /**
84 * Performs chunked queries to transcribe the given audio files, returning the data in iterations.
85 * Data is chunked according to which ever occurs first:
86 * A maximum of three files per request, or;
87 * A maximum of 5 MiB before chunking.
88 *
89 * @param {FileList | File[]} files The files to upload
90 * @returns {AsyncGenerator<TranscriptionModel[]>} The transcribed audio files.
91 * @throws {TranscriptionError} When the transcription request fails to complete.
92 */
93 async* batchTranscribeFiles(files) {
94 let filesToSubmit = [];
95 let fileCounter = 0;
96 let byteCounter = 0;
97
98 for (const file of files) {
99 if (fileCounter === this.MAX_BATCH_COUNT || byteCounter > this.BATCH_BYTE_LIMIT) { // 5 MiB
100 yield await this.transcribeFiles(filesToSubmit);
101 filesToSubmit = [];
102 byteCounter = 0;
103 fileCounter = 0;
104 }
105
106 filesToSubmit[fileCounter++] = file;
107 byteCounter += file.size;
108 }
109
110 if (filesToSubmit.length > 0) {
111 yield await this.transcribeFiles(filesToSubmit);
112 }
113 }
114
115 /**
116 * Performs a query to transcribe the given audio files.
117 *
118 * @param {FileList | File[]} files The files to upload.
119 * @returns {Promise<TranscriptionModel[]>} The transcribed audio file.
120 * @throws {TranscriptionError} When the transcription request fails to complete.
121 */
122 async transcribeFiles(files) {
123 const objects = [];
124
125 for (const file of files) {
126 const object = JSON.parse(this.TEST_JSON);
127 object.file_name = file.name;
128 objects.push(object);
129 }
130
131 return objects;
132 }
133
134 /**
135 * Performs a query to transcribe the given audio files.
136 *
137 * @param {FileList | File[]} files The files to upload.
138 * @returns {Promise<TranscriptionModel[]>} The transcribed audio file.
139 * @throws {TranscriptionError} When the transcription request fails to complete.
140 */
141 async transcribeFilesActual(files) {
142 const that = this;
143 const formData = new FormData();
144
145 let audioFileKeys = "";
146 for (let i = 0; i < files.length; i++) {
147 const f = files[i];
148 const key = "audioFile" + i;
149
150 formData.append(key, f, f.name);
151 audioFileKeys += key + "|";
152 }
153 formData.append("audioFileKeys", audioFileKeys);
154
155 try {
156 const response = await fetch(
157 that.queryUrl,
158 {
159 method: "POST",
160 body: formData
161 }
162 );
163
164 if (!response.ok) {
165 console.error(`Transcription API failed with status ${response.status} and message ${response.statusText}`);
166 throw new TranscriptionError(response.statusText, undefined, response.status);
167 }
168
169 return await response.json();
170 }
171 catch (e) {
172 console.error(`Transcription failed with reason ${e}`);
173 throw new TranscriptionError(undefined, "Unknown");
174 }
175 }
176}
Note: See TracBrowser for help on using the repository browser.