He Asked "Not Any Lions"?

hanaliのブログです

Mountaineering as Track Making(MATM) 研究序章 ~「山を登る行為がそのまま音楽になると人生が捗るのでは」仮説

この記事はYAMAP エンジニアAdvent Calendar 2019の二日目の記事です。

qiita.com

はじめに

「時間」というリソースは、全人類に平等に有限であります。

我々山を愛する者にとって、労働や育児、自己研鑽の時間を割きながら、なんとか登山のための時間を捻出する、というのはとても骨が折れる行為です。

そしてそれはトラック・メイキング(音楽制作)をする人にとっても同じで、忙しい日常の中で一人で自己と向かいあって音楽を作る、というのは大変な作業です。

そして何ということでしょう、山を愛し、トラック・メイクをする人も人類の中には一定数いるわけで、その中の一人が私です。

仕事と山の隙間をぬってちょいちょい音楽を作ってリリースするという趣味を持つ私。hanaliという名義でspotify等でも聞けたりします。

ototoy.jp

この2つのカードをどう切るか、ということに七転八倒する中、2015年には子供が生まれてからはそれに「育児」というカードも加わります。

そして2019年にはヤマップという会社に転職し、イチから学ぶべきことが山積みに。

登山・音楽・育児・学習・お酒・・・カードが増えすぎてとにかく時間ががない!

どうすれば良いのか?

そこでやはり、当然の帰結としてこんなことを考えます。

「何とか山と音楽を一緒にこなせないか?」

登山の素晴らしさからインスピレーションを受けて、音楽を作る・・・みたいな甘っちょろいことではない。

山に行く行為がそのまま音楽制作になる。

これをテクノロジーによって解決できれば、私(および同じ悩みを抱える諸氏)にとって革命的なソリューションになる・・・!

ということでMountaineering as Track Making(MATM) 研究を初めてみました。

現時点での研究成果

いきなり結論ですが、まだまだ研究を始めたばかりで、成果として出せるものはミニマム。しかしそれをまずは恥ずかしがらずに共有するところから始めてみます。

まずは最初に題材となるのが、このYAMAPの山の記録です(by私)。

yamap.com

大崩山という、宮崎県の痺れるかっこいい山がありまして、登山道のストイックさと岩をブチかましてくる景観のハードコアさが爽快な山です。

ま、山について語ると長くなるので置いておきますが、注目してほしいのはここ。

f:id:tokita93:20191202014347p:plain
標高グラフ

YAMAPでGPSで記録を取り、アップすると、こんな感じで軌跡から生成した標高データをグラフとして表示してくれます。

とても素敵ですね!

しかしこのグラフ、音楽視点で見てみると、音の波形っぽいということに気づいた方が多くおられるのではないかと思います。

そうすると、「この軌跡のグラフで、 音を生成できるのではないか?」と思い至るのは当然の流れです、

これをウェーブテーブルシンセの波形として使用すれば、山行独自の音を作ることができるのでは?

f:id:tokita93:20191202014934p:plain
標高から波形を生成

はいできました。

上記はMax/MSPの画面ですが、注目してほしいのが右下の波形データの画像。

上のYAMAPの軌跡データが読み取られ、波形として設定されていることが確認できるかと思います。

上の軌跡データをMax/MSPで読み込み、wavetableシンセ(wave~)の波形として音を鳴らす仕組みを作ってみました。

これにより、1山行ごとにユニークな音色を作ることができます

つまり、山に行くごとにオリジナルな音を作ることができる=Mountaineering as Track Making(MATM)の第一歩を踏み出せた・・・・! という塩梅であります。

さてその音色は!? ということは一度置いておいて、ここに至るまでの技術的な解説をします。

ちなみに予め断っておくと、私はNode.jsもMax/MSPもひよっ子レベルのスキルです。なんとかかんとかググったりしながら書いています。 間違っている点や良くないコードもあると思いますので、何かあればご指摘していただけると助かります・・・!

Max/MSP、そしてNode for Max

DAWや音楽制作ソフトに詳しい人は、Max/MSPというソフトを聞いたことがある、もしくは使ってみたことがある人は多いかと思います。

ノードをドラッグ&ドロップで繋いで処理を作り、音楽制作や映像制作を行えるこのソフト。

www.mi7.co.jp

かつては「エレクトロニカ」みたいな言葉が流行りだしたとき(2000年前後)に、ノンプログラミング&アルゴリズミックに音楽が作れるツールとして一躍有名になりました。

その後、シーンに定着しつつも革新的なアップデートを繰り広げ、テクノロジーによって音楽をアップデートし続けるソフトであります。

って語れるほど私は使っているわけではないわけで。何度かチャレンジしたんですが、なかなか音楽制作に使えるほどまで熟練しないまま、ずっと傍から眺めていたソフトでありました。

が、ver.8でかなりイカした機能がリリースされました。

それが Node for Max(N4M)。Node.jsをMax/MSP上で普通に呼び出して使える機能! f:id:tokita93:20191202025256j:plain

Node.jsが使えるということはnpmが使えるわけで、npmで提供されているあらゆるパッケージをMax/MSPで使える。ほぼ無限のコネクタビリティ!

Max/MSPでは面倒だった計算処理もNode.jsのスクリプトで行うことができるので、この組み合わせは熱いのでは・・・!

ということで、今回はNode for Max(N4M)を使って処理(パッチ)を作ることにしました。

出来上がったパッチ

f:id:tokita93:20191202022254p:plain
パッチ全体像
ということで出来上がったパッチです(Max/MSPで作る処理の単位を「パッチ」と言います)

このパッチは、松本昭彦さんの以下のウェーブテーブル音響合成の説明とサンプルパッチをパク大変参考にさせていただいています。

akihikomatsumoto.com  

Max/MSPではこんな感じで箱(オブジェクト)を組み合わせて処理を作っています。

今回の例でいくと、右側で指定している「node.script」というオブジェクトがnode for Maxのオブジェクトで、こちらにYAMAPの活動日記ID(アクティビティID)を渡して、軌跡データ=波形データを取ってきている形です。

データは整えた後peek~に渡してbuffer~に設定し、それを表示してます(この辺の説明は割愛)。

ちなみにちょっとハマったんですが、node.scriptは「 @autostart 1 @watch 1」というアトリビュートを渡しておくのがコツです。これにより、自動スタート&ファイルを監視してリロードが行われます。 試行錯誤しながら処理を作る開発段階では大変役に立ちます。

node.scriptでやっている処理は?

いろいろと突貫工事で汚いんですが、下のようなコードです。

const puppeteer = require('puppeteer');
const fs = require('fs');
const requestify = require('requestify');
const che = require('cheerio');

const max = require('max-api');
const gpxfile  = './points.xml';

const getGpx = (async (act_id) => {
  const browser = await puppeteer.launch({ headless: true});;
  const page = await browser.newPage();

~中略・・・ログイン処理~
  const cookies = await page.cookies();
  let gpx;
  await requestify.get(points_url, {
    cookies: cookies
  }).then( respoonse => {
    gpx = respoonse.body;
  });
  await browser.close();z

  return gpx;
});



max.addHandler('open', async (act_id) => {
  const gpx = await getGpx(act_id);
  fs.writeFileSync(gpxfile, gpx);

  const gpx_data = fs.readFileSync(gpxfile, "utf-8");
  const $ = che.load(gpx_data,{ xmlMode: true });
  let arr = [];
  $('ele').each((i, elem) => {
    const v = $(elem).text()*1;
    arr.push(v);
  });
  const maxEle = Math.max.apply(null, arr);
  const minEle = Math.min.apply(null, arr);
  const rate = 2/(maxEle-minEle);
  arr.forEach( (e,i) => {
    arr[i] = (e-minEle)*rate - 1;
  });
  max.outlet(arr);
});

処理の流れとしては以下の感じです。

  • puppeteerでログインしてcookie取得(この辺は現状のYAMAPの画面仕様に依存しすぎるので微妙な感じ)
  • requestifyにcookieを渡して軌跡データをダウンロード(軌跡のダウンロードにログインが必要なため)
  • cheerioで軌跡データの「ele」要素(高さ)のみを取得して、-1~1に分布するように計算し直す
  • max.outletに渡してmaxにリストを渡す

とさらっと書いてみたものの、私のNode.jsスキルの無さから、いろいろ調べながらやってハマりまくりました・・。

puppeteerは上ではheadless: trueにしていますが、開発中はfalseにして実際のブラウザの挙動みながら試行錯誤していました。

puppeteerでファイルダウンロードまでしたかったんですが、ダウンロードしようとするとなぜかブラウザが閉じてしまう挙動に悩まされたので、そこだけrequestifyで行うようにしたりしてます。

また、返しているarrayのlengthをbuffer~のsamplesizeに渡したかったんですが、lengthがMax/MSP上でうまく取れずに一旦挫折・・・。再チャレンジしてみます。

ということで出来上がった音

がこちら。

ぼぉぉおおお・・。大崩山はこの音です。 大崩山の峻険さに思い起こさせる荘厳な音・・・というには多少無理がありますね。 まだまだ磨き上げが必要そうです。

しかしまずは第一歩! ということで、ここから音楽に発展させるための研究を今後行っていきます。

追加でいろいろ試してみた

ブログを公開したところ、「じゃああの山行ならどうなるの?」という声が上がりました。 興味深いので試してみました。

afroblue 氏の青梅丘陵トレラン波形。

yamap.com f:id:tokita93:20191203235902p:plain

なかなかいかつい波形ですね! このトレランを聞いてみましょう。

先程とは音がより重厚感あふれる音になりましたね。ストイックなスピード感がトレランですね。

では勝手にヤマップCXO 安藤さんの山行を音にしてみたりします。

yamap.com

f:id:tokita93:20191204000932p:plain
4泊5日の雲ノ平・北アルプス縦走

を音にするとこうです。

f:id:tokita93:20191204001123p:plain

やはり長い縦走ならではの、立体感に溢れた重厚な音に痺れますね。音の向こうに北アルプスの遠大な山景が見えてきます。

ちなみにこの録音はAudacityというソフトでキャプチャしてるんですが、波形を拡大してみると

f:id:tokita93:20191204001453p:plain

律儀に山行記録を波形にしていることが理解できて、笑えます。

そしてここまでくると、これらの山行をミックスしてみたくなるのは当然の流れです。

大崩山・トレラン・雲ノ平を山岳シンセサイズした結果の音がこちらです。

こちらも波形を見てみると、忠実にシンセサイズ(合成)したことが伺えてとても楽しいですね。

f:id:tokita93:20191204003337p:plain

うーん、正直いってとても楽しい。山岳シンセサイズ界におけるディズニーランドと言っても過言ではないでしょう。

新しい沼を見つけてしまった絶望感と感動がいまここにあります。

さらにやりたいこと

  • 写真を音に変換してリズムにする
  • Aphex twinのmetasyth でやった、音のスペクトラムで画像を表現するやつをやってみたい www.wired.com
  • GPXデータを変数としてあらゆる音に適用する
  • 今回は高さデータしか使用していないが、緯度経度情報も変数として豊かな情報を持っているはず
  • それを音楽制作の変数として余すところなく利用したい

結論

MATMは始まったばかり・・・・。 山と音楽とプログラミングで精度を高めていきます。 乞うご期待!