😽
Node.js でマイクから音声を拾いミュートを判定する
はじめに
この記事では Node.js でマイクから音声を拾いミュートを判定する記事を紹介します。
作業プロジェクトの準備
TypeScript の簡易プロジェクトを作成します。
長いので折りたたんでおきます。
package.json を作成
package.json
を作成します。
$ mkdir -p mute-sample
$ cd mute-sample
$ pnpm init
package.json
を変更します。
package.json
{
"name": "mute-sample",
"version": "1.0.0",
"description": "",
- "main": "index.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "keywords": [],
- "author": "",
- "license": "ISC"
+ "main": "index.ts",
+ "scripts": {
+ "typecheck": "tsc --noEmit",
+ "dev": "vite-node index.ts",
+ "build": "tsc"
+ },
+ "keywords": [],
+ "author": "",
}
TypeScript & vite-node をインストール
TypeScript と vite-node をインストールします。補足としてこちらの理由のため ts-node
ではなく vite-node
を利用します。
$ pnpm install -D typescript vite-node @types/node
TypeScriptの設定ファイルを作成
tsconfig.json
を作成します。
$ npx tsc --init
tsconfig.json
を上書きします。
tsconfig.json
{
"compilerOptions": {
/* Base Options: */
"esModuleInterop": true,
"skipLibCheck": true,
"target": "ES2022",
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": true,
/* Strictness */
"strict": true,
"noUncheckedIndexedAccess": true,
"checkJs": true,
/* Bundled projects */
"noEmit": true,
"outDir": "dist",
"module": "ESNext",
"moduleResolution": "Bundler",
"jsx": "preserve",
"incremental": true,
"sourceMap": true,
},
"include": ["**/*.ts", "**/*.js"],
"exclude": ["node_modules", "dist"]
}
git
を初期化します。
$ git init
.gitignore
を作成します。
$ touch .gitignore
.gitignore
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
dist/
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
.env
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
動作確認コードを作成
動作を確認するためのコードを作成します。
$ touch index.ts
index.ts
console.log('Hello, World');
型チェック
型チェックします。
$ pnpm run typecheck
動作確認
動作確認を実施します。
$ pnpm run dev
Hello, World
コミットします。
$ git add .
$ git commit -m "初回コミット"
Mute を判定
ミュートを判定するためのコードを作成します。
インストール
$ pnpm install node-record-lpcm16
sox
が必要なためインストールします。
$ brew install sox
コードの作成
$ touch demo01.ts
demo01.ts
const record = require('node-record-lpcm16');
import { Buffer } from 'buffer';
const THRESHOLD = 20; // 無音と判定する音量レベルの閾値
const MUTE_DURATION = 3; // ミュートと判定する無音状態が続く秒数
const SAMPLE_RATE = 44100; // サンプリングレート
const BUFFER_SIZE = 4096; // チャンクのサイズ
const CHUNKS_PER_SECOND = SAMPLE_RATE / BUFFER_SIZE; // 1秒間に受け取るチャンクの数
// 無音状態が続いているチャンクの数
let silentChunks = 0;
// 録音データを受け取るインスタンス
const mic = record.record({
sampleRate: SAMPLE_RATE,
threshold: 0,
verbose: false
});
// 録音データを受け取るストリーム
const micInputStream = mic.stream();
// 録音データを受け取る
micInputStream.on('data', (data: Buffer) => {
// チャンクのデータをInt16Arrayに変換
const intData = new Int16Array(data.buffer);
// 音量レベルを計算
const volumeLevel = intData.reduce((sum, value) => sum + Math.abs(value), 0) / intData.length;
// 無音状態の判定
// 音量レベルが閾値未満のときは無音と判定
if (volumeLevel < THRESHOLD) {
silentChunks += 1;
} else {
silentChunks = 0;
}
// ミュート状態の判定
if (silentChunks > CHUNKS_PER_SECOND * MUTE_DURATION) {
console.log('Muted');
// ミュート状態が続いているときの処理
}
});
// エラー処理
micInputStream.on('error', (err: Error) => {
console.error('Error: ', err);
});
// ストリーム終了時の処理
mic.start();
console.log('Recording...');
// SIGINTシグナル(Ctrl+Cなど)を受け取ったときに録音を停止
process.on('SIGINT', () => {
mic.stop();
console.log('Stopping recording...');
});
$ pnpm vite-node demo01.ts
コミットします。
$ git add .
$ git commit -m "Node.js でマイクから音声を拾いミュートを判定する"
Mute を判定
ミュートを判定するためのコードを作成します。
コードの解説
CHUNKS_PER_SECOND
は 1 秒間に受け取るチャンクの数を計算しています。
const THRESHOLD = 20; // 無音と判定する音量レベルの閾値
const MUTE_DURATION = 3; // ミュートと判定する無音状態が続く秒数
const SAMPLE_RATE = 44100; // サンプリングレート
const BUFFER_SIZE = 4096; // チャンクのサイズ
const CHUNKS_PER_SECOND = SAMPLE_RATE / BUFFER_SIZE; // 1秒間に受け取るチャンクの数
無音状態が続いているチャンクの数です。
let silentChunks = 0;
録音データを受け取るインスタンスです。
const mic = record.record({
sampleRate: SAMPLE_RATE,
threshold: 0,
verbose: false
});
録音データを受け取るストリームです。
const micInputStream = mic.stream();
ストリームからデータを受け取る処理です。
micInputStream.on('data', (data: Buffer) => {
...
});
ボリュームレベルが閾値未満のときは無音と判定します。MUET_DURATION
秒間無音状態が続いたときにミュートと判定します。
// チャンクのデータをInt16Arrayに変換
const intData = new Int16Array(data.buffer);
// 音量レベルを計算
const volumeLevel = intData.reduce((sum, value) => sum + Math.abs(value), 0) / intData.length;
// 無音状態の判定
// 音量レベルが閾値未満のときは無音と判定
if (volumeLevel < THRESHOLD) {
silentChunks += 1;
} else {
silentChunks = 0;
}
// ミュート状態の判定
if (silentChunks > CHUNKS_PER_SECOND * MUTE_DURATION) {
console.log('Muted');
// ミュート状態が続いているときの処理
}
エラー処理です。
micInputStream.on('error', (err: Error) => {
console.error('Error: ', err);
});
録音を開始します。
mic.start();
console.log('Recording...');
SIGINT シグナル(Ctrl+C など)を受け取ったときに録音を停止します。
process.on('SIGINT', () => {
mic.stop();
console.log('Stopping recording...');
});
さいごに
この記事では Node.js でマイクから音声を拾いミュートを判定する記事を紹介しました。
作業リポジトリ
作業リポジトリはこちらです。
Discussion