RaspberryPiを使って、GoogleスピーカからSoxで変調した音声を流す

投稿者: | 2022年12月27日

概略の説明

RaspberryPi2BからGoogleスピーカに音声データを送信してGoogleスピーカ から再生する際に、Soxを使って変調した音声をリアルタイムで生成するシステムについての説明です。

RaspberryPi内に(A)node.js+Expressによるwebサーバおよび(B)Googleスピーカに対して再生する音声のURLを指示する機能を構築し、更に(A)に対して(A2)HTTPリクエストに応じてSoxを起動しその出力結果をExpressを通して送信する機能、を付加することでこの動作が実現できます。

システム構成

ここまでの説明をシステム構成にまとめると次の図のようになります。

「User Web Browser」の部分は上記の説明にはありませんが、実用的にシステムにアクセスするためのUIとして作成する必要があります。

本記事での説明箇所

本記事では、(A2)HTTPリクエストに応じてSoxを起動しその出力結果をExpressを通して送信する機能について説明します。

その他、Googleスピーカ・RaspberryPiを使用したシステムの過去記事はこちらを参照してください。(A)node.js+Expressによるwebサーバなども説明しています。

本記事作成に当たっての使用機器および開発環境

本記事は、以下の機器を使用してシステムの動作を検証しています。

  • RaspberryPi 2B (OS : Raspberry Pi OS (32-bit) Lite)
    (node.jsおよびTypeScript使用)
  • Google Nest mini

SOXのインストール

ターミナル等でRaspberryPi にログインして、以下のコマンドでインストールします。

sudo apt-get install alsa-utils sox libsox-fmt-all

以下、参考記事

https://mukudori-noise.skr.jp/RaspberryPi3_3.html

ソースコード(A2)

RaspberryPi上のnode.js環境に、TypeScriptで記述しています。以下は、(A2)HTTPリクエストに応じてSoxを起動しその出力結果をExpressを通して送信する機能に関するものです


import express from 'express';


const app: express.Express = express();


app.get("*.wav", (req: express.Request, res: express.Response, next: express.NextFunction) => {

	const fs = require('fs');

	// ここでファイルパスを取得
	// path.joinの箇所は実際のサーバのディレクトリ構成等に合わせて適当に変更してください
	const filepath = path.join(fileDir, decodeURI(req.path));

	try {
		fs.stat(filepath, (err, stat) => {
			if (err) {
				next(err)
			}
			// ファイル名をエンコードする
			const basename = path.basename(filepath);
			const filename = encodeURIComponent(basename);

			// ヘッダーセットする
			res.setHeader('Content-Type', 'audio/wav');
			res.setHeader('Content-disposition', `inline; filename*=utf-8''${filename}`);

			const { spawn } = require('node:child_process');

			// 適当なsox effectsを設定
			// req.queryで変数を渡して条件分岐をすると面白い
			const command = `sox ${filename} -t wav - reverb`; // リバーブ
			// const command = `sox ${filename} -t wav - echo 0.8 0.9 400 0.3 800 0.25 1200 0.1 1600 0.05`; // エコー
			// const command = `sox -m -t sox "|sox ${filename} -t sox - pitch -190 echo 0.8 0.9 50 0.5" -t sox "|sox ${filename} -t sox - pitch 270 echo 0.8 0.9 60 0.8" -t wav -`; // 不協和音
			// コマンドエラーになる場合があるのでタイムアウトを設定
			let sp = spawn(command, [], { shell: true, timeout: 30000 });
			sp.on('error', (err) => {
				next(err);
				sp.kill();
			});
			sp.on('close', (err) => {
				console.log('spawn close');
			});

			// 標準出力をパイプでexpress.Responseに送る
			sp.stdout.pipe(res).on('error', (err) => {
				sp.kill();
				slk.Err(err);
			});

		// express.Responseのcloseイベントを検出
		// 念のため、確実にspawnプロセスをkillしておく
		res.on('close', ()=> {
			console.log('the response has been sent');
			setTimeout(() => {
				if (sp) if (!sp.killed) { sp?.kill(); }
			}, 30000);
		});
	    });
	} catch (err) {
	    next(err);
	}
});

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA