【GAS】スプレッドシートの数字譜を元にピアノの音で自動演奏~解説編(2)Googleドライブにあるピアノの音階ファイルをAudio要素として取り込むまで~
この記事は、外部ファイルとしてピアノの音源を用意し、それをスプレッドシートに用意した数字譜を元に自動演奏させる、以下にご紹介するGAS(Google Apps Script)アプリの説明記事です。
音楽を奏でるGASのプログラム41例の多くは、ピ・ポ・パ・・・という人工音によるものだと思いますが、本記事でご紹介する方法は、Googleドライブ上に音源ファイルを用意することで、ピアノの音色などを楽しめるのが特長です。
簡単なトイピアノ程度の機能ですが、インストール不要で、楽譜だけ編集するだけで、短い音楽をメンバーと共有できますので、簡単な合唱曲や合奏曲を練習するときの便利ツールなどに使えるかも知れません。
Googleドライブ上にあるピアノの音階ファイルをテンプレートに取り込む①~まず音階毎の音源ファイルを用意する~
このGAS(Google Apps Script)で作るWEBアプリでは、ピアノの各音階を鳴らすのに、音階毎の音源ファイルを利用します。
そのため、GASを作動させるための事前作業として、音源ファイルを、楽器や演奏アプリを鳴らして地道に採取する作業が必要になります。
今回はG3~G6までの37音階をファイルで用意します。(後の記事でご紹介)
上記の音階の音域は、ピアノでいうと、中央のドを起点にして、左側のソから、2オクターブ先のソまでの範囲になります。
(トイピアノを意識した少し高めの音域としました)
なお、音楽に詳しい方は周知の事ながら、音階名は、「ド」を「C」として、以降順に、以下の様にアルファベットで表し、オクターブ番号を添えて「C5」などと表記します。音源ファイルにはこの音階名を採用しています。(半音階は#(シャープ)の意味で、sを添えています)
また、音の長さは♩=60で4分音符相当の長さです。ですので鳴らすと実は固定長で鳴るのですが、単純なメロディであれば、不思議と気になりません(筆者が鈍感なのかもしれません)。
これらの音源ファイル一式を、Googleドライブ上に適当なサブフォルダを作っった中にアップしておきます。
(音源ファイルは後ほどご案内します)
Googleドライブ上にあるピアノの音階ファイルをテンプレートに取り込む②~音源ファイルをスプレッドシート上にリスト化する~
次に、保存した音源フェアの「ファイルID」を確認し、他の情報を加えてスプレッドシート上にリスト化します。これも事前での作業になります。
まず、スプレッドシートを作成し、名前が「音源」というシートを作ります。
そこに、以下の様なイメージのリストを表記します。
上記で1行目は、音源ファイルをアップしているサブフォルダのIDを表記しています。
また3行目以降は1行ずつ、A列がMIDI(ミディ)番号・B列がファイル名・C列がファイルIDという内容でリストにしています。
なおMIDI(ミディ)番号というのは、コンピュータ内で使われている音階名で、正確にはMIDIノート番号といいます。あまり本質的ではないですが、文字と数字のまじった記号よりも数字だけの方が扱い易いし、単純に音程の高低が数字の大小によって把握しやすいので、このWEBアプリでも利用します。
ここで、「ファイルID」は、Googleドライブ上のファイルを特定するための番号であり、ファイルを選んで右クリックする事で表示される「リンクを取得」メニューから知る事ができます。
ファイルIDを取得するには、リンク用のURLをコピーしてメモ帳などに一端貼り付け、以下の太字の部分を再度コピーします。
https://drive.google.com/file/d/★ファイルID★/view?usp=sharing
こうして、冒頭の以下のリストを作成します。
ファイルIDを効率よく取得する方法
前述のファイルIDはユーザが設定したファイル毎に割り当てられ、こちらでご紹介したり用意する事ができませんので、ユーザ各人がファイルIDを調べて記載する必要があります。
しかし、音階の数が多いので、手間が大変かと思いますので、以下にファイルIDを調べるためのスクリプトをご紹介します、
(下記で★スプレッドシートのID★、★保存したサブフォルダのID★ の部分は、音階ファイルのリストを記載しているスプレッドシートのIDに打ち替えてください。)
//対象フォルダ内のすべてのファイルを取得するプログラム
function myGetFiles() {
var myApp = SpreadsheetApp.openById('★スプレッドシートのID★');
var mySheet = myApp.getSheetByName('ファイル名取得');
var myFolder = DriveApp.getFolderById('★保存したサブフォルダのID★');
var myFiles = myFolder.getFiles();
var iLin=1;
var Col=1;
while (myFiles.hasNext()) {
var file = myFiles.next();
mySheet.getRange(iLin,Col).setValue(file.getName());
mySheet.getRange(iLin,Col+1).setValue(file.getId());
iLin++;
}
}
使い方ですが、スプレッドシートにシートを追加して、「ファイル名取得」というシート名にします。
次に、スプレッドシートの「拡張機能」メニューから「Apps Script」を選ぶと、スクリプトのエディタが立ち上がります。
そこに上記のスクリプトを貼り付けて保存します。
実行ボタンを押してプログラム走らせます。
すると、以下の様な結果が「ファイル名取得」シートに書き出される筈です。
これを編集して、冒頭のリストを作成すると早いでしょう。
以上が事前作業になります。
Googleドライブ上にあるピアノの音階ファイルをテンプレートに取り込む③~音源ファイルをテンプレート内のAudio要素として取り込む~
ここからは、GASを作動させた際の動作の説明になります。
最終的には、Googleドライブ上に用意した、以下の様な音階別の音声ファイルを、GASを作動させる事で・・・
・・・テンプレートにとりこんで、以下の様にAudio要素にします。
この時、以前の記事でご紹介した、BASE64エンコードの・・・方法を利用します。
では、具体的にどう取り込んでいるのか、以下ご説明します。
音源フェイルとファイルIDのリスト情報を取得
まず行うのは、音源フェイルとファイルIDのリスト情報の取得です。
以下はそのスクリプトの一部です。
・・(前略)・・
//=========音源データの取得=========
//対象シートをシートの名前を指定して取得
var mySheet = myApp.getSheetByName('音源');
//データ記録範囲の位置を記載
var nRow0=3;
var nCol0=1;
//データ記録範囲として、行数と列数を記載
var nRow=37;
var nCol=3;
//データ記録範囲を指定して範囲を取得
var myCells = mySheet.getRange(nRow0,nCol0,nRow,nCol);
・・(中略)・・
かいつまんで言いますと、まず音源のリストの以下以下囲み部分を、getRange()関数で取得しています。
var myCells = mySheet.getRange(指定範囲)
注意)「指定範囲」は以下の囲み分(最後は記載のある最終行まで)です。
そして、テンプレートmyHTMLに、 myHTML .myCellsという変数を追加して取得した情報を埋め込みます。
//=========テンプレートの生成と値の埋込み=========
//HTMLファイルのテンプレートをファイル名を指定して取得
var myHTML = HtmlService.createTemplateFromFile('index');
//★★テンプレートに埋め込む変数値を指定する★★
//音源の埋込み
myHTML.myCells = myCells.getValues();
・・(後略)・・
myHTML.myCells = myCells.getValues();
更に、getBae64Data(ファイルID) という関数を定義して、ファイルIDを指定すると、BASE64エンコードを返す関数を、以下の様に定義しておきます。
//テンプレートから呼び出して利用
function getBae64Data(id) {
const file = DriveApp.getFileById(id);
const data = file.getBlob().getBytes();
return Utilities.base64Encode(data);
}
ここまでしたら、後はテンプレート側で情報を取得します。
まず、スクリプトによって埋め込まれた myCells という変数からMIDI音階番号とファイルIDの情報を取得します。
以下がその部分ですが、スクリプトレット<? ~ ?>の中で、次の様なコードを記載します。
---テンプレート内のコードです---
<?
for(var i=1;i<37;i++){
var Row = myCells[i-1];
//テキスト要素(音階名)、Audio要素:このID名は「sound + MIDI番号」にしておく
?><p><?=Row[1]?><audio id="sound<?=i+54?>" controls src="" preload="auto"></audio></p><?
//隠しテキスト(音階名):このID名は「FileID + MIDI番号」にしておく
?><p hidden id="FileID<?=i+54?>"><?=Row[2]?></p><?
}
?>
上記コードでは、myCellsから1行ずつ抜き取って、その3列目の要素としてファイルIDを収集して、ID番号をMIDI番号とした<p>要素にしています。
概要は以下を参照ください。
for(i行目:midi番号を指しています){
var Row = myCells[i行目];
<P>
ID=MIDI番号:i行目+54
テキスト=<?= Row[3列目] ?>
非表示
</P>
}
こうして一端、情報を非表示とした<P要素>で取り込んでおくと、後でどのモジュールからも利用できて便利です。
実際には、後のモジュールでこの<P要素>に、ID番号を指定する事で
アクセスして、その記載内容を取得する事でファイルIDを取得しています。
コード概要は以下です。
---テンプレート内のコードです---
for(MIDI番号){
//音階名を取得する配列
fileId[MIDI番号] = document.getElementById("MIDI番号").textContent;
}
そして、取得したファイルIDを使って、(かなり省略していますが)以下の様なコードによって、該当ファイルをBASE64エンコードした結果を取得し、予め用意したAudio要素にセットしていきます。
---テンプレート内のコードです---
・・・(<P>要素をIDで指定して、テキスト情報を収集)・・・・
fileId[i] = document.getElementById("FileID"+i).textContent;
・・・google.script.run でスクリプト内の関数を作動・・・・
google.script.run
.withSuccessHandler(res => {
・・・Audio要素に音源をbase64によりセット・・・・
myaudio[i].setAttribute("src", "data:audio/mp3;base64," + res);
})
・・・スクリプト内の関数を作動・・・・
.getBae64Data(fileId[i]);
}
かなりラフな説明になってしまいましたが、以上の様なコードによって、テンプレート内にピアノ音源をセットしたAudio要素を作成しています。
ちなみに、以下がGASを作動させた所です。
起動すると、じわじわと、Audio要素に音源がセットされていく様子が判ります。
ここまでが、GASを立ち上げた際に、演奏前に行われる内容です。
ここで一端記事を切り、スプレッドシートに記載された内容を元に演奏する方法は次回の記事でご説明します。