EXP. remove microphone stream 4, fin

This commit is contained in:
wataru 2023-02-20 04:49:34 +09:00
parent fccd631c00
commit eb12f3db64
25 changed files with 1323 additions and 2394 deletions

View File

@ -1,10 +1 @@
<!DOCTYPE html>
<html style="width: 100%; height: 100%; overflow: hidden">
<head>
<meta charset="utf-8" />
<title>Voice Changer Client Demo</title>
<script defer src="index.js"></script></head>
<body style="width: 100%; height: 100%; margin: 0px">
<div id="app" style="width: 100%; height: 100%"></div>
</body>
</html>
<!doctype html><html style="width:100%;height:100%;overflow:hidden"><head><meta charset="utf-8"/><title>Voice Changer Client Demo</title><script defer="defer" src="index.js"></script></head><body style="width:100%;height:100%;margin:0"><div id="app" style="width:100%;height:100%"></div></body></html>

File diff suppressed because one or more lines are too long

31
client/demo/dist/index.js.LICENSE.txt vendored Normal file
View File

@ -0,0 +1,31 @@
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@
"@babel/preset-env": "^7.20.2",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.18.6",
"@types/node": "^18.13.0",
"@types/node": "^18.14.0",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"autoprefixer": "^10.4.13",
@ -39,7 +39,7 @@
"html-webpack-plugin": "^5.5.0",
"npm-run-all": "^4.1.5",
"postcss-loader": "^7.0.2",
"postcss-nested": "^6.0.0",
"postcss-nested": "^6.0.1",
"prettier": "^2.8.4",
"rimraf": "^4.1.2",
"style-loader": "^3.3.1",
@ -51,7 +51,7 @@
"webpack-dev-server": "^4.11.1"
},
"dependencies": {
"@dannadori/voice-changer-client-js": "^1.0.70",
"@dannadori/voice-changer-client-js": "^1.0.72",
"@fortawesome/fontawesome-svg-core": "^6.3.0",
"@fortawesome/free-brands-svg-icons": "^6.3.0",
"@fortawesome/free-regular-svg-icons": "^6.3.0",

View File

@ -8,7 +8,6 @@ export const useAudioConfig = (): AudioConfigState => {
const [audioContext, setAudioContext] = useState<AudioContext | null>(null)
useEffect(() => {
const createAudioContext = () => {
console.log("click window")
const ctx = new AudioContext()
document.removeEventListener('touchstart', createAudioContext);
document.removeEventListener('mousedown', createAudioContext);
@ -18,9 +17,6 @@ export const useAudioConfig = (): AudioConfigState => {
document.addEventListener('mousedown', createAudioContext);
}, [])
console.log("AUDIO CONTEXT", audioContext)
const ret: AudioConfigState = {
audioContext
}

View File

@ -236,11 +236,12 @@ export const useDeviceSetting = (): DeviceSettingState => {
// }
const onOutputRecordStartClicked = async () => {
setOutputRecordingStarted(true)
await appState.workletSetting.startOutputRecording()
await appState.workletNodeSetting.startOutputRecording()
}
const onOutputRecordStopClicked = async () => {
setOutputRecordingStarted(false)
await appState.workletSetting.stopOutputRecording()
const record = await appState.workletNodeSetting.stopOutputRecording()
downloadRecord(record)
}
const startClassName = outputRecordingStarted ? "body-button-active" : "body-button-stanby"
@ -258,7 +259,7 @@ export const useDeviceSetting = (): DeviceSettingState => {
</div>
)
}, [audioOutputForGUI, outputRecordingStarted, appState.workletSetting.startOutputRecording, appState.workletSetting.stopOutputRecording])
}, [audioOutputForGUI, outputRecordingStarted, appState.workletNodeSetting.startOutputRecording, appState.workletNodeSetting.stopOutputRecording])
useEffect(() => {
[AUDIO_ELEMENT_FOR_PLAY_RESULT, AUDIO_ELEMENT_FOR_TEST_ORIGINAL, AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK].forEach(x => {
@ -338,6 +339,53 @@ export const useDeviceSetting = (): DeviceSettingState => {
}, [audioInputRow, audioMediaInputRow, audioOutputRow, audioOutputRecordingRow, useServerMicrophone])
const downloadRecord = (data: Float32Array) => {
const writeString = (view: DataView, offset: number, string: string) => {
for (var i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
};
const floatTo16BitPCM = (output: DataView, offset: number, input: Float32Array) => {
for (var i = 0; i < input.length; i++, offset += 2) {
var s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
};
const buffer = new ArrayBuffer(44 + data.length * 2);
const view = new DataView(buffer);
// https://www.youfit.co.jp/archives/1418
writeString(view, 0, 'RIFF'); // RIFFヘッダ
view.setUint32(4, 32 + data.length * 2, true); // これ以降のファイルサイズ
writeString(view, 8, 'WAVE'); // WAVEヘッダ
writeString(view, 12, 'fmt '); // fmtチャンク
view.setUint32(16, 16, true); // fmtチャンクのバイト数
view.setUint16(20, 1, true); // フォーマットID
view.setUint16(22, 1, true); // チャンネル数
view.setUint32(24, 48000, true); // サンプリングレート
view.setUint32(28, 48000 * 2, true); // データ速度
view.setUint16(32, 2, true); // ブロックサイズ
view.setUint16(34, 16, true); // サンプルあたりのビット数
writeString(view, 36, 'data'); // dataチャンク
view.setUint32(40, data.length * 2, true); // 波形データのバイト数
floatTo16BitPCM(view, 44, data); // 波形データ
const audioBlob = new Blob([view], { type: 'audio/wav' });
const url = URL.createObjectURL(audioBlob);
const a = document.createElement("a");
a.href = url;
a.download = `output.wav`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// 出力の録音データ(from worklet)がストアされたら実行
useEffect(() => {
if (!appState.outputRecordData || appState.outputRecordData?.length == 0) {

View File

@ -24,7 +24,6 @@ export const useSpeakerSetting = () => {
const dst = appState.clientSetting.clientSetting.correspondences?.find(x => {
return x.sid == dstId
})
console.log("calcDefaultF0Factor", srcId, dstId, src, dst)
const recommendedF0Factor = dst && src ? dst.correspondence / src.correspondence : 0
return recommendedF0Factor
}

View File

@ -12,7 +12,7 @@ export const useStateControlCheckbox = (className: string, changeCallback?: (new
const currentValForTriggerCallbackRef = useRef<boolean>(false);
// (4) トリガチェックボックス
const callback = useMemo(() => {
console.log("generate callback function", className);
// console.log("generate callback function", className);
return (newVal: boolean) => {
if (!changeCallback) {
return;

View File

@ -1,23 +1,19 @@
{
"name": "@dannadori/voice-changer-client-js",
"version": "1.0.70",
"version": "1.0.72",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@dannadori/voice-changer-client-js",
"version": "1.0.70",
"version": "1.0.72",
"license": "ISC",
"dependencies": {
"@types/readable-stream": "^2.3.15",
"amazon-chime-sdk-js": "^3.11.0",
"install": "^0.13.0",
"localforage": "^1.10.0",
"microphone-stream": "^6.0.1",
"path-browserify": "^1.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"readable-stream": "^4.3.0",
"socket.io-client": "^4.6.0"
},
"devDependencies": {
@ -2121,17 +2117,6 @@
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"dev": true
},
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"dependencies": {
"event-target-shim": "^5.0.0"
},
"engines": {
"node": ">=6.5"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@ -2388,25 +2373,6 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/batch": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
@ -2546,33 +2512,11 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
"node_modules/bytes": {
"version": "3.0.0",
@ -3480,14 +3424,6 @@
"node": ">= 0.6"
}
},
"node_modules/event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
@ -3498,6 +3434,7 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"dev": true,
"engines": {
"node": ">=0.8.x"
}
@ -4144,6 +4081,15 @@
"util-deprecate": "~1.0.1"
}
},
"node_modules/hpack.js/node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/html-entities": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
@ -4237,25 +4183,6 @@
"node": ">=0.10.0"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -4327,15 +4254,8 @@
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/install": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz",
"integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==",
"engines": {
"node": ">= 0.10"
}
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"node_modules/internal-slot": {
"version": "1.0.5",
@ -5019,28 +4939,6 @@
"node": ">=8.6"
}
},
"node_modules/microphone-stream": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/microphone-stream/-/microphone-stream-6.0.1.tgz",
"integrity": "sha512-yD7B2SfxaB+pLlWLcX4NNk2y81xwCyXGrB7q67nbP/Jq8JpIebX2iQV/M+LNZUleSg0A2cWYpb+wmdDs5nm3Fg==",
"dependencies": {
"buffer-from": "^1.1.1",
"readable-stream": "^3.6.0"
}
},
"node_modules/microphone-stream/node_modules/readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
@ -5665,11 +5563,6 @@
"node": ">= 0.8"
}
},
"node_modules/path-browserify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
},
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@ -5860,14 +5753,6 @@
"node": ">=6.0.0"
}
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@ -6100,17 +5985,17 @@
}
},
"node_modules/readable-stream": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz",
"integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==",
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10"
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
"node": ">= 6"
}
},
"node_modules/readdirp": {
@ -6753,20 +6638,6 @@
"wbuf": "^1.7.3"
}
},
"node_modules/spdy-transport/node_modules/readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@ -6777,13 +6648,34 @@
}
},
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"dependencies": {
"safe-buffer": "~5.1.0"
"safe-buffer": "~5.2.0"
}
},
"node_modules/string_decoder/node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/string.prototype.matchall": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz",
@ -7243,7 +7135,8 @@
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true
},
"node_modules/utils-merge": {
"version": "1.0.1",
@ -9690,14 +9583,6 @@
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"dev": true
},
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"requires": {
"event-target-shim": "^5.0.0"
}
},
"accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@ -9885,11 +9770,6 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"batch": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
@ -9999,19 +9879,11 @@
"update-browserslist-db": "^1.0.10"
}
},
"buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
"bytes": {
"version": "3.0.0",
@ -10677,11 +10549,6 @@
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"dev": true
},
"event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
},
"eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
@ -10691,7 +10558,8 @@
"events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"dev": true
},
"execa": {
"version": "5.1.1",
@ -11175,6 +11043,15 @@
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
},
@ -11248,11 +11125,6 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -11303,12 +11175,8 @@
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"install": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz",
"integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA=="
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"internal-slot": {
"version": "1.0.5",
@ -11801,27 +11669,6 @@
"picomatch": "^2.3.1"
}
},
"microphone-stream": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/microphone-stream/-/microphone-stream-6.0.1.tgz",
"integrity": "sha512-yD7B2SfxaB+pLlWLcX4NNk2y81xwCyXGrB7q67nbP/Jq8JpIebX2iQV/M+LNZUleSg0A2cWYpb+wmdDs5nm3Fg==",
"requires": {
"buffer-from": "^1.1.1",
"readable-stream": "^3.6.0"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
@ -12286,11 +12133,6 @@
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"dev": true
},
"path-browserify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@ -12423,11 +12265,6 @@
"fast-diff": "^1.1.2"
}
},
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@ -12597,14 +12434,14 @@
}
},
"readable-stream": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz",
"integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==",
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10"
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"readdirp": {
@ -13117,19 +12954,6 @@
"obuf": "^1.1.2",
"readable-stream": "^3.0.6",
"wbuf": "^1.7.3"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"statuses": {
@ -13139,11 +12963,20 @@
"dev": true
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
"safe-buffer": "~5.2.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
}
}
},
"string.prototype.matchall": {
@ -13448,7 +13281,8 @@
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true
},
"utils-merge": {
"version": "1.0.1",

View File

@ -1,6 +1,6 @@
{
"name": "@dannadori/voice-changer-client-js",
"version": "1.0.70",
"version": "1.0.72",
"description": "",
"main": "dist/index.js",
"directories": {
@ -48,13 +48,9 @@
"dependencies": {
"@types/readable-stream": "^2.3.15",
"amazon-chime-sdk-js": "^3.11.0",
"install": "^0.13.0",
"localforage": "^1.10.0",
"microphone-stream": "^6.0.1",
"path-browserify": "^1.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"readable-stream": "^4.3.0",
"socket.io-client": "^4.6.0"
}
}

View File

@ -1,10 +1,15 @@
export declare const RequestType: {
readonly voice: "voice";
readonly config: "config";
readonly startRecording: "startRecording";
readonly stopRecording: "stopRecording";
readonly start: "start";
readonly stop: "stop";
};
export type RequestType = typeof RequestType[keyof typeof RequestType];
export declare const ResponseType: {
readonly volume: "volume";
readonly inputData: "inputData";
};
export type ResponseType = typeof ResponseType[keyof typeof ResponseType];
export type VoiceChangerWorkletProcessorRequest = {
requestType: RequestType;
voice: ArrayBuffer;
@ -12,3 +17,9 @@ export type VoiceChangerWorkletProcessorRequest = {
volTrancateThreshold: number;
volTrancateLength: number;
};
export type VoiceChangerWorkletProcessorResponse = {
responseType: ResponseType;
volume?: number;
recordData?: Float32Array[];
inputData?: Float32Array;
};

View File

@ -100,7 +100,7 @@ export class VoiceChangerClient {
//// Input デバイスがnullの時はmicStreamを止めてリターン
if (!this.setting.audioInput) {
console.log(`Input Setup=> client mic is disabled.`)
this.vcNode.stopRecording()
this.vcNode.stop()
await this.unlock(lockNum)
return
}
@ -137,14 +137,14 @@ export class VoiceChangerClient {
this.inputGainNode.connect(voiceFocusNode.start) // input node -> vf node
voiceFocusNode.end.connect(this.vcNode)
} else {
console.log("input___ media stream", this.currentMediaStream)
this.currentMediaStream.getTracks().forEach(x => {
console.log("input___ media stream set", x.getSettings())
console.log("input___ media stream con", x.getConstraints())
console.log("input___ media stream cap", x.getCapabilities())
})
console.log("input___ media node", this.currentMediaStreamAudioSourceNode)
console.log("input___ gain node", this.inputGainNode.channelCount, this.inputGainNode)
// console.log("input___ media stream", this.currentMediaStream)
// this.currentMediaStream.getTracks().forEach(x => {
// console.log("input___ media stream set", x.getSettings())
// console.log("input___ media stream con", x.getConstraints())
// console.log("input___ media stream cap", x.getCapabilities())
// })
// console.log("input___ media node", this.currentMediaStreamAudioSourceNode)
// console.log("input___ gain node", this.inputGainNode.channelCount, this.inputGainNode)
this.inputGainNode.connect(this.vcNode)
}
console.log("Input Setup=> success")
@ -155,11 +155,11 @@ export class VoiceChangerClient {
}
start = () => {
this.vcNode.startRecording()
this.vcNode.start()
this._isVoiceChanging = true
}
stop = () => {
this.vcNode.stopRecording()
this.vcNode.stop()
this._isVoiceChanging = false
}
@ -251,11 +251,11 @@ export class VoiceChangerClient {
configureWorklet = (setting: WorkletSetting) => {
this.vcNode.configure(setting)
}
startRecording = () => {
this.vcNode.startRecording()
startOutputRecording = () => {
this.vcNode.startOutputRecording()
}
stopRecording = () => {
this.vcNode.stopRecording()
stopOutputRecording = () => {
return this.vcNode.stopOutputRecording()
}

View File

@ -5,7 +5,6 @@ import { DefaultEventsMap } from "@socket.io/component-emitter";
export type VoiceChangerWorkletListener = {
notifyVolume: (vol: number) => void
notifyOutputRecordData: (data: Float32Array[]) => void
notifySendBufferingTime: (time: number) => void
notifyResponseTime: (time: number) => void
notifyException: (code: VOICE_CHANGER_CLIENT_EXCEPTION, message: string) => void
@ -20,6 +19,9 @@ export class VoiceChangerWorkletNode extends AudioWorkletNode {
// performance monitor
private bufferStart = 0;
private isOutputRecording = false;
private recordingOutputChunk: Float32Array[] = []
constructor(context: AudioContext, listener: VoiceChangerWorkletListener) {
super(context, "voice-changer-worklet-processor");
this.port.onmessage = this.handleMessage.bind(this);
@ -78,14 +80,42 @@ export class VoiceChangerWorkletNode extends AudioWorkletNode {
}
private postReceivedVoice = (data: ArrayBuffer) => {
// Int16 to Float
const i16Data = new Int16Array(data)
const f32Data = new Float32Array(i16Data.length)
// console.log(`[worklet] f32DataLength${f32Data.length} i16DataLength${i16Data.length}`)
i16Data.forEach((x, i) => {
const float = (x >= 0x8000) ? -(0x10000 - x) / 0x8000 : x / 0x7FFF;
f32Data[i] = float
})
// アップサンプリング
let upSampledBuffer: Float32Array | null = null
if (this.setting.sendingSampleRate == 48000) {
upSampledBuffer = f32Data
} else {
upSampledBuffer = new Float32Array(f32Data.length * 2)
for (let i = 0; i < f32Data.length; i++) {
const currentFrame = f32Data[i]
const nextFrame = i + 1 < f32Data.length ? f32Data[i + 1] : f32Data[i]
upSampledBuffer[i * 2] = currentFrame
upSampledBuffer[i * 2 + 1] = (currentFrame + nextFrame) / 2
}
}
const req: VoiceChangerWorkletProcessorRequest = {
requestType: "voice",
voice: data,
voice: upSampledBuffer,
numTrancateTreshold: 0,
volTrancateThreshold: 0,
volTrancateLength: 0
}
this.port.postMessage(req)
if (this.isOutputRecording) {
this.recordingOutputChunk.push(upSampledBuffer)
}
}
private _averageDownsampleBuffer(buffer: Float32Array, originalSampleRate: number, destinationSamplerate: number) {
@ -121,8 +151,6 @@ export class VoiceChangerWorkletNode extends AudioWorkletNode {
// console.log(`[Node:handleMessage_] `, event.data.volume);
if (event.data.responseType === "volume") {
this.listener.notifyVolume(event.data.volume as number)
} else if (event.data.responseType === "recordData") {
this.listener.notifyOutputRecordData(event.data.recordData as Float32Array[])
} else if (event.data.responseType === "inputData") {
const inputData = event.data.inputData as Float32Array
// console.log("receive input data", inputData)
@ -227,9 +255,9 @@ export class VoiceChangerWorkletNode extends AudioWorkletNode {
this.port.postMessage(req)
}
startRecording = () => {
start = () => {
const req: VoiceChangerWorkletProcessorRequest = {
requestType: "startRecording",
requestType: "start",
voice: new ArrayBuffer(1),
numTrancateTreshold: 0,
volTrancateThreshold: 0,
@ -238,9 +266,9 @@ export class VoiceChangerWorkletNode extends AudioWorkletNode {
this.port.postMessage(req)
}
stopRecording = () => {
stop = () => {
const req: VoiceChangerWorkletProcessorRequest = {
requestType: "stopRecording",
requestType: "stop",
voice: new ArrayBuffer(1),
numTrancateTreshold: 0,
volTrancateThreshold: 0,
@ -248,6 +276,27 @@ export class VoiceChangerWorkletNode extends AudioWorkletNode {
}
this.port.postMessage(req)
}
startOutputRecording = () => {
this.recordingOutputChunk = []
this.isOutputRecording = true
}
stopOutputRecording = () => {
this.isOutputRecording = false
const dataSize = this.recordingOutputChunk.reduce((prev, cur) => {
return prev + cur.length
}, 0)
const samples = new Float32Array(dataSize);
let sampleIndex = 0
for (let i = 0; i < this.recordingOutputChunk.length; i++) {
for (let j = 0; j < this.recordingOutputChunk[i].length; j++) {
samples[sampleIndex] = this.recordingOutputChunk[i][j];
sampleIndex++;
}
}
return samples
}
}

View File

@ -127,7 +127,7 @@ export type WorkletSetting = {
volTrancateLength: number
}
export const DefaultWorkletSetting: WorkletSetting = {
numTrancateTreshold: 188,
numTrancateTreshold: 100,
volTrancateThreshold: 0.0005,
volTrancateLength: 32
}

View File

@ -21,7 +21,6 @@ export type ClientState = {
bufferingTime: number;
responseTime: number;
volume: number;
outputRecordData: Float32Array[] | null; // Serverから帰ってきたデータをレコードしたもの
// 情報取得
getInfo: () => Promise<void>
@ -55,7 +54,6 @@ export const useClient = (props: UseClientProps): ClientState => {
const [bufferingTime, setBufferingTime] = useState<number>(0)
const [responseTime, setResponseTime] = useState<number>(0)
const [volume, setVolume] = useState<number>(0)
const [outputRecordData, setOutputRecordData] = useState<Float32Array[] | null>(null)
// (1-4) エラーステータス
const errorCountRef = useRef<number>(0)
@ -85,9 +83,6 @@ export const useClient = (props: UseClientProps): ClientState => {
},
notifyVolume: (vol: number) => {
setVolume(vol)
},
notifyOutputRecordData: (data: Float32Array[]) => {
setOutputRecordData(data)
}
})
@ -133,7 +128,6 @@ export const useClient = (props: UseClientProps): ClientState => {
bufferingTime,
responseTime,
volume,
outputRecordData,
// 情報取得
getInfo,

View File

@ -65,8 +65,8 @@ export const useClientSetting = (props: UseClientSettingProps): ClientSettingSta
return (_clientSetting: VoiceChangerClientSetting) => {
if (!props.voiceChangerClient) return
for (let k in _clientSetting) {
const cur_v = clientSetting[k]
const new_v = _clientSetting[k]
const cur_v = clientSetting[k as keyof VoiceChangerClientSetting]
const new_v = _clientSetting[k as keyof VoiceChangerClientSetting]
if (cur_v != new_v) {
storeSetting(_clientSetting)
props.voiceChangerClient.updateClientSetting(_clientSetting)

View File

@ -90,9 +90,7 @@ export const useServerSetting = (props: UseServerSettingProps): ServerSettingSta
const cur_v = serverSetting[k]
const new_v = setting[k]
if (cur_v != new_v) {
console.log("update server setting!!!4", k, cur_v, new_v)
const res = await props.voiceChangerClient.updateServerSettings(k, "" + new_v)
console.log("update server setting!!!5", res)
setServerSetting(res)
setItem(INDEXEDDB_KEY_SERVER, res)

View File

@ -12,6 +12,8 @@ export type WorkletNodeSettingState = {
workletNodeSetting: WorkletNodeSetting;
clearSetting: () => Promise<void>
updateWorkletNodeSetting: (setting: WorkletNodeSetting) => void
startOutputRecording: () => void
stopOutputRecording: () => Promise<Float32Array>
}
@ -51,8 +53,8 @@ export const useWorkletNodeSetting = (props: UseWorkletNodeSettingProps): Workle
return (_workletNodeSetting: WorkletNodeSetting) => {
if (!props.voiceChangerClient) return
for (let k in _workletNodeSetting) {
const cur_v = workletNodeSetting[k]
const new_v = _workletNodeSetting[k]
const cur_v = workletNodeSetting[k as keyof WorkletNodeSetting]
const new_v = _workletNodeSetting[k as keyof WorkletNodeSetting]
if (cur_v != new_v) {
_setWorkletNodeSetting(_workletNodeSetting)
setItem(INDEXEDDB_KEY_WORKLETNODE, _workletNodeSetting)
@ -63,11 +65,26 @@ export const useWorkletNodeSetting = (props: UseWorkletNodeSettingProps): Workle
}
}, [props.voiceChangerClient, workletNodeSetting])
const startOutputRecording = useMemo(() => {
return () => {
if (!props.voiceChangerClient) return
props.voiceChangerClient.startOutputRecording()
}
}, [props.voiceChangerClient])
const stopOutputRecording = useMemo(() => {
return async () => {
if (!props.voiceChangerClient) return new Float32Array()
return props.voiceChangerClient.stopOutputRecording()
}
}, [props.voiceChangerClient])
return {
workletNodeSetting,
clearSetting,
updateWorkletNodeSetting,
startOutputRecording,
stopOutputRecording
}
}

View File

@ -11,8 +11,7 @@ export type WorkletSettingState = {
setting: WorkletSetting;
clearSetting: () => Promise<void>
setSetting: (setting: WorkletSetting) => void;
// startOutputRecording: () => void
// stopOutputRecording: () => Promise<void>
}
export const useWorkletSetting = (props: UseWorkletSettingProps): WorkletSettingState => {
@ -34,7 +33,7 @@ export const useWorkletSetting = (props: UseWorkletSettingProps): WorkletSetting
})
} else {
_setSetting({
numTrancateTreshold: 150,
numTrancateTreshold: 100,
volTrancateThreshold: 0.0005,
volTrancateLength: 32,
})
@ -68,26 +67,11 @@ export const useWorkletSetting = (props: UseWorkletSettingProps): WorkletSetting
await removeItem(INDEXEDDB_KEY_WORKLET)
}
// const startOutputRecording = useMemo(() => {
// return () => {
// if (!props.voiceChangerClient) return
// props.voiceChangerClient.startOutputRecordingWorklet()
// }
// }, [props.voiceChangerClient])
// const stopOutputRecording = useMemo(() => {
// return async () => {
// if (!props.voiceChangerClient) return
// props.voiceChangerClient.stopOutputRecordingWorklet()
// }
// }, [props.voiceChangerClient])
return {
setting,
clearSetting,
setSetting,
// startOutputRecording,
// stopOutputRecording
}
}

View File

@ -7,13 +7,13 @@
/* */
"forceConsistentCasingInFileNames": true,
// /* */
// "strict": true,
// "noImplicitAny": true,
// "strictNullChecks": true,
// "noUnusedLocals": true,
// "noUnusedParameters": true,
// "noImplicitReturns": true,
/* */
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
/* Module */
"moduleResolution": "node",

View File

@ -1,7 +1,7 @@
{
"compilerOptions": {
"target": "ES2020",
"lib":["ES2020"],
"lib": ["ES2020"],
"outDir": "./worklet/dist",
"declaration": true,
/* */

View File

@ -5,6 +5,7 @@ module.exports = {
resolve: {
extensions: [".ts", ".js"],
fallback: {
// "buffer": false
}
},
module: {
@ -28,12 +29,6 @@ module.exports = {
libraryTarget: "umd",
globalObject: "typeof self !== 'undefined' ? self : this",
},
plugins: [
new webpack.ProvidePlugin({
Buffer: ["buffer", "Buffer"],
process: "process/browser",
}),
],
externals: {
react: "react",
"react-dom": "reactDOM",

View File

@ -1,8 +1,8 @@
export const RequestType = {
"voice": "voice",
"config": "config",
"startRecording": "startRecording",
"stopRecording": "stopRecording"
"start": "start",
"stop": "stop"
} as const
export type RequestType = typeof RequestType[keyof typeof RequestType]
@ -17,7 +17,7 @@ export type ResponseType = typeof ResponseType[keyof typeof ResponseType]
export type VoiceChangerWorkletProcessorRequest = {
requestType: RequestType,
voice: ArrayBuffer,
voice: Float32Array,
numTrancateTreshold: number
volTrancateThreshold: number
volTrancateLength: number
@ -67,14 +67,14 @@ class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
this.volTrancateThreshold = request.volTrancateThreshold
console.log("[worklet] worklet configured", request)
return
} else if (request.requestType === "startRecording") {
} else if (request.requestType === "start") {
if (this.isRecording) {
console.warn("[worklet] recoring is already started")
return
}
this.isRecording = true
return
} else if (request.requestType === "stopRecording") {
} else if (request.requestType === "stop") {
if (!this.isRecording) {
console.warn("[worklet] recoring is not started")
return
@ -83,18 +83,6 @@ class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
return
}
const arrayBuffer = request.voice
// データは(int16)で受信
const i16Data = new Int16Array(arrayBuffer)
const f32Data = new Float32Array(i16Data.length)
// console.log(`[worklet] f32DataLength${f32Data.length} i16DataLength${i16Data.length}`)
i16Data.forEach((x, i) => {
const float = (x >= 0x8000) ? -(0x10000 - x) / 0x8000 : x / 0x7FFF;
f32Data[i] = float
})
// console.log("[worklet] i16Data", i16Data)
// console.log("[worklet] f32Data", f32Data)
if (this.playBuffer.length > this.numTrancateTreshold) {
console.log("[worklet] Buffer truncated")
while (this.playBuffer.length > 2) {
@ -102,21 +90,11 @@ class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
}
}
// アップサンプリングしてPlayバッファに蓄積
let f32Block: Float32Array
for (let i = 0; i < f32Data.length; i++) {
const frameIndexInBlock = (i * 2) % this.BLOCK_SIZE //
if (frameIndexInBlock === 0) {
f32Block = new Float32Array(this.BLOCK_SIZE)
}
const currentFrame = f32Data[i]
const nextFrame = i + 1 < f32Data.length ? f32Data[i + 1] : f32Data[i]
f32Block![frameIndexInBlock] = currentFrame
f32Block![frameIndexInBlock + 1] = (currentFrame + nextFrame) / 2
if (f32Block!.length === frameIndexInBlock + 2) {
this.playBuffer.push(f32Block!)
}
const f32Data = request.voice
const chunkNum = f32Data.length / this.BLOCK_SIZE
for (let i = 0; i < chunkNum; i++) {
const block = f32Data.slice(i * this.BLOCK_SIZE, (i + 1) * this.BLOCK_SIZE)
this.playBuffer.push(block)
}
}

View File

@ -524,4 +524,8 @@ class VoiceChanger():
if self.settings.recordIO == 1:
self.stream_in.write(unpackedData.astype(np.int16).tobytes())
self.stream_out.write(result.tobytes())
if self.settings.inputSampleRate != 24000:
result = resampy.resample(result, 24000, 48000).astype(np.int16)
return result