@christianriedl/utils 1.0.92 → 1.0.94

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.
@@ -0,0 +1,16 @@
1
+ export interface IStop {
2
+ stop(): void;
3
+ running: boolean;
4
+ }
5
+ export interface IPause extends IStop {
6
+ resume(): void;
7
+ pause(): void;
8
+ paused: boolean;
9
+ }
10
+ export declare class Audio {
11
+ static recordAudio(endThreshold: number, onEnd: (blob: Blob) => void, onStateChange?: () => void, onError?: (e: string) => void): Promise<IPause>;
12
+ static getSpeechSynthesizeVoices(): Promise<SpeechSynthesisVoice[]>;
13
+ static speechSynthesize(text: string, voiceName?: string, language?: string, rate?: number, pitch?: number, onEnd?: () => void, onStateChange?: () => void, onError?: (e: string) => void): Promise<IPause>;
14
+ static speechTranscribe(language?: string, retStop?: IStop): Promise<SpeechRecognitionResult>;
15
+ static playAudio(audioData: ArrayBuffer, onEnd?: () => void): Promise<IStop>;
16
+ }
package/dist/audio.js ADDED
@@ -0,0 +1,154 @@
1
+ export class Audio {
2
+ static async recordAudio(endThreshold, onEnd, onStateChange, onError) {
3
+ const constraints = { "video": false, "audio": true };
4
+ const stream = await navigator.mediaDevices.getUserMedia(constraints);
5
+ const recordedChunks = [];
6
+ const recorder = new MediaRecorder(stream, { mimeType: "audio/webm" });
7
+ recorder.ondataavailable = async (ev) => {
8
+ recordedChunks.push(ev.data);
9
+ const arr = await ev.data.arrayBuffer();
10
+ if (endThreshold && recordedChunks.length > 1 && ev.data.size < endThreshold)
11
+ recorder.stop();
12
+ };
13
+ recorder.start(1000);
14
+ recorder.onstop = () => {
15
+ const blob = new Blob(recordedChunks, { type: recordedChunks[0].type });
16
+ onEnd(blob);
17
+ };
18
+ if (onError) {
19
+ recorder.onerror = (ev) => onError(ev.type);
20
+ }
21
+ return {
22
+ stop: () => {
23
+ if (recorder.state != 'inactive') {
24
+ recorder.stop();
25
+ onStateChange?.();
26
+ }
27
+ },
28
+ pause: () => {
29
+ if (recorder.state != 'paused') {
30
+ recorder.pause();
31
+ onStateChange?.();
32
+ }
33
+ },
34
+ resume: () => {
35
+ if (recorder.state == 'paused') {
36
+ recorder.resume();
37
+ onStateChange?.();
38
+ }
39
+ },
40
+ get paused() { return recorder.state == 'paused'; },
41
+ get running() { return recorder.state == 'recording'; }
42
+ };
43
+ }
44
+ static getSpeechSynthesizeVoices() {
45
+ const synth = window.speechSynthesis;
46
+ var promise = new Promise(function (resolve, reject) {
47
+ synth.onvoiceschanged = (ev) => {
48
+ const voices = synth.getVoices();
49
+ resolve(voices);
50
+ };
51
+ });
52
+ return promise;
53
+ }
54
+ static async speechSynthesize(text, voiceName, language, rate, pitch, onEnd, onStateChange, onError) {
55
+ let voice;
56
+ const synth = window.speechSynthesis;
57
+ const utterance = new SpeechSynthesisUtterance();
58
+ utterance.text = text;
59
+ if (voiceName || language) {
60
+ const voices = await Audio.getSpeechSynthesizeVoices();
61
+ const voice = voiceName ? voices.find((v) => v.name == voiceName) : voices.find((v) => v.lang == language);
62
+ utterance.voice = voice ? voice : null;
63
+ }
64
+ if (rate)
65
+ utterance.rate = 1;
66
+ if (pitch)
67
+ utterance.pitch = 1;
68
+ if (language)
69
+ utterance.lang = "language";
70
+ if (onEnd) {
71
+ utterance.onend = (ev) => {
72
+ onEnd();
73
+ onStateChange?.();
74
+ };
75
+ }
76
+ if (onStateChange) {
77
+ utterance.onpause = (ev) => onStateChange();
78
+ utterance.onresume = (ev) => onStateChange();
79
+ utterance.onstart = (ev) => onStateChange();
80
+ }
81
+ if (onError) {
82
+ utterance.onerror = (ev) => {
83
+ onError(ev.error);
84
+ };
85
+ }
86
+ synth.speak(utterance);
87
+ return {
88
+ stop: () => {
89
+ if (synth.pending || synth.speaking)
90
+ synth.cancel();
91
+ },
92
+ pause: () => {
93
+ if (!synth.paused)
94
+ synth.pause();
95
+ },
96
+ resume: () => {
97
+ if (synth.paused)
98
+ synth.resume();
99
+ },
100
+ get paused() { return synth.paused; },
101
+ get running() { return synth.pending || synth.speaking; }
102
+ };
103
+ }
104
+ static async speechTranscribe(language, retStop) {
105
+ const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
106
+ const speechRecognition = new SpeechRecognition();
107
+ let ended = false;
108
+ if (retStop) {
109
+ retStop.stop = () => {
110
+ if (!ended)
111
+ speechRecognition.stop();
112
+ };
113
+ }
114
+ const promise = new Promise(function (resolve, reject) {
115
+ if (language)
116
+ speechRecognition.lang = language;
117
+ speechRecognition.continuous = false;
118
+ speechRecognition.maxAlternatives = 1;
119
+ speechRecognition.onresult = (ev) => {
120
+ ended = true;
121
+ const result = ev.results[ev.resultIndex];
122
+ resolve(result);
123
+ };
124
+ speechRecognition.onerror = (ev) => {
125
+ ended = true;
126
+ reject(ev.error);
127
+ };
128
+ speechRecognition.start();
129
+ });
130
+ return promise;
131
+ }
132
+ static async playAudio(audioData, onEnd) {
133
+ const context = new AudioContext();
134
+ const buffer = await context.decodeAudioData(audioData);
135
+ const source = context.createBufferSource();
136
+ let ended = false;
137
+ source.onended = (ev) => {
138
+ ended = true;
139
+ if (onEnd)
140
+ onEnd();
141
+ };
142
+ source.buffer = buffer;
143
+ source.connect(context.destination);
144
+ source.start(0);
145
+ return {
146
+ stop: () => {
147
+ if (!ended)
148
+ source.stop();
149
+ },
150
+ get running() { return !ended; }
151
+ };
152
+ }
153
+ }
154
+ //# sourceMappingURL=audio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio.js","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -12,6 +12,5 @@ export * from './appConfig';
12
12
  export * from './statistics';
13
13
  export * from './marshalBE';
14
14
  export * from './marshalLE';
15
- export * from './useSpeechRecognition';
16
- export * from './useSpeechSynthesis';
15
+ export * from './audio';
17
16
  export { InjectionKey, Ref, ref, reactive, toRaw, isRef, watchEffect } from 'vue';
package/dist/index.js CHANGED
@@ -12,7 +12,6 @@ export * from './appConfig';
12
12
  export * from './statistics';
13
13
  export * from './marshalBE';
14
14
  export * from './marshalLE';
15
- export * from './useSpeechRecognition';
16
- export * from './useSpeechSynthesis';
15
+ export * from './audio';
17
16
  export { ref, reactive, toRaw, isRef, watchEffect } from 'vue';
18
17
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAqB,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,OAAO,EAAqB,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@christianriedl/utils",
3
- "version": "1.0.92",
3
+ "version": "1.0.94",
4
4
  "description": "Interfaces, local storage, service worker, configuration, application state",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,45 +1,29 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, watch } from 'vue'
3
- import { useSpeechRecognition } from '@christianriedl/utils';
3
+ import { Audio } from '@christianriedl/utils';
4
4
 
5
- const props = defineProps<{ lang?: string, maxAlternatives?: number, size?: string, cls?: string, variant?: string }>();
5
+ const props = defineProps<{ lang?: string, size?: string, cls?: string, variant?: "elevated" | "outlined" | "flat" | "text" | "tonal" | "plain" | undefined }>();
6
6
  const emits = defineEmits<{
7
- (e: 'result', result: string): void,
8
- (e: 'listening', isListening: boolean): void,
9
- (e: 'final', visFinal: boolean): void
7
+ (e: 'result', result: SpeechRecognitionResult): void
10
8
  }>();
11
9
 
12
10
  const sz = props.size ? props.size : "large";
13
- const cls = props.cls ? props.cls : "bg-office";
11
+ const cls = props.cls ? [props.cls] : ["bg-office"];
14
12
  const vari = props.variant ? props.variant : "tonal";
15
- const lang = ref('de-AT');
16
- const maxAlternatives = ref(1);
17
-
18
- if (props.lang)
19
- lang.value = props.lang;
20
- if (props.maxAlternatives)
21
- maxAlternatives.value = props.maxAlternatives;
22
-
23
- const speech = useSpeechRecognition({
24
- lang,
25
- maxAlternatives,
26
- continuous: false,
27
- })
28
-
29
- const sampled = ref<string[]>([])
30
-
31
- function onStart() {
32
- speech.result.value = ''
33
- speech.start()
13
+ const language = props.lang ? props.lang : 'de-AT';
14
+ const color = ref('primary');
15
+
16
+ async function onStart() {
17
+ color.value = 'error';
18
+ var result = await Audio.speechTranscribe(language);
19
+ color.value = 'primary';
20
+ emits('result', result);
34
21
  }
35
-
36
- const { isListening, isFinal, stop, result } = speech
37
- watch(isListening, isListening => emits("listening", isListening.value));
38
- watch(isFinal, isFinal => emits("final", isFinal.value));
39
- watch(result, result => emits("result", result.value));
40
22
  </script>
41
23
 
42
24
  <template>
43
- <v-btn :size="sz" :class="cls" :variant="vari" icon="$microphone" :color="isListening ? 'red' : 'white'", @click="onStart"></v-btn>
25
+ <v-btn :variant="vari" :class="cls" @click="onStart">
26
+ <v-icon :size="sz" icon="$microphone" :color="color"></v-icon>
27
+ </v-btn>
44
28
  </template>
45
29
 
@@ -0,0 +1,106 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch, inject, toRef, onMounted } from 'vue'
3
+ import { Audio, appConfigSymbol, IPause } from '@christianriedl/utils';
4
+
5
+ const props = defineProps<{ text: string, language?: string, autoStart?: boolean }>();
6
+
7
+ const appConfig = inject(appConfigSymbol)!;
8
+ const text = toRef(props, "text");
9
+ const voiceName = ref(appConfig.localStorage.getItemString("ttsVoice", ""));
10
+ const pitch = ref(appConfig.localStorage.getItemNumber("ttsPitch", 10) / 10);
11
+ const rate = ref(appConfig.localStorage.getItemNumber("ttsRate", 10) / 10);
12
+ const voice = ref<SpeechSynthesisVoice>(undefined as unknown as SpeechSynthesisVoice)
13
+ const voices = ref<SpeechSynthesisVoice[]>([])
14
+ const configure = ref(false);
15
+ const paused = ref(false);
16
+ const ended = ref(false);
17
+ let pause: IPause;
18
+
19
+ async function onPlay() {
20
+ if (pause && pause.paused) {
21
+ pause.resume();
22
+ paused.value = false;
23
+ }
24
+ else {
25
+ paused.value = ended.value = false;
26
+ pause = Audio.speechSynthesize(text.value, voiceName.value, props.language, rate.value, pitch.value,
27
+ () => {
28
+ ended.value = true
29
+ },
30
+ () => {
31
+ paused.value = pause.pause;
32
+ ended.value = !pause.running;
33
+ });
34
+ }
35
+ }
36
+
37
+ function onPause() {
38
+ if (pause && !pause.paused) {
39
+ pause.pause();
40
+ }
41
+ }
42
+ function onStop() {
43
+ if (pause)
44
+ pause.stop();
45
+ }
46
+ function onVoiceChange() {
47
+ appConfig.localStorage.setItem("ttsVoice", voiceName.value);
48
+ voice.value = voices.value.find((v) => v.name == voiceName.value)!;
49
+ }
50
+ function onPitchChange() {
51
+ appConfig.localStorage.setItem("ttsPitch", Math.floor (pitch.value * 10));
52
+ }
53
+ function onRateChange() {
54
+ appConfig.localStorage.setItem("ttsRate", Math.floor(rate.value * 10));
55
+ }
56
+ async function onSettings() {
57
+ configure.value = !configure.value;
58
+ if (configure.value && voices.value.length == 0) {
59
+ voices.value = await Audio.getSpeechSynthesizeVoices();
60
+ }
61
+ }
62
+ if (props.autoStart) {
63
+ watch(text, text => { if (text) onPlay(); });
64
+ }
65
+ </script>
66
+
67
+ <template>
68
+ <v-row dense>
69
+ <v-col cols="12">
70
+ <v-btn v-if="speech.status.value == 'play'" @click.stop="onPause">
71
+ <v-icon icon="$pause" />
72
+ </v-btn>
73
+ <v-btn v-else @click.stop="onPlay">
74
+ <v-icon icon="$play" />
75
+ </v-btn>
76
+ <v-btn @click.stop="onStop">
77
+ <v-icon icon="$stop" />
78
+ </v-btn>
79
+ <v-btn @click.stop="onSettings">
80
+ <v-icon icon="$settings" />
81
+ </v-btn>
82
+ </v-col>
83
+ </v-row>
84
+ <v-row v-if="configure" dense>
85
+ <v-col cols="12">
86
+ <v-select density="compact" v-model="voiceName"
87
+ item-title="name"
88
+ :items="voices"
89
+ persistent-hint
90
+ @update:modelValue="onVoiceChange"
91
+ dense solo hide-details single-line>
92
+ </v-select>
93
+ </v-col>
94
+ </v-row>
95
+ <v-row v-if="configure" dense>
96
+ <v-col cols="6">
97
+ <v-slider v-model="pitch" label="Pitch" density="compact" min="0.5" max="2" step="0.1"
98
+ @update:modelValue="onPitchChange"></v-slider>
99
+ </v-col>
100
+ <v-col cols="6">
101
+ <v-slider v-model="rate" label="Rate" density="compact" min="0.5" max="2" step="0.1"
102
+ @update:modelValue="onRateChange"></v-slider>
103
+ </v-col>
104
+ </v-row>
105
+ </template>
106
+