kintoneで録音したい!
というわけで
下記サイトを参考に作ってみました。
- cybozu developer network ナレッジノート-MediaRecorder APIを使って録音アプリをつくろう
- ブラウザで録音してwavで保存
- 音ファイル(拡張子:WAVファイル)のデータ構造について
- MediaRecorder - Web API | MDN
出来上がったもの(音量注意)
アプリの準備
フィールドは一つだけ。
| フィールド種類 | フィールドコード |
|:-:|:-:|:-:|
| 添付ファイル | 音声 |
JavaScript
ライブラリの準備お忘れなく
kintone REST API Client
https://unpkg.com/@kintone/rest-api-client@2.0.14/umd/KintoneRestAPIClient.min.js
kintone UI Component
https://unpkg.com/kintone-ui-component/umd/kuc.min.js
Luxon
https://js.cybozu.com/luxon/2.0.1/luxon.min.js
また、音声ファイルにプレイヤーをつける方法はコチラを参考にどうぞ。
一覧表示後イベントでボタン設置
録音開始ボタンと録音停止ボタンを設置します。
// 一覧画面のメニュー下にボタン設置する
const sp = kintone.app.getHeaderSpaceElement();
const fname = new Kuc.Text({
placeholder: "音声ファイル名",
suffix: ".wav",
textAlign: "left",
visible: true,
disabled: false,
});
const startButton = new Kuc.Button({
text: "🔴録音開始",
type: "submit",
});
const stopButton = new Kuc.Button({
text: "⬛録音停止",
type: "submit",
disabled: true,
});
sp?.appendChild(fname);
sp?.appendChild(startButton);
sp?.appendChild(stopButton);
参考サイトのソースをコピーしたものを貼り付けていく
ボタン設置の次の行から
冒頭で紹介した参考サイト(ブラウザで録音してwavで保存)
からコピーしてきてESLintに怒られたところを直したりしたコードを貼り付けます。
// 録音に必要な変数など宣言
let audio_sample_rate = null;
let scriptProcessor = null;
let audioContext = null;
const audioData = [];
const bufferSize = 1024;
// 録音データを保存する
const saveAudio = async () => {
// 録音データを加工する
// kintoneに保存する
}
録音データを加工する
冒頭で紹介したこちらのページを参考に録音した音声データをファイルにします。
// 録音データを加工する
// viewに文字を1文字ずつ書き込む
const writeString = (v, offset, string) => {
for (let i = 0; i < string.length; i++) {
v.setUint8(offset + i, string.charCodeAt(i));
}
};
// float(32bit)を16bitに変換する
const floatTo16BitPCM = (output, offset, input) => {
for (let i = 0; i < input.length; i++, offset += 2) {
const s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
}
};
// 録音終了
await audioContext.close();
// audioDataを1次元配列にする。
const sampleLength = audioData.reduce((a, c) => a + c.length, 0);
const samples = new Float32Array(sampleLength);
let sampleIdx = 0;
for (let i = 0; i < audioData.length; i++) {
for (let j = 0; j < audioData[i].length; j++) {
samples[sampleIdx] = audioData[i][j];
sampleIdx++;
}
}
// PCM 16bit なので、1要素あたり 16bit = 2Byte つまり要素数*2でwavのチャンクサイズを計算する
const wavsize = samples.length * 2;
const buffer = new ArrayBuffer(44 + wavsize);
const view = new DataView(buffer);
writeString(view, 0, "RIFF"); // RIFF識別子 RIFFヘッダ 4Byte
view.setUint32(4, 32 + wavsize, true); // チャンクサイズ (44Byte -8 )+ データサイズ 4Byte
writeString(view, 8, "WAVE"); // フォーマット WAVEヘッダ 4Byte
writeString(view, 12, "fmt "); // fmt識別子 4Byte
view.setUint32(16, 16, true); // fmtチャンクのバイト数 4Byte
view.setUint16(20, 1, true); // 音声フォーマットID 2Byte
view.setUint16(22, 1, true); // チャンネル数 2Byte
view.setUint32(24, audio_sample_rate, true); // サンプリング周波数 4Byte
view.setUint32(28, audio_sample_rate * 2, true); // 1秒あたりバイト数の平均 データ速度 4Byte
view.setUint16(32, 2, true); // ブロックサイズ 2Byte
view.setUint16(34, 16, true); // ビット/サンプル サンプルあたりのビット数 2Byte
writeString(view, 36, "data"); // サブチャンク識別子 data 4Byte
view.setUint32(40, wavsize, true); // サブチャンクサイズ 波形データのバイト数 4Byte
floatTo16BitPCM(view, 44, samples); // 波形データ
kintoneに音声ファイルを保存
kintone REST API Clientのファイル保存メソッドを使って、
作成した音声データをアップロードします。
// kintoneに保存する
const client = new KintoneRestAPIClient();
const date = luxon.DateTime.local();
const FILE = {
name: fname.value
? fname.value + "_" + date.toFormat("yyyyMMddHHmmss") + ".wav"
: "音声_" + date.toFormat("yyyyMMddHHmmss") + ".wav",
data: new Blob([view], { type: "audio/wav" }),
};
const { fileKey } = await client.file.uploadFile({
file: FILE,
});
const { id } = await client.record.addRecord({
app: kintone.app.getId(),
record: {
音声: {
value: [{ fileKey }],
},
},
});
// 保存処理が終わったらリロード
location.reload();
録音開始ボタンと録音停止ボタンの処理
// 録音開始ボタン
startButton.addEventListener("click", function () {
startButton.text = "💖録音中💖";
stopButton.disabled = false;
// getUserMedia
const stream = navigator.mediaDevices
.getUserMedia({ audio: true, video: false })
.then(handleSuccess);
});
// 録音停止ボタン
stopButton.addEventListener("click", function () {
saveAudio();
});
参考サイトのコード残りを貼り付ける
再び・・・冒頭で紹介した参考サイト(ブラウザで録音してwavで保存)
からコピーしてきてESLintに怒られたところを直していったコードを貼り付けます。
MediaRecorder - Web API | MDNをご参照ください。
// save audio data
const onAudioProcess = (e) => {
const input = e.inputBuffer.getChannelData(0);
const bufferData = new Float32Array(bufferSize);
for (let i = 0; i < bufferSize; i++) {
bufferData[i] = input[i];
}
audioData.push(bufferData);
};
// getusermedia
const handleSuccess = (stream) => {
audioContext = new AudioContext();
audio_sample_rate = audioContext.sampleRate;
scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
const mediastreamsource = audioContext.createMediaStreamSource(stream);
mediastreamsource.connect(scriptProcessor);
scriptProcessor.onaudioprocess = onAudioProcess;
scriptProcessor.connect(audioContext.destination);
};
全体的なコードはコチラ
お好みで保存時にファイル名を変えてみたり、コメントを追加してみたり、色々できそうな気がします。
まとめ
楽器の練習を録音していちいちkintoneに保存するのが面倒だったので作ってみました。
録音の重要な部分は参考サイトのコードをほぼほぼコピーして作りましたが
なんとかこれで、自分の演奏を客観的に見る(?)ことができるゾ!