RRR日記(5)
長嶋 洋一
2025年1月3日(金)
ボストンで50回目のICMCがあるので応募してね・・・というCFPが届いてきたICMAは、去年末に断捨離の一環としてrenewalしないという選択をしたので、このような情報がいつまで届くかは不明、という2025年の年明けがやってきた。
元日の晩には久しぶりに博多から帰省した長男、そして近隣の次男と共にJoyJoyサンカラ4時間を堪能したのだが、毎週欠かさず歌唱力維持のためにヒトカラしている父に比べて、カラオケなど忘れるほど久しぶりだという息子たちの歌うこと歌うこと。 まだまだ頑張らねば、と思い知らされた新年である。
![]()
執筆者への納本として届いたのに続いて、会員向けの郵送でも届いたので、日本技術士会「技術士」誌の特集記事として執筆した この記事 もWebに正式に置いた(日本技術士会のHPでも公開されるらしい)。 奈良学園大の紀要に投稿した論文も、紀要の別刷が届いたら公開となる。
これで当面の目標としては、今月一杯が発表応募期限となっている、日本音楽即興学会大会(神戸大)への応募というのが第一となる。 ネタ的には、ちょうど進めつつある、テルミン奏者の竹内正実さんとのプロジェクト(→ウェルネスに近いテーマ)が、音楽療法として歴史のある日本音楽即興学会のテーマの一つにうまく絡められたら・・・という作戦を温めているのだ。
ネットからの記事として、 本州最長クラス「行き止まりローカル線」の終着駅に行ってみた というのも知った。 「行き止まり」と言えば、何といっても 最北端を攻めた旅2017 の以下の「稚内駅」なのだが、乗り鉄としては、ちょっと気になる記事だったのでここにリンクを置いておき、いずれ何かのイベントに引っ掛けて出掛けるチャンスを待つことにした。
![]()
昨日から今日の昼過ぎまでかけて、大晦日~元日に見られなかったテレビ番組の追いかけチェック(録画/tver)に費やして「格付けチェック」・「月曜から夜ふかし」・「おもしろ荘」などを消化し、さらに「箱根駅伝」(往路・復路)も断片的にリアルタイムで視聴し、そしてようやく午後になって、「Arduinoテルミン」の続き、という宿題に追い付いた。
実はまだ、懸案だった「周波数データのディジタル転送」に関するアイデアは完全にクリアになっていない状態なのだが、前回のArduino Nano[1]の「test012.ino」で「過去2回のデータの(移動)平均」というのにちょっと良さげな感触があったので、まずはこれを「10段の移動平均」に改訂、実験してみた。 しかし明らかに、10段では確かに周波数の変動は平滑されるものの、肝心のレスポンスが体感として明らかに悪化したので(指先からの細かいビブラートが鈍ってしまう)、結論として5段の移動平均とした「test013.ino」にまとまった。
![]()
そして今度は音源のArduino Nano[2]の「test111.ino」のサイン波を他の音色にしてみよう・・・と、まずMozziで遊んでいた2021年11月末の Sketching日記(12) を発掘して、Mozziのサンプルが並ぶ このページ から、「PWM_Phasing」というのを見つけて、ちょっとだけ変更して、「test112.ino」というのを作ってみた。 音色は音域と関係するので、ここでは基準ピッチをオクターブ下げてあるが、detuneのピッチを「F」と「F*0.99」とさせてみると、上のようになかなかいい感じなものが出来た。 YouTube動画 でどれだけ伝わるかは不明だが、新年早々、こういうところからスタートというのは、まずまずのところだろう。 これで今夜は、気持ち良く「ババ抜き最弱王」を見ることが出来そうだ。
2025年1月4日(土)
高校サッカー選手権で過去に2度優勝している静岡学園が、準々決勝で敗れるところをライヴで見届けた。 試合全体で圧倒的に攻めていた(シュート数は9対1)のに、最後の決め手を欠いて得点できずPK戦で負けたのだが、これはデジャヴのように、Jリーグのエスパルスとジュビロで何度となく見てきた「静岡県サッカーの弱さ」であり、さらにはW杯などで何度となく見てきた「日本サッカーの弱さ」だった。
そんな午後には、Arduinoテルミン実験の音源部分: Arduino Nano[2]に関して、 このページ の「ReverbTank_STANDARD」というのを参考に、「リバーブのかかったサイン波」として「test113.ino」というのを作ってみた(完成度に問題があったのでYouTube記録などはパス)。 これにより、もしかすると、頑張れば「エフェクトのかかったサウンドもMozziで出来るカモ」というところまで確認できた。2025年1月5日(日)
遂にこの日がやってきた。 思えば去年の暮れ、この日記の Part(4) のラスト付近、「2024年12月27日(金)」のところに書いて以来、この年末年始は、布団に入ればCPAP機器と格闘しつつ(寝ても覚めても)、断片的に何度となく頭の中で思考実験してきたアイデアを実験する時がやってきた。 キーワードは「power」と「mantissa」なのだが、敢えてYahooやWikipediaで調べずに過去の記憶を掘り出してきたのだが、ここにきて全体が整理されたので、ようやく mantissa を眺めてみて、構想が正しかったと確信したのだ。
![]()
キーワード「power」・「mantissa」は上のWikipediaでは「exponent」・「mantissa」とされていたが、かつて楽器メーカで音源LSIの設計に取り組み始めた頃、職場の先輩(近藤さん高氏さん)から米国の某特許を解説してもらっていた時には「power」・「mantissa」だった。 僕が 作るサウンドエレクトロニクス の 第4章 と 第5章 で解説したように、楽器の音源として「ピッチ(musical pitch)」を設定する原理としては、大きく「トップオクターブ方式」と「波形読出し位相累算方式」の2種類があり、同じ「周波数データ」に対して、前者はピッチが高いほど周波数の精度が悪くなり、後者はその逆になるという特徴がある。
現在、実験しているArduinoテルミンの場合には、ピッチ設定のArduino Nano[1]から「周波数データ」が直接パラレル転送されて音源のArduino Nano[2]でMozziサウンド合成するのだが、「周波数データ」は12ビット幅であり、これをリニアのまま使用すると、周波数が高い領域では精度がまずまず(周波数を表現するに十分なビット幅)なのに対して、周波数が低い領域(数百Hz以下)では、整数の切り捨て誤差によって、聴感上だいぶ精度の悪さが気になる。 なんせ、例えば220Hz~440Hzというオクターブではデータが220分割しかないので、とても12等分平均律と合奏できるような音階を正しく生成できないのである。 今回はまず、以下のように12ビットの「周波数データ」を定義してみたので、あとはそれぞれのArduinoに実装するだけである。上の定義は、音源であるArduino Nano[2]の側から見たものなのだが、これを試しに実装したところで確認する方法がない。 一方、上流のArduino Nano[1]はこの定義に従ったディジタル伝送データをまず作らないといけないのだが、シリアルモニタで確認できるので、順序としてはこちらを先にすることになる。 せっかくなので、入力として「設定周波数データ」(Hz単位)が与えられたとして、この定義の12ビットを生成するアルゴリズムを以下に整理してみることにした。 これはまぁ、Arduino IDEでいきなり試行錯誤をゴリゴリ書き出すよりも、きちんとした方法(プログラミング)ということになる。
- 周波数データ(12ビット幅)を上位「power」3ビットと下位「mantissa」9ビットに分割する
- 「power」3ビットには7を加算する → これを pow(7~14) と呼ぶ
- 「mantissa」9ビットの最上位に「隠れビット」(1)を加えて10ビット幅(512~1023)にする → 0.5≦仮数<1
- 最終的な周波数はfloat演算で「2^pow × mantissa/1024」Hz
- 周波数の最小値は、power=0 で pow=7、仮数=0.5 なので 128 * 0.5 = 64Hz
- 周波数の最大値は、power=7 で pow=14、仮数=ほぼ1 (1023/1024) なので 16384 * ほぼ1 = ほぼ16384Hz
- 各オクターブ内の周波数はそれぞれ、9ビット精度(512~1023)で刻まれる
ちなみに、入力周波数がfloatであるものの、元々カウンタを使ったArduino Nano[1]のスケッチからはintのデータが入ってくるので、小数点以下までの精度の周波数とするには、整数のみ対応しているmap()関数をfloatにする必要がある。 ところがちょっと調べてみると、ちゃんと ここ に、以下のような美しい方法が出ていた。
- 入力周波数データ(float)を F (Hz) とする。以下はfloat演算で、結果のmantissaは切り捨てて整数にする
- F < 64 の場合は「使わない周波数」(→発音しない)として power=0, mantissa=0 にする (変換終了)
- あるいは もし F < 128 なら power=0 , mantissa = F * 8 - 512 (変換終了、以下同じ)
あるいは もし F < 256 なら power=1 , mantissa = F * 4 - 512
あるいは もし F < 512 なら power=2 , mantissa = F * 2 - 512
あるいは もし F < 1024 なら power=3 , mantissa = F - 512
あるいは もし F < 2048 なら power=4 , mantissa = F / 2 - 512
あるいは もし F < 4096 なら power=5 , mantissa = F / 4 - 512
あるいは もし F < 8192 なら power=6 , mantissa = F / 8 - 512
あるいは power=7 , mantissa = F / 16 - 512- このPowerを周波数データ(12ビット幅)の上位「power」3ビットとする
- このmantissaを周波数データ(12ビット幅)の下位「mantissa」9ビットとする
![]()
これで準備は出来たので、さっそくArduino Nano[1]での実験を開始した。 ここまで動いていた最新のスケッチは「test013.ino」(5段の移動平均処理まで)である、と確認した上で、新たに「test014.ino」でスタートである。 繋がれたArduino Nano[2]は何かヘンな音を出すかもしれないが、アンプのボリュームを絞って当面は無視することになる。
・・・そしてこの部分に午後たっぷりの格闘があったのだが、色々あったものの、結果としては、ほぼ「想定していたもの」が完成した。 以下は周波数計測のArduino Nano[1]のスケッチ「test014.ino」である。
以下は音源担当 : Arduino Nano[2]のスケッチ「test114.ino」であり、素直にサイン波を鳴らすというものである。int i, pitch[5]; float mapfloat(float x, float in_min, float in_max, float out_min, float out_max){ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } void setup() { OCR0A = 1; OCR2A = 249; OCR2B = 125; OCR1A = 32767; TCCR0A = _BV(WGM00) | _BV(WGM01) | _BV(COM0A0); TCCR0B = _BV(WGM02) | _BV(CS02) | _BV(CS01); TCCR2A = _BV(COM2B1) | _BV(COM2B0) | _BV(WGM21) | _BV(WGM20); TCCR2B = _BV(WGM22) | _BV(CS22) | _BV(CS21); TCCR1A = _BV(WGM10) | _BV(WGM11) | _BV(COM1A0); TCCR1B =_BV(WGM12) | _BV(WGM13) | _BV(CS12) | _BV(CS11); for(i=2; i<4; i++) pinMode(i, OUTPUT); for(i=6; i<19; i++) pinMode(i, OUTPUT); Serial.begin(115200); } void loop() { int freq, latest; float ffreq; while(digitalRead(6)){} while(!digitalRead(6)){} OCR1A = 32767; TCNT1 = 0; while(digitalRead(6)){} latest = 10000 - (int)TCNT1; if(latest > 1360) latest = 1370; for(i=4; i>0; i--) pitch[i] = pitch[i-1]; pitch[0] = latest; freq = 0; for (i=0; i<5; i++) freq = freq + pitch[i]; freq = freq / 5; if(freq < 510) ffreq = 0.; else if(freq > 1370) ffreq = 4000.; else ffreq = mapfloat((float)freq, 510., 1370., 64., 4096.); Serial.print("ffreq = "); Serial.println(ffreq); if(ffreq < 64.0){ freq = 0; } else if(ffreq < 128.0){ freq = (int)(ffreq * 8.0) - 512; } else if(ffreq < 256.0){ freq = 0b001000000000 | ((int)(ffreq * 4.0) - 512); } else if(ffreq < 512.0){ freq = 0b010000000000 | ((int)(ffreq * 2.0) - 512); } else if(ffreq < 1024.0){ freq = 0b011000000000 | ((int) ffreq - 512); } else if(ffreq < 2048.0){ freq = 0b100000000000 | ((int)(ffreq / 2.0) - 512); } else if(ffreq < 4096.0){ freq = 0b101000000000 | ((int)(ffreq / 4.0) - 512); } else if(ffreq < 8192.0){ freq = 0b110000000000 | ((int)(ffreq / 8.0) - 512); } else { freq = 0b111000000000 | ((int)(ffreq / 16.0) - 512); } digitalWrite(2, (freq & 0b100000000000) >> 11); digitalWrite(7, (freq & 0b010000000000) >> 10); digitalWrite(8, (freq & 0b001000000000) >> 9); digitalWrite(18, (freq & 0b000100000000) >> 8); digitalWrite(10, (freq & 0b000010000000) >> 7); digitalWrite(11, (freq & 0b000001000000) >> 6); digitalWrite(12, (freq & 0b000000100000) >> 5); digitalWrite(13, (freq & 0b000000010000) >> 4); digitalWrite(14, (freq & 0b000000001000) >> 3); digitalWrite(15, (freq & 0b000000000100) >> 2); digitalWrite(16, (freq & 0b000000000010) >> 1); digitalWrite(17, (freq & 0b000000000001) ); }以下は音源担当 : Arduino Nano[2]のスケッチ「test112.ino」をこのフォーマットに改訂しつつ、デチューンのフランジャー効果でなくてオクターブ違いに重ねたものである。#include <MozziGuts.h> #include <Oscil.h> #include <tables/sin2048_int8.h> Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> aSin(SIN2048_DATA); #define CONTROL_RATE 128 int i; void setup() { for(i=2; i<4; i++) pinMode(i, INPUT); for(i=7; i<9; i++) pinMode(i, INPUT); for(i=10; i<18; i++) pinMode(i, INPUT); startMozzi(CONTROL_RATE); aSin.setFreq(440); } void updateControl(){ float man, pitch; int pw, sum = 0; sum = sum | (digitalRead(2) << 11); sum = sum | (digitalRead(7) << 10); sum = sum | (digitalRead(8) << 9); sum = sum | (digitalRead(3) << 8); sum = sum | (digitalRead(10) << 7); sum = sum | (digitalRead(11) << 6); sum = sum | (digitalRead(12) << 5); sum = sum | (digitalRead(13) << 4); sum = sum | (digitalRead(14) << 3); sum = sum | (digitalRead(15) << 2); sum = sum | (digitalRead(16) << 1); sum = sum | digitalRead(17); pw = (sum >> 9) + 7; man = ((float) (sum & 0b111111111 | 0b1000000000)) / 1024.; pitch = ((float) (1 << pw)) * man; aSin.setFreq(pitch); } AudioOutput_t updateAudio(){ return MonoOutput::from8Bit(aSin.next()); } void loop(){ audioHook(); }#include <MozziGuts.h> #include <Phasor.h> Phasor <AUDIO_RATE> aPhasor1; Phasor <AUDIO_RATE> aPhasor2; #define CONTROL_RATE 128 int i; void setup() { for(i=2; i<4; i++) pinMode(i, INPUT); for(i=7; i<9; i++) pinMode(i, INPUT); for(i=10; i<18; i++) pinMode(i, INPUT); startMozzi(CONTROL_RATE); } void updateControl(){ float man, pitch; int pw, sum = 0; sum = sum | (digitalRead(2) << 11); sum = sum | (digitalRead(7) << 10); sum = sum | (digitalRead(8) << 9); sum = sum | (digitalRead(3) << 8); sum = sum | (digitalRead(10) << 7); sum = sum | (digitalRead(11) << 6); sum = sum | (digitalRead(12) << 5); sum = sum | (digitalRead(13) << 4); sum = sum | (digitalRead(14) << 3); sum = sum | (digitalRead(15) << 2); sum = sum | (digitalRead(16) << 1); sum = sum | digitalRead(17); pw = (sum >> 9) + 7; man = ((float) (sum & 0b111111111 | 0b1000000000)) / 1024.; pitch = ((float) (1 << pw)) * man / 2.; aPhasor1.setFreq(pitch); aPhasor2.setFreq(pitch * 0.5f); } AudioOutput_t updateAudio(){ char asig1 = (char)(aPhasor1.next()>>24); char asig2 = (char)(aPhasor2.next()>>24); return ((int)asig1-asig2)/2; } void loop(){ audioHook(); }
![]()
YouTube動画 ではまず、前半では既に書き込んだスケッチ「test114.ino」で鳴らして、後半ではUSBをモバイルバッテリーからパソコンに繋ぎ替えて、コンパイル→アップロードして音源を切り替えるという「荒技」をやってみた。 USB接続とか周囲の電磁環境が変わるとアンテナ特性が変化してしまうものの、まぁ、とりあえず去年からの懸案はなんとか乗り越えられた感じで、いよいよ明日からはお仕事も始まるが、まずまずの新年となった。
2025年1月6日(月)
朝には開院の30分前から待って、いつもの耳鼻科通院(鼓膜アトピー処置+薬の処方+SAS予後報告)にトップバッターで行った。 先月末に日赤で経過診断を受けた際に、CPAP装置からの圧力が口から漏れて目覚める点を相談していたのだが、結論から言えば「口が開かないようにする」という手立てが決定的に必要なのだった。 それ以来の年末年始では、Amazonで仕入れた「唇止めテープ」と「3点式顎サポーター」をテイジン(CPAP提供)からの2点式顎サポーターに加えてガシガシに頭部を包囲することで、確かに日赤の院長が言うように「高圧で目覚めるのではない」(高圧の状態で無呼吸を避けて寝ている)という事を理解できた。 これを続けて、今月末には再び日赤での検査入院で、自宅の[装置+装備]一式を装着して生体計測すると、翌月の結果診断を受けて、この耳鼻科に対応が移管する(元々ここから紹介状が日赤に行ったので戻ってくる)ことになる。今日は晩には某バイト[1]に出掛けて、いよいよ目前になってきた受験生を応援するのだが、それにも増して、今日という日は「Arduinoテルミン」プロジェクトにも大きな進展となる節目のようである。 竹内さんからは、白木のマトリューシカが届くことになり、さらに僕からの提案で、このプロジェクトに照岡さんを巻き込むことになった。 これはもう「鬼に金棒」というやつで、アナログ要素が非常に大きいテルミンでは、またまた照岡さんパワーが炸裂することになるだろう。 もちろん僕も、こんな面白い実験(それも手元で超ローコストに実験試作が可能)は滅多にないので、ところどころで学会発表のネタも探りつつ、ますます頑張っていくことになりそうだ。
そして、近日中に竹内さんから実験用筐体(白木のマトリューシカ)が届くということで、昨日のArduinoスケッチのちょっとした表記バグ改訂やゼロ値処理を加えたスケッチを、新たに上のように動作確認・整備した。 今後はこれをスタートラインとして進めていくことになる。
- Arduino Nano[1]のスケッチ「test021.ino」 --- ピッチ検出template、5段移動平均、浮動小数点出力
- Arduino Nano[2]のスケッチ「test121.ino」 --- 浮動小数点入力 → サイン波(ゼロ処理)
- Arduino Nano[2]のスケッチ「test122.ino」 --- 浮動小数点入力 → デチューン矩形波(ゼロ処理)
さらに今朝、フト目覚めた瞬間に思い付いたのが、この実験中のテクニックを駆使した、ちょっと面白そうな「新楽器」のアイデアである。 これはもう、インパクト勝負ということで、ほぼ技術的には最終的な結論まで「見透かして」あり、あとはその存在感を際立たせる外見上の作戦まであれこれ浮かんできた。 何か、ライヴの機会がやってきたら、まずはこれを制作していくことにした。
![]()
日本技術士会のページに行ってみると、上のように会報「技術士」に寄稿した原稿(なんとカラー版)だけでなく1月号全部が、会員でなくてもゲット出来るようになっていた。 ただし、おそらく オリジナル はいずれ消えてしまうだろうから、とりあえず ローカル にも置いてみた。 こういうのを永続的に公開していた方が「社会に貢献」したことになると思うのだが、まぁいろいろ大人の事情で続かないのだろう。
そして去年あたりから、何故か秋月電子に行けなかった謎も解決した。 例のやつで、僕の使っているFirefoxのバージョンがだいぶ古いので、秋月電子のサイトから弾かれていたのだが、何とかOSのバージョンとしてはまだOKらしく、Safariでアクセスしたら無事に繋がった。 そこで、色々とおもちゃを作ってきた某バイト[2]の関係で、ロボット制御のマイコンに挿すプラグを補充しようか・・・と「オーダー」して、代金をJapanNet銀行(名前がPayPay銀行に変わったらしい)サイトから「振り込み予約」した。
ところがその後、秋月電子の「オーダー履歴」を見て、どうも過去に注文したやつと間違えた・・・と判明して、泣く泣く(間違い品は受け取って捨てる)、正しいものをさらにオーダーして、この代金も再びJapanNet銀行に行って「振り込み予約」した。 そしてさらに両サイトを良く眺めてみると、どうも秋月電子は銀行振込の場合には入金確認後に発送手続きに取り掛かる(入金が無ければキャンセルと見做す)とあり、一方でJapanNet銀行の振り込み予約(明日の朝9時に処理)には「キャンセル」手続き用のボタンがあった。 結局、間違った方の振込予約はキャンセルして、秋月電子には駄目モトでその顛末(送金された方だけ発送してね)を「注文に関するお問い合わせ」フォームからメイルしてみた。 これで無事に無駄な誤注文キャンセルが処理されたとすれば、地球にも僕の懐にも優しく、万々歳である。2025年1月7日(火)
![]()
自分が この記録 に載せていた「第18回例会」で、竹内さんが「テルミン氏生誕100年を迎えて~ロシアに於けるテレミンの昨日」という発表と共にデモ演奏していた・・・という指摘を受けて発掘してみれば、なんと僕も同じこの研究会で発表していた。 しかし当時は、おそらくオンドマルトノのコンサートやイメージラボでやっていた「サイバー尺八」や大阪芸大見学ツアーなどの印象の陰に隠れていたためか、僕にとって竹内さんは「どこかでお会いしていたような・・・」という存在でした、という、とても失礼な失態をしでかしていたのだった(^_^;)。 まぁ、改めて正式にお会いしてお話して、新しいプロジェクトがスタートしたので、全ては結果良しとしよう。
そんな今朝も、まずまず快適なCPAPによる熟睡からの起床の瞬間に、またまたアイデアが浮かんできた。 こういうのはメモしておかないと忘れてしまうので、ネタバレのようだが、どうせ誰もやらないと半確信しているので、上のように(後日の追記の余白まで用意して)書いてしまおう。 分かる人には分かるが謎めいて書いているのはもちろんのことである。
- ピッチ検出Arduino[1]の浮動小数点ディジタル出力ラインを74HC245でバッファリングして、多数のMozzi音源Arduino[2]群に出す(完全同期追従協奏)
→ 個々に生SP出力、全体を縄文杉のようにレジンで固めた「大樹」の造形へ- ピッチ検出Arduino[1]のシリアルモニタ出力(115200) → Maxで受けて音源/エフェクトはMax化(敢えて愚策)
- ピッチ検出Arduino[1]を2個用いて、片方はピッチ、片方は音量に使う (本物テルミン風味)
- ペンタトニックだけでなく各種スケールにしたら面白いのか検討
![]()
午後には上のように、日本音楽即興学会・第16回大会(2025年3月・神戸大)への発表申込みGoogleフォームの2ページ目を開いたまま、深く深く考察と作文の時間となった。 なんせ、たしかJASMIMの場合には、ここで書いた「要旨」がそのまま当日の予稿に載るというシステムだったので、如何にここで学会の「形式的採択プロセス」を通過するために「キャッチーなテーマ」かつ「中身がある体」で記述するか、というのが最大の勝負なのだ。 実験をちょっと始めたといっても、まだまだ当面のArduinoテルミンは姿もなく(今日の夕方にようやく筐体となる白木のマトリューシカが届く予定)、あるいは竹内さんとのコラボレーションもまだ明文化されておらず、さらに水面下で照岡さんが参加したというのもマル秘にしておきたい。 しかし、これまで幾度となく応募の時点で実体が伴わないまま発表応募して、当日までに実現してきた修羅場の経験をここで生かさない手はないので、ここは(1)竹内さんテルミンPJと、(2)バイオフィードバック・リハビリテーションと、(3)ウェルネス・エンタテインメントと、(4)NIME的新楽器/生体計測ネタと、のいずれにも「接触せずに静電誘導だけ」するような記述を目指して苦闘した。
関連して、これまで一度も見たことのなかったWikipediaの テルミン を読んでみると、なかなかの物語だったが、いくつか発表申込のためのヒントも新たに発見できた。 また、Moog社の Theremini(マニュアル)というのは、エフェクタまで内蔵した「テルミン風インターフェースを持つシンセ」だったのだが、やはり デモ動画 を見ると、ペンタトニックに限らず「スケール」を割り当てると、どうしても「いかにも」感が表面化してきて、これはちょっと残念な感じだった。 別なところの デモ動画 のお姉ちゃんはピアノ出身なのか、ちょっと音感(の精度)が悪いことに気付いていないのが、これも残念だった。 要するに、既に100年の歴史を超えたテルミンというのは、あまりに容易な発音原理と操作原理のために、逆にその古典カリスマ的なイメージに縛られているようで、これはクラシックとかポップス/ロックという「音楽」そのものを超えていかないと、ちょっと新しい可能性というのは難題なのだろう。 突破口はもしかして、案外「リハビリ」なのかもしれない。
2025年1月8日(水)
今年も卒業生の知世ちゃんからオリジナルのカレンダーが届いた。 これはずっと、SUACの僕の研究室で毎年、飾られていたものであり、現在では僕の自室で輝いている。 知世ちゃんは、 僕のサイト で「山村知世」と検索すれば厖大なリンクが並んでいるように、現在も焼津で、主として静岡県で活躍するイラストレータなのだ。 さっそく以下のようにスキャンしてみたが、今年は初めてフォントで「セリフ」が入っていたり、表紙には構想時の手描き絵コンテ風が並んでいたり、ちょっと新しい境地に挑戦している印象がある。 これからも活躍していって欲しい。
![]()
今日は午前中に内科のかかりつけ医に行って常用薬の処方を受けてから薬局に行く、という散歩があり、午後には今年もJoyJoyヒトカラ6時間という修行のスタートである。 明日にはいよいよArduinoテルミンの新たなスタートとなるが、その一方で、またまた某バイト[2]ロボット教室の生徒たちを喜ばせるようなArduinoゲームの構想が浮かんできたので、こちらにも着手していくことになる。 クロネコヤマトからのメイルによれば、秋月電子に注文した部品も今日のどこかで宅配ボックス(ガスメーター)に届きそうだし、いい感じに新年が始まってきた。
竹内さんからのOKも出たということで、日本音楽即興学会の大会に発表応募した内容は上のようなものであり、ちょっとタイトルが「敢えてダサい」のだが、これは本当に敢えてなのだ。 これまでネーミングで「昭和」だの「平成」だのを付ける、というのは毛頭思いつかなかったのだが、何故か「Reiwa」というのはもしかして後に英語版にしても通用するような言葉の響きがあるのと、「テルミン誕生から1世紀以上」・「まさに温故知新」というあたりが、どうもこのタイトルを思い付いた原動力のようで、かねて学生によく言ってきた「作品の構想時からネーミング/タイトルは重要。おいおいそれに引っ張られてデザインが進むよ」というのを、自分にも適用する感じとなった。令和Thereminプロジェクト 音楽に興味があれば誰でも知っている「テルミン」という楽器が発明されてから1世紀以上が経過した。「触れない楽器」テルミンとは、アンテナと人間の手との距離 (電気的には静電誘導)に応じた連続的なピッチを鳴らす単音楽器であり、Moog社の本格的なものから学研「大人の科学」付録テルミンやおもちゃ的な楽器玩具まで、 多くの「そういう楽器」群を生み出してきた。30年前、御茶ノ水で買ったトランジスタ3石の安価な「イシバシテルミン」は音量コントロールも出来なかったが、現在は その半額で液晶表示付きの数十音色テルミンがAmazonから入手できる。 ロシアに渡って直系の演奏法を学んだテルミン奏者の竹内正実氏は、演奏活動だけでなくマトリューシカにテルミンを仕込んだ「マトリュミン」をアンサンブル楽器として 普及させ、289人の合奏というギネス記録を打ち立てた(2019)。竹内氏はコンサート出演中に脳出血を発症して後遺症で右半身に麻痺が残ったが、自身がリハビリを 続けながら左右の手の役割を入れ替えた「左利き」テルミン奏者として再起、マトリョミン演奏を脳卒中リハビリや認知症予防に活用する活動も進めている。 これまで多くの新楽器・生体計測インターフェースを開発してきた筆者は、竹内氏との新しいコラボレーションとして、この令和の時代にあらためて「テルミン温故知新」を 目指すプロジェクトを進めている。本発表では、大きく4つの方針 : (1)竹内氏が目指す新・マトリュミンの実験・試作開発への協力、(2)バイオフィードバック・ リハビリテーションの道具として触覚/触感センサを活用してきた発展から「触らない」意義の再発掘、(3)ウェルネス・エンタテインメントとして人間の即興性を解放する 音楽療法的ツールとしてのテルミン、(4)テルミン的な「新楽器」のための新しい音楽コンセプトの探求、について報告・議論したい。2025年1月9日(木)
昨日のJoyJoyヒトカラでは6時間で59曲を完走し、CPAPによって体調が良好であることを実感できた。
IAMASの小林茂さんから去年の暮れ(12/28)にMACS-MLに届いていたのは、「2025年5月27日から30日まで、大阪国際会議場+オンラインで2025年度 人工知能学会全国大会(第39回)が開催されます。この機会に、徳井直生さん(Qosmo・Neutone)と共同でオーガナイザーを担当するセッション『人工知能と創造性 ― 人の模倣を超えて』を提案いたしました」というもので、「昨今のAGI(汎用人工知能)に関する言説を読むにつれ、人間という生物の知的な能力をintelligenceという尺度のみで測ろうとすることの危険性にはあらためて自覚的であるべきだと思えてきます。だからこそ、人工知能と人間の協働により、コンセプトそのものを拡張し、新たなジャンルを生み出すような『変革的創造性』を実現するにはどうすればいいかという議論の重要性は、セッション提案時からさらに増していると感じております」・「芸術、工学、哲学などの領域を越境的に活動する研究者・実践者の参加により議論したいと考えておりますので、該当する活動に取り組んでいる方は、ぜひご応募ください」という素晴らしい企画だった。 流石に即応できない重要テーマなのでGmailの「保管倉庫」に入れて温めていたのだが、(やや言い訳がましいが)もろもろの事情から、無理矢理にこじつけた内容での無理矢理な発表応募については断念バーグする、と決めた。 ただし、もしかするとオンライン聴講するかもしれない。
![]()
竹内テルミンPJについては、遂に照岡さんも参加して、関連情報を3人同報メイルで共有する形がスタートした。 竹内さんが当面求めている「新マトリュミン」については試作開発が進んできたモデルというのがあり、マル秘の回路図も提供いただいたが、僕が実験していくものはこれとは別に進む方向性となった。
そして、久しぶりに本格的にテルミンについて考えてみる機会を得たためか、関連したあれこれの思いが去来し、CPAPと共に夢と現の狭間で沈思黙考する日々となっている。 かつて音楽情報科学研究会の「夜のセッション」で議論していた、「誰でも簡単に演奏できる楽器 vs 修練を積まないと演奏できない楽器」論争とか、リハビリや音楽療法における「目標」や満足度(ウェルネス)の意味とか、「協奏」と「即興」の関係(テルミンの長所である「曖昧さ」はこの点では諸刃の刃でもある)とか、テルミン最大の弱点である「無音」・「間」の音楽的な意義とか、かなり本質的な課題を改めて色々と考えることになり、これはしばらく続きそうな予感がしている。
![]()
『Lenovoは1月7日、ボタン1つで画面が上に伸びるビジネスノートPC「ThinkBook Plus Gen 6 Rollable」を「CES 2025」で発表した。「世界初のローラブルディスプレイ搭載AI PC」という』というニュースも届いたが、いやいやこれは、変態的で凄い。 専用キーを押すか、カメラに向けてハンドジェスチャーをすると、14型のディスプレイが縦方向に伸び、最大16.7型になるとのことで、シート状のディスプレイがキーボードの下に巻き込まれていて、それが出てくるようだが、喜んで「伸びーる」「縮ーむ」を繰り返していたら、壊れないのか心配になってしまう。 目の付けどころがシャープなこういうアイデアは日本から出てきて欲しかったが、まぁ落ちぶれた現在の日本メーカではもう無理なのだろうか。
![]()
そして午前中にホームセンターに行って「0.5mm銅板」を仕入れて、ハサミでチョキチョキと切って曲げたのをマトリューシカ上半身の上の方に(押し込んだだけで)固定し、マトリューシカ下半身の底部にはちょうどスピーカの大きさがぴったりだったのでドリルで穴あけして、銅板はワニグチで挟んだだけ、ダブルArduino基板も空中になんとなく固定されているだけ・・・という最初の「試作」が完成してしまった。 これまで3次元加速度センサ(重力の3次元成分を検出)でのテルミン的デモでは、センサの載ったArduino基板が水平からマイナス90度~プラス90度までの大きな変化(上に上げたらピッチが高くなる)があったのに対して、たった10分ほど練習した YouTube動画 (近付けるとピッチが上がるというのはちょっと体感的に不慣れ)では、どうも調子が出なかった。 クラシックの有名な音楽が伴奏なのでYouTubeに叱られるかと思ったら、あまりに演奏が下手くそなので著作権によるストップも無かったのだが、おそらくクラシックのこの曲は著作権切れということなのかもしれない。 まぁ、とにかく最初の実験試作はこんな感じであり、「まだまだ改良の余地あり」というか、「これでは駄目」というのがはっきりしたのが収穫である。
2025年1月10日(金)
今日は夕方から某バイト[2]に出かけるだけの予定であり、世間は連休と言うが関係ない僕の予定としては、日曜日には今月から某バイト[2]の葵町教室が、これまでの午後のアドバンスに加えて午前のミドルも増えたり、月曜日には祝日も関係ない某バイト[1]で晩に出掛けるぐらいである。 この空いた時間には、1ステップ進んだArduinoテルミンの実験はちょっと棚上げして、何日か前に思い付いていた新しいArduinoシステム(まぁ一種のゲーム)でも作ってみたい。
![]()
本家MakerのMLからは、最近の色々なMakerたちの活動が紹介されたが、その冒頭にあったのは、上のようなGeorgia Tech College of DesignのRaghavasimhan Sankaranarayanan氏の「インド音楽を演奏するバイオリンロボット」だった。 YouTube動画 を見てみると、バイオリン演奏の「命」とも言えるビブラートは皆無で、さらにスタカートやアタックといったバイオリンの大きな魅力も全て捨象した上で、インド音楽のサーランギのようなサウンドに特化したものだった。 これはこれで素晴らしい味がある。
ここで思い出したのが、ニューヨークのLEMURである。 16年前の YouTube動画 を発掘したが、彼らはこの楽器ロボットをNIME2004の会場であるSUACに持ち込んで、木村まりさんの作品"GuitarBotana"の素晴らしいバイオリン即興のライヴ伴奏パートとして「伝説の名演」を行ったのだ。 この何度観てもゾクゾクする記録動画は僕の手元にあるのだが、ここに置くこともYouTubeに上げることも出来ず、これまでも講義の中でだけ学生に鑑賞させてきた。 まぁ、伝説は伝説ということで、あの場にいた人だけの伝説ということにしよう。
![]()
![]()
そして昼頃には このように 謎なハードウェアを1時間ほどで作って、あとは午後じゅうかかって、プログラミングに没頭した。 そしてとりあえずのバージョンが完成したところで、某バイト[2]に持参して、「ロボット教室」の生徒たちにウケた。 ウケただけでなく、まだ未完成なのだが、どのようにしたら面白くなるか、を小学生と真剣に議論した。 なんせ小学生というのは、「面白いゲームのプロ」なのだ。 これを受けて、明日からは改良のプログラミングである。
2025年1月11日(土)
昨日の午後、テルミン竹内さんからは「デジタル制御のマトリョミンについてあれこれアイデアをめぐらせています」という熱い長いメイルが届いていたようなのだが、Arduinoプログラミングに没頭していて気付かず、そのまま夕方に某バイト[2]に出掛ける時間になってしまったので、今朝まで放置してしまった。 まぁ、電子メイルというのは、僕が使い始めた1987年頃からの変わらない常識として、「およそ24時間以内にリプライすればいいもの」という存在なので、ケータイに届いたメイルに24時間追われる人はそれもいいが、これでもいいのだ。 なので今朝は朝イチでとりあえず以下のようにメイル(照岡さんも同報)した。 自分の書いたものであればここに転載するのもまぁ自由なので、せっかくだから一部を置いておこう。「認知症マトリョミン演奏サロン」の貴重な動画をありがとうございました。 マスクのために体験者のお二人の表情があまりわからないのですが、被験者のウェルネス(達成感・充実感など)を評価するためにこれまで心理学者は主として「事後のアンケート」を行ってきました が、演奏体験の短期記憶が危うい認知症クライアントでは、なかなか直前の動作補助体験の感想、あるいは音楽体験の全体の印象を客観的に回答するのは難しそうです。最近では脳波センシング とかfMRIで脳内活動を計測して、色々な感情での脳内活動データの蓄積とAIでディープラーニングさせれば、「本人が理路整然と語らなくても脳がどうだったか客観的に分かる」みたいな乱暴な 研究も報告されていますね。 今回はテルミンという存在そのものについても色々と考える機会となっていますが、それはあちこち色々なので、おいおい整理して共有したいと思います。 あの動画では二人の体験者が同じメロディーでなくハーモニー、つまり別々のメロディーを奏でているので、特に「知っているメロディーをなぞっている」のでない人の方は、ハーモニーの効果/意味 まで理解/体験するというのは、かなり高度なタスクのような気がしました。伴奏に対して皆んなで同じメロディーをなぞる、という協奏体験とは違うものになっていると思います。 「テルミンのような楽器の側に音の高さの基準がない楽器に取り組む場合、余程の熱意があるか勘所がよくないと、まるで音楽に近づけることは叶わず、失望に終わるだけかも」というのは、昔の 音楽情報科学研究会では、1996年に照岡さんと私が大阪芸大で竹内さんのテルミン演奏に接した頃、あるいはその7-8年前から、ずっと議論されてきたテーマです。「誰でも簡単に弾ける楽器を」 vs 「簡単に弾けないのが楽器の魅力」というやつです。これについてはここでは深入りしませんが、テルミンはまぁ、後者なので、難しさ(やり甲斐)がありますね。 私はサラリーマンの親父がピアノが高価で買えなかったので小5までバイオリンを習っていました。バイオリンはギターのようにフレットが無いので、常に鳴っている音に聞き耳を立てて、自分の中の 音階感、あるいは他者とのアンサンブルを聞くことになるので、音感が育ったことを親父に感謝しています。河合楽器に入社して新入社員研修でピアノの調律を1ヶ月やってみると、数セント以下まで 聞き分けられている(本物の調律師のレベル)と判明しました。後には音律の専門家である東京芸大の白砂先生とも交流し、自分が開発した電子ピアノに「裏モード」として、独学で整理研究した 55種類の音律 (https://nagasm.org/ASL/temper/) を選択できるように密かに仕込んで、実際に製品が世に出ました。(裏モードの入り方は秘密なので、実際にこれを渋谷KAWAIの店頭の 製品で鳴らしたのはたぶん私だけです) そんな私は大学時代に合唱団で指揮をしていました(合唱曲の作曲も独学[本屋に行けば「芸大和声」という教科書が入手可能]ですが、だいぶしました https://nagasm.org/ASL/chorus/ )。 そこで戸惑ったのですが、ピアノ出身者の歌う音感(協和感)がかなり悪いことでした。ピアノという楽器は、その鍵盤を弾けば間違いなくその音が出る・・・という暗黙の依存性があるために、バイオリン 出身の私からみれば驚くほど「ハモり感覚」に鈍感なのでした。アカペラの合唱で、4パートとか6パートとかの合唱で、私が「アルトはあと1枚、高めに」・「バリトンはちょっとだけ落として」などと 聞き分けて指示すると、ピュアなハモりが実現するのは手品のように驚かれていましたが、「指揮者の耳」は私の宝でした。 一方で、白鍵を適当に弾いてもDiatonic Scaleの枠組みで、あるいは黒鍵を適当に弾いてもPentatonic Scaleの枠組みでいずれも不協和音もなく音楽的に破綻していないので、デタラメに 白鍵だけ、あるいは黒鍵だけを弾いていても、人間は楽しいです。これは幼児が遊んでいるケースではっきり見えますし、この背景には人類が遺伝子として獲得したサウンド体験の調和構造があるので、 老若男女、人種性別年齢などを問わず、地球上に共通です。 テルミンとは、「余程の熱意があり」、「勘所がよい」人が取り組むと、非常に反応してくる楽器だと思います。ただしピッチの聞こえ方というのは大人になって後天的に成長するのには限界があり、一方で、 サウンドは聴覚的に聞こえていても、その微妙なピッチは聞こえない人(健常者)もいる、というのはいくつもの合唱団で指揮者をしてきた経験から、知っています。人によっては、手取り介入演奏動作と いうのは、行き先の見えないトレーニング(修行)になってしまうかもしれません。その一方で、可動域を動かした手に反応して、音楽的に気持ちいいスケール(調和感)が鳴ってくれる「知育玩具」 としては、幼児でも高齢者でも楽しめる(ウェルネス)道具となる可能性があると思います。 また、リハビリ/認知症対策とはちょっと違って、「音楽療法」の領域で、つまり既存の曲とか関係なく、自由に即興であのピロピロを鳴らすことで自分の潜在的な抑圧を解放する・・・というアプローチに ついても、日本音楽即興学会の場で学んできているところで、この可能性も考えたいです。 とりあえず、あれこれ書き散らしてしまいましたが、そういう色々を考えております。
![]()
そして午前中には運転手としてクルマを出したりしたのだが、お昼に帰宅してから午後の2時間ほどで、懸案だった仕様も追加してArduinoスケッチが完成し、記録としての YouTube動画 も撮ってみた。 回路図を描くほどのこともないので、接続情報を以下にメモしておくことにする。 LEDテープについては、最初は2本それぞれを操作するという想定で、敢えて両者とも「素数」として、LED[1]は31個、LED[2]は23個のLEDとして、それぞれにプッシュスイッチを付けたのだが、最終的にはスイッチを使うのは[1]の方だけとなった。
そして、 YouTube動画 で動いている(当面はこれにFixする予定)Arduinoのスケッチ「DoubleGame02.ino」は以下である。 いつものように即興的にプログラミングしたので、まぁ1週間もすれば自分でも解読困難となるだろう。
- Arduino UNO
- 電源はUSBにモバイルバッテリを繋ぐ想定
- +5VとGND間に10KΩのボリュームを繋いでその出力電圧はA0へ
- LEDテープ[1][2]の電源は5V、その根元にプッシュスイッチを付けて+5Vに10kΩでプルアップ(SW=OnでLow出力、SW=OffでHigh出力)
- D13 --- LEDテープ[1]のdata
- D12 --- LEDテープ[2]のdata
- D11 --- 圧電ブザーの+端子 (-端子はGND)
- D10 --- プッシュスイッチ[1]
- D9 --- プッシュスイッチ[2] ※ 現状では未使用
ちなみにここでのLEDテープは RRR日記(4) の「2024年11月29日(金)」のところで、以下のようにAmazonから買ったものだった。 わずか40日ほど前のことになるが、同時に2本を駆動するとか、特定ビットだけ光らせてそのビットを移動させるとか、まずまず使いこなせてきたのは嬉しい。 これと別途に、たまたま秋月電子から仕入れてみた「0.96インチ 128×64ドット有機ELディスプレイ(OLED) 白色」というのがあるので、Adafruit社のディスプレイに加えて持ち駒とするために、これもどこかで実験してみたい。#include <FastLED.h> #define LED_PIN1 13 #define LED_PIN2 12 #define NUM_LEDS1 31 #define NUM_LEDS2 23 CRGB leds1[NUM_LEDS1],leds2[NUM_LEDS2]; int i, musicScale[] = {523,587,659,698,784,880,988,1047}; int volume_value, gate_time, sw_old[] = {1,1}, sw_new[] = {0,0}, sw_eve[] = {0,0}; int timing[] = {0,0,0}, points[] = {0,0}, target, vectors; void setup() { for(i=9; i<11; i++) pinMode(i, INPUT); for(i=0; i<9; i++){ tone(11, musicScale[i]); delay(40); noTone(11); delay(60); } gate_time = 10000; vectors = 1; all_black(); target_set(); sw_old[0] = digitalRead(10); delay(200); } void loop(){ volume_test(); if(++timing[2] > 50){ timing[2] = 0; sw_new[0] = digitalRead(10); if(sw_new[0] != sw_old[0]){ sw_old[0] = sw_new[0]; if(sw_new[0] == 0){ melody_again(4); if(points[0] == target){ Led1_set3(); melody_end(); all_black(); target_set(); delay(1500); } else { Led2_set(points[1], 1); points[1]++; if(points[1]>22){ for(i=40; i>10; i--){ tone(11, i*50); delay(50); } delay(300); noTone(11); all_black(); target_set(); delay(2000); } } } } } if(++timing[1] > gate_time){ timing[1] = 0; Led1_set2(points[0], 8, 4); points[0] = points[0] + vectors; if(vectors == 1){ if(points[0] >= NUM_LEDS1){ points[0] = NUM_LEDS1 - 1; vectors = -1; melody_again(0); } } else{ if(points[0] < 0){ points[0] = 0; vectors = 1; melody_again(2); } } } } void target_set(){ target = 1 + (target + random(5,16)) % 28; points[1] = 0; } void volume_test(){ if(++timing[0] > 100){ timing[0] = 0; volume_value = analogRead(A0); gate_time = volume_value * 8 + 700; } } CRGB color_set(int color){ if(color<1 || color>8) return(CRGB::Black); switch(color){ case 1: return(CHSV(96, 255, 127)); // Red case 2: return(CHSV(0, 255, 127)); // Green case 3: return(CHSV(160, 255, 127)); // Blue case 4: return(CRGB::Gray); // White case 5: return(CHSV(60, 255, 127)); // Yellow case 6: return(CHSV(128, 255, 127)); // Pink case 7: return(CHSV(87, 255, 127)); // Ogange case 8: return(CHSV(random8(), 255, 127));// Random } } void Led1_set(int num, int color){ FastLED.addLeds<WS2811, LED_PIN1, RGB>(leds1, NUM_LEDS1); for(i=0; i<NUM_LEDS1; i++) leds1[i] = CRGB::Black; leds1[num] = color_set(color); FastLED.show(); } void Led1_set2(int num, int color1, int color2){ FastLED.addLeds<WS2811, LED_PIN1, RGB>(leds1, NUM_LEDS1); for(i=0; i<NUM_LEDS1; i++) leds1[i] = CRGB::Black; leds1[num] = color_set(color1); leds1[target] = color_set(color2); FastLED.show(); } void Led1_set3(){ FastLED.addLeds<WS2811, LED_PIN1, RGB>(leds1, NUM_LEDS1); for(i=0; i<NUM_LEDS1; i++) leds1[i] = CRGB::Black; for(i=0; i<11; i++) leds1[i*3] = CHSV(random8(), 255, 127); FastLED.show(); } void Led2_set(int num, int color){ FastLED.addLeds<WS2811, LED_PIN2, RGB>(leds2, NUM_LEDS2); for(i=0; i<NUM_LEDS2; i++) leds2[i] = CRGB::Black; for(i=0; i<num+1; i++) leds2[i] = color_set(color); FastLED.show(); } void all_black(){ FastLED.addLeds<WS2811, LED_PIN1, RGB>(leds1, NUM_LEDS1); for(i=0; i<NUM_LEDS1; i++) leds1[i] = CRGB::Black; FastLED.show(); FastLED.addLeds<WS2811, LED_PIN2, RGB>(leds2, NUM_LEDS2); for(i=0; i<NUM_LEDS2; i++) leds2[i] = CRGB::Black; // leds2[0] = color_set(1); FastLED.show(); } void melody_end(){ tone(11, musicScale[0]); delay(120); tone(11, musicScale[2]); delay(120); tone(11, musicScale[4]); delay(120); tone(11, musicScale[7]); delay(400); noTone(11); } void melody_again(int pitch){ tone(11, musicScale[pitch]); delay(30); noTone(11); }
![]()
そして午後になって、竹内さんから借用するME04型マトリョミン(白木筐体)が届いた。 これについてはおいおいレポートしてみるが、さすがにしっかりとした操作性で、これぞ拘りの「楽器」であった。 筐体の上半身を外してアンテナを剥き出しにすると、まるで動作が変化したのには驚いた。 木製の筐体上半身の部分には、もしかして内側にぐるりと金属箔でも貼られているのでは・・・という予想は完全に外れたので、そのあたりから調べる必要がある。 竹内さんにいただいた白木マトリューシカ筐体は高さ16cmほどだったが、こちらの借用ME04型マトリョミンは高さ21cmでだいぶ太いので、このあたりも大きく影響しそうである。 明日は午前午後と某バイト[2]があり、明後日の祝日は晩の某バイト[1]の予定に加えて、午前にはどうやら今年も「孫とイチゴ狩り」という予定が加わりそうなので、いずれにしても来週なかば以降となる。
2025年1月13日(月)
昨日はネットから「ブラウザ上ではダブルクリックするな」という この記事 が届いたが、これはなかなか怖い情報で、ましてや僕のように敢えて古いバージョンのOSと古いバージョンのブラウザを使っている者には恐怖とも言える。 とりあえず「ブラウザ上ではダブルクリックしない」と自分に言い聞かせてネットに向かうことにしよう。
そんな昨日は午前も午後も某バイト[2]に行き、特にすっかりお馴染みになってきた午後のアドバンス教室の連中には、数日前に作ったばかりの新作で遊んでもらったが、「ひたすら高速連打」というズルい技に対しての対策必要性を感じたので、今日はさらにArduinoのスケッチを改訂した。 今度は、シューティングLEDの移動スピード(さりげに最高速度はさらに高速にした)に対応して、1回のミスで増えていく赤色LEDの「増加数」を増やすことにした。 「増加数」は低速では5、そして最高速では2、ということで、23個もあってちょっと冗長だった「ミス記録」用の赤色LEDはスグにオーバーフローして「失敗」するようになった。 この改訂(所要15分もかからなかった)は、今週金曜の教室の連中にお披露目してみよう。
さらに今朝の目覚め瞬間あたりで、またまた新しい「Arduinoでゲーム」のアイデアを思い付いたのだが、システムの概要(使用するセンサとか結果表示手法)は思い付いたものの、肝心の実現形態/態様についてはまだ白紙となっている。 これはちょっと考える必要があるので、しばらく悶々とCPAP熟睡の合間(夜中に2-3回は目覚めてアダプタ/装具を装着し直すので、その後の寝入り端など)に引き続き考えていくことにした。2025年1月14日(火)
この日は運転手で午前中に義母の施設や市役所やスーパーに行って終わり、夕方には臨時依頼(代打講師)で某バイト[1]に行くことになったので、午後の欠片の時間は昨日の高校サッカー決勝PK戦(放送中に時間切れで一番いいところが放送されず)をtverで見たり、藤井王将の第1局の逆転の様子を棋譜から追いかけたりして過ごした。
その今日の朝イチに出掛ける前には、竹内テルミン改良型が直面している課題(のヒントとなる視点)について、ちょっと寝ている間に思い付いたことがあったのでメイルしてから出掛けたのだが、どうやら照岡さんコメントでもこの見立て(仮説)は有効のようで、改良型に取り組む技術者にも連絡されたようである。 この見立て(手法)は、僕の「ダブルArduinoテルミン」(現在のところ頭打ちの問題点があってストップ中)に対しては、プラスに役立つこととちょっと生きないことの両方があるのだが、その実験についてはまだ先になりそうだ。2025年1月15日(水)
このところ連日、断片的な外出の予定が午前や午後に入ってまとまった時間が取れず、SUAC時代に比べれば屁でもないのだがそこはかとなく「忙しいかも」なんて思っているが、今日は午後に週一の楽しみ(修行)のJoyJoyヒトカラ6時間の予定があるだけなので、ちょっと「書く」時間もある。
この日記の「2025年1月11日(土)」のところに、竹内テルミンPJに関して僕が書いたメイルを一部転載していたが、その翌日にも以下のようにメイルしていたので、ここに置いておくことにした。 なんせこの「日記」は、ブログと違って誰も何も突っ込めないので誰も読んでいないだろう・・・と思っていたら、ごく少数、マニアな人が読んでくれているので、役立ちそうな情報は一種の終活として遺しておきたいのだ。ここで再び思い出したのが、僕が開発したKAWAI初代の電子ピアノ「PT200」・「PM100」である。 辿ってみると1年1ヶ月ほど前、 Sketching日記(20)(SUACでラストの日記) の「2023年12月21日(木)」のところに書いていたのを発掘した。 せっかくなので、その部分のHTMLソースをここにコピペ追記再録し、さらに今回はちょっと「禁断の領域」まで捕捉してみることにした。「テルミンを能動的に弾く中でフィードバックがあり、意図した音に近づいた場合、自らの行為が『報酬』に繋がる」というメカニズムは、楽器演奏の上達に関して、共通のものでしょう。 一方、テルミンは自分でピッチを作らないといけないので、「鳴っている音を聞き、それに対応して補正する」というのが主要なお仕事になります。 ここには「音痴」という問題があります。カラオケが苦手な人が「私は音痴なので・・・」という場合、その95%はトレーニングによって補正、成長して、まぁ普通の音感となります。ただし一部には 医学的な意味の本物の音痴というのもいて、これは「聴覚→発声→それを聞く」という脳内コネクションの一部にある障害です。僕が合唱団の指揮者として、新入生一人一人に声を出してもらって パート分けをするのですが、過去に一人、いました。本人は歌は好きなのですが、「(何も無しで)ドレミファソラシド」と歌って、とお願いすると、「ドレミファソラシド」とだんだん上がっていくのですが、 最初のドと最後のドで5度ぐらいしかない(^_^;)という凄い音感でした。彼はしばし合唱団にいましたが、本人もどうも違うようだ・・・と納得したのか、早期に退団しました。その方が彼のためかもです。 このような医学的音痴を除く大部分の人はまぁちょっとズレてるなぁ(これは合唱指導者から言えば姿勢・呼吸・発声。発音などによって補正成長可能)、という人も、例えばカラオケの伴奏に合わせて、 自分の知っている曲であればまずまず歌えます。これは成長に伴って獲得した音感です。 さてテルミンですが、おそらくこの楽器に魅力を感じて深入りする人は、もう少し上のレベルなのだと思います。私の合唱仲間でも、普通にハモって気持ちいい、というレベルでなく、おぉぉ倍音が 聞こえたのでユニゾンもハモりも純正になっている・・・などという喜びの境地を共有する仲間がいます。以下は2000年に私の大学のイベントにそういう仲間を呼んで普段は一緒に歌ったことがない のに練習時間15分ほどでぶっつけ本番したものです。まぁショボいですが(^_^;)。 https://www.youtube.com/watch?v=3nTeDW1jg2Y , https://www.youtube.com/watch?v=ZR16tOnAHaI 上は6人で4声、下は6人で6声です。ほぼ同時にハモるホモフォニーでなく、それぞれが別に動くポリフォニーにハマる合唱人というのはそのオタク度からすればテルミンと並ぶかもしれません。 伴奏は例えばピアノであれば12等分平均律なので、いきなり「平均律vs純正律」の問題が発生します。バイオリンでもフルートでも、単独で無伴奏のメロディーを演奏する場合には、ピタゴラス律 ないし純正律に従った音階に補正している(上級者の場合)、というのは昔から音楽心理学では定説です。 「無伴奏でテルミン単独演奏」・「伴奏にテルミン独奏」・「無伴奏でテルミンのユニゾン合奏」・「伴奏にテルミンユニゾン合奏」・「テルミンで複数パート(ハモり)合奏(1パートは一人)」・「テルミンで 複数パート(ハモり)合奏(それぞれのパートは複数人)」の6パターンはそれぞれ、求められるもの、やり甲斐、難易度、音楽的な意味、それぞれすべて異なっている、というのが私の持論です。
![]()
電子ピアノのPT200/PM100をネットで検索すると、 取扱説明書 がKAWAIのサーバからゲット出来た。 さらに このページ (バックアップは これ )の中にある「河合楽器」というタームを検索すると、上のような懐かしい写真が出てきた。 「裏モード」の55種類はもちろん記述されていないが、以下のように「表モード」として6種類の音律が選択できるという仕様だった(ただし音律(1)と音律(5)は同じもののようなので、どうやら取扱説明書を制作したチームのミスと思われる)。
![]()
そして、もう売り切れだとは思うが こんなページ もあって、断捨離中なので購入しないものの(大阪からの送料が10万円ぐらいかかる)、下のような綺麗な写真をゲット出来てしまった。 なお、ページが文字化けした場合にはText Encodingを「Unicode」にすれば見える。
![]()
![]()
![]()
発売されてもう40年になるので、おそらく「動くPT200/PM100」はもうこの世に無いだろう・・・ということで、ここからが、これまで書いたことのなかった禁断の領域である(^_^;)。 かなり控えめに書いた 作るサウンドエレクトロニクス の「Part4 ハイブリッドシンセサイザ」の延長線上の世界であり、こちらも併せて読んでもらえると、少しは理解できるかもしれない。 以下の写真は、そのどこかで登場するあるパーツの、表装モールドの無い試作品である。
![]()
それまでの電子ピアノというのは、「トップオクターブ方式」音源で全ての鍵盤の原信号が常時供給されていて、エンベロープ(高速アタック→低速ディケイ→鍵盤OFFでリリース)付加回路が簡単なトランジスタ1石程度で鍵盤ごとにズラリと並び、強弱の「鍵盤タッチ量」には対応していないという「擬似ピアノ」だった。 「タッチレスポンス」というキーワードが何と言っても電子ピアノ開発の目標であり、KAWAIとして初めて「タッチレスポンスのある本当の電子ピアノ」の開発が、入社4年目の僕の最初の大きな仕事となった。 以下、★印は僕がKAWAIで「初めてやった」ことである。
PT200(88鍵)とPM100(76鍵)は、音域以外の電子回路/マイコンソフトウェアの部分は同一であり、違いは「鍵盤のアクション」にある。 廉価版のPM100はシンセサイザーと同様のプラスチック鍵盤であり、シンセでは単純な接点でON/OFFなのに対して、鍵盤押し下げに対応して2段階で次々にオンになる構造の導電ゴムによる時間差検出接点を素材チームが新たに開発して、鍵盤下の基板に並べた。 PT200では「鍵盤のタッチ感」に拘る人々が、なんと木製鍵盤の1鍵1鍵にいちいち、弦を打たないハンマーを突き上げる本格的な(擬似)アクション機構を「指先への物理的反応」のためだけに設置した( → なので弾くと内部でコツンコツンと音がした)。 そのためにPM100はシンセのようにぺったらなのに対して、PT200では空中で後方のアクションが動く空間のために、鍵盤の奥が異様に高いフォルムとなっている。
僕はまず各社の特許を迂回するデータ処理方式を考案して特許を出願しつつ、富●通のゲートアレイで「128鍵の2段接点のそれぞれがONになる時間差を検出して、マイコンの周辺チップとして任意のタイミングでそれらを読み出せる」というLSI★を設計開発した。 検出精度はマイクロ秒オーダであり、ピアノの演奏動作を思い浮かべると分かるように、最高速の打鍵では1msecオーダなのに、そーーっと弾くと数秒オーダになる、という時間差の数千倍のダイナミックレンジをMIDIベロシティの127ステップに収めるため、指数関数的データ処理を含む時間差検出システムをマイコンのソフトウェアでなく、ハードウェアとしてチップ上に実装した(特許対策)。
音量だけでなく、ピアノという楽器の音色は時間的に変化する必要があり、さらに打鍵ベロシティごとにも音量と音色が変化する。 これは、鍵盤ON→「原波形を作る」→「時間的音色/音量変化」→鍵盤OFF→「時間的音色/音量変化」という古典的なシンセサイザのスタイルが発音数だけ必要になるということで、「同時発音数」の制限が生じて、「12音ポリフォニック」楽器となった。 ピッチ生成の部分では、12個の周波数可変クロックが必要となるが、ちょうどIntelの 8253 という3個のカウンタ/タイマを内蔵したCPU周辺チップがあったので、このCMOS版の71054を4個ずらりと並べて計12チャンネルとした。 ちなみにMIDIのシリアル通信のために、同じファミリの 8251 というUSARTのCMOS版の71051も、(たぶん)使用した。
「12音ポリフォニック」(同時に12音しか鳴らない)ということで、ここには「アサイナ」(発音割り当て)という技術が必須となる。 鍵盤を次々にONにしていって12音を超えた場合にどうするか・・・という方式(13音目は無視、古いイベントから消音する、最低音の1音だけは残す、中音域の目立たない音を消す、音量の減衰を全て追跡して一番小さくなった音を消す、などなど)には各社それぞれの特許があってなかなか悩ましい。 ピアノの場合にはこれに加えて、ダンパーペダル(これを踏むと押された鍵盤のOFF情報を保留してペダルOFFの際に一斉に作用させる。本物のピアノのように全ての弦のダンパー解放によって共鳴する効果は電子ピアノでは無し)と、ソステヌートペダル(これを踏んでいる時点で押された鍵盤だけOFF情報が保留されペダルOFFの際に作用させる)という難しいアサイナが必要となるが、他社特許を迂回しつつの難題を、初めて使うマイコン★(おそらく 8049 だったと思われるがやや曖昧)で頑張ってプログラミング(当然アセンブラ)した。
同時発音数12チャンネルの音源回路については、当時の技術としては完全ディジタルでもアナログシンセ(単音/2音)でも比較的廉価な電子ピアノのためにはコスト的に見合わなかった。 そこで音色変化については、新しく登場したばかりで気になって実験していたSCF(スイッチトキャパシタフィルタ)IC★を時間的変化するLPFにする、というアイデアを初めてこの電子ピアノで実装したが、これは後の世代には継承されなかった。 アサイナと組み合わせて、マイコンが高速にソフトウェア的に各発音チャンネルの時間経過をモニタして、対応するSCFクロックを変化させるという荒技である。 さらに音量変化についても、アナログスイッチ4066等を駆使した充放電回路によってエンベロープを生成して、こちらもマイコンが高速にソフトウェア的に各発音チャンネルの発音後時間経過をモニタして、対応するエンベロープ推移を変化させた。
このような「ディジタル制御・アナログ処理」の音源システムを基板上に12チャンネル回路も並べることはサイズ的(コスト的)に制限があったが、ここで当時、コラボレーションを模索していたデ●ソー社(当時は「日本電●」)が「アナログハイブリッドIC」を製造できる、という話に飛びついた。 自社内に半導体製造ラインやICチップ製造ラインを持っている同社の担当者と打ち合わせして、自動車業界に比べて桁違いに少量なのでビジネスになりにくいものの、「1台あたり12個を使う」という数量効果をアピールして、半ば試行的にPT200/PM100では、音源(SCF音色変化、4066エンベロープ生成)というハイブリッドIC★をデ●ソー社に製造委託して、基板上にずらりと12個並べた。 その後、同社とコラボレーションする製品は出てこなかったが、まぁ楽器業界と自動車業界では「数」が違うのだから仕方ない。
電子ピアノ全体の回路を設計し、タッチレスポンスLSIを新規開発し、音源ハイブリッドICを新規開発し、新規採用のマイコンのプログラムを全て開発した僕(誰かが作った/使ったものの再利用よりも、チャレンジングな新しいものが好き!)はさらに、当時、興味があって調べていた「音律」を盛り込むことにした。 取扱説明書 にあるように、当時までの楽器業界においてちょっと異端なのだが、ピアノと言えば12等分平均律なのに対して、さらに6種類の「音律」を設定★できる仕様とした(部品コストを1円もかけずに機能が向上するという理由で了承された)。 他社もかなり動揺したらしく、その後に各社から出てくる電子楽器には音律が入ったりしてきた。 しかし僕は当時、 音律について にある55種類の音律に関して完全に理解して、さらにそれらを作り出すデータも完備していた。
そこで、プログラムROMエリアの容量が限られたマイコンのプログラムを最適化で圧縮すると共に、この55音律を生成するための基礎データもプログラムROMの片隅に置いて、「秘密の暗号」によって、6種類だけでなくこの55種類の音律を全て選択して設定できるようにした。 取扱説明書では「トーンセレクターボタンを6→5→4→3→2→1の順に押してから、6のボタンを押す」というのが音律設定モードの入り方であり、その次に押されたトーンセレクターボタンに対応した6種類の音律が設定できる。 だいたい、電子楽器の「音色設定ボタン」というのは、試しに何かを選んだらまずは鍵盤を弾いてみて音色を確かめるので、「鍵盤を引かずに音色設定ボタンを続けて押す」という行動は、普通には有り得ないので、ここがヒントとなった。
55音律を設定する「裏モード」では、「鍵盤を引かずに音色設定ボタンを、連続して、ある10桁の暗号の順番に押す」というのが入り方であり、この超不自然な操作が成功すると、なんと6個の音色設定ボタンのLEDが、「135のみ点灯」→「246のみ点灯」→・・・というのを1秒間隔で繰り返して、続いての「音律選択」を促しつつ、待機する。 そこで、鍵盤の最低音のAからクロマティック(黒鍵白鍵全て)に始まる番号として1から55までを最初に弾いた鍵盤で、その音律番号が指定され、その後は電源を入れ直すまで、PT200/PM100はその「裏音律」の楽器に変貌するのだ。 これは実際に製品が世の中に出てから、渋谷の道玄坂にあったKAWAIショップの店頭で実際に僕が密かに確認したので、間違いない。
問題はその10桁の暗号なのだが、僕はとてもランダムな10個の数字の羅列は記憶できないので、知っている数列(口ずさめる)を利用することになる。 そして僕が知っている暗唱数列というのは、√2 (=1.41421356) と √3 (=1.7320508) と √5 (=2.2360679) と π (=3.1415926535) と e (=2.718281828459045) の5つしかないのである。 これ以上はここでは書けない。 何故なら、この5種類の暗唱数列のうちあるもののある部分を、僕のキャッシュカード暗証番号やiPadパスコードなどとして日々、使っているからである。ここまで午前中に一気に書いてきたが、考えてみると「禁断の領域」だというのに、FONT COLORを薄めに変えたことで逆に目立つ気もする(^_^;)ので、そのうち、どこかでさりげにFONT COLOR設定を元に戻すことにした。
昨日から断片的に考えているのだが、まだ新しいArduino応用システムの実現形のアイデアが思い浮かばず、関連しそうなキーワードでネットの画像検索で数千枚の画像をザザーーッと眺めたりしたのだが、既存の製品のフォルムというのは案外、漠然としたアイデア検討にとっては参考にならないことも判明した。 これはまだまだ温めつつ、考えていくことにしよう。2025年1月16日(木)
昨日のJoyJoyヒトカラは先週と同じ6時間で59曲だった。 いつも大抵はヒトカラの途中で喉に限界が来て「響声破笛丸」の助けを求める「ドラッグタイム」があるのだが、なんと昨日はノードラッグで快走した。 これもCPAPのお陰なのかもしれない。
そのCPAPと共に布団の中で夢うつつしている最中に、昨日に書いた「禁断の領域」に間違いや抜けがある・・・と気付いて、さっそく朝イチで訂正・加筆した。 「マイコン」に東芝の TLCS-90 という強力なやつを使ったのは、電子ピアノではなくて、その次の「Sound Pallette」(ここでは完全ディジタル音源LSIを開発したのが最大の貢献)の方だったようである。これまであまり考えたことが無かったのだが、入社してから11ヶ月の「新入社員実習」期間を経て、翌年3月に本社の研究開発センターに配属されてから3-4年程度の若造に、これだけ好きなようにシステムの電子関係のほぼ全部を開発させてくれたKAWAIには本当に感謝するしかない。 ただし当時の電子楽器のトレンド(本流)とはちょっと違うところにあったのも事実であり、僕が最初に出したPT200/PM100というのは「とりあえずの繋ぎ」(YAMAHAやROLANDが出してきたのでKAWAIも何か電子ピアノを出しておかないと)というものだったようにも思える。 半導体技術は発展途上であり、当時のメモリはとにかく容量が小さくて(マイコン8049は2KバイトのROMと128バイトのRAMしかない)、上述のようなハイブリッドな方法で実現(他社も同様)していたのだが、その裏では、マスクROMの大容量化が着々と進んでいた。 そしておよそ2年後には全ての楽器メーカの電子ピアノは「PCM方式」(サンプリング方式)一色になり、そこから現在までトレンドは変わっていない。
僕の1年後輩で、6ヶ月の「新入社員実習」を経て研究開発センターに来たM君は、1日中ずっと防音室で作業する日々を何年間も過ごした。 その元となったのは、2週間ほど確保したスタジオにスタインウェイの最上級フルコンサート・グランドピアノを持ち込んで、打鍵ロボットでなく著名なプロのピアニストが、88鍵を別々に弾いて全てをディジタル録音した、というサウンド・データベースである。 ピアノの全ての鍵盤について、均一なコンディションで、「ppp」・「pp」・「p」・「mp」・「mf」・「f」・「ff」・「fff」(低音域では長いと30秒ほど)の打鍵を、テヌートからスタカートまでのタッチで多段階に弾き分けるのは、プロのピアニストにしか出来ない。 このディジタル録音データをそのままでなく、ピアノメーカならではの視点からUnixシステムを使った手作業で1音1音「磨き上げる」というステップがあって、その結果が大容量マスクROMにぎっしりと収まって基板上にずらっと並んでしまえば、完全にディジタル化された電子ピアノでは、鍵盤タッチに対応した音域・音色・音量のPCMデータを単純に「読み出す」だけでよくなった。 そして僕が開発したタッチレスポンスLSIは、しばらくの間は、これらの電子ピアノでも活躍したようである。2025年1月17日(金)
30年前の1995年1月17日、この日は月曜日だったので僕は自宅にいた。 その2年前から5年後ぐらいまでの8年間(1993~1999)、僕は「ASL長嶋技術士事務所所長」と共に「京都芸術短期大学(映像学科)[京都・北白川]非常勤講師」・「(財)イメージ情報科学研究所[大阪・千里中央]非常勤研究員」・「神戸山手女子短期大学(生活学科/音楽科)[神戸・元町]非常勤講師」というのを続けていて、毎週3日(基本的には水~金)、浜松から関西に行き、大阪・梅田のマルビルを定宿として2泊3日は関西、という生活をしていたので、3/7の確率があった阪神大震災の直撃を、たまたま免れた。
大学からは「講義は停止」との連絡が届いて(もちろん行ける筈もない)、大学キャンパスも施錠されたまま警備会社も入れなかった。 当時は自宅からダイヤルアップしたNifty-Serveに繋いだ状態からインターネットに入ることが出来たのだが、僕は震災翌日には、まだ誰も入っていない2号館にある、ふーみんが助手となって僕がやっていた講義のための 25台のSGI Indy にリモートログインできて、Indyたちは震災直後の停電から電源が復旧するとちゃんと起動して、壊れたファイルを自動修復していたのを確認した。 震災から数日後にようやく同僚が2号館に入って確認してみると、何台ものIndyのディスプレイが床に落ちいてたというが、本体は稼働していたというのは凄い。
![]()
僕が神戸に行ったのは2月の中旬ころになってからで、ふーみんと落ち合ってJRで住吉あたりまで行って(その先は壊れていて不通)、そこから三宮に向かうバスを待つ行列に3時間ほど並んで、全て倒れている阪神高速の横を三宮まで行った。 現在ネットで「阪神大震災」と画像検索すれば出てくる惨状そのままで、屋根にブルーシートが載ってぐちゃっと潰れた家があるのに、その隣の家はまったく無傷、というモザイク(コントラスト)にも驚いたが、元々このあたりの多くは小さな川があったところ(河岸段丘)を埋め立てているので、たまたま家の建っているその下の地盤が砂なのか硬いのか、という違いの反映だった。 三宮から元町まで歩いて一番印象に残っているのは、街に立っている「電柱」が1本1本、全て別々に傾いているので、自分の方向感覚として「垂直」というのが失われたグラグラ感だった。 行く時にまだ傾いて残っていたビルが、帰りには完全に倒れていたのも目撃した。
![]()
そして、それよりもさらに印象に残っていたのは、三宮から梅田まで戻ってきたときの、梅田地下街である。 これからJRに乗って梅田から神戸方面に向かうという人たちは、両手に水タンクを携えているのだが、逆方向の人たちは、もう震災1ヶ月後ということで日常の通勤通学の混雑だった。 梅田地下街にたくさんある居酒屋やパチンコ屋などの繁華街も通常の賑わいであり、被災地がまだまだヒイヒイ言っているのに、そこから数十分だけ電車に乗ってきてみれば猥雑な日常がある・・・というのはなかなかショックな風景だった。
この年には、上のように単著としては4冊目の「はじめて学ぶ情報セキュリティ」という本も出したが、震災直後から自動復旧したIndyにリモートログインした話とかは、この本をテキストとして行ったセミナーでもまずまず、ウケた。 現在では何という事もないのだが、それが30年前のことだったのである。 合掌。
![]()
![]()
そんな思いが去来する1月17日だが、ネットでは Nintendo Switch 2 の予告映像が出てきて、いろいろと盛り上がっているらしい。 僕はまぁ、KAWAI時代やSUAC時代に任天堂と関係したマル秘な話についてはまだ自粛したいのでここでは書かないし、Nintendo DSは上のように 「数独」 のために買ったものの、スーファミとかWiiとかNintendo Switchについては触れたこともないので、まぁ静観するだけである。 ネット記事によれば注目ポイントは「世界的にどう価格設定するのか」・「転売対策」・「あっと驚く"隠し玉"は?」だそうだが、まぁこれも、任天堂のお手並み拝見というところだろう。
朝からメイルが2往復ほどして、テルミン竹内さんに関しては、何故か急転直下、「仕事の進め方が違っていて協働できない」とのことで、今回はプロジェクトに入らないまま「終了」という感じになってしまった。 ただし、僕とすれば久しぶりに「楽器」について色々と考えるきっかけになったし、実験のために白木マトリューシカ筐体(小)をいただいたので、今後もまだまだ実験は続けられる。 ME04マニュアル もスキャンしてみたが、「ME04 マトリョミン マニュアル」と検索するとネットで色々と出てきたので、そっちでも入手可能なもののようだ。 来週には竹内さんのイベントに一般来場者として参加する(そこで借用したME04マトリュミンを返却する予定)ので、それまでに時間を作って、アンテナや基板など、内部の写真でも撮ってみよう。 マトリュミンの操作性(ゼロ点調整ボタンとゼロHzから鳴ってくる様子)については、あらためて動画記録するまでもなく、 この動画 の最初の1分間を見れば十分で、他にとりたてて動画に撮るべき情報は無い。 まぁ、人いろいろ、人生いろいろ、色んなことがあるのだ。2025年1月18日(土)
昨日の晩の某バイト[2]では、いつもはアドバンス4人を相手にしている教室なのだが、同じ部屋で一緒にミドル4人を相手にしている先生が別の教室に代打で行った関係で、臨時に8人の2コースをまとめて担当するという日(過去にも2度ほどあったパターン)になった。 生徒の一人が前回作ったロボットを自宅に忘れてきてパーツ不足で満足に作れなかったり(→僕の電子工作セットの体験を6種類みっちり)、キットに4枚しか入っていない「ギヤL」が何故か3枚しか無かったために、僕が作ったサンプルを壊して貸し出した際にサンプルがバラバラになってしまったり・・・とあれこれドタバタしたのだが、新作の改訂ArduinoシューティングGameはなかなかにウケた。 そんなわけで今日は明日の教室(午前ミドルと午後アドバンス)に向けて、再度、新たにサンプルを製作する必要が出てきて、さらに2日前に起きた奥さんの親戚のおばさんの自宅トラブル(転んで骨折→救急車)の介護フォローの運転手で出かけたりという予定もあり、何かと忙しい週末となった。
![]()
ネットではちょうどAbemaTVで「朝日杯」の本戦トーナメントが今日から始まって2局同時に生中継されていて、明日には藤井七冠なども登場するのだが、ちょっとじっくり見ている余裕はなさそうだ。
そして何とかサンプル教材を完成させて(テキストに無いオリジナル両極LEDを付加)、明日の準備を完了してみると、AbemaTVでは午前の2局の勝者同士(永瀬拓矢 vs 佐々木勇気)[←佐々木という棋士は何人もいるので勇気まで書く必要あり]の戦いが始まっていた。 これをBGVとして構想していたアイデアの続きなどを考えたり、ややペースが落ちてきたものの週一はキープしている「Martinを愛でる時間」などで過ごす午後となった。2025年1月19日(日)
昨日の「朝日杯」の「永瀬拓矢 vs 佐々木勇気」戦は、さすがに秒読みの終盤ではAI評価値も劇的なアップダウンを繰り返す激戦となって、なかなか見応えがあった。 このところのタイトル戦で藤井聡太七冠と対決している二人なので、さすがの実力である。 今日は藤井七冠の入っている4人ブロックの「2局同時」→(休憩)→「勝者同士」というライブ中継がAbemaTVであるのだが、午前と午後に某バイト[2]があるので、残念ながらライブ観戦は出来ないのだった。
昨日と今日は、大学入試共通テストというのもやっているのだが、去年までは試験監督の当事者として毎年、緊張感と充実感のあるお仕事を担当してきたが、今年はもう無縁となって、「情報」という科目が登場したなどというニュースも、他人事として通り過ぎるだけである。 まぁ、某バイト[1]の方では、ローテーションによってはたまに高校生も担当するという意味ではまだ関係しているのだが、やはり過去に担当していた「出題(作問)担当」というお仕事が、まぁ何と言っても最高の「緊張感と充実感」なのだ。 ただし残念ながら、ここでは詳しく書けない。
![]()
考えてみれば、過去には定番だった「大学院」→(就職せず)「大学講師/助手」→「助教授」→「教授」というように大学教員コースを進んだ人と違って、僕は会社員から退社独立して、フリーとして非常勤講師として複数の大学で教えているところに(技術士仲間の勉強会の縁から)お呼びがかかってSUACの助教授としてスタートしたので、ここからして既に例外的である。
前身の静岡県立大学浜松短期大学部(看護学科)を膨らませた文化政策学部(国際文化・文化政策・芸術文化)とは別に、デザイン学部(生産造形[工業デザイン]・技術造形[メディアデザイン]・空間造形[建築/都市デザイン])を新設するために、2000年4月の開学よりも2年前ぐらいから、静岡県の新大学設立準備財団に呼ばれて何度も東京に行って会議を重ねて、ゼロから「デザイン学部」・「技術造形学科」のカリキュラム作成に関われた、というのも、数多い大学教員の中でも稀有の貴重な体験となった。
55歳頃から、修士課程をスキップして大学院博士(後期)課程に社会人入学して新たな勉強を出来た、というのも得難い経験だし、学歴として「博士(後期)課程単位取得満期退学」までという大学教員もかなり多いのに、3年間の博士課程でなく期限一杯ギリギリの6年間で博士号まで取得できたというのは、これまた得難い経験となった。 このチャレンジが無かったら、僕の人生に「哲学」「美学」という芳醇な領域は無縁のままだったのだから。 そして最後にフリーに戻る時には、意識していなかった(学科長とか委員長などの肩書き職を全て忌避してきた)のに、「名誉教授」という肩書き(実質的には看板だけで何も実体は無い)を得られたので、まぁ、全てに感謝しかないのだ。2025年1月20日(月)
![]()
昨日の朝日杯は、決勝トーナメント第1局で勝ち上がった藤井七冠が次の対局で服部慎一郎六段に負けた、という結果だった。 僕の過去の日記のどこかに、タイトル戦で七冠まで取って、あと1つという時に、タイトル戦以外の一般棋戦(朝日杯を含む)4つも全て藤井聡太、という凄い「絵」となったのを上げていた・・・と探したのだが、上のようにようやく発掘できた。 この藤井聡太が負けた、という事だけでニュースになるが、これは彼の衰えではなくて、同世代の強者が上がってきたこと(服部慎一郎六段は棋士レーティングで藤井2121.3、永瀬1946.2、に次いで3位の1905.6、で伊藤叡王1900.3、よりも上)、悉く対局者がスペシャル「藤井対策」を練ってきている、という状況でのことなので、まだまだ藤井進化は続くのだ。
![]()
昨日は大学入試共通テストの2日目で、初めて「情報」という科目が登場したようだが、なんとその中のプログラミングの問題は上のようなものだったらしい。 これは受験生を舐めているというのか、こんなのがプログラミングだという悪しき「常識」を定着させようというのか、まぁ驚いた。 「ロボット教室」の小学生の方がずっと上のアルゴリズムをやっている・・・と言いたいが、残念ながらSCRATCHっぽいプログラミング環境には「変数」という概念が無い(「定数」というのはあって、ループ回数とかモーター回転強度とかセンサ閾値などは設定できる)ので、あまり面白いものは作れない。 「ロボット」という名前があるので、どうしても「制御」という部分に力点を置かざるを得ないのは仕方ないのか。
現在、 この動画のシステム はさらにオリジナルの「両極LEDモジュール」を2つ増設して、「一定間隔で白と青が交互に点滅」しつつ、「ボリュームの推移でドレミファソラシが鳴るのに対応して赤と青が交互に光り、その輝度は音階が上がるほど明るい」という動作まで同時並行しているのだが、「ifループ」しかないので、ifループの出力にまたまたまifループがある・・・という7段階の異常に「横に広がった」フローチャート(プログラム)になっている。
![]()
そして午前中にまず、今週中に竹内さんに返却する予定のマトリュミン「ME04」について、とりあえず外観(と筐体上部を開けた状態の内部の外観)の写真を このように 記録した。 借用のみで返却することから、底部と側面の多数のビス等を外して「基板を取り出して眺める」ことはググッと堪えたが、スポンジを出して電池ケースを引き出して、再び格納しただけなのに、稼働させてみると「ゼロ点補正」が動かなくなったのには驚いた。
電池ケースには「ここが上面」というシールがあり、「ここ(底部)まで押し下げること」とも書かれている。 この電池ケースを、指示通りにかなり底部(筐体下部の内側に「ここまで」というシールが貼られている)に押し込むと正常動作に戻ったのだが、この電池ケースをその正規位置から1センチほど上に上げる(押し下げ不足)だけで、基本動作の「ゼロ点補正」が出来ない(OKのブルーLEDが光らない)というほど、筐体内部の金属体の質量と位置関係がクリチカルにアンテナ/周波数発生部に影響しているのだ・・・というのは大きな収穫だった。
![]()
買い物にちょっと出掛けて午後に帰宅してみると、「1967年創刊の老舗鉄道雑誌『鉄道ジャーナル』が休刊することが分かった。21日発売の2025年3月号誌面で休刊することを発表した。ウェブ版へ移行する予定はなく、4月発売の6月号で休刊する」というニュースが届いた。 「鉄道ファン」という雑誌があるのは知っていたが、まぁ「鉄道ジャーナル」というのも、名前だけはどこかで見かけたかもしれない。 しかし思い出してみても、過去に購入した記憶は「鉄道ファン」すら無いのだから、まぁ仕方ない。 「撮り鉄」が多い鉄道マニアは、僕ですら こんなページ を置いているぐらいだから、自分で鉄道写真を撮ること自体が面白いので、プロであっても他人の撮った写真を求めて毎月こういう雑誌を購読するという行動にはいかない気がする。 お約束の「休刊」というのも、まぁ「廃刊」だろう。 「休刊」と宣言したのに「廃刊」にならずにしばらくしてから「復刊」した、という実例があるのであれば教えて欲しい。
2025年1月21日(火)
今日は予定が何も入っていない、という久しぶり(前回がいつだったか記憶にない)の日である。 ちょっと「Arduinoテルミン」の次の実験に取り組むmotivationが低減してしまったし、このところ1週間ほど断片的に構想妄想している新しいArduinoモノの実現にはまだ着想到達していないし、何をしようかな・・・とネットニュースを見ていると、「ロックといえば米国?英国? 英国のロックは、本当に「ビートルズからすべてが始まった」と言ってしまっていいのか?」というYAHOO記事へのリンクのある5ちゃんニュースに行き着いた。
![]()
あまり「実はロック少年だった」などと積極的に公言していないものの、やはり僕のルーツには「ロック」があり、特にBrithsh Rock、さらに言えばProgressive Rockというのは骨身の芯まで染み付いた、ある意味で僕の音楽的根幹なのだ。 それは、これまでに公演してきた僕の Computer Music作品の記録 を観て/聴いていただければ分かるように、「なんや、ワレ、プログレやんけ」と簡単に看破されてしまうのである。 音楽情報科学する諸氏(国内・海外問わず)の多くが、「実は根がプログレ」というのは完全に事実であり、これまでの音楽情報科学研究会やICMC/NIMEなどで何度となくお互いに実感し合ってきたものだ(ただしここでは、僕とPink FloydやYESなどの付き合いについて長々と語ることはググッと堪えておく)。
![]()
この記事の最後は、「したがって未来の音楽=ロックンロール/ロックが生まれる可能性は、アメリカにもイギリスにもあった。(続く)」などと思わせぶりに終わって、上のように「第2回を読む」~「第6回を読む」のなかなか挑発的なタイトルの記事へのリンクが並んでいる。 ここまでは、YAHOOニュース内の記事なのだが、このリンクからはいよいよ外に出るというのが上手い仕組みで、もともとこの記事は「※本記事は『ロックの歴史』(中山康樹)の抜粋です」ということで、いわば一種の「呼び込み広告」でもあったのだった。 そしてとりあえず「第2回を読む」をクリックしてみるとYAHOOを離れて、「今なら無料登録でお得なクーポンをGET! 無料会員に登録すると、会員限定記事が毎月2本読めるようになります」という以下の広告丸出しのページで、ミックジャガー(僕はとりたてて好きではない)が迎えてくれた。
![]()
ここの「米国にとってジャズは自国の文化だが、英国には外来文化にすぎない」というキャッチーな見出しはまずまず魅力的だが、ここから読めるのも全て「『ロックの歴史』(中山康樹)の抜粋」であり、『30年代には米国の音楽が「禁止」に… イギリスが「ジャズ後進国」になったわけ』・『世界大戦後の徴兵を免れた若者たちが「英国ロック」を築き上げた』・『ロックの分母? 50年代の英国で流行った音楽「スキッフル」とは何か』・『キース・リチャーズ「俺たちは、空爆の残骸と瓦礫に囲まれて育った」』・『1960年に徴兵制度が廃止されたことで、英国からビートルズが誕生した』・・・と断片的に並ぶトピックを見ていると、なんだか「抜粋」でなく、いっそのことこの本の全部を読みたくなる。 そこで『ロックの歴史』(中山康樹)をAmazonで調べると、以下のように出てきた。
![]()
このページには<本書の内容>として、第1章「イギリス・ロック史とアメリカ」から第15章「ロックが帰っていく場所」までの内容がキチンと並んでいるし、「エルヴィス・プレスリーの限界」・「なぜリヴァプールだったのか」など著者の立ち位置も明確である。 ただしこの本が出たのは10年以上も昔であり、以下のようにAmazonでは「新品」としての購入メニューが無くて、いずれも定価990円に500円ほどの送料を足した「新古本(未読の状態で10年ほど熟成)」である、と気付いた。 大人がポチッて購入するのは簡単な廉価本なのだが、どうも「Brithsh Rock」という言葉で僕が求めているものとは違う、と判明した。 著者を調べてみると、この本を出した翌2015年に悪性リンパ腫のため死去したようで、基本的にはジャズに基盤を置く音楽評論家だった。 この本のような掘り込みで「プログレ」ものがあれば、2-3倍しても即購入なのだが、ここまで来て冷静に「断念バーグ」となった。
![]()
ここでしばし、ちょっとした外出などのお仕事が飛び込んできて中断したが、考えてみれば正面から「プログレ」というのを検索したことが一度も無かった・・・と気付いて、さっそくAmazonで検索してみると、上のように最上段には何故か「カロヤン プログレ」などの育毛剤が並んで動揺した(ちなみに僕の親父は若くして頭髪に不自由していたのを垣間見て、小学生だった僕は親父の「カロヤン ハイ」という育毛剤をこっそり風呂上がりに頭に振っていた。祖父(親父の親父)もスキンヘッドだったからなのだが、結果的にはこの歳まで頭髪に不自由しないことになった遠因かもしれないので、感謝感謝)。 しかしよく見ると、次の段からはようやく「プログレ本」が出てきたので、「カテゴリ」に「本」を選択して「プログレ」にすればいいのだ、という結論に達した。 まぁ、プログレというのはProgressiveということで、「進歩的な」という真っ当な意味があるので、Progressiveの意味として「まるでプログレ」という風に反応してしまうのは、一部のオタクだけなのだった。
![]()
そこで改めてAmazonの「本」カテゴリから「プログレ」と検索して出てきたもののAmazonページのリンク集を、以下のようにリスト化してみた。 これを順に眺めて概要をチェックして、ピンときた、ググッときた、というものを注文すればいいのだ。
イマドキというのか、Kindle版しかないものも3点ほどあったし、もはや中古本としてしか入手できないものもいくつもあったが、まぁプログレなんて言って通用するのはもはや老人だけなので、これは仕方ない。 このリストを作った段階でピンときたのは2番目の「1970年代のプログレ --- 5大バンドの素晴らしき世界 ---」なのだが、これは「キング・クリムゾン」・「ピンク・フロイド」・「イエス」・「エマーソン・レイク&パーマー」・「ジェネシス」を扱っていて、まぁ僕にとってプログレとして崇拝する「ピンク・フロイド」・「イエス」が入っているのは当然、そして「キング・クリムゾン」・「エマーソン・レイク&パーマー」についてもよく知っているものの「並」水準、さらに「ジェネシス」についてはバンド名しか知らないという品揃えなので、ちょっとだけ不満もあるが、結局この中古本を注文した。
- もうすぐエピタフ どうしてプログレを好きになってしまったんだろう♯9第三印象 ¥2,310
- 1970年代のプログレ --- 5大バンドの素晴らしき世界 --- (ワニブックスPLUS新書) 中古品: ¥780
- どうしてプログレを好きになってしまったんだろう ¥1,980
- PROG MUSIC DISC GUIDE プログレッシヴ・ロック/メタル/オルタナティヴの現在形 ¥2,420
- プログレッシブ・ロック入門 中古品: ¥1,663
- ピンク・フロイド VS キング・クリムゾン プログレ究極対決 --- ロックの未来を変えた2大バンドの両極 ¥2,420
- プログレのパースペクティブ: Cdbest100 : Music Magazine増刊 中古品: ¥980
- プログレ百物語: プログレッシヴ・ロック名盤百選 Kindle 価格: ¥300
- プログレ百物語: プログレッシヴ・ロック動画百選 Kindle 価格: ¥300
- ヒプノシス ロック名盤デザイン秘話【2000部完全限定】 ¥8,800
- 聴いとけ!名盤 --- 博士とケンタと名盤研究所:ソウルからプログレまで、時代を映す名盤たち(22世紀アート) Kindle 価格: ¥880
- プログレ「箱男」 中古品: ¥3,330
![]()
午後には、あれこれArduino界隈を調べ物して、上のようないくつかのメモ(順不同)を今後に向けて残した。 空欄は、今後の加筆によって加えられる余地である。 これが何かに結実するのは、まぁ、ぼちぼちこれからとなる。
- Seeeduino Lite(中身はLeonardoコンパチ)では、たぶんLeonardo対応のMozziは走る? → 実験すべし
- MozziのControl Rateのタイムスロットで個別点灯LEDテープの駆動が可能かどうか実験すべし
- MozziのControl Rateのタイムスロットで3軸加速度センサのアナログ入力の積分が可能かどうか実験すべし
- Arduinoテルミンの周波数帯域マッピング → 指数関数的重み付けの実験
- 超小型OLEDモジュールを使って何か出来ないか検討
2025年1月22日(水)
朝イチでAmazonから届いていたのは発送済み:「1970年代のプログレ - 5大バンドの素晴らしき世界...」というメイルだった。 ところが、その「配送状況を確認」というボタンを押すと、以下のようにアマゾンのページに飛んで「注文済み」→「発送済み」まで色が付いて、その先に→「配達中」に進む(ここに来るとクロネコヤマトから「配達時間の問い合わせ」というメイルが来て指定できる)・・・という状況だったのだが、問題はそのウインドウに続いている「その下」だった。
![]()
さすがAmazonと言うべきか、「1970年代のプログレ - 5大バンドの素晴らしき世界 - (ワニブックスPLUS新書) に基づいたお客様へのおすすめ商品です」ということで以下のように4冊並んでいるうちの左の2冊は、昨日チェックしていた。 そして何と3冊目には、「ピンク・フロイド・アンソロジー」という本があるではないか!!!
![]()
これはもう、絶対にクリックして以下の商品ページに進むこと「必定」である。 しかしこの段階では、「主な収録記事」の項目は2冊目あたりから薄く隠れていて、見えない。 そして当然ながら、その一覧を見ることになる。 注文クリックに向かって一直線、Amazon商法、全開の展開である。
![]()
しかしこの「主な収録記事」の全貌を以下のように確認してみると、断捨離を極めて本棚を空けてきた僕にとって、1870円でそこに加えるには不満が多い内容だった。 プログレおっさんが求めているのは、「狂気」(ぎりぎり「ザ・ウォール」)までOKとしても、それより前の、「おせっかい」や「原子心母」こそが欲しいのである。 「おせっかい」(Meddle)の1曲目の「吹けよ風、呼べよ嵐」(One Of These Days)やB面マルマルの「Echoes」を語らずして「ピンク・フロイド・アンソロジー」と名乗るとは、片腹痛い限りなのだった。
若い人には分からないだろうが、昔はレコードというものがあり、中学生の僕は「おせっかい」(Meddle)のレコードを毎日聴いて、遂には聞き潰して(レコード針に擦られる磨耗によって、レコード盤というのは寿命を迎える)、このアルバムは2枚目(もしかすると3枚目)まで、お小遣いをはたいて同じものを買い直して、毎日聞き狂っていた(骨の髄までプログレに染まった)のである。・・・ということで、この本の購入をググッと断念バーグして一日が始まった。
- 特集 ピンク・フロイド[2002年1月号]
- 特集 ピンク・フロイド/狂気[2006年3月号]
- 特集 ピンク・フロイド『狂気』の真実[2011年10月号]
- 特集 ピンク・フロイド『ザ・ウォール』[2012年4月号]
- 特集 ピンク・フロイド『対(TSUI)』[2014年9月号]
- 特集 ピンク・フロイド『永遠(TOWA)』[2014年12月号]
- 特集 シド・バレット[2006年10月号]
- 特集 ロジャー・ウォーターズ[2017年7月号]
- 特集 ピンク・フロイド 進化の夜明け[2016年12月号]
- 特別復刻 「ピンク・フロイドを語る」一柳慧×宇野亜喜良×石坂敬一×中村とうよう[ニューミュージック・マガジン1971年8月号]
ちなみにまだ書いていなかったと思うのでここで書いておくと、僕の葬式(があるとしたら)で流して欲しい音楽は次の2曲のうちのいずれかでお願いしたい。 せっかくなので、現在YouTubeで生きているリンクも添えておこう。 第一候補についてはここ45年ほど、第二候補についてもここ30年ほど、不変である。 第一候補はPink Floydのアルバム"Meddle"の最終曲(B面マルマル、23分30秒ほど)の"Echoes" ★ ★ ★ 。 第二候補はマーラー「復活」の第1楽章(希望としては小澤征爾かバーンスタイン。こちらも22-23分ほど) ★ ★ ★ ★ 。 ちなみに葬式では第1楽章だけで終わり、最後まで流さないこと(復活してしまうので)。
![]()
![]()
そして午前にクリエート浜松であった「いきいき人権講座」(全体は3部構成)というのに出掛けて行き、竹内さんのテルミン演奏や講話を体験してきた。 第1部は竹内さん(+2人)による、テルミンやマトリュミンの演奏/合奏というミニコンサートであり、全ての曲は事前録音のピアノ伴奏の再生を伴っていた。 いずれも簡単な曲なのだが、3パートの合奏の時に気付いたのは、「これは合唱よりも超難しい」という事だった。 合唱であれば、他パートの「聞こえてくる音」と、自分の身体で鳴っている「自分の音」とは違うので、アンサンブルとして合わせるのは慣れれば簡単なことなのだが、テルミン/マトリュミン3重奏(ユニゾンでなく)の場合、他パートの「聞こえてくる音」と、自分が鳴らす「自分の音」とが、全て同じ音色なので、「自分を見失わない」という音楽的タスクの難易度は遥かに高いものとなる。
第2部は「マトリュミンの体験」というコーナーだが、会場に持ってきたマトリュミンは2台であり、竹内さん以外の2人がそれを持ってあちこち移動して・・・と行うので、実際に体験できたのは数人に一人だけ(僕はパス)だった。 第3部は竹内さんの「講話」ということで、実際には司会者が質問して竹内さんが語る、というスタイルで進んだ。 全体としては上のチラシを見ても分かるように、浜松市の事業の枠組みで「無料」で開催しているものの、中身としては「マトリュミン教室の生徒を発掘したい」という強い意志に満ち溢れたイベントになって、今回の場合には主催者の「人権」というテーマとの接点(障害者の人権尊重)がだいぶ希薄になっていたが、まぁ仕方ないのだろう。
このイベントに参加したことで、「テルミン」という楽器のさらに新しい可能性と課題について知ることが出来たので、だいぶpriorityは後退したものの、おいおいArduinoテルミンを再開した時には、そこに生きていくであろう収穫を得た。
![]()
そして午後になって気付いてみると、AbemaTVでは「将棋 西山女流三冠 編入試験第5局へ 勝てば初の女性プロ棋士」というライヴ中継をやっていた。 奨励会からプロ四段棋士になったばかりのピチピチの5人と対戦する五番勝負というのはなかなか過酷なのだが、よくここまで2勝2敗で合格に王手をかけたものだ。 最終局のラスボスは柵木幹太四段(26)ということで、「奨励会時代の三段リーグで1度も勝てていない」相手らしい。 そんな後ろ髪をひかれながら予約したJoyJoyに出掛けるので、結果は帰宅してからor明日に知ることになりそうだ。
2025年1月23日(木)
西山女流三冠は2勝3敗でプロ棋士編入ならずだった。 忖度なく戦った柵木四段もプロなのだ。
昨日のJoyJoyヒトカラでは、今回はいつものように漢方薬の助けを受けたが、6時間で59曲を完走した。 なんと今月は、8日15日22日と、3週連続の水曜日で3回とも「たまたま」(全く選曲が違うのに)59曲ということになった。 ただし来週の水曜日は日赤に行って「自宅のCPAPを装着しての検査入院」という予定があるので、JoyJoyは木曜日の予定である。
![]()
米国では「トランプ米大統領は20日、連邦政府が男性と女性の2つの性別のみを認め、変更はできないとする大統領令を出した」ということで、5ちゃん(「引き続きハゲとデブは差別されるのか」との苦情も)には上のような謎の「バイデン政権が認める性別一覧」という画像が貼られていた。 知らない単語が続出しているし、天気記号のようなそれぞれの意味もなかなか謎なのだが、こんなに多様な多様性がある・・・というのは知らなかった。 スポーツの世界でも色々と問題が起きているし、これはなかなか簡単にはコメント出来ないのだ。
![]()
午後イチには、「2025年1月21日(火)」のところの最後にリストしていた項目の実験をしてみた。 手元には何枚かの6軸センサを搭載したSeeeduino Lite(中身はLeonardoコンパチ)があり、この6軸センサ(加速度3軸、ジャイロ3軸、実はさらに温度センサの計7チャンネル)は非常に優秀なので、これを使ってボードを傾けた状態に応じてMozziを鳴らしたい、という着想である。 そして結論から書くと、「このボード(Leonardoコンパチ)はMozziが走る」というpositiveな結果と、「この6軸センサはMozziと相容れない」というnegativeな結果が同時に得られた。 上のArduinoスケッチでハイライトしている「Wire.h」というのが曲者で、アナログでなくシリアル通信をしてセンサ値をゲットしているのだが、どうもMozziが内部的に行っている処理とバッティングする模様である。 エラーメッセージには、割り込みベクトルが二重に定義されている、と書かれていたので、そこそこ厳しい割り込み処理をダブルで使うのは無理なのだろう。 これで、当初の構想はそのままでは駄目、と判明したが、まずまず大きな収穫でもあるのだ。
![]()
そして遂に「1970年代のプログレ --- 5大バンドの素晴らしき世界 ---」が届いた。 中古といっても完全に美品であり、得した気分である。 これから読むのだが、とりあえず著者紹介を見てみると、やはりと言うべきか、僕より1歳だけ若い、もろ同世代だった。 やっぱり、プログレはそうでなくっちゃ。
・・・そして約1時間ほど集中の時間を経て、あっという間にこの本を読み終えてしまった。 なんせジェネシスを無理やりに加えて「5大バンド」と著者が言っているものの、やはりプログレと言えば「キング・クリムゾン」・「ピンク・フロイド」・「イエス」・「エマーソン・レイク&パーマー」の4大バンドというのが定番であり、ジェネシスのところは完全にすっ飛ばした。 さらに多少は知っているものの、クリムゾンとELPについても僕としてはそれ程の思い入れは無いので、この2バンドについても斜め読みモードとなる。 結局、Pink FloydとYESの部分についてはフムフムと頷きつつじっくり読んだのだが、全体としてはこの本の半分ぐらいは不要だったので(それでも本棚に残すことになった)、1時間ほど、僕自身も著者と同じような中学生時代だったなぁ・・・と思い出しつつの読書となった。 これはまぁ、片面20分超で1曲というプログレのレコード盤にドキドキしながら針を置いたという経験の無い、最近の若い人にはちょっと無理かもしれないなぁ。
![]()
読了した「1970年代のプログレ --- 5大バンドの素晴らしき世界 ---」を本棚に入れる時に、上のような本がある事に気付いた。 これは2006年に「復刻版」が出たというニュースで衝動買いしたものの、一度も開いたことが無かったのだが、久しぶりにみっちり「本を読んだ」という充実感の残滓のためか、手が伸びたのである。 そしてここからまた約2時間半、集中してこの分厚い本「復刻版 大宇宙の旅」を、終わりまで一気に読み切ってしまった。 こんな素晴らしい本を、読まずに本棚に置くだけだった事を恥じたが、まぁ、これで一つは功徳を積んだような気もする。 しかし疲れた。
2025年1月26日(日)
一昨日の金曜日は朝から孫の幼稚園での参観会に行き、一緒に遊んだり園庭で運動した。 昨日の土曜日は このように 大井川鉄道・SLの旅(新金谷~川根温泉笹間渡)に行った。 終点の川根温泉笹間渡から千頭までは台風被害で不通のままだが、その川根温泉笹間渡にある「道の駅」併設の温泉(露天風呂からは鉄道が見える)も堪能した。 この「鉄旅」は当然、 「乗り鉄」の記録 にも付記したが、最近の「旅」頻度の低下は僕にしてはなかなかのもので、ちょっと寂しいところもある。
![]()
AbemaTVでは王将戦第2局の2日目の中継をしていたが、たしかこれは午前中で無料が終わりになって、あとは有料となるので前回も視聴を投了した記憶がある。 Arduinoテルミンに関しては、2月2日が日本音楽即興学会大会の発表応募期限であり、その後に(形式的な)採択プロセス[採否は大会実行委員会が審査します]があって、結果の通知が「2月中旬ごろ」とあるので、そこでGOサインを受けたら再開するとして、当面は棚上げすることにした。
某バイト[2]の生徒向けにあれこれ作っては遊んでもらっている「Arduinoモノ」については、けっこう生徒から「また次を期待します」的なリクエストを受けているので、これは定期的にupdateしていかないといけない。 そこで午後に、まだ最終形態が未定のまま、やりかけていた「Seeeduino Liteに6軸センサが載っているボード」(Mozziとの共存はNG)を使って、上のような項目に従って実験を進めてみた。 続きは明日以降にやっていこう。
- Seeeduino LiteのオンボードSWに「ポートの電圧を+5Vと+3.3V」に切り替えるものがある
- 6軸センサボードが「+3.3Vシステム」のため、現状ではこのSWが「+3.3V」になっている
- ボード上のコネクタとしては「+5Vと+3.3V」の両方が出ている
- ブザーを繋いだところ、+3.3Vポート出力でも十分に鳴った
- NeoPixel_LED_Strip(個別駆動LEDテープ)の電源は+5Vが必要なのでボード上から+5Vを供給
- NeoPixel_LED_Stripのデータは+3.3Vでもスレショルドを超えているのでOKではないか・・・と想定
- 実験してみて、想定通りと確認できた(^_^)
- 今日のところは「加速度(1軸)をシリアル出力」と「45個のNeoPixel_LED_Stripの駆動」を並行する動作まで確認した
- このLEDテープとSeeeduino Liteを、ちょっと幅の広い30cmのプラスチック定規の両面に貼った
- この定規を傾けると、加速度(1軸)のデータは -16000~+16000 あたりの値で大きく変化する
- この定規を正確に水平にするのは、なかなかに難度が高い → ここをゲーム的にしたい !!
2025年1月27日(月)
![]()
昨日はライヴ中継が見られないので晩にチェックしてみると、やはり藤井七冠が王将戦第2局も勝利していた。 軍曹・永瀬九段の研究を打破する棋譜を眺めてみたが、やはり、流石と言うしかない。 これまでのタイトル戦、京都で負けなしとのことだが、伏見稲荷大社で美味しいいなり寿司を食べたのが効いたようである。
そして今日は午前中から午後にかけて、昨日から持ち越した新Arduinoシステムのプログラミングに没頭する、という幸せな時間を過ごした。 その結果、遂に、およそ1週間ほど懸案だった「傾きセンサ」もののアイデアが、「30cmプラスチック定規」という土台を得て、完成した。 使い方については YouTube動画 を見れば分かるようにシンプルであり、タスクとしては、「輝点の表示ポイントを移動させ、定規の中央点(ここでは白色になる)を3秒以内に10回通過させる」というのが目標である。 3秒を超えるとレッドLEDの「失敗」結果がサウンドと共に表示され、カウントはクリアされる。 また、定規の両端にあるレッドゾーンに入ると通過カウント数はリセットされる。
![]()
上のArduinoスケッチで工夫したのが「傾きと輝点ポイントの移動速度との関係」であり、大きく傾けると速度が低下して、ほぼ水平に近いところでは高速で移動する、というのが、このGameの面白さとなっている。 6軸センサのうち1軸加速度だけを使って、およそ「-16000~+16000」のデータを現在値に加算するための重み付けを変えて、普通であれば大きく傾ければより高速に移動する筈のところ、「逆感覚」で水平近傍で高速に移動、というのが「売り」である。 これをロボット教室の生徒たちがどう感じるのか、今から反応が楽しみとなった。#include <Wire.h> #include <FastLED.h> #define LED_PIN 9 #define NUM_LEDS 45 CRGB leds[NUM_LEDS]; const int MPU_addr=0x68; int16_t AcX; int i, musicScale[] = {523,587,659,698,784,880,988,1047}; int points, old_flag = 0; long sum_point = 22000, sum_point2, differ, score = 0; unsigned long myTime1, myTime2, myTime; void setup(){ Wire.begin(); Wire.beginTransmission(MPU_addr); Wire.write(0x6B); // PWR_MGMT_1 register Wire.write(0); // wakes up the MPU-6050 Wire.endTransmission(true); for(i=0; i<9; i++){ tone(11, musicScale[i]); delay(40); noTone(11); delay(60); } Serial.begin(9600); FastLED.addLeds<WS2811, LED_PIN, RGB>(leds, NUM_LEDS); for(i=0; i<NUM_LEDS; i++) leds[i] = CRGB::Black; FastLED.show(); } void loop(){ Wire.beginTransmission(MPU_addr); Wire.write(0x3B); // starting ACCEL_XOUT_H Wire.endTransmission(false); Wire.requestFrom(MPU_addr,2,true); // 2 registers AcX=Wire.read()<<8|Wire.read(); // ACCEL_XOUT if(AcX != 0){ if(-10 < AcX && AcX < 10) differ = 500 / AcX; if(-20 < AcX && AcX < 20) differ = 1000 / AcX; else if(-100 < AcX && AcX < 100) differ = 20000 / AcX; else if(-300 < AcX && AcX < 300) differ = 40000 / AcX; else differ = 80000 / AcX; sum_point2 = sum_point + differ; if(sum_point2 > 44000) sum_point2 = 44000; else if(sum_point2 < 2) sum_point2 = 0; else sum_point = sum_point2; points = sum_point / 1000; } check_and_display(); } void check_and_display(){ for(i=0; i<NUM_LEDS; i++) leds[i] = CRGB::Black; if(points == 22){ leds[22] = color_set(4); if(old_flag == 0){ tone(11, musicScale[5]); delay(45); noTone(11); delay(45); tone(11, musicScale[3]); delay(60); noTone(11); old_flag = 1; if(score ==0) myTime1 = millis(); score++; if(score > 9){ myTime2 = millis(); myTime = myTime2 - myTime1; if(myTime < 3000){ for(i=0; i<15; i++) leds[3*i+1] = color_set(3); FastLED.show(); melody_end(); delay(1500); old_flag = 0; score = 0; points = 20; return; } else{ for(i=0; i<23; i++) leds[2*i] = color_set(1); FastLED.show(); for(i=40; i>10; i--){ tone(11, i*50); delay(50); } delay(300); noTone(11); delay(1500); old_flag = 0; score = 0; points = 20; return; } } } } else if(points < 2){ leds[0] = color_set(1); leds[1] = color_set(1); tone(11, 100); delay(50); noTone(11); delay(200); score = 0; } else if(points > 42){ leds[43] = color_set(1); leds[44] = color_set(1); tone(11, 200); delay(50); noTone(11); delay(200); score = 0; } else{ leds[points] = color_set(8); old_flag = 0; } FastLED.show(); } CRGB color_set(int color){ if(color<1 || color>8) return(CRGB::Black); switch(color){ case 1: return(CHSV(96, 255, 127)); // Red case 2: return(CHSV(0, 255, 127)); // Green case 3: return(CHSV(160, 255, 127)); // Blue case 4: return(CRGB::Gray); // White case 5: return(CHSV(60, 255, 127)); // Yellow case 6: return(CHSV(128, 255, 127)); // Pink case 7: return(CHSV(87, 255, 127)); // Ogange case 8: return(CHSV(random8(), 255, 127));// Random } } void melody_end(){ tone(11, musicScale[0]); delay(120); tone(11, musicScale[2]); delay(120); tone(11, musicScale[4]); delay(120); tone(11, musicScale[7]); delay(400); noTone(11); }2025年1月28日(火)
「フジテレビ記者会見10時間23分」などというニュースで溢れた今朝は、以下の新しい錯視「拡大する穴」の情報とか、QRコードのピクセルをさらに分割して2種類のURLを盛り込んだ 2つのURLを読み取れるQRコード などという面白い情報も飛び込んできた。
![]()
今日は何も予定の無い日なのだが、月末ということで某バイト[2]のテキスト(たいてい月末に翌月分のテキスト情報がアップロードされる)の作業に時間を費やす日となった。 なんせ、提供されるテキストは「電子ブック」の形式(Webブラウザで見て、ページを繰って参照する)なので、以下のような作業を、テキスト1本ごとに、いちいち手作業にてPDFにまとめないとイケナイのだ。 まるまるダウンロードで持って行かれるのが嫌なのだろうが、この作業は月イチとはいえ、まずまず苦痛でもある。
そして午前から午後まで、淡々とこの単純作業を続けたのだが、途中で出てきた「8*8ドットマトリクスLED」のところで、最近ではLOWとCOLUMNでマトリクスを組んで表示するという古典的なテクニックというよりは、コントローラ「MAX7219」という便利なものを使っている・・・と知った。 そこでちょっと調べてみても ここ とか ここ とか ここ とか ここ とか ここ とかに情報があり、価格もかなり安いことが判明した。
- ヒューマンのサイトにログインする
- 教室指定のログインでさらに進む
- タブメニューから「ロボット教室」or「ロボプロ教室」のテキストページに行く
- テキスト集ページにずらーーーっと並ぶテキスト(アイコン)から選ぶ ★
- 画面が「電子ブック」になる(表示は見開き2ページ単位)◆
- ツールバーの「印刷」ボタンを押すと、そのページの「左側」「右側」を選ぶサブウインドウが出る
- これで左右のどちらかを指定して「プリント」ボタンを押す
- MacOSのプリント設定ウインドウが出てくるので、この下にある「PDF」プルダウンメニューから「PDFに書き出し」を選ぶ
- PDFに書き出す(行き先はdefaultでdesktop)サブウインドウが出てくるので、ここに連番で 01, 02, ・・・という数字を入れる
- 「電子ブック」をめくって次のページに進む (→以下◆ループ)
- この作業を最後まで繰り返すとデスクトップに 01.PDF, 02.PDF, ・・・ というのがずらりと並ぶ
- 最後まで連番PDFが揃ったら「電子ブック」を閉じて、MacOSのPreviewで「01.PDF」を開く
- ウインドウ左側のツールバーにサムネイルを表示するようにする
- デスクトップの02.PDFをツールバーの01.PDFの下にドラッグドロップして入れる
- これを最後のPDFまで繰り返す
- 最後にPreviewの「印刷」を指定して、プリント設定ウインドウの下にあるメニューから「PDFに書き出し」を選ぶ
- 最終的に書き出すテキストPDF名を入力して全ページが一体となったPDFを書き出す
- このPDFを必要箇所に移動させて、最後にデスクトップに並んだ01.PDF, 02.PDF, ・・・ を消去する
- 「テキストを選ぶ」の画面★に戻って次のテキストの作業を行う (→以下★ループ)
![]()
そこでついついAmazonに行って「Arduino 8*8 LED matrix MAX7219」と調べてみると、出るわ出るわ、マトリクス1個だけでなく「4連」や「8連」や「4連2列」などというのも出てきた。 とりあえず、コントローラで楽をしてLEDマトリクスを点灯させたことは無かったので、これは実験だ・・・とさっそくポチってしまった。 明日の晩から明後日にかけて、日赤に行って「CPAPを装着しての生体計測」検査があるのだが、なんとその検査から帰ってきた木曜日には届いてしまうらしい。 ライブラリ等を調べたりの実験はそれ以降として、さらにひたすら、夕方までテキストPDF化の単純作業を続けたが、それでも今日だけでは半分にも届かず、作業は終わらなかった。 というのも、毎月の新しいテキスト(Basic/Middle/Advence)だけでなく、今回はこれから新たに受け持つかもしれない「ロボプロ」の過去のテキストも(合計80本ほど)残っていたためである。 過去の全てを手元にPDF化(→自分のiPadに入れて教室に持参用)してしまえば楽になる(あとは毎月の差分 * コース数)ので、ここは頑張るしかないのだ。
2025年1月29日(水)
今日は夕方に日赤に出かけるまでに何とか・・・と、昨日から続くテキストPDF化の作業を進める一日となったが、あまりに相手が厖大なため、「ロボプロ3年目」の4テーマ(計24ファイル)を後回しに作業するとして、まずはその前までを午前~午後に進めた。 ネットからは「すごすぎて鳥肌…歩き方が人間そっくりな中国製ロボットが話題」という、下の素晴らしい動画(アニメGIF)も届いたが、伝説の 先行者 (当時の日本は鼻高々に中国を見下ろして茶化していた)から、遂に ここ (現在では遥かに抜き去られてしまって全く手も足も出ない)まで来たのか・・・と、甚だ感慨深いものがある。 まさに、日本の「失われた30年」なのだ(その30年間に僕は成長させてもらったのだが)。
![]()
午後にもさらに少しだけ作業を続けた(新たに6本のPDFを作成)が、ここでこれまでに作業完了したファイルを並べてみたら上のように、だいぶ厖大になっているのだった。 これらPDFはWeb某所に上げてあり、教室に持参するiPad2台からも見られるようにダウンロードしてiPad内に置いていて、たまたまテキストを忘れた生徒とかに見せたりしている。 これを改めて眺めてみると、どうもBasicの[P][Q][R]が抜けていたらしい・・・とも気付いたが、過去のものはどんどん消えていくので、ちょっと回収出来なさそうだ。 あと残り18本のPDFを作ったら、こちらも某所に上げてiPadに格納することになる。 まぁ、続きはぼちぼち、今週末に向けて作業することになりそうだ。
上は、その作業中にちょっと気になった点のメモであり、まだ今後、追記する余地を残している。 まぁ良く出来たテキストなのだが、あちこち突っ込みどころも満載であり、ただしその「突っ込み」は、ここにはググッと堪えて書かないことにしている。
- ジャイロ3軸センサは「角速度」?「角加速度」? → 確認すべし
- Arduinoでも「タイマ割り込み」が使えるらしい → 調べるべし
2025年1月30日(木)
朝帰りした。 昨日の夕方、日赤に行って、昨夜は「PSG・CPAP検査」をやってきたのである。 この日記のPart4 の「2024年11月8日(金)」のところにあるように、11月上旬に日赤に行って、一晩「PSG検査」を受けていた。 そして11月末に日赤に行って結果をみて「CPAPをしましょう」ということになり、12月上旬にテイジンからCPAP機器が自宅に届いて使い始めて、12月末にはその経過診断を受けて、今回の「PSG・CPAP検査ということになった。 つまり、自宅で使っているCPAPを装着した状態で、再び「PSG検査」をしたわけで、来月下旬にこの結果を確認すると、日赤での対処は終了となって、紹介状を書いてくれた僕の自宅に近い耳鼻科医が予後を診る「かかりつけ医」になる、という図式である。
![]()
![]()
![]()
今回はもう、入院検査の段取りが分かっているので、上のようにまず部屋に入った段階で綺麗な状態のセッティングなどを撮って、センサ類とCPAPのアダプタ(頭に固定するバンド類と鼻を覆うキャップまで。この先にCPAP機器からのホースが繋がる)を装着した状態で、また看護師さんに写真を撮ってもらった。 見た目はオッサンが凄い装置に雁字搦めにされている異様な風景だろうが、別に痛くも辛くもなくて、これでCPAPを装着すれば、鼾と無縁のまずまず良好な安眠となる事を既に知っているので、何の不安も無いのだった。 ただし、寝返りがほぼ出来ないこともあり、睡眠薬を1錠飲んだものの(前回は追加でさらに1錠飲んだ)、なかなか眠れなかった。
そして今朝6時に検査が終わり、今回は途中のコンビニに寄ってカフェラテなど飲みながら自宅に戻り、いつものように朝シャワーを浴びて頭髪の導電ジェルなどを綺麗さっぱり洗い流せば、無事に日常に復帰した。 CPAP検査の後で朝からそのまま仕事に行く人も多いらしい(大変だと思う)ので、僕などはマシなのだ。 今日は午前~午後にお仕事して、午後からはいつものJoyJoyヒトカラという、週一のお楽しみである。
ちなみに この日記のPart4 の「2024年11月8日(金)」のところに書いていた、アンドロイドから写真をMacに転送するソフト「Image Capture」が何故か今回はスマホを認識しなかったので、9枚の写真(元の生データだと計30数MB)を3回に分けて自分宛にGmailで送ることになった。 別の「スマホからMacに写真を転送する」という「ApowerManager」というのもインストールしてみたが、こちらもスマホを認識しなかった。 まぁ、こういう事はたまにしか無い(普段はデジカメで撮る)ので、あまり深入りしない事にした。音楽情報科学研究会からは、上のように3月研究会のプログラムが届いた。 残念ながら九州大学には行けないのだが、各発表のタイトルだけずらっと並べて眺めてみると、「育つ楽器」とか「布スピーカー」とか、ちょっと面白そうなものは全て九大の城一裕先生のところの学生さんのようで、「布スピーカー」のところのタイトルはおそらくHTMLの文字化け(半角不等号で括ると起きる)のようである。 ChatGPTネタはどうも海外よりも1年遅れぐらいのようだが、これは予稿が公開されたらよく見てみよう。音楽情報科学研究会(SIGMUS)の第142回研究発表会 日程:2025年3月6日(木)14:00 - 3月8日(土)17:00 会場:九州大学 大橋キャンパス(福岡県福岡市) 1. String Mixer: 統計的撥弦楽器音合成のための弦単位位置埋め込み 2. 鍵盤表示を必要としないピアノのジェスチャー演奏のための打鍵判定と2種類の隠れマルコフモデルでの運指列からの音符列推定 3. 超球面上の確率的表現学習を用いたマルチモーダル音楽情報検索 4. グラフニューラルネットワークによるオルガン小曲集の非和声音認識 5. 移調を考慮した自己教師あり学習による文脈化コード埋め込みの獲得 6. ChatGPTを用いたテキスト入力型楽曲生成AIへのプロンプト最適化システム 7. 個別楽器音に基づく知覚的楽曲間類似度表現学習 8. 拍対応情報の利用による楽譜と実演奏の音符対応情報の自動生成法 9. ゲームとシーンの特徴を同時に考慮するBGM検索エンジン:主観評価の実施 10. MusicMap: A Novel Map-based Approach to Music Representation using Audio Features 11. レイヤー構造に基づく楽曲推薦手法の提案と音楽発掘サービスKiiteへの応用 12. 変分オートエンコーダによるドラムからボーカルパーカッションへの楽器音変換と評価 13. CNMF改良モデルを用いた音響モザイクによる4ch空間エフェクトおよびモーフィングへの応用 14. 知覚感情の不整合:人間が作曲した音楽,音楽を記述したテキスト,テキスト楽音合成による音楽の比較 15. 歌声の発音タイミングの知覚時刻に関する分析 16. AI生成音楽に対するバイアスと知覚 17. ヒットチャートと経済指標との相関関係:音楽を通じた日本とイギリスの国民性についての考察 18. 自動運指制御と偏心制御によるフルート演奏支援機構の試作 19. 呼吸や心臓の音による音楽表現の可能性 -「布スピーカー」の開発と<>と<<ふれる>>の作品制作を通して- 20. 微分音コードが発音可能なXR弦楽器システムの構築と評価 21. 発声・歌唱の研究におけるオープンイヤー型ヘッドフォンの可能性について 22. 菌糸材を用いた“育つ楽器“の制作 23. ウェアラブルキーボードによるピアノ運指情報取得の一検討 24. ユーフォニアム半自動演奏のための人工唇制御機構の開発 25. クラリネット半自動演奏ロボットの試作 26. 方向別インパルス応答を用いた音場再生・創生の試み (27. MUS142国際会議既発表・デモセッション) 27-1. Using Item Response Theory to Aggregate Music Annotation Results of Multiple Annotators 27-2. SyncViolinist: Music-Oriented Violin Motion Generation Based on Bowing and Fingering 27-3. ブレイキンDJ楽曲リアルタイム可視化CG合成システム 27-4. 変分オートエンコーダによるボーカルパーカッションへの楽器音変換システム 27-5. 篳篥の「運指」と笙の「合竹」のクロス集計表の有用性 ― 笙の役割が旋律であることの検証 27-6. ALTERN@TE WING: 既存のメディアミックス作品から二次創作されたオリジナル楽曲データベース 27-7. 歌詞に基づく歌声アノテーションのためのインタフェース構築 27-8. Sound Installation using Trees as Radio Antennas: Expressing sense of place through local ecology 27-9. 手の輪郭を用いたポリゴナル・シンセサイザーの制作 28. 弦管打複合電子楽器:kiMeraの有用性評価と展望 29. 音楽サブスクリプションにおける楽曲印象型プレイリスト推薦の効果性 30. TPS-ExJ改良の基礎的検討ースケール・モード理論への対応に向けてー 31. ピッチ分析を用いた薩摩琵琶の声楽パートにおける修飾表現の抽出 32. スーパーマーケットにおけるBGMの現状と消費者の価格評価への影響 33. 面心立方セルオートマトンの状態遷移の可聴化 34. 音楽サブスク時代における音楽体験の実態とデザイン 35. 歌声MIDI採譜における作曲スタイルを導入した言語モデルの効果の検証 36. メロディの拍子変換に関する一試行 37. 歌詞の音韻特徴量に対する適合度を考慮した歌唱曲の生成と自動評価 38. 可視化を利用したアカペラ練習システムの提案 39. 主観評価ラベル付きギター演奏データセットの作成および演奏評価システムの構築 40. テキストプロンプトによる楽器別楽曲編集AI 41. 日本のポピュラー音楽のコード進行の変遷を分析する方法の検討 42. オンライン化とデータ可視化により微分音をはじめとする音の評価・分析プロセスを効率化するWebアプリケーション 43. 歩行中の風景画像とユーザーの感情に基づいた音楽生成システム 44. 日本語ヒット曲における歌詞の感情とコード進行の相関分析と可視化 45. ピアニスト角野隼斗の作品と活動の特徴とその受容研究 46. 歌唱テクニック習得支援に向けた3D可視化手法の評価 47. アイドルグループ楽曲スタイルにもとづく音楽コーパス 48. LLMによる楽曲推薦結果を精査するための可視化 49. ファラデー波の波動模様による3つの可視化手法を通じたサイマティクスの再考 50. Systematic Review of Decision-Making Processes in Astronomical Data Sonification 51. 半自動楽器ロボットを活用した初心者向け演奏上達支援システムの開発 52. 大局的構造生成のための小節特徴量系列モデリングに基づく階層的自動作曲 53. 絵画鑑賞時の視線移動に基づく音楽生成 54. 手の輪郭を用いたポリゴナル・シンセサイザーの制作 55. 目標演奏と異なるドライシグナル条件下でのオーディオエフェクト推定 56. ピッチとテンポの変更によるポピュラー音楽への印象変化に関する研究 57. J-POPアカペラアレンジにおけるシラブルの楽曲構造ごとの使用傾向と楽器との関係性の分析 58. 女声三部合唱曲の主旋律推定の性能向上に関する検討 59. 同一曲を演奏するオーケストラの音響特徴量比較のための可視化分析 60. 同一演奏家による同一演奏曲の差異をどう評価するか 61. リハーサルマークによる旋律クラスタリングに基づく電子オルガン譜リダクション 62. 音楽大学の学生を対象としたソルフェージュ視唱の学習観の調査 63. ピアノ演奏MIDIデータに基づいたフレーズ・アーチングの分析 64. サプライズに着目した芸術的逸脱の定量的分析2025年1月31日(金)
八潮市の道路陥没事故は、全国津々浦々に同様の危険性を提起したのだが、津波に洗われるような場所に野球場を作ろうとするよりも、まずは市内の上下水道網の老朽化調査と対策を優先するべきではないのか(浜松でも毎年のように水道管破裂のニュースがある)・・・という判断が出来ない人々には困ったもんである。
昨日のJoyJoyヒトカラでは6時間で60曲を完走した。 今月の戦果は以下であり、それ以前の情報については この日記のPart4 の「2024年12月29日(日)」のところにある。今日は晩の某バイト[2]に行って、いよいよ電子工作の 新作 をお披露目、という楽しみがある。
- 2025年1月1日(水) 4時間 19曲 (拓人泰人)
- 2025年1月8日(水) 6時間 59曲
- 2025年1月15日(水) 6時間 59曲
- 2025年1月22日(水) 6時間 59曲
- 2025年1月30日(木) 6時間 60曲
昨日の午後にはAmazonで注文していた「4連8*8マトリクスLED」が届いていたのだが、某バイト[2]の来月のサンプル教材(2コース分)を作ったりする必要があり、遊ぶのはちょっと先になるかもしれない。 ジャイロ3軸センサの確認と、Arduinoのタイマ割り込みの宿題も頭に残っているので、こちらもおいおい着手となる。
![]()
ネットニュースからは、「売ってOK? システム要件に満たないPCにWindows 11を導入する"インストーラーUSB"が販売される」という記事が流れてきた。 僕はWindowsについては全く知らないのだが、どうも5ちゃんに書かれていた「Windowsの要求スペックはMicrosoftとハードメーカーの癒着の結果でしかない」という事実が背景にあるらしい。 さらに「このブートディスクは外部ツールを使うと無料で作成することができるため、個人でUSBなどのメディアを持っているならば、特段購入する必要はない。"Rufus"などを利用して作成できるが、それらの操作が難しいユーザーのための製品であるようだ」とのことで、Amazonではちゃんと以下のようにこういうUSBメモリを売っていた。 なかなかに、ぁゃιぃ世界のようだ。
![]()
そして夕方に某バイト[2]に出掛けるまでの時間を費やして、作業が残っていた24本のロボプロコースのテキストPDF化作業、さらにタームごとの講習マニュアルなどもせっかくなので全てまとめてみた。 さらにロボプロコースで登場するArduinoサンプルスケッチを調べてみると、全て「Windowsベース」限定となっていたが、臨時に我が家のWindowsノートを引っ張り出してきてexeになっているインストーラからライブラリ/サンプル集を解凍してMacに持ってきた。
ただし、 このページ と格闘したのだが、どうしても手作業でIDEのライブラリにインストール出来なかったので、いちいちヘッダファイルを横に置く必要がある模様である。 まぁ、そういうのはこれまでにもやって来たので、何とかなるだろう。2025年2月1日(土)
この週末は何も予定が無いということで、午前から宿題の消化に取り掛かった。 ジャイロセンサについては、僕はこれまで完全に「角速度じゃなくて角加速度のセンサ」と思っていたのだが、 ここ とか ここ とか ここ とか ここ とか ここ とか ここ とか ここ とかを見てみると、どうも僕が間違っていたようである。 講義の中で嘘を教えてきた学生たち、ごめんなさい(^_^;)。 角加速度は、ジャイロの出力を微分して求めることになるようだ。
そしてArduinoの割り込みについても、ちょっと調べてみたら ここ とか ここ とか ここ とか ここ とか ここ とか ここ とかに詳細に語られていた。 過去にはAKI-H8やKAWAI時代のマイコンでは、色々な割り込みをアセンブラで記述していたのだが、「Arduinoは非力なので」という思い込みから、これは盲点だった。 僕が Arduino日記 の中で、「ArduinoではMIDI受信は出来ない」と結論付けていたが、正しくは「ArduinoのC言語記述でポーリングによってはMIDI受信は出来ない(受け落とす)」ということであり、こうなると、MIDIをシリアル割り込みでFIFOに積んで・・・という実験をしなければならない、という新しい宿題となった。
![]()
昼過ぎには、滅多にかかってこない僕のスマホに電話がかかってきたが、数日前にも同じ午後あたりのにかかってきたのと同じ、「0800-・・・・」という番号だった。 どうせ海外からの詐欺電話だろうと数日前には即刻出ずに切っていた("decline"[拒否]をタップ)のだが、今回、ちょっと 0800から始まる電話番号はどこから? というページを見てみると、どうも国内のフリーダイヤルのようだった。 そこで、履歴に残っていたのでブラウザのURL欄に直接、この電話番号を入れてみると、上のように見事に「事業者名称 ハシヅメ(特殊詐欺グループ)」と出てきた。 『不動産事業で登記をしているが、電話口では「ライフサポートセンター」や「東京電力」、「東北電力」などの実在する電力業者を名乗っており電力料金詐欺の恐れあり。代表者がオフィス24の元役員やJMホールディングスの株主ということもあり、業務上で得た個人情報を流用して複数電話番号を使いしらみ潰しに詐欺電話をかけてアンケートを実施し詳細個人情報の収集ならびに販売している模様』とあったので、無視は正解だった。
![]()
午後には、8*8マトリクスLEDを実験してみよう・・・といくつか調べたが、どうも僕のバージョン(MacOSXおよびFirefox)ではGitHubで「code」のボタンがハイライトしなくて「ZIP download」出来ないと判明したが(Safariでも同様)、Chromeを使ってみると出来たので、今後はこういう手を駆使することになる。
そして午後じゅう集中して、3種類の「Max7219」を使ったライブラリをGitHubからゲットして、その3種類それぞれの中で「使える」主だったものを自前のテストスケッチとして動作させるところまで一気に進んだ。 上のデスクトップにある5個のArduinoスケッチはそれぞれ、全て手元の「8*8マトリクスLED*4連モジュール」で動作を確認できたものなので、明日あたりに実験動画を撮ったりしてみたい。 ここまで進んだら、もうその先には楽しい楽しい世界が待っているのだ。2025年2月2日(日)
![]()
朝イチで飛び込んできたニュースは、「EdgeがChromeで開いたタブを盗み取る謎現象が発生しているとの報告」 ★ ★ ★ というもので、MS恒例の「またか」、というやつである。 まったくマイクロソフトというのは本当に芯の芯まで腐った会社だなと昔からずっと思っているのだが、他のブラウザでアクセスしたタブの情報を盗む機能を実装するよう強制されたエンジニアが気の毒でたまらない。 ブラウザ同士が独立している(他のブラウザのクッキーやタブなどはお互いに絶対に触らない)、というのはインターネットの基盤を成す常識の筈なのだが。
![]()
そして午前にはまず上のように、昨日やってみた3種類の「Max7219」を使ったライブラリの実験スケッチを、一部整理改訂しつつ再現した。 最後のサンプルはほぼ2周していてちょっと無駄に冗長なものの、 YouTube動画 も撮ってみた。 最初のライブラリは日本人のKase Misao氏の Arduino_MAX7219_Library というもので、Processingライクな処理を作ってみたとのことで、これは今後、さらに調べていきたいものである。
上(動画の最初に出てくる)はその第1号のスケッチ「test001.ino」であり、LEDマトリクスとArduinoを結線するdefaultのピン対応として、上のようにArduinoの10/11/13ピンをMatrixのCS/Din/CLKに対応させた。 これは今後、他の2種のライブラリでも同じにしてある。 おまじないとしてsetup()で「dm.begin();」をすること、そして「dm.draw();」によって初めて表示に反映される(それまではメモリに点灯情報を設定するだけ)、というのがこのライブラリの特徴であり、「dm.allOff();」では表示は消えない(内部メモリの情報がクリアされだけ)という注意点がある。 (0,0)から(31,0)までドットを打ってラインを作り、それを次々に0行目から7行目まで進めるという動作であり、「dm.point(j, i);」で4ブロックの8*8マトリクスの任意の座標を光らせることが出来るので、これは最低限のスタートラインとなる。/* Arduino MAX7219 * 11 -------- DIN * 10 -------- CS * 13 -------- CLK */ #include<MAX7219_DotMatrix.h> #define MATRIX_ROW 1 #define MATRIX_COL 4 #define MATRIX_DOT 8 MAX7219_DotMatrix dm = MAX7219_DotMatrix(MATRIX_ROW, MATRIX_COL); void setup() { dm.begin(); dm.allOff(); dm.draw(); } void loop() { for(int i=0; i<MATRIX_DOT*MATRIX_ROW; i+=1){ for(int j=0; j<MATRIX_DOT*MATRIX_COL; j+=1 ){ dm.point(j, i); dm.draw(); delay(10); } } dm.allOff(); dm.draw(); delay(1000); }上(動画の2番目に出てくる)は「7*5」ドットのテキスト表示のスケッチ「test002.ino」であり、「MAX7219_DotMatrix_charSet.h」をincludeしておく必要がある。 この例では2つの文字列を、表示位置の指定と共に繰り返している。#include <MAX7219_DotMatrix_charSet.h> #define MATRIX_ROW 1 #define MATRIX_COL 4 #define MATRIX_DOT 8 MAX7219_DotMatrix_charSet dm = MAX7219_DotMatrix_charSet(MATRIX_ROW, MATRIX_COL); void setup() { dm.begin(); } void loop() { dm.allOff(); dm.setCursor(1,1); dm.printStr("Hello"); dm.draw(); delay(1000); dm.allOff(); dm.setCursor(0,0); dm.printStr("World"); dm.draw(); delay(1000); }上(動画の3番目に出てくる)は「7*5」ドットのテキスト表示をスクロールさせるスケッチ「test003.ino」であり、「dm.setScrollStr(tmp);」と指定しておくとずっとスクロール表示する。 コメントアウトしている「dm.setBlink(true);」を有効にすると、全体がブリンクしながらスクロールする。 ここまでがライブラリ「Arduino_MAX7219_Library」を使った最初のとっかかりであり、どうもこのライブラリには ここ を見ると豊富な機能があるようなので、さらに深堀りしていきたい。#include<MAX7219_DotMatrix_charSet.h> #define MATRIX_ROW 1 #define MATRIX_COL 4 #define MATRIX_DOT 8 MAX7219_DotMatrix_charSet dm = MAX7219_DotMatrix_charSet(MATRIX_ROW, MATRIX_COL); void setup() { dm.begin(); String tmp = "A0 = " + (String)analogRead(A0); dm.setScrollStr(tmp); // dm.setBlink(true); // blink } void loop() { dm.draw(); }上(動画の4番目に出てくる)はVUメータのような動作をするスケッチ「test101.ino」であり、Rob Tillaart氏の作った MATRIX7219 というライブラリを使ってみた最初の実験である。 ちなみにdefaultのピン対応は変更して、「Arduino_MAX7219_Library」と同一にしてある。 4つの8*8マトリクスのうち左端と右端の2個では、「mx.setReverse();」によって左右反転して、8列のランダム表示によってVUメータっぽくしているが、中央の2個のマトリクスを見ると、「mx.setRow(3, 7, 2);」では下から3行目のラインにデータ[7]=[11100000]が(左右反転)表示されているように、マトリクスを指定してビットマップで任意の表示が可能のようだ。 これはドット単位よりもキャラクタ系では使えるものであり、アセンブラ出身の僕としては好きなライブラリと言える。 このライブラリのexamplesには「MATRIX7219_performance」というスケッチがあり、それぞれのコマンドの処理時間を計測しているあたり、Rob Tillaart氏にしっかりとしたエンジニア魂を見た。#include "MATRIX7219.h" uint8_t dataPin = 11; uint8_t selectPin = 10; uint8_t clockPin = 13; uint8_t count = 4; MATRIX7219 mx(dataPin, selectPin, clockPin, count); void setup() { mx.begin(); mx.clear(); mx.setBrightness(3); mx.setReverse(false); mx.setRow(3, 7, 2); mx.setRow(4, 5, 3); } void loop() { mx.setReverse(true); for (int n = 1; n < 9; n++){ mx.setRow(n, (1 << random(7)) - 1, 1); delay(10); } mx.setReverse(false); for (int n = 1; n < 9; n++) { mx.setRow(n, (1 << random(7)) - 1, 4); delay(10); } }上(動画の5番目に出てくる長い[2周している]やつ)はスケッチ「test201.ino」であり、majicDesignsの作った MD_MAX72XX というライブラリの、「こんなに出来るんだぜ!!」というデモスケッチである。 詳細はスケッチを見ればわかるが、ここでのピン対応はdefaultで「Arduino_MAX7219_Library」と同じだった。 さらに「#define MAX_DEVICES 11」とあったのを「#define MAX_DEVICES 4」に変更したので、もっと長いマトリクスでもコントロールできるようである。 ここは個人というよりは「お仕事」に近いらしく、「If you like and use this library please consider making a small donation using [PayPal]」ということで「寄付してね」と書かれていた(^_^;)。#include <MD_MAX72xx.h> #define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW #define MAX_DEVICES 4 #define CLK_PIN 13 #define DATA_PIN 11 #define CS_PIN 10 MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); #define DELAYTIME 100 // in milliseconds void setup(){ mx.begin(); mx.clear(); } void loop(){ zeroPointSet(); delay(1000); mx.clear(); delay(1000); rows(); delay(1000); mx.clear(); delay(1000); columns(); delay(1000); mx.clear(); delay(1000); cross(); delay(1000); mx.clear(); delay(1000); stripe(); delay(1000); mx.clear(); delay(1000); checkboard(); delay(1000); mx.clear(); delay(1000); bullseye(); delay(1000); mx.clear(); delay(1000); bounce(); delay(1000); mx.clear(); delay(1000); spiral(); delay(1000); mx.clear(); delay(1000); intensity(); delay(1000); mx.clear(); delay(1000); scanLimit(); delay(1000); mx.clear(); delay(1000); transformation1(); delay(1000); mx.clear(); delay(1000); transformation2(); delay(1000); mx.clear(); delay(1000); wrapText(); delay(1000); mx.clear(); delay(1000); } void zeroPointSet(){ if (MAX_DEVICES > 1) mx.setChar((2*COL_SIZE)-1, '0'); for (uint8_t i=0; i<ROW_SIZE; i++){ mx.setPoint(i, i, true); mx.setPoint(0, i, true); mx.setPoint(i, 0, true); delay(DELAYTIME); } } void rows(){ for (uint8_t row=0; row<ROW_SIZE; row++){ mx.setRow(row, 0xff); delay(2*DELAYTIME); mx.setRow(row, 0x00); } } void checkboard(){ uint8_t chkCols[][2] = { { 0x55, 0xaa }, { 0x33, 0xcc }, { 0x0f, 0xf0 }, { 0xff, 0x00 } }; for (uint8_t pattern = 0; pattern < sizeof(chkCols)/sizeof(chkCols[0]); pattern++){ uint8_t col = 0; uint8_t idx = 0; uint8_t rep = 1 << pattern; while (col < mx.getColumnCount()){ for (uint8_t r = 0; r < rep; r++) mx.setColumn(col++, chkCols[pattern][idx]); idx++; if (idx > 1) idx = 0; } delay(10 * DELAYTIME); } } void columns(){ for (uint8_t col=0; col<mx.getColumnCount(); col++){ mx.setColumn(col, 0xff); delay(DELAYTIME/MAX_DEVICES); mx.setColumn(col, 0x00); } } void cross(){ mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); for (uint8_t i=0; i<ROW_SIZE; i++){ for (uint8_t j=0; j<MAX_DEVICES; j++){ mx.setColumn(j, i, 0xff); mx.setRow(j, i, 0xff); } mx.update(); delay(DELAYTIME); for (uint8_t j=0; j<MAX_DEVICES; j++){ mx.setColumn(j, i, 0x00); mx.setRow(j, i, 0x00); } } for (int8_t i=ROW_SIZE-1; i>=0; i--){ for (uint8_t j=0; j<MAX_DEVICES; j++){ mx.setColumn(j, i, 0xff); mx.setRow(j, ROW_SIZE-1, 0xff); } mx.update(); delay(DELAYTIME); for (uint8_t j=0; j<MAX_DEVICES; j++){ mx.setColumn(j, i, 0x00); mx.setRow(j, ROW_SIZE-1, 0x00); } } for (uint8_t i=0; i<ROW_SIZE; i++){ for (uint8_t j=0; j<MAX_DEVICES; j++){ mx.setColumn(j, i, 0xff); mx.setRow(j, ROW_SIZE-1-i, 0xff); } mx.update(); delay(DELAYTIME); for (uint8_t j=0; j<MAX_DEVICES; j++){ mx.setColumn(j, i, 0x00); mx.setRow(j, ROW_SIZE-1-i, 0x00); } } mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON); } void bullseye(){ mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); for (uint8_t n=0; n<3; n++){ byte b = 0xff; int i = 0; while (b != 0x00){ for (uint8_t j=0; j<MAX_DEVICES+1; j++){ mx.setRow(j, i, b); mx.setColumn(j, i, b); mx.setRow(j, ROW_SIZE-1-i, b); mx.setColumn(j, COL_SIZE-1-i, b); } mx.update(); delay(3*DELAYTIME); for (uint8_t j=0; j<MAX_DEVICES+1; j++){ mx.setRow(j, i, 0); mx.setColumn(j, i, 0); mx.setRow(j, ROW_SIZE-1-i, 0); mx.setColumn(j, COL_SIZE-1-i, 0); } bitClear(b, i); bitClear(b, 7-i); i++; } while (b != 0xff){ for (uint8_t j=0; j<MAX_DEVICES+1; j++){ mx.setRow(j, i, b); mx.setColumn(j, i, b); mx.setRow(j, ROW_SIZE-1-i, b); mx.setColumn(j, COL_SIZE-1-i, b); } mx.update(); delay(3*DELAYTIME); for (uint8_t j=0; j<MAX_DEVICES+1; j++){ mx.setRow(j, i, 0); mx.setColumn(j, i, 0); mx.setRow(j, ROW_SIZE-1-i, 0); mx.setColumn(j, COL_SIZE-1-i, 0); } i--; bitSet(b, i); bitSet(b, 7-i); } } mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON); } void stripe(){ const uint16_t maxCol = MAX_DEVICES*ROW_SIZE; const uint8_t stripeWidth = 10; for (uint16_t col=0; col<maxCol + ROW_SIZE + stripeWidth; col++){ for (uint8_t row=0; row < ROW_SIZE; row++){ mx.setPoint(row, col-row, true); mx.setPoint(row, col-row - stripeWidth, false); } delay(DELAYTIME); } } void spiral(){ int rmin = 0, rmax = ROW_SIZE-1; int cmin = 0, cmax = (COL_SIZE*MAX_DEVICES)-1; while ((rmax > rmin) && (cmax > cmin)){ for (int i=cmin; i<=cmax; i++){ mx.setPoint(rmin, i, true); delay(DELAYTIME/MAX_DEVICES); } rmin++; for (uint8_t i=rmin; i<=rmax; i++){ mx.setPoint(i, cmax, true); delay(DELAYTIME/MAX_DEVICES); } cmax--; for (int i=cmax; i>=cmin; i--){ mx.setPoint(rmax, i, true); delay(DELAYTIME/MAX_DEVICES); } rmax--; for (uint8_t i=rmax; i>=rmin; i--){ mx.setPoint(i, cmin, true); delay(DELAYTIME/MAX_DEVICES); } cmin++; } } void bounce(){ const int minC = 0; const int maxC = mx.getColumnCount()-1; const int minR = 0; const int maxR = ROW_SIZE-1; int nCounter = 0; int r = 0, c = 2; int8_t dR = 1, dC = 1; // delta row and column while (nCounter++ < 200){ mx.setPoint(r, c, false); r += dR; c += dC; mx.setPoint(r, c, true); delay(DELAYTIME/2); if ((r == minR) || (r == maxR)) dR = -dR; if ((c == minC) || (c == maxC)) dC = -dC; } } void intensity(){ uint8_t row; row = 0; for (int8_t i=0; i<=MAX_INTENSITY; i++){ mx.control(MD_MAX72XX::INTENSITY, i); if (i%2 == 0) mx.setRow(row++, 0xff); delay(DELAYTIME*3); } mx.control(MD_MAX72XX::INTENSITY, 8); } void scanLimit(void){ mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); for (uint8_t row=0; row<ROW_SIZE; row++) mx.setRow(row, 0xff); mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON); for (int8_t s=MAX_SCANLIMIT; s>=0; s--){ mx.control(MD_MAX72XX::SCANLIMIT, s); delay(DELAYTIME*5); } mx.control(MD_MAX72XX::SCANLIMIT, MAX_SCANLIMIT); } void transformation1(){ uint8_t arrow[COL_SIZE] = { 0b00001000, 0b00011100, 0b00111110, 0b01111111, 0b00011100, 0b00011100, 0b00111110, 0b00000000 }; MD_MAX72XX::transformType_t t[] = { MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TFLR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TRC, MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TFUD, MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TINV, MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TINV }; mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); for (uint8_t j=0; j<mx.getDeviceCount(); j++) mx.setBuffer(((j+1)*COL_SIZE)-1, COL_SIZE, arrow); mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON); delay(DELAYTIME); mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::ON); for (uint8_t i=0; i<(sizeof(t)/sizeof(t[0])); i++){ mx.transform(t[i]); delay(DELAYTIME*4); } mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::OFF); } void transformation2(){ MD_MAX72XX::transformType_t t[] = { MD_MAX72XX::TINV, MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TINV, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSD, MD_MAX72XX::TSU, MD_MAX72XX::TSD, MD_MAX72XX::TSU, MD_MAX72XX::TFLR, MD_MAX72XX::TFLR, MD_MAX72XX::TFUD, MD_MAX72XX::TFUD }; mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::OFF); for (uint8_t j=0; j<mx.getDeviceCount(); j++) mx.setChar(((j+1)*COL_SIZE)-1, '0'+j); delay(DELAYTIME*5); for (uint8_t i=0; i<(sizeof(t)/sizeof(t[0])); i++){ mx.transform(t[i]); delay(DELAYTIME*3); } } void wrapText(){ mx.wraparound(MD_MAX72XX::ON); for (uint16_t j=0; j<mx.getDeviceCount(); j++) mx.setChar(((j+1)*COL_SIZE)-1, (j&1 ? 'M' : 'W')); delay(DELAYTIME*5); for (uint16_t i=0; i<3*COL_SIZE*MAX_DEVICES; i++){ mx.transform(MD_MAX72XX::TSL); delay(DELAYTIME/2); } for (uint16_t i=0; i<3*COL_SIZE*MAX_DEVICES; i++){ mx.transform(MD_MAX72XX::TSR); delay(DELAYTIME/2); } for (uint8_t i=0; i<ROW_SIZE; i++){ mx.transform(MD_MAX72XX::TSU); delay(DELAYTIME*2); } for (uint8_t i=0; i<ROW_SIZE; i++){ mx.transform(MD_MAX72XX::TSD); delay(DELAYTIME*2); } mx.wraparound(MD_MAX72XX::OFF); }
ここまで整理してきて、3つのライブラリのうち MD_MAX72XX は半分「商用」モード、 MATRIX7219 は性能重視のアセンブラ魂、そして Arduino_MAX7219_Library は「Processing風」という可能性を見た。 まずは3番目のやつを攻略してみたいのだが、それより喫緊には「某バイト[2]のサンプル教材作り」という優先度があるので、ここでArduinoを離れて(IDEをquitして)、まずはそちらに取り組むことにした。
![]()
そして午後じゅうかかって、ようやくサンプル教材が完成したのだが、これは教室に持って行く段階で壊れそうでなかなか大変、と判明した。 その後にネットニュースを調べて判明したのだが、今日は「第50期 棋王戦 五番勝負」(藤井聡太棋王 vs 増田康宏八段)の第1局だった。 局面は毎度のことで、AI評価値が「50%ずつ」のままでじりじりと持ち時間が消費される展開で、これは晩の決着までは長そうである。 さらに「H3ロケット5号機によるみちびき6号の打ち上げ」のライブ中継が17時半からだ・・・とも判明して、いやいや今日の夕方~晩は大変なようである。 全国的に雪とか雨とかだそうで、こういう日はネット中継に浸るというのも手なのだろう。
その後、 ここ を見ながら、上のようなArduinoスケッチ「test004.ino」を実験してみたのだが、その結論として「これは完全にunderspec」と判明した。 つまり、いくらProcessing風に座標計算してくれても、たかだか8*32ドットという「超・荒い」ディスプレイなので、ライン描画も三角形も矩形も四角形も楕円も、全てとても残念な荒い荒い形になって、まったく使い物にならないのだった。 これでこのライブラリとは決別して、 MATRIX7219 に集中する、と決定した。#include<MAX7219_DotMatrix.h> #define MATRIX_ROW 1 #define MATRIX_COL 4 #define MATRIX_DOT 8 MAX7219_DotMatrix dm = MAX7219_DotMatrix(MATRIX_ROW, MATRIX_COL); void setup() { dm.begin(); dm.allOff(); dm.draw(); delay(1000); } void loop() { dm.line(0,0,31,7); disp(); dm.line(0,7,31,0); disp(); dm.triangle(15,0,0,7,31,5); disp(); dm.rect(1,1,30,6); disp(); dm.quad(10,0,0,4,20,7,31,3); disp(); dm.ellipse(20,4,10,3); disp(); delay(3000); } void disp(){ dm.draw(); delay(3000); dm.allOff(); dm.draw(); delay(1000); }
そして早めの夕食をとって、上のようにロケット打ち上げのライヴ中継と、棋王戦のライヴ中継の2画面を堪能した。 ここに並行して肝心のArduinoプログラミングまでやる事はちょっと無理ということで、手元では赤から青に切り替えた8*8マトリクスLEDがずっと光っているまま、2画面を眺める時間となった。 将棋はいよいよ双方の持ち時間が無くなってきて、AIの評価値も一手ごとにフラフラ振れる・・・というなかなかスリリングな展開となっている。 そしてどうやらロケット打ち上げ成功、となってYouTubeライヴ中継の画面を閉じて、再びArduinoの実験に入った。
じりじり進む棋王戦を見ながら、Arduinoで「MATRIX7219」ライブラリの一つ一つの項目をテストしてみて、上のような経験則を得た。 要するに最もシンプルなのが「MATRIX7219」なのだった。 ただし、至れり尽くせりの「MD_MAX72XX」も捨て難いので、追って実験してみたい。
- 当面、「MATRIX7219」ライブラリを使う
- ピン対応は「MATRIX7219 mx(11,10,13,4);」で統一しておく
- 最初にsetup()で「mx.begin();」を宣言する
- 明るさは「mx.setBrightness(3);」とする
- LEDの全クリアは「mx.clear();」
- データの表示形式は「mx.setRow(a, b, c);」
- a は8*8マトリクスの下からのライン番号 ※ 1~8 である (0~7じゃない)
- b は表示すべきデータ(8桁バイナリ)
- c は表示するマトリクスの左からの番号 ※ 1~4 である (0~3じゃない)
- b のバイナリのLSBが左、MSBが右 ← 「mx.setReverse(false);」の宣言のあとずっと (default)
- b のバイナリのLSBが右、MSBが左 ← 「mx.setReverse(ture);」の宣言のあとずっと
- defaultは指定されたビットを光らせる「mx.setInvert(false);」
- これを「mx.setInvert(true);」と宣言すると以降の表示は全ビット反転
- タテヨコを替えた「mx.culumnRow(a, b, c);」などというものは無い
そして晩には、藤井棋王が危なげなく初戦に勝つところまで見届けた。 どうも最近の藤井七冠は、途中まではAI評価値が相手を下回ってファンをやきもきさせるのが普通になっていて、そこから「AI超えの一手」とか「相手の緩手を咎める一手」とかを披露する・・・というパターンが多くなって、「一気の藤井曲線」パターンを敢えて避けるという茨の道を志向しているように思う。 タイトル戦の相手は全て、練りに練った藤井対策の事前研究をぶつけてくるのに、あくまでフラットな白紙状態で臨んだ対局の現場で、その場で深く深く沈思黙考して解決していく、という姿勢は実に素晴らしい。2025年2月3日(月)
Arduinoの「8*8マトリクスLED4段モジュール」の実験をする、というフェーズだったが、午後に2時間ほどかけて このように ハンダ付けをして作ったのは、以下のような「実験プラットフォーム」である。 昨日までのモジュールで新たにArduinoスケッチを実験してもいいのだが、何事にも「ついで」というのは必要なので、ここでは「ついで」に、過去に1個500円ぐらいで購入してまだ触っていなかった、以下の「小型OLEDモジュール」を繋いでみることにした。
![]()
通常はArduino IDEのシリアルモニタを使うのが簡単なのだが、「8*8マトリクスLED4段モジュール」もコントロールにI2Cを使うためか、シリアルモニタにデータを垂れ流すスケッチを書き込むと、IDEがけっこうな頻度で「シリアルデバイスを見失う」ために、いちいちUSBから抜いてまた挿して・・・というのを繰り返すのが面倒だったことも理由である。 こちらのモジュールもI2Cで別ピンを使うのだが、両者混在の実験も兼ねている。 そして、さらにとりあえず4個ほどプッシュスイッチを繋いで、さらにゲーム用にブザーも繋いでみた。 回路図を描くほどのこともなく、ハードウェアのメモとしては以下のようになる。
このOLEDモジュールは、SSD1306というチップがコントローラとなっているが、既に僕のArduino IDEのライブラリを調べてみると、インストールが必要だという「Adafruit GFX Library by Adafruit」と「Adafruit SSD1306 by Adafruit」については、AdafruitのOLEDモジュールで実験した時にインストールが完了していた。 そして、日本語でもこのモジュールは色々なWebサイトに記事があったので、手当たり次第に実験してみることにした。 かなり小さいものではあるが、「道具」としてこのモジュールを使えば、今後はもうAdafruitのOLEDモジュールは追加購入する必要もなくなりそうだ。
- 「8*8マトリクスLED4段モジュール」での使用ピンは上述のように、10(CS)、11(Data)、13(CLK)
- ブザーは12ピン
- 「SSD1306OLEDモジュール」での使用ピンはA4とA5(SCLとSDA)なのでアナログでA4とA5は使用不可
- スイッチは10KΩプルアップ(負論理)で、ピンは6(黄)、7(青)、8(赤)、9(緑)
![]()
YAHOOで検索して出てきた順に実験したが、最初は このページ からである。 このページにあったデモサンプルプログラムはAdafruit社のもののようで、無事に「8*8マトリクスLED4段モジュール」で「MATRIX7219」ライブラリを使った表示との共存を確認できた。 このスケッチ「test001.ino」は膨大な描画デモがあるので、今後、さらに検討していくことになる。
次は このページ にあった、比較的シンプルに見えるサンプルである。 こちらはシリアルモニタを使わず、さらにテキスト表示にserial.printとserial.printlnライクなものまであった。 これを「test101.ino」として、こちらも「MATRIX7219」ライブラリを使った表示との共存を確認できた。 数値を表示するだけなら、これはかなり便利なものだろう。
次は このページ にあったサンプルスケッチである。 そのままコンパイルして走ったのだが、テキストサイズを変えたりできて、なかなか便利だった。 ところが、何故か手直し(関係なさそうなところ)すると、今度はコンパイル→アップロードしても正しく走らない、という現象が何度も起きて困った。 これを「test201.ino」として、その後、あれこれ調べてサイトのサンプルのバグ(初期化が抜けていた)を修正して、「MATRIX7219」ライブラリを使った表示との共存を確認できた。 なかなか高機能なので、これもきちんと調べてみたい。
次は このページ にあったサンプルスケッチである。 これを「test301.ino」として、こちらもサイトのバグ(初期化が抜けていた)を修正して、「MATRIX7219」ライブラリを使った表示との共存を確認できた。 テキストサイズを指定して表示する、という最低限のものとしては、十分に使えるものである。
![]()
ここまでで、とりあえず4種類の「SSD1306・OLEDモジュール」のサンプルプログラムが、「8*8マトリクスLED4段Max7219モジュール」と共存できる形で出揃った。 コンパイルしてみると、ArduinoUNOのメモリの65%ぐらいまでいくものと30%台のものまであり、機能と容量とで使い分けるのがbestなのだろう。 色々と実験してみたい項目が出てきたが、今日は晩に某バイト[1]もあり(中3生は明日と明後日が私立高校の入試なので激励すべし・・・と連絡が来ている)、続きは明日以降になったが、なかなか充実の一日である。
2025年2月4日(火)
今日は何も予定が無いということで、このところの懸案である「8*8マトリクスLED4段Max7219モジュール」の3種類のライブラリを詳しく攻略する、という第一の目標に対して、昨日は、ある意味で「割り込み」として、道具として使う「SSD1306・OLEDモジュール」が加わり、この道具のための4系統のサンプルも攻略したい、という第二の目標が登場した。 本来であればこれを進めるところなのだが、実はさらに残っていた懸案として「Arduinoタイマ割り込みを使う」という第三の目標があるので、まずはここから攻めてみる事にした。
![]()
とりあえず「Arduino timer interrupt」と検索すると、いきなり本命の このページ に行き着き、該当するライブラリをダウンロード/インストールしてみると、上のように、これは最新の「TimerInterrupt-master」というものだったのだが、既に僕のArduino IDEにはほぼ同じ構成の「TimerInterrupt」というライブラリがインストールされていて、さらに別の「TimerInterrupt_Generic」という巨大なものもインストールされていて、ここではCPUタイプごとにそれぞれのexamplesが並んでいた。 この3つを共存させていると問題が起きるとすれば、最新の「TimerInterrupt-master」だけ残していけばいいのだろう。
そしてさらに検索結果から、関連した「Arduinoタイマ割り込み」を解説するページとして、 解説サイト(1) とか 解説サイト(2) とか 解説サイト(3) とか 解説サイト(4) とかのページも発見した。 ところが検索の最後には日本語の Arduinoでtimerを使った割込み処理をライブラリ無しで使う方法 というページが見つかり、「ライブラリ無し」という言葉に惹かれて、まずはここから調べることにした。 結局、今日のお勉強の図式としては、以下のような順序となっていて、上から順に解決していく(→解決した場合にはその下をパス出来る)事になった。ということで、まず Arduinoでtimerを使った割込み処理をライブラリ無しで使う方法 を眺めてみた。 中身としては、アセンブラでばりばり多重の割り込みを駆使してきた僕にとっては、完全に「釈迦に説法」ものだったのでずんずん進んでいくと、以下の表が出てきた。 内部的に多数持っているタイマを外部ピンにも割り当てている(PWM出力はタイマを使っているし、考えてみればブザーも同じだった)ので、これを見ると例えば現在のArduinoシステムではブザーは12ピンとなっているが、もしtone()を使うと、ブザー出力の12ピンだけでなくTimer2の「対応PWMピン」である11ピンに影響が出る可能性がある。 11ピンをPWM出力に使うのは出来ないとしても、問題は11ピンを「8*8マトリクスLED4段Max7219モジュール」に使っているので、これが駄目となれば大きな問題となる。
![]()
そこで このページ に従って、最初の実験として「Timer2を使用してオンボードLEDを点滅」というのをやってみることにした。 繋がっているデバイス等を無視して、以下のようにexamplesの「blink」を起点とした「test001.ino」を作ってみた。 Clock = 16MHzで 1 count = 0.0625μsec なので、プリスケーラで最大の1024分周として、1 count = 64μsec となる。 これでカウント値を255とすれば、割り込み間隔は16.384msecとなるので、割り込みルーチン内でさらに61回ソフトタイマを回せば、999.424msecとほぼ1秒になる筈である。
そして無事に上のスケッチ「test001.ino」で、13ピンのオンボードLEDは1秒点滅の動作となった。 ただし、この13ピンは現在、「8*8マトリクスLED4段Max7219モジュール」のCLKピンに繋がっているので、上のスケッチに過去の「8*8マトリクスLED4段Max7219モジュール」スケッチを合体させてどうなるか、まずはここから実験が必須となった。 そこでこのスケッチを一旦保存して、さらにduplicateして「test002.ino」とrenameして、これに過去にもっとも簡単(モジュールを全消灯するだけ)というスケッチを合体させた、以下のようなプログラムとなった。int soft_counter; void setup() { pinMode(13, OUTPUT); TCCR2A = 0; TCCR2B = 0; TCCR2A |= 0b00000010; //CTC mode TCCR2B |= 0b00000111; //1024分周 OCR2A = 256-1; //256カウントごと TIMSK2 |= (1 << OCIE2A); soft_counter = 0; } ISR (TIMER2_COMPA_vect) { if(++soft_counter > 61){ soft_counter = 0; digitalWrite(13, !digitalRead(13)); } } void loop() { }すると案の定、「8*8マトリクスLED4段Max7219モジュール」は全消灯したものの、オンボードLEDは点灯しなくなった。 これは当然のことで、逆にこれでLED点滅したら、ちょっと怖い。 結論として、「タイマ割り込みに関連するポートは他の用途に同時には使えない」という基礎的な原則を確認できた。 そして、16ビットのTimer1も、対応PWMピンとして9ピン/10ピンとあるので、こちらも現状の「8*8マトリクスLED4段Max7219モジュール」との共存はNGとなる。#include<MAX7219_DotMatrix.h> #define MATRIX_ROW 1 #define MATRIX_COL 4 #define MATRIX_DOT 8 MAX7219_DotMatrix dm = MAX7219_DotMatrix(MATRIX_ROW, MATRIX_COL); int soft_counter; void setup() { pinMode(13, OUTPUT); TCCR2A = 0; TCCR2B = 0; TCCR2A |= 0b00000010; //CTC mode TCCR2B |= 0b00000111; //1024分周 OCR2A = 256-1; //256カウントごと TIMSK2 |= (1 << OCIE2A); soft_counter = 0; dm.begin(); dm.allOff(); dm.draw(); } ISR (TIMER2_COMPA_vect) { if(++soft_counter > 61){ soft_counter = 0; digitalWrite(13, !digitalRead(13)); } } void loop() { }
残るTimer0については、対応ピンとして5ピン/6ピンとあるが、現状まだ使っていないプッシュボタンが[6,7,8,9]ピンだったので、6ピンだけ他に引っ越せば、あとは「delay()やmillis()などを使わない」という制約の下で共存の可能性がある。 それもやってみたのだが、しかし結果はNGだった。 やはり、外部ピンと結び付いているタイマ割り込みというのは、過去に活用したマイコンとはちょっと違って、色々と制約がある模様だ・・・と判明した。 かといって、周辺ポートはがんがん活用したいので、当面「Timer割り込みは使わない」という従来のポリシーを維持することとしたい。 これで今日のメニューの第一項目「タイマ割り込みについての理解と実験」は「使わない」という結論になったので、続く「SSD1306・OLEDモジュール」の各サンプルの解析に進むことにした。
![]()
まず起点として、昨日の実験でゲットした4種類の「SSD1306・OLEDモジュール」のサンプルスケッチ「test001」・「test101」・「test201」・「test301」を上のように全てデスクトップに広げて、ちょっとOLED画面は見にくい(さらにチラチラするのは仕方ない)のだが、この順にコンバイル・アップロードして、その動作を YouTube動画 に記録してみた。
次いで、これら(「8*8マトリクスLED4段Max7219モジュール」と共存しているところが重要)をduplicateして(元のファイルは保存)、ファイル名前の番号もincrementして、不要なコメントとか使わないものを除外して、今後に使えそうなオリジナル起点としてのスケッチを作ることにした。 「test002」はAdafruit社のゴージャスなデモ満載だったのだが、とりあえず欲しいのは色々なテキスト表示(サイズ、反転、スクロールなど)なので、さらにこの部分だけ切り出した「test003」も作った。 そしてこの「test003」は、調べてみると「test101」や「test301」の機能を全て含んでいるので100番台と300番台はパスして、残った「test202」に取り掛かった。
しかし「test202」で目新しいものは「特定の行だけスクロール」という技だけだったので、これも「test003」に取り込んで200番台も終わり、結論として「SSD1306・OLEDモジュール」でテキスト表示をするサンプルとしては、以下の「test003.ino」で全てカバー出来ることになった。 これは大いなる進展である。これで今日のメニュー第二項目「SSD1306・OLEDモジュール」の活用にまで目処が立った。 そこで、ここで得られた「test003.ino」をduplicateして「SSD1306_text.ino」とrenameして、それを「LED8*8*4Matrix」ディレクトリ直下に、3種類のライブラリと並べて置いた。 これにより、ようやく今日のメニュー第三項目「8*8マトリクスLED4段Max7219モジュール」の各ライブラリの攻略に進めることになる。 長い道のりだったが、だいぶ体感としてはこれらのモジュールが身近になった気がする。#include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include "MATRIX7219.h" MATRIX7219 mx(11,10,13,4); Adafruit_SSD1306 display(128, 64, &Wire, -1); int i; void setup() { mx.begin(); mx.setBrightness(3); mx.clear(); mx.setRow(8, 0x0c, 4); display.begin( SSD1306_SWITCHCAPVCC, 0x3C ); display.clearDisplay(); display.display(); } void loop() { testdrawstyles(); demo_wait(); testdrawchar(1); demo_wait(); testdrawchar(2); demo_wait(); testscrolltext(1); demo_wait(); testscrolltext(2); demo_wait(); } void testdrawstyles(void) { display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); // Draw white text display.setCursor(0,0); display.println(F("Hello, world!")); display.setTextColor(BLACK, WHITE); // Draw 'inverse' text display.println(3.141592); display.setTextSize(2); display.setTextColor(WHITE); display.print(F("0x")); display.println(0xDEADBEEF, HEX); display.display(); } void testscrolltext(int j) { display.clearDisplay(); display.setTextSize(j); display.setTextColor(WHITE); display.setCursor(0, 0); display.println("Full/Some"); display.println("screen"); display.println("scrolling!"); display.println("Hello,"); display.println("World!"); display.display(); delay(1000); display.startscrollright(0x00, 0x00); delay(3000); display.stopscroll(); delay(1000); display.startscrollleft(0x00, 0x00); delay(3000); display.stopscroll(); delay(1000); display.startscrollright(0x00, 0x02); delay(3000); display.stopscroll(); delay(1000); display.startscrollleft(0x00, 0x02); delay(3000); display.stopscroll(); delay(1000); display.startscrollright(0x00, 0x06); delay(3000); display.stopscroll(); delay(1000); display.startscrollleft(0x00, 0x06); delay(3000); display.stopscroll(); delay(1000); display.startscrollright(0x00, 0x0f); delay(3000); display.stopscroll(); delay(1000); display.startscrollleft(0x00, 0x0f); delay(3000); display.stopscroll(); } void demo_wait(){ delay(1000); display.clearDisplay(); display.display(); delay(1000); } void testdrawchar(int j) { display.clearDisplay(); display.setTextSize(j); display.setTextColor(WHITE); display.setCursor(0, 0); display.cp437(true); // Use full 256 char 'Code Page 437' font for(i=0; i<256; i++) { if(i == '\n') display.write(' '); else display.write(i); } display.display(); }
ここからは、対応ライブラリが「Arduino_Max7219_Lib」・「MATRIX7219」・「MD_MAX72XX」の3種類に分かれる。 今度はテキストというよりは実際のドットマトリクス・グラフィクスになるので、きちんと進めていかないといけないが、タイマ割り込みは使わない、それぞれのピンは重複の心配が無い、などの「押さえ」が出来ているので、試行錯誤を進めるための環境としてはだいぶ進展しているのだった。日曜日(2025年2月2日(日))の最後のところを見返してみると、「MATRIX7219」ライブラリというのは8*8マトリクスを1個単位でアクセスするので、バイナリで表示パターンを作って、それが4個のマスにそれぞれ表示されるという場合には適している。 ちょうどたまたま、4個のプッシュスイッチを並べたので、これが4個のマスに対応している・・・というゲームには向いている。 その一方で、全体を8*32のドットマトリクス(スクリーン)としてアクセスするのにはあまり適していない。 例えば、あるキャラクタを8*8の範囲内で描画するのは簡単だとしても、それを隣のマスにスクロールさせる・・・というのは相当に面倒なことになる。 そして「MD_MAX72XX」ライブラリはまさに、そのあたりが強いのだ、と確認できたので、いつでも「MATRIX7219」に戻ってこれるとして、ここはやはり「MD_MAX72XX」を探っていく必要がある、ということになった。
そこで「MD_MAX72XX」ディレクトリ下にあった「test201」をduplicateして「test202」とrenameして、最小限のグラフィック機能を切り出す作業に没頭した。 結局、[使えそう・参考になりそう]という部分を切り出すと、定義部分からsetup()とloop()の中身としては上のところまでスリムになった。 4個の8*8モジュールをまたいでいるのは、最後のtransformation()だけのようで、あとは目の錯覚でまたいでいるように見えていただけだった。 このtransformation()はちょっと一見すると謎なのだが、ここを攻略するのがどうやら「肝」である・・・と判明したところで、今日の作業はオシマイとなった。 明日はJoyJoyもあるものの、少しずつ進んでいくことにしよう。#include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> Adafruit_SSD1306 display(128, 64, &Wire, -1); #include <MD_MAX72xx.h> #define MAX_DEVICES 4 #define CLK_PIN 13 #define DATA_PIN 11 MD_MAX72XX mx = MD_MAX72XX(MD_MAX72XX::PAROLA_HW, 10, MAX_DEVICES); #define DELAYTIME 100 // in milliseconds void setup(){ mx.begin(); mx.clear(); display.begin( SSD1306_SWITCHCAPVCC, 0x3C ); display.clearDisplay(); display.display(); } void loop(){ cross(); delay(1000); mx.clear(); delay(1000); stripe(); delay(1000); mx.clear(); delay(1000); checkboard(); delay(1000); mx.clear(); delay(1000); bullseye(); delay(1000); mx.clear(); delay(1000); bounce(); delay(1000); mx.clear(); delay(1000); transformation(); delay(1000); mx.clear(); delay(1000); }2025年2月6日(木)
昨日は午後のJoyJoyヒトカラ(6時間で62曲を完走した)に出掛けるまでの時間をかけて、いよいよ「8*8マトリクスLED4段Max7219モジュール」と「SSD1306・OLEDモジュール」が加わってオリジナル化してきたArduinoシステムのプログラミングに取り掛かった。 ところが最初の簡単な部分でいきなりトラブルに見舞われて、原因探求に時間がかかったが、以下の図をあらためて眺めることでようやく解決した。
![]()
僕はいつものようにブザーを、空いている12ピンとGNDに付けたのだが、いつもの起動サウンド「ドレミファソラシド」が、setup()の置かれた場所によって機能したりしなかったりした。 そこで気付いたのは、ブザーのtone()はTimer2、つまり3ピンと11ピンに関係しているが、Max7219ライブラリが11ピンをDataPinとして使っているので、それより前に置いた場合にはブザーが鳴るのに、「mx.begin();」の後だと11ピン(Timer2)をライブラリが占有するので、tone()が無視される、という現象だった。
この影響は、delay()を使うとTimer0に関係することで、スイッチを繋いでいた6ピンでも起きていた。 結局、「3,5,6,11」ピンについては、tone()とdelay()のために空けておくべし、という経験則を得た。 そのために、3種類のライブラリで共通にしていた11ピンのDataPinを4ピンに、さらにスイッチを繋いでいた6ピンを2ピンに移動することになった。 このあたりは今後もおそらく僕のシステムでは共通になるので、以下のように整理してみた。上のリストまでを書いて出掛けたのでここからが今日なのだが、実は昨日の作業は中途半端なところで時間となったため、問題は完全に解決していなかった。 今日になってさらに確認の実験を進めたところ、どうも「MD_MAX72XX」ライブラリでは何故か他のどこかでまだバッティングがあって、SSD1306・OLEDモジュールと8*8マトリクスLED4段Max7219モジュールとブザー、という混在が上手くいかなかったが、「MATRIX7219」ライブラリであればこの混在が可能である・・・と判明した。 豊富なグラフィックサンプルのある「MD_MAX72XX」ライブラリ(RAWだけでなくCOLUMNでの描画指定も出来る)は魅力なのだが、他デバイスとの相性が悪いとなれば、シンプルに行ごとの8ビットバイナリを表示できる「MATRIX7219」ライブラリを採用するしかないのだ。
- 13ピン - 「8*8マトリクスLED4段Max7219モジュール」のClock
- 12ピン - Buzzer
- 11ピン - (Buzzerのためにreserved)
- 10ピン - 「8*8マトリクスLED4段Max7219モジュール」のCS
- 9ピン - Switch汎用入力ポート
- 8ピン - Switch汎用入力ポート
- 7ピン - Switch汎用入力ポート
- 6ピン - (delay()のためにreserved)
- 5ピン - (delay()のためにreserved)
- 4ピン - 「8*8マトリクスLED4段Max7219モジュール」のData
- 3ピン - (Buzzerのためにreserved)
- 2ピン - Switch汎用入力ポート
- 1ピン - (増設serialのためにreserved)
- 0ピン - (増設serialのためにreserved)
- A5ピン - (「SSD1306・OLEDモジュール」のためにreserved)
- A4ピン - (「SSD1306・OLEDモジュール」のためにreserved)
- A3ピン - 使用可能
- A2ピン - 使用可能
- A1ピン - 使用可能
- A0ピン - 使用可能
そして午後になって、とりあえず3つのデバイスが共存するexampleとして、上のような「demo001.ino」まで到達してみたのだが、どうも細部にはまだ不安が残っている。 あの表では「9ピン、10ピン」がPWMに使われると書かれていたものの、プログラムではPWM出力(analonWrite())は使っていないのだが、どうもスイッチスキャンと「8*8マトリクスLED4段Max7219モジュール」にこれらを割り当てている関係で、試行錯誤プログラムの実験で、あるブロックを他の場所に移動しただけで何かが機能しなくなったり何かが復活したりするのだ。#include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> Adafruit_SSD1306 display(128, 64, &Wire, -1); #include <MATRIX7219.h> MATRIX7219 mx(4,10,13,4); int i, musicScale[]={523,587,659,698,784,880,988,1047}; int sw_old[]={0,0,0,0}, sw_new[4], sw_port[]={2,7,8,9}, mode[]={0,0,0,0}; int timing[4]; void setup(){ mx.begin(); mx.clear(); mx.setBrightness(3); mx.setReverse(false); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); testdrawchar(1); delay(500); display.clearDisplay(); display.display(); delay(500); text_initial_menu(); for(i=0; i<4; i++) pinMode(sw_port[i], INPUT); timing[0] = 0; for(i=0; i<9; i++){ tone(12,musicScale[i]); delay(40); noTone(12); delay(60); } } void loop(){ if(++timing[0] > 3000){ timing[0] = 0; for(i=0; i<4; i++){ sw_new[i] = ! digitalRead(sw_port[i]); if(sw_new[i] != sw_old[i]){ sw_old[i] = sw_new[i]; if(sw_new[i] == 1){ mode[i] = ! mode[i]; if(mode[i] == 1) { tone(12,musicScale[2*i]); delay(40); noTone(12); mx.setRow(2*i+1, 0x0c, i+1); } else{ mx.setRow(2*i+1, 0, i+1); } } } } } } void text_initial_menu(){ display.clearDisplay(); display.setTextSize(1); display.setCursor(0,0); display.setTextColor(BLACK, WHITE); // Draw 'inverse' text display.print(" Hello, World ! "); display.setTextColor(WHITE); // Draw white text display.setCursor(10,10); display.print("Yellow"); display.setCursor(10,20); display.print("Blue"); display.setCursor(10,30); display.print("Red"); display.setCursor(10,40); display.print("Green"); display.display(); } void testdrawchar(int j) { display.clearDisplay(); display.setTextSize(j); display.setTextColor(WHITE); display.setCursor(0, 0); display.cp437(true); // Use full 256 char 'Code Page 437' font for(i=0; i<256; i++) { if(i == '\n') display.write(' '); else display.write(i); } display.display(); }
このところの寒さでやや体調が落ちていることもあり(CPAPの影響か、一年中ずっと抗アレルギー薬を服薬しているのに今年は鼻炎がキツい)、とりあえず今日はここまでにしようか・・・と、昨日から始まっていた王将戦を追いかけてみる事にしたが、ここで再び「午後は有料中継」の壁に阻まれた。 棋士の表情や解説は無いものの、棋譜(+AI判定)だけライブ中継しているサイトを発見した(これは良かったんだっけ??)のでそちらに行ったが、まぁ、それも仕方ない。
![]()
そして解説の無い静かな棋譜(このサイトはBGM[サウンド]として鳥の声だけ、という素晴らしい設定)を眺めていたが、手持ち無沙汰なので再びArduinoプログラミングを再開して、結局、まったく途上のものなのだが、一応、「スイッチに応じて何かの芸をする」という「8*8マトリクスLED4段Max7219モジュール」の雛形が出来てきた。 まだこのプログラムはdelay()を使っている(→次に改訂すべき最優先事項)ので全くダメダメなのだが、この状態でロボット教室の生徒に意見をもらって、そこから何かに仕立て上げるというのも良い作戦かもしれない。
せっかくなので、暫定版であるが、 YouTube動画 も記録してみた。 ここから発展させるという可能性だけは、ちょっと見えてきた。 王将戦はまだまだAI評価値が二転三転するという展開で、終局はやっぱり晩になりそうだ。2025年2月7日(金)
以下に謎の写真が並んだが、左は「古代ギリシャの彫刻にパソコンが掘られていた?」という完全にガセのねたネットニュース、そして右は現在、僕が使っている時計のSEIKOにあったオリジナル画像である。 「カシオ1980円を愛用していた僕は、2008年に亡父の腕時計を形見に受け取ってからはそちらをずっと使ってます。盤面上部のSEIKO文字は外れてしまったのですが、修理してまだまだ使えるとのことで。これを着けていることで、旅行中に過去に何度かトラブルを回避できた『お守り』になってます」と某所に書き込んだ時にゲットしてみた。
![]()
![]()
そして某バイト[2]の関係で、昼間にちょっと外出して郵便局に行ったり、晩にロボット教室に行ったりした合間の時間は全て、昨日から続けていた新システムの試作実験に費やした。 しかし「ゲーム」化するためのアイデアの部分で行き詰まっていて、そのまま試作を教室に持っていったのだが、そこで生徒と一緒にこれを触っていて、遂に新しいアイデアに行き着いた。 やはり、小学生の感性はとても新鮮なのだった。 このネタを持ち帰ったので、明日は終日Arduinoプログラミングに没頭する、と決めた。
2025年2月8日(土)
そして昨日の続きの楽しい楽しいArduinoプログラミングを午前中ずっと進めて、とりあえずのバージョンが完成した。 シューティングゲームのような運動神経は不要、ランダムに任せてスイッチを押すだけなのだが、偶然、絵合せのようにパターンが連結すると、位相によって異なる動きを楽しむことが出来る、というものになった。 YouTube動画 も記録してみたが、これはたまたまの一発撮りであって、毎回、違うことが延々と起きることになる。
![]()
まだ試作中で暫定のArduinoスケッチ「demo004.ino」は上のようなものであり、ビットマップに描いた固定データを参照することなく、ばりばり「数理造形」とも言うべきそれぞれ簡単なアルゴリズムによって、いちいち計算の末に描画している。 明るい部屋に行くとちょっとおとなしめの輝度なのだが(最低値)、これを上げると目が疲れるので、こんなところである。 これを明日の某バイト[2]に持参して、午前のミドルクラスと午後のアドバンスクラスの生徒たちに提供して、反応を見てみることにした。#include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> Adafruit_SSD1306 display(128, 64, &Wire, -1); #include <MATRIX7219.h> MATRIX7219 mx(4,10,13,4); int musicScale[]={523,587,659,698,784,880,988,1047}; int sw_old[]={0,0,0,0}, sw_new[4], sw_port[]={2,7,8,9}, mode[]={0,0,0,0}; int pattern[4][3], disp_speed=1500; void setup(){ for(int i=0; i<4; i++) pinMode(sw_port[i], INPUT); mx.begin(); mx.clear(); mx.setBrightness(1); // develop = 1, final = 3 mx.setReverse(false); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); text_menu(); randomSeed(analogRead(0)); tone(12,440); delay(50); noTone(12); delay(50); tone(12,880); delay(50); noTone(12);; } void loop(){ int i,k=0; for(i=0; i<4; i++) k += mode[i]; if(k==0) delay(30); for(int i=0; i<4; i++){ sw_new[i] = ! digitalRead(sw_port[i]); if(sw_new[i] != sw_old[i]){ sw_old[i] = sw_new[i]; if(sw_new[i] == 1){ mode[i] = ! mode[i]; if(mode[i] == 1) { pattern[i][0] = random(0,25); // Random Range Setting !! pattern[i][1] = 0; pattern[i][2] = 0; tone(12,500+50*pattern[i][0]); delay(40); noTone(12); } else{ for(int n=1; n<9; n++) mx.setRow(n, 0, i+1); } text_menu(); } } } for(i=0; i<4; i++){ if(mode[i] == 1) display_patterns(i); } } void display_patterns(int i){ int n; switch(pattern[i][0]){ case(24): if(pattern[i][2] == 0){ if(pattern[i][1] == 4){ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else if(pattern[i][1] == 3){ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else if(pattern[i][1] == 2){ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x42, i+1); mx.setRow(7, 0x42, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else if(pattern[i][1] == 1){ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x42, i+1); mx.setRow(7, 0x42, i+1); mx.setRow(3, 0x24, i+1); mx.setRow(6, 0x24, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else{ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x42, i+1); mx.setRow(7, 0x42, i+1); mx.setRow(3, 0x24, i+1); mx.setRow(6, 0x24, i+1); mx.setRow(4, 0x18, i+1); mx.setRow(5, 0x18, i+1); } if(++pattern[i][1]>4) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*2.5) pattern[i][2] = 0; break; case(23): if(pattern[i][2] == 0){ if(pattern[i][1] == 0){ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else if(pattern[i][1] == 1){ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else if(pattern[i][1] == 2){ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x42, i+1); mx.setRow(7, 0x42, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else if(pattern[i][1] == 3){ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x42, i+1); mx.setRow(7, 0x42, i+1); mx.setRow(3, 0x24, i+1); mx.setRow(6, 0x24, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else{ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x42, i+1); mx.setRow(7, 0x42, i+1); mx.setRow(3, 0x24, i+1); mx.setRow(6, 0x24, i+1); mx.setRow(4, 0x18, i+1); mx.setRow(5, 0x18, i+1); } if(++pattern[i][1]>4) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*2.5) pattern[i][2] = 0; break; case(22): if(pattern[i][2] == 0){ if(pattern[i][1] == 3){ mx.setRow(1, 0xff, i+1); mx.setRow(8, 0xff, i+1); mx.setRow(2, 0x81, i+1); mx.setRow(7, 0x81, i+1); mx.setRow(3, 0x81, i+1); mx.setRow(6, 0x81, i+1); mx.setRow(4, 0x81, i+1); mx.setRow(5, 0x81, i+1); } else if(pattern[i][1] == 2){ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x7e, i+1); mx.setRow(7, 0x7e, i+1); mx.setRow(3, 0x42, i+1); mx.setRow(6, 0x42, i+1); mx.setRow(4, 0x42, i+1); mx.setRow(5, 0x42, i+1); } else if(pattern[i][1] == 1){ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x3c, i+1); mx.setRow(6, 0x3c, i+1); mx.setRow(4, 0x24, i+1); mx.setRow(5, 0x24, i+1); } else{ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x18, i+1); mx.setRow(5, 0x18, i+1); } if(++pattern[i][1]>3) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*4) pattern[i][2] = 0; break; case(21): if(pattern[i][2] == 0){ if(pattern[i][1] == 0){ mx.setRow(1, 0xff, i+1); mx.setRow(8, 0xff, i+1); mx.setRow(2, 0x81, i+1); mx.setRow(7, 0x81, i+1); mx.setRow(3, 0x81, i+1); mx.setRow(6, 0x81, i+1); mx.setRow(4, 0x81, i+1); mx.setRow(5, 0x81, i+1); } else if(pattern[i][1] == 1){ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x7e, i+1); mx.setRow(7, 0x7e, i+1); mx.setRow(3, 0x42, i+1); mx.setRow(6, 0x42, i+1); mx.setRow(4, 0x42, i+1); mx.setRow(5, 0x42, i+1); } else if(pattern[i][1] == 2){ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x3c, i+1); mx.setRow(6, 0x3c, i+1); mx.setRow(4, 0x24, i+1); mx.setRow(5, 0x24, i+1); } else{ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x18, i+1); mx.setRow(5, 0x18, i+1); } if(++pattern[i][1]>3) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*4) pattern[i][2] = 0; break; case(20): if(pattern[i][2] == 0){ for(n=0; n<8; n++){ if(n == pattern[i][1]) mx.setRow(8-n, 0xff, i+1); else mx.setRow(8-n, 0, i+1); } if(++pattern[i][1]>7) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(19): if(pattern[i][2] == 0){ for(n=0; n<8; n++){ if(n == pattern[i][1]) mx.setRow(n+1, 0xff, i+1); else mx.setRow(n+1, 0, i+1); } if(++pattern[i][1]>7) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(18): if(pattern[i][2] == 0){ for(n=0; n<8; n++) mx.setRow(n+1, 0x80 >> pattern[i][1], i+1); if(++pattern[i][1]>7) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(17): if(pattern[i][2] == 0){ for(n=0; n<8; n++) mx.setRow(n+1, 1 << pattern[i][1], i+1); if(++pattern[i][1]>7) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(16): if(pattern[i][2] == 0){ for(n=0; n<2; n++){ mx.setRow(4*n+1, 0x33, i+1); mx.setRow(4*n+2, 0x33, i+1); mx.setRow(4*n+3, 0xcc, i+1); mx.setRow(4*n+4, 0xcc, i+1); } } else if(pattern[i][2] == disp_speed*4){ for(n=0; n<2; n++){ mx.setRow(4*n+1, 0xcc, i+1); mx.setRow(4*n+2, 0xcc, i+1); mx.setRow(4*n+3, 0x33, i+1); mx.setRow(4*n+4, 0x33, i+1); } } if(++pattern[i][2] > disp_speed*8) pattern[i][2] = 0; break; case(15): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow(((15-pattern[i][1])%8)+1, (0xaa00 >> (pattern[i][1]+1)) & 0xff, i+1); mx.setReverse(false); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(14): if(pattern[i][2] == 0){ mx.setRow(((15-pattern[i][1])%8)+1, (0xaa00 >> (pattern[i][1]+1)) & 0xff, i+1); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(13): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow((pattern[i][1]%8)+1, (0xaa00 >> (pattern[i][1]+1)) & 0xff, i+1); mx.setReverse(false); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(12): if(pattern[i][2] == 0){ mx.setRow((pattern[i][1]%8)+1, (0xaa00 >> (pattern[i][1]+1)) & 0xff, i+1); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(11): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow(((15-pattern[i][1])%8)+1, (0xcc00 >> (pattern[i][1]+1)) & 0xff, i+1); mx.setReverse(false); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(10): if(pattern[i][2] == 0){ mx.setRow(((15-pattern[i][1])%8)+1, (0xcc00 >> (pattern[i][1]+1)) & 0xff, i+1); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2]>disp_speed) pattern[i][2] = 0; break; case(9): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow((pattern[i][1]%8)+1, (0xcc00 >> (pattern[i][1]+1)) & 0xff, i+1); mx.setReverse(false); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(8): if(pattern[i][2] == 0){ mx.setRow((pattern[i][1]%8)+1, (0xcc00 >> (pattern[i][1]+1)) & 0xff, i+1); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(7): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow(((15-pattern[i][1])%8)+1, (0xff00 >> (pattern[i][1]+1)) & 0xff, i+1); mx.setReverse(false); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(6): if(pattern[i][2] == 0){ mx.setRow(((15-pattern[i][1])%8)+1, (0xff00 >> (pattern[i][1]+1)) & 0xff, i+1); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(5): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow((pattern[i][1]%8)+1, (0xff00 >> (pattern[i][1]+1)) & 0xff, i+1); mx.setReverse(false); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(4): if(pattern[i][2] == 0){ mx.setRow((pattern[i][1]%8)+1, (0xff00 >> (pattern[i][1]+1)) & 0xff, i+1); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed) pattern[i][2] = 0; break; case(3): if(pattern[i][2] == 0){ for(n=0; n<4; n++){ mx.setRow(2*n+1, 0x55, i+1); mx.setRow(2*n+2, 0xaa, i+1); } } else if(pattern[i][2] == disp_speed*4){ for(n=0; n<4; n++){ mx.setRow(2*n+1, 0xaa, i+1); mx.setRow(2*n+2, 0x55, i+1); } } if(++pattern[i][2] > disp_speed*8) pattern[i][2] = 0; break; case(2): if(pattern[i][2] == 0){ mx.setRow(pattern[i][1]+1, (1 << random(7)) - 1, i+1); if(++pattern[i][1]>7) pattern[i][1] = 0; } if(++pattern[i][2]>250) pattern[i][2] = 0; break; case(1): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow(pattern[i][1]+1, (1 << random(7)) - 1, i+1); mx.setReverse(false); if(++pattern[i][1]>7) pattern[i][1] = 0; } if(++pattern[i][2]>250) pattern[i][2] = 0; break; case(0): if(pattern[i][2] == 0) for(n=0; n<8; n++) mx.setRow(n+1, 0xff, i+1); else if(pattern[i][2] == disp_speed*2) for(n=0; n<8; n++) mx.setRow(n+1, 0x00, i+1); if(++pattern[i][2] > disp_speed*12) pattern[i][2] = 0; break; } } void text_menu(){ display.clearDisplay(); display.setTextSize(1); display.setCursor(0,0); display.setTextColor(WHITE); display.print(" Hello, World ! "); color_reverse(0); display.setCursor(20,15); display.print(" Yellow "); show_number(0); color_reverse(1); display.setCursor(20,27); display.print(" Blue "); show_number(1); color_reverse(2); display.setCursor(20,39); display.print(" Red "); show_number(2); color_reverse(3); display.setCursor(20,51); display.print(" Green "); show_number(3); display.display(); } void show_number(int j){ display.setTextColor(WHITE); display.print(" "); if(mode[j] == 1) display.print(pattern[j][0]); else display.print(" "); } void color_reverse(int j){ if(mode[j] == 1) display.setTextColor(BLACK, WHITE); else display.setTextColor(WHITE); }2025年2月10日(月)
昨日は午前と午後に某バイト[2]があり、実験・試作を進めていた「8*8マトリクスLED4段Max7219モジュール」(SSD1306・OLEDモジュールのディスプレイ付き)をロボット教室の生徒たちに試してもらっていたが、実は昨日の朝に思い付いたパターンを追加しようとして、バグのために見限って持参したものだった。そして、上のような課題が明確になったので、今日は朝からずっと集中して、遂に「完成バージョン」となった。 新しいパターンについてはきりがないので、ここで打ち止めとして、全部で34種類(全てクリアを含む)のパターンが、4個のブロックごとにランダムに選ばれ、表示ループの位相はボタン操作をスタートとすることになった。 チャタリングは完全に消えて、押せばどんどん新しいものを出して、全体として「並ぶ面白さ」を追求できるものになった。 以下はそのスケッチ「demo006.ino」である。
- 表示ON/OFFのトグル動作としたが、消えずにどんどん新しい表示に変えた方がスムース
- ボタンスイッチのスキャンにチャタリングがあり、一度押したのに「ON→OFF」などと解釈されることがある
- 未完成だったパターンを完成させたい
- さらに新しいパターンを思い付いた
プログラミングの常だが、次第に全体は洗練されてきて、特に最後に作った26番から33番までの8種類は、元となるパターン配列(8*8)を数理造形で作って、さらに表示のために「8ビットのバイナリデータのビットを上下反転させる」(2ビット目であれば5ビット目と交換)というのを調べたりして、なかなか収穫だった。 これは今週の金曜日に、またまたミドル+アドバンスの合体クラス(計9人)を担当する予定なので、そこでウケると確信している。 動作の YouTube動画 も撮ってみたが、これは実際に触って体験することで本当に楽しめるものになったと思う。 これで気持ちよく、晩には某バイト[1]に向かうことが出来そうだ。#include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> Adafruit_SSD1306 display(128, 64, &Wire, -1); #include <MATRIX7219.h> MATRIX7219 mx(4,10,13,4); int musicScale[]={523,587,659,698,784,880,988,1047}; int disp_speed=2500, scan_timer=0, sw_old[]={0,0,0,0}, sw_new[4], sw_port[]={2,7,8,9}; int pattern[4][3], ttable[8][8]; void setup(){ for(int i=0; i<4; i++){ pinMode(sw_port[i], INPUT); for(int j=0; j<3; j++) pattern[i][j] = 0; } table_set(); mx.begin(); mx.clear(); mx.setBrightness(3); // develop = 1, final = 3 mx.setReverse(false); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); text_menu(); randomSeed(analogRead(0)); tone(12,440); delay(50); noTone(12); delay(50); tone(12,880); delay(50); noTone(12);; } void loop(){ for(int i=0; i<4; i++) display_patterns(i); if(++scan_timer > 100){ scan_timer = 0; sw_scan(); } } void sw_scan(){ for(int i=0; i<4; i++){ sw_new[i] = ! digitalRead(sw_port[i]); if(sw_new[i] != sw_old[i]){ sw_old[i] = sw_new[i]; if(sw_new[i] == 1){ pattern[i][0] = random(0,34); // Random Range Setting !! pattern[i][1] = 0; pattern[i][2] = 0; tone(12,500+50*pattern[i][0]); delay(40); noTone(12); text_menu(); } } } } void display_patterns(int i){ int k,n; switch(pattern[i][0]){ case(33): if(pattern[i][2] == 0){ if(pattern[i][1] == 8) for(k=1; k<9; k++) mx.setRow(k, 0x00, i+1); else{ for(n=0; n<8; n++){ mx.setRow(1+n, bit_reverse(ttable[7-pattern[i][1]][n]), i+1); } } if(++pattern[i][1]>8) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*3) pattern[i][2] = 0; break; case(32): if(pattern[i][2] == 0){ if(pattern[i][1] == 8) for(k=1; k<9; k++) mx.setRow(k, 0x00, i+1); else{ for(n=0; n<8; n++){ mx.setRow(1+n, bit_reverse(ttable[pattern[i][1]][n]), i+1); } } if(++pattern[i][1]>8) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*3) pattern[i][2] = 0; break; case(31): if(pattern[i][2] == 0){ if(pattern[i][1] == 8) for(k=1; k<9; k++) mx.setRow(k, 0x00, i+1); else{ for(n=0; n<8; n++){ mx.setRow(8-n, bit_reverse(ttable[7-pattern[i][1]][n]), i+1); } } if(++pattern[i][1]>8) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*3) pattern[i][2] = 0; break; case(30): if(pattern[i][2] == 0){ if(pattern[i][1] == 8) for(k=1; k<9; k++) mx.setRow(k, 0x00, i+1); else{ for(n=0; n<8; n++){ mx.setRow(8-n, bit_reverse(ttable[pattern[i][1]][n]), i+1); } } if(++pattern[i][1]>8) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*3) pattern[i][2] = 0; break; case(29): if(pattern[i][2] == 0){ if(pattern[i][1] == 8) for(k=1; k<9; k++) mx.setRow(k, 0x00, i+1); else{ for(n=0; n<8; n++){ mx.setRow(1+n, ttable[7-pattern[i][1]][n], i+1); } } if(++pattern[i][1]>8) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*3) pattern[i][2] = 0; break; case(28): if(pattern[i][2] == 0){ if(pattern[i][1] == 8) for(k=1; k<9; k++) mx.setRow(k, 0x00, i+1); else{ for(n=0; n<8; n++){ mx.setRow(1+n, ttable[pattern[i][1]][n], i+1); } } if(++pattern[i][1]>8) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*3) pattern[i][2] = 0; break; case(27): if(pattern[i][2] == 0){ if(pattern[i][1] == 8) for(k=1; k<9; k++) mx.setRow(k, 0x00, i+1); else{ for(n=0; n<8; n++){ mx.setRow(8-n, ttable[7-pattern[i][1]][n], i+1); } } if(++pattern[i][1]>8) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*3) pattern[i][2] = 0; break; case(26): if(pattern[i][2] == 0){ if(pattern[i][1] == 8) for(k=1; k<9; k++) mx.setRow(k, 0x00, i+1); else{ for(n=0; n<8; n++){ mx.setRow(8-n, ttable[pattern[i][1]][n], i+1); } } if(++pattern[i][1]>8) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*3) pattern[i][2] = 0; break; case(25): if(pattern[i][2] == 0) for(n=0; n<8; n++) mx.setRow(n+1, 0xff, i+1); else if(pattern[i][2] == disp_speed*4) for(n=0; n<8; n++) mx.setRow(n+1, 0x00, i+1); if(++pattern[i][2] > disp_speed*12) pattern[i][2] = 0; break; case(24): if(pattern[i][2] == 0){ for(n=7; n>-1; n--){ if(n == pattern[i][1]) mx.setRow(8-n, 0xff, i+1); else mx.setRow(8-n, 0x80 >> pattern[i][1], i+1); } if(++pattern[i][1]>7) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*4) pattern[i][2] = 0; break; case(23): if(pattern[i][2] == 0){ for(n=7; n>-1; n--){ if(n == pattern[i][1]) mx.setRow(8-n, 0xff, i+1); else mx.setRow(8-n, 1 << pattern[i][1], i+1); } if(++pattern[i][1]>7) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*4) pattern[i][2] = 0; break; case(22): if(pattern[i][2] == 0){ for(n=0; n<8; n++){ if(n == pattern[i][1]) mx.setRow(n+1, 0xff, i+1); else mx.setRow(n+1, 0x80 >> pattern[i][1], i+1); } if(++pattern[i][1]>7) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*4) pattern[i][2] = 0; break; case(21): if(pattern[i][2] == 0){ for(n=0; n<8; n++){ if(n == pattern[i][1]) mx.setRow(n+1, 0xff, i+1); else mx.setRow(n+1, 1 << pattern[i][1], i+1); } if(++pattern[i][1]>7) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*4) pattern[i][2] = 0; break; case(20): if(pattern[i][2] == 0){ if(pattern[i][1] == 4){ for(k=1; k<9; k++) mx.setRow(k, 0x00, i+1); } else if(pattern[i][1] == 3){ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else if(pattern[i][1] == 2){ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x42, i+1); mx.setRow(7, 0x42, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else if(pattern[i][1] == 1){ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x42, i+1); mx.setRow(7, 0x42, i+1); mx.setRow(3, 0x24, i+1); mx.setRow(6, 0x24, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else{ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x42, i+1); mx.setRow(7, 0x42, i+1); mx.setRow(3, 0x24, i+1); mx.setRow(6, 0x24, i+1); mx.setRow(4, 0x18, i+1); mx.setRow(5, 0x18, i+1); } if(++pattern[i][1]>4) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*6) pattern[i][2] = 0; break; case(19): if(pattern[i][2] == 0){ if(pattern[i][1] == 0){ for(k=1; k<9; k++) mx.setRow(k, 0x00, i+1); } else if(pattern[i][1] == 1){ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else if(pattern[i][1] == 2){ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x42, i+1); mx.setRow(7, 0x42, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else if(pattern[i][1] == 3){ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x42, i+1); mx.setRow(7, 0x42, i+1); mx.setRow(3, 0x24, i+1); mx.setRow(6, 0x24, i+1); mx.setRow(4, 0x00, i+1); mx.setRow(5, 0x00, i+1); } else{ mx.setRow(1, 0x81, i+1); mx.setRow(8, 0x81, i+1); mx.setRow(2, 0x42, i+1); mx.setRow(7, 0x42, i+1); mx.setRow(3, 0x24, i+1); mx.setRow(6, 0x24, i+1); mx.setRow(4, 0x18, i+1); mx.setRow(5, 0x18, i+1); } if(++pattern[i][1]>4) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*6) pattern[i][2] = 0; break; case(18): if(pattern[i][2] == 0){ if(pattern[i][1] == 3){ mx.setRow(1, 0xff, i+1); mx.setRow(8, 0xff, i+1); mx.setRow(2, 0x81, i+1); mx.setRow(7, 0x81, i+1); mx.setRow(3, 0x81, i+1); mx.setRow(6, 0x81, i+1); mx.setRow(4, 0x81, i+1); mx.setRow(5, 0x81, i+1); } else if(pattern[i][1] == 2){ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x7e, i+1); mx.setRow(7, 0x7e, i+1); mx.setRow(3, 0x42, i+1); mx.setRow(6, 0x42, i+1); mx.setRow(4, 0x42, i+1); mx.setRow(5, 0x42, i+1); } else if(pattern[i][1] == 1){ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x3c, i+1); mx.setRow(6, 0x3c, i+1); mx.setRow(4, 0x24, i+1); mx.setRow(5, 0x24, i+1); } else{ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x18, i+1); mx.setRow(5, 0x18, i+1); } if(++pattern[i][1]>3) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*8) pattern[i][2] = 0; break; case(17): if(pattern[i][2] == 0){ if(pattern[i][1] == 0){ mx.setRow(1, 0xff, i+1); mx.setRow(8, 0xff, i+1); mx.setRow(2, 0x81, i+1); mx.setRow(7, 0x81, i+1); mx.setRow(3, 0x81, i+1); mx.setRow(6, 0x81, i+1); mx.setRow(4, 0x81, i+1); mx.setRow(5, 0x81, i+1); } else if(pattern[i][1] == 1){ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x7e, i+1); mx.setRow(7, 0x7e, i+1); mx.setRow(3, 0x42, i+1); mx.setRow(6, 0x42, i+1); mx.setRow(4, 0x42, i+1); mx.setRow(5, 0x42, i+1); } else if(pattern[i][1] == 2){ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x3c, i+1); mx.setRow(6, 0x3c, i+1); mx.setRow(4, 0x24, i+1); mx.setRow(5, 0x24, i+1); } else{ mx.setRow(1, 0x00, i+1); mx.setRow(8, 0x00, i+1); mx.setRow(2, 0x00, i+1); mx.setRow(7, 0x00, i+1); mx.setRow(3, 0x00, i+1); mx.setRow(6, 0x00, i+1); mx.setRow(4, 0x18, i+1); mx.setRow(5, 0x18, i+1); } if(++pattern[i][1]>3) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*8) pattern[i][2] = 0; break; case(16): if(pattern[i][2] == 0){ for(n=0; n<2; n++){ mx.setRow(4*n+1, 0x33, i+1); mx.setRow(4*n+2, 0x33, i+1); mx.setRow(4*n+3, 0xcc, i+1); mx.setRow(4*n+4, 0xcc, i+1); } } else if(pattern[i][2] == disp_speed*6){ for(n=0; n<2; n++){ mx.setRow(4*n+1, 0xcc, i+1); mx.setRow(4*n+2, 0xcc, i+1); mx.setRow(4*n+3, 0x33, i+1); mx.setRow(4*n+4, 0x33, i+1); } } if(++pattern[i][2] > disp_speed*12) pattern[i][2] = 0; break; case(15): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow(((15-pattern[i][1])%8)+1, (0xaa00 >> (pattern[i][1]+1)) & 0xff, i+1); mx.setReverse(false); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*2) pattern[i][2] = 0; break; case(14): if(pattern[i][2] == 0){ mx.setRow(((15-pattern[i][1])%8)+1, (0xaa00 >> (pattern[i][1]+1)) & 0xff, i+1); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*2) pattern[i][2] = 0; break; case(13): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow((pattern[i][1]%8)+1, (0xaa00 >> (pattern[i][1]+1)) & 0xff, i+1); mx.setReverse(false); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*2) pattern[i][2] = 0; break; case(12): if(pattern[i][2] == 0){ mx.setRow((pattern[i][1]%8)+1, (0xaa00 >> (pattern[i][1]+1)) & 0xff, i+1); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*2) pattern[i][2] = 0; break; case(11): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow(((15-pattern[i][1])%8)+1, (0xcc00 >> (pattern[i][1]+1)) & 0xff, i+1); mx.setReverse(false); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*2) pattern[i][2] = 0; break; case(10): if(pattern[i][2] == 0){ mx.setRow(((15-pattern[i][1])%8)+1, (0xcc00 >> (pattern[i][1]+1)) & 0xff, i+1); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2]>disp_speed*2) pattern[i][2] = 0; break; case(9): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow((pattern[i][1]%8)+1, (0xcc00 >> (pattern[i][1]+1)) & 0xff, i+1); mx.setReverse(false); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*2) pattern[i][2] = 0; break; case(8): if(pattern[i][2] == 0){ mx.setRow((pattern[i][1]%8)+1, (0xcc00 >> (pattern[i][1]+1)) & 0xff, i+1); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*2) pattern[i][2] = 0; break; case(7): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow(((15-pattern[i][1])%8)+1, (0xff00 >> (pattern[i][1]+1)) & 0xff, i+1); mx.setReverse(false); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*2) pattern[i][2] = 0; break; case(6): if(pattern[i][2] == 0){ mx.setRow(((15-pattern[i][1])%8)+1, (0xff00 >> (pattern[i][1]+1)) & 0xff, i+1); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*2) pattern[i][2] = 0; break; case(5): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow((pattern[i][1]%8)+1, (0xff00 >> (pattern[i][1]+1)) & 0xff, i+1); mx.setReverse(false); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*2) pattern[i][2] = 0; break; case(4): if(pattern[i][2] == 0){ mx.setRow((pattern[i][1]%8)+1, (0xff00 >> (pattern[i][1]+1)) & 0xff, i+1); if(++pattern[i][1]>15) pattern[i][1] = 0; } if(++pattern[i][2] > disp_speed*2) pattern[i][2] = 0; break; case(3): if(pattern[i][2] == 0){ for(n=0; n<4; n++){ mx.setRow(2*n+1, 0x55, i+1); mx.setRow(2*n+2, 0xaa, i+1); } } else if(pattern[i][2] == disp_speed*6){ for(n=0; n<4; n++){ mx.setRow(2*n+1, 0xaa, i+1); mx.setRow(2*n+2, 0x55, i+1); } } if(++pattern[i][2] > disp_speed*12) pattern[i][2] = 0; break; case(2): if(pattern[i][2] == 0){ mx.setReverse(true); mx.setRow(pattern[i][1]+1, (1 << random(7)) - 1, i+1); mx.setReverse(false); if(++pattern[i][1]>7) pattern[i][1] = 0; } if(++pattern[i][2]>500) pattern[i][2] = 0; break; case(1): if(pattern[i][2] == 0){ mx.setRow(pattern[i][1]+1, (1 << random(7)) - 1, i+1); if(++pattern[i][1]>7) pattern[i][1] = 0; } if(++pattern[i][2]>500) pattern[i][2] = 0; break; case(0): if(pattern[i][2] == 0) for(int n=1; n<9; n++) mx.setRow(n, 0, i+1); if(++pattern[i][2]>10000) pattern[i][2] = 0; break; } } void text_menu(){ display.clearDisplay(); display.setTextSize(1); display.setCursor(0,0); display.setTextColor(BLACK, WHITE); display.print(" Hello, World ! "); display.setTextColor(WHITE); display.setCursor(20,15); display.print(" Yellow = "); display.print(pattern[0][0]); display.setCursor(20,27); display.print(" Blue = "); display.print(pattern[1][0]); display.setCursor(20,39); display.print(" Red = "); display.print(pattern[2][0]); display.setCursor(20,51); display.print(" Green = "); display.print(pattern[3][0]); display.display(); } int bit_reverse(int k){ return( ((k&1)<<7)|((k&2)<<5)|((k&4)<<3)|((k&8)<<1)|((k&16)>>1)|((k&32)>>3)|((k&64)>>5)|((k&128)>>7) ); } void table_set(){ int i; ttable[0][0] = 0x01; for(i=1; i<8; i++) ttable[0][i] = 0x00; ttable[1][0] = 0x02; ttable[1][1] = 0x03; for(i=2; i<8; i++) ttable[1][i] = 0x00; for(i=0; i<2; i++) ttable[2][i] = 0x04; ttable[2][2] = 0x07; for(i=3; i<8; i++) ttable[2][i] = 0x00; for(i=0; i<3; i++) ttable[3][i] = 0x08; ttable[3][3] = 0x0f; for(i=4; i<8; i++) ttable[3][i] = 0x00; for(i=0; i<4; i++) ttable[4][i] = 0x10; ttable[4][4] = 0x1f; for(i=5; i<8; i++) ttable[4][i] = 0x00; for(i=0; i<5; i++) ttable[5][i] = 0x20; ttable[5][5] = 0x3f; for(i=6; i<8; i++) ttable[5][i] = 0x00; for(i=0; i<6; i++) ttable[6][i] = 0x40; ttable[6][6] = 0x7f; ttable[6][7] = 0x00; for(i=0; i<7; i++) ttable[7][i] = 0x80; ttable[7][7] = 0xff; }2025年2月11日(火)
「果報は寝て待て」という言葉が現実となった。 昨日2/10は、甲子園巨人戦ライトスタンド・チケットの「ファンクラブ先行抽選申込」に先週応募していたのだが、その「FC抽選先行当落発表メール」というのが、2/10の15時から順にある、と予告されていたので待っていたのに、待てど暮らせどメイルが届かなかった。 おそらく当選から順に発信しているのでたぶん駄目で、そうなると今日2/11の「ファンクラブ先行販売(座席指定なし)」か、明日2/12の「ファンクラブ先行販売(座席指定あり)」か、その後の「一般販売」に期待するしかない・・・と諦めて寝ていた。
そして今朝、メイルを確認すると、「FC抽選先行にお申込いただきありがとうございました。まずはこの度、当落メールの配信に遅れが生じましたこと、深くお詫び申し上げます。ご不便、ご迷惑をおかけし大変申し訳ございませんでした。厳正なる抽選の結果、【当選】となりましたのでご案内いたします」(^o^)とのメイルが朝の6時になって届いていた。
![]()
こういうところでせっかくの運を使い果たしていいのか・・・という点はあるが、このところ頑張ってArduinoプログラミングに頑張ってきた御褒美だ、と考えることにした。 絶景のライトスタンド、それも座席指定できない先行抽選だったが、今回はなんと「通路側」(買い出しやトイレに便利)ということで、これはとてもとても嬉しいのだ。 これでとりあえず何ヶ月か、日々をさらに前向きに生きていく原動力を得た感じである。
午前には施設の義母を訪問したりしたものの、今日は全国的に祝日、さしたる情報も届いてこないということで、午後から心機一転、ちょっとした「整理」をやっていくことにした。 昨日、完成したものを含めて、これまでに某バイト[2]のロボット教室で「電子工作の事例」として生徒に提示する、新たに製作した「作品」や過去に作った「作品」がだいぶ増えてきたので(現在10作品)、ここらで備忘録としてまとめておこう・・・という事である。
![]()
![]()
![]()
ArduinoのC言語ソース(スケッチ)は自分の手元にあるものの、YAHOO画像検索の「ヒューマン+ロボット+マイコン」で出てきた、上の写真にある「マイコンブロック」は(ある種のArduinoファミリだと思われるものの)詳細不明であり、専用「タブレット」の特殊な専用アプリ(Scratch風)でプログラム開発する必要がある。 僕はテスター片手に独自解析して、この「マイコンブロック」に繋がる「スライドボリーム」や「光センサ」(4個)や「PWM両極LED」(モーター制御のところに繋ぐと、-100%~+100%の範囲で2色のLEDのいずれかが数値に対応した強度で光る)をオリジナル開発してきた。
![]()
この「マイコンブロック」とオリジナルI/Oを使った作品も2つあるのだが、そのプログラムは開発用タブレット(Android)上のScratch風のものであり、内部表現されたソースコードは手に入らない。 そこで今回、スクリーンショットを撮ってMicroSDカード経由でMacに持ってきて、繋ぎ合わせて1枚の「プログラム画像」としてみたのが上の2枚である。
そしてあとは、 「日記」シリーズなどの記録 からこの「RRR日記」シリーズを順に発掘して、できれば製作順になるように(新たに名前も付けて)並べてみることにした。 けっこう時間がかかりそうな気もするが、例えば後で同じものを作りたい時には是非とも必要な情報なので、ぼちぼちじっくり進めていこう。[1] SharpMozzi_Theremin
![]()
Sharpの赤外線距離センサを2個使って、ArduinoにはMozziを走らせて、外部スピーカ(最終形としてはLittleBitsの小型スピーカをArduinoの隣に直付けしている)を鳴らすテルミンみたいな作品である。 片方の距離センサはピッチ、もう片方はディストーション(深いビブラート)にしているので、両手を上空でワラワラ動かすと「宇宙人の声が聞こえる」と生徒に評判のサウンドおもちゃである。 2個の赤外線距離センサの値に応じてオンボードのRGB-LEDの色も対応して変化する。 記録としては RRR日記(1) の「2024年5月23日(木)」のところにあり、 YouTube動画 も撮っていた。 Arduinoスケッチ(上の写真に入っている「Sharp_Vibrate_LED.ino」にあるので省略)を見れば回路も簡単で、アナログA0とA1に赤外線距離センサを繋いで、RGB-LEDの接続は3,5,6ピンである。
[2] SlideVolume_MusicMachine
そして夕方には、以心伝心というのか、ロボット教室の社長から電話があって、今年から 浜松ジュニアロボットコンテスト というのを開催するとのことで相談を受けて、僕は「アルディーノ部門」というのを提案して、そのサンプル(Arduinoを使っていれば何でもOK)動画を提供することになった。 これはまさに、今日の午後から作り始めたところだったのだ。 明日は午後にJoyJoyの予定があるものの、今週さらに続きを進めていくことにした。
![]()
これはロボット教室の「マイコンブロック」を使った初めての作品で、 このメイキング のようにスライドボリュームをオリジナルI/Fとして作って、「ドレミファソラシ」しか鳴らせないものの、ロボット教室のロボットでは実現できない「連続値コントロール」を実現したものである。 記録としては RRR日記(2) の「2024年6月25日(火)」のところにあり、メイキングの段階ではこれだけだったが、現在の最終確定版では「一定間隔で点滅する2種類のLED」と、「音階に対応して2色のLEDが点灯し、その明るさが音階に対応(高いほど明るい)」という動作も並行している。
![]()
上が「マイコンブロック」のプログラムであるが、このように横に広がるプログラムというのはロボット教室(縦長の逐次処理が多い)では珍しい。 このメイキング のページのいちばん下にあるYouTube動画は試作版のボリュームだけで、2種類のLEDまで加わった最終版の動画を撮っていなかったので、新たに YouTube動画 も撮ってみた(追記:実は ここ で撮っていたことが判明した)。 なお、この動画で常に点滅している白/青LEDのブロックを「マイコンブロック」用に改造した点については、 メイキング の解説を参照されたい。
夕方にネットニュースを見てみると、『阪神は11日、正午からファンクラブの全会員を対象にした、甲子園、京セラドーム大阪主催公式戦の先行販売を開始した。藤川球児新監督への期待の表れか、チケットを販売するサイト「甲チケ」では、午後1時50分時点で16万人超がアクセス待ちの状態となっている。「甲チケ」では今年から指定された時間内にアクセスすれば抽選でサイトにつながる「仮想待合室」を導入。販売開始直後からファンが殺到している』・・・とのことで、どうやら先週の「会員先行予約抽選」に当選したのは、かなりラッキーだったようだ。2025年2月12日(水)
![]()
前日からの仕掛り仕事を跨いだ朝というのは、起床した段階で何だかポジティブである。 今日は午後から週一の修行=JoyJoyヒトカラに出掛けるまで、と時間も限られているので、ネットニュースから届いた上の画像を何もコメントせず置くだけにして、朝から「ロボット教室に持参している電子工作おもちゃ」一覧を作成する作業の続きに入った。
[3] Mozzi_FM_Synthesizer
![]()
これは2021年の終わり頃に作った、「Mozziシリーズ」の一つである。 某バイト[2]に持って行く、という文脈での記録は RRR日記(2) の「2024年7月13日(土)」のところにある。 最初はArduino UNOの Mozzi初号機 も持って行ったのだが、かさばるので現在ではArduinoNanoの2号機だけにしている。 そのメイキングは Mozzi2号機 にある。 いつものように何も考えずにアドリブで作って、気付いてみればポートのビット対応を逆順に繋いでいたのだが、これを手直しせずにArduinoのスケッチの方で尻拭いしている。
上がそのArduinoスケッチであり、スピーカを9ピンとGNDに繋ぎ、あとは6個のボリュームを+5VとGNDの間に繋いでその電圧をA0~A5に与えているだけである。 電源としてはUSBにモバイルバッテリを繋ぐだけだが、パソコンに繋いでArduino IDEを起動すると、シリアルモニタ(9600bps)でこの6個のボリュームの値を見られるようになっているようだ。#include <MozziGuts.h> #include <Oscil.h> #include <tables/cos2048_int8.h> #include <Smooth.h> #include <AutoMap.h> Oscil<COS2048_NUM_CELLS, AUDIO_RATE> aCarrier(COS2048_DATA); Oscil<COS2048_NUM_CELLS, AUDIO_RATE> aModulator(COS2048_DATA); Oscil<COS2048_NUM_CELLS, CONTROL_RATE> kIntensityMod(COS2048_DATA); int mod_ratio = 5; // brightness (harmonics) long fm_intensity; // carries control byte gain; // A5 master volume float smoothness = 0.95f; Smooth <long> aSmoothIntensity(smoothness); AutoMap map_A0_carrierFreq(0,1023,22,440); AutoMap map_A1_intensity(0,1023,700,10); AutoMap map_A2_modSpeed(0,1023,10000,1); AutoMap map_freq1_A3(0,1023,1,20); AutoMap map_freq2_A4(0,1023,1,10); AutoMap map_volume(0,1023,0,255); void setup(){ Serial.begin(9600); startMozzi(); } void updateControl(){ int input_A0 = mozziAnalogRead(0); // A0 int carrier_freq = map_A0_carrierFreq(input_A0); Serial.print("A0 = "); Serial.print(input_A0); Serial.print("\t"); int input_A1 = mozziAnalogRead(1); // A1 int LDR1_calibrated = map_A1_intensity(input_A1); Serial.print("A1 = "); Serial.print(input_A1); Serial.print("\t"); int input_A2 = mozziAnalogRead(2); // A2 Serial.print("A2 = "); Serial.print(input_A2); Serial.print("\t"); float mod_speed = (float)map_A2_modSpeed(input_A2)/1000; kIntensityMod.setFreq(mod_speed); int input_A3 = mozziAnalogRead(3); // A3 int knob2Val = map_freq1_A3(input_A3); Serial.print("A3 = "); Serial.print(input_A3); Serial.print("\t"); int input_A4 = mozziAnalogRead(4); // A4 int FRQ = map_freq2_A4(input_A4); Serial.print("A4 = "); Serial.print(input_A4); Serial.print("\t"); int mod_freq = carrier_freq * mod_ratio * FRQ; int volumeData = mozziAnalogRead(5); // A5 gain = map_volume(volumeData); aCarrier.setFreq(carrier_freq); // set the FM oscillator frequencies aModulator.setFreq(mod_freq); fm_intensity = ((long)LDR1_calibrated * knob2Val * (kIntensityMod.next()+128))>>8; Serial.println(); } int updateAudio(){ long modulation = aSmoothIntensity.next(fm_intensity) * aModulator.next(); return MonoOutput::from16Bit(aCarrier.phMod(modulation) * gain); } void loop(){ audioHook(); }
たまにいるのだが、先日はこのマシンに超ハマッた生徒がいて、延々と「即興ライブ演奏」していた。 記録をとったのかどうか不明だったが、せっかくなので僕もアドリブ操作してみたのを YouTube動画 として置いてみた。[4] BlueWhiteLED_Patterns
![]()
去年の10月あたりに作ったこのシステムは、Arduino UNOに8個*2列のLEDを、「白色LED列」と「青色LED列」としてオンボード搭載し、さらに2個のスライドボリューム(速度、輝度)とボタンスイッチ(モード切り替え)を付けて、色々なバターンでLED点灯させている、という「数理造形の基本」というものである。 後にAdafruit社のOLEDでモードを表示するように改造したのだが、とりあえず RRR日記(3) の「2024年10月4日(金)」のところに メイキング があり、翌日の「2024年10月5日(土)」のところに YouTube動画 と、Arduinoスケッチがある。 AdafruitのOLED表示部分が加わったのは次の作品「RFID_Reader」を作った後であり、 RRR日記(4) の「2024年11月9日(土)」のところにあった。
[5] RFID_Reader
![]()
過去にAmazonで多量のRFIDリーダとRFIDタグを仕入れていたのを思い出して、ちょっと小学生には難しいのだが、RFIDごとに固有の製造番号を16進表示する・・・というシステムを作ってみた。 RRR日記(4) の「2024年11月6日(水)」のところに メイキング(途中) に続く メイキング(完成) とArduinoスケッチがあり、翌日の「2024年11月7日(木)」のところに YouTube動画 がある。
[6] 4_LightSounds
![]()
ロボット教室の「マイコンブロック」用にも、「光センサ」というパーツは1個だけ入っているのだが、光トランジスタを使った高性能な光センサがあるので、これを「マイコンブロック」用にオリジナル製作して、4個並べてそれぞれ光が当たると対応した音階(C/E/G/B)のサウンドが鳴るように作ったのがこのシステムである。 RRR日記(4) の「2024年11月12日(火)」のところに メイキング があり、 YouTube動画 も撮っていた。 教室ではこのLED懐中電灯だけでなく、「いい子は注意して使ってね」ということで他人の目に向けないという注意と共に、禁断のレーザー発光器でもビームをこの光センサに当てて、すると部屋の反対側まで移動しても反応する(レーザー光は拡散しないので到達する)という実験をやったりした。 以下がそのプログラム(専用タブレットで作った「マイコンブロック」用)である。
![]()
[7] 2vs2_GripGame
![]()
握力の筋トレ用の「グリップ」を4個使って、過去にはGainerに繋いで獲得した「握力データ」(小型ジョイスティックを使用)をMaxに送った学生作品に利用していたのを再利用して、新たにAmazonで仕入れた 個別RGB駆動LEDテープ を初めて使って開発したシステムである。 その後、さらに2作品でこのLEDテープが大活躍している。 RRR日記(4) の「2024年12月2日(月)」のところに メイキング があり、Arduinoスケッチもあり、さらに YouTube動画 も撮っていた。
教室では対戦型ということもあって、生徒たちには大受けなのだが、グリップは大人用のために小学生にはちょっとだけ厳しく、ムキになってグリップを握ると小型ジョイスティックの移動を媒介しているL字金具が外れてしまう(→ポイントされない)という弱点がある。 こういう体育会系のゲームというのは、「eスポーツ」とは別に、誰でも熱中するのだろう。
![]()
過去の記録を発掘していて、このあたり(2024年12月~2025年1月)で「Arduino+Mozziでテルミン」というネタに没頭する期間となっていたのを確認した。 実験と試作はまだ途中なので「ロボット教室」には持参していなかったのだが、 RRR日記(4) の「2024年12月27日(金)」のところに上のようなハードウェア()での「テルミン風実験」の風景と YouTube動画 が置かれていた。 また、この日記(RRR日記[5])の冒頭、「2025年1月3日(金)」にも、音色などを改良した実験の YouTube動画 が置かれていた。
そしてこの日記(RRR日記[5])の「2025年1月5日(日)」のところには、それらをまとめて改良した YouTube動画 と、それぞれのArduinoスケッチが置かれていた。 さらにこの日記(RRR日記[5])の「2025年1月9日(木)」のところには、白木マトリューミンにシステムを入れて音楽演奏に挑戦した YouTube動画 も置かれていた。 まったく未熟/未完成であるものの、これが現在の最新形であり、これらも「Arduinoの活用例」としては使えるために、ここにメモしておくことにした。[8] LEDtape_Shooting
![]()
これまでのところ、もっとも「ロボット教室」の生徒たちが熱中するのがこのシステムである。 個別RGB駆動LEDテープ 上を輝点が移動して、ターゲット(シーンごとにランダムの場所に移動する)上のタイミングでボタンスイッチを押すだけなのだが、ボリュームで輝点の移動速度が変えられるので、なかなかに燃えるのだ。 この日記(RRR日記[5])の「2025年1月11日(土)」のところに最初の試作バージョンの YouTube動画 とArduinoスケッチが置かれているが、後にスピードに対応してペナルティ(Game Overとなる失点)の幅を変えたり、プログラムも改良した。 以下がその最終版のArduinoスケッチである。
この最終版のYouTube動画(見た目はほとんど変わらない)はまだ撮っていなかったようだが、1日に何度も、「スタンド付きWebカメラを棚から出して、USBハブに挿して、デスクの上を撮影用に整えて、QuickTimePlayerX を起動して Movie Recording ないし Screen Recording を選択して、一発撮りで録画して、YouTubeにアップロードして、いくつもの動画オプションをOFFにして、最後にここにURLをコピペする」という単純作業を再び繰り返す気にはならないので、もし今後、気が向いたらやってみて、この下にでも追記していこう。#include <FastLED.h> #define LED_PIN1 13 #define LED_PIN2 12 #define NUM_LEDS1 31 #define NUM_LEDS2 23 CRGB leds1[NUM_LEDS1],leds2[NUM_LEDS2]; int i, musicScale[] = {523,587,659,698,784,880,988,1047}; int volume_value, gate_time = 10000, sw_old[] = {1,1}, sw_new[] = {0,0}, sw_eve[] = {0,0}; int timing[] = {0,0,0}, points[] = {0,0}, target, led_step = 2, vectors = 1; void setup() { for(i=9; i<11; i++) pinMode(i, INPUT); for(i=0; i<9; i++){ tone(11, musicScale[i]); delay(40); noTone(11); delay(60); } all_black(); sw_old[0] = digitalRead(10); delay(200); } void loop(){ volume_test(); if(++timing[2] > 50){ timing[2] = 0; sw_new[0] = digitalRead(10); if(sw_new[0] != sw_old[0]){ sw_old[0] = sw_new[0]; if(sw_new[0] == 0){ melody_again(4); if(points[0] == target){ Led1_set3(); melody_end(); all_black(); delay(1500); } else { points[1] = points[1] + led_step; Led2_set(points[1]-1, 1); if(points[1]>22){ for(i=40; i>0; i--){ tone(11, i*50); delay(50); } delay(1000); noTone(11); while(digitalRead(9) == HIGH){} all_black(); delay(1000); } } } } } if(++timing[1] > gate_time){ timing[1] = 0; Led1_set2(points[0], 8, 4); points[0] = points[0] + vectors; if(vectors == 1){ if(points[0] >= NUM_LEDS1){ points[0] = NUM_LEDS1 - 1; vectors = -1; melody_again(0); } } else{ if(points[0] < 0){ points[0] = 0; vectors = 1; melody_again(2); } } } } void volume_test(){ if(++timing[0] > 100){ timing[0] = 0; volume_value = analogRead(A0); gate_time = volume_value * 7 + 400; led_step = 2 + volume_value / 256; } } CRGB color_set(int color){ if(color<1 || color>8) return(CRGB::Black); switch(color){ case 1: return(CHSV(96, 255, 127)); // Red case 2: return(CHSV(0, 255, 127)); // Green case 3: return(CHSV(160, 255, 127)); // Blue case 4: return(CRGB::Gray); // White case 5: return(CHSV(60, 255, 127)); // Yellow case 6: return(CHSV(128, 255, 127)); // Pink case 7: return(CHSV(87, 255, 127)); // Ogange case 8: return(CHSV(random8(), 255, 127));// Random } } void Led1_set(int num, int color){ FastLED.addLeds<WS2811, LED_PIN1, RGB>(leds1, NUM_LEDS1); for(i=0; i<NUM_LEDS1; i++) leds1[i] = CRGB::Black; leds1[num] = color_set(color); FastLED.show(); } void Led1_set2(int num, int color1, int color2){ FastLED.addLeds<WS2811, LED_PIN1, RGB>(leds1, NUM_LEDS1); for(i=0; i<NUM_LEDS1; i++) leds1[i] = CRGB::Black; leds1[num] = color_set(color1); leds1[target] = color_set(color2); FastLED.show(); } void Led1_set3(){ FastLED.addLeds<WS2811, LED_PIN1, RGB>(leds1, NUM_LEDS1); for(i=0; i<NUM_LEDS1; i++) leds1[i] = CRGB::Black; for(i=0; i<11; i++) leds1[i*3] = CHSV(random8(), 255, 127); FastLED.show(); } void Led2_set(int num, int color){ if(num > 22) num = 22; FastLED.addLeds<WS2811, LED_PIN2, RGB>(leds2, NUM_LEDS2); for(i=0; i<NUM_LEDS2; i++) leds2[i] = CRGB::Black; for(i=0; i<num+1; i++) leds2[i] = color_set(color); FastLED.show(); } void all_black(){ FastLED.addLeds<WS2811, LED_PIN1, RGB>(leds1, NUM_LEDS1); for(i=0; i<NUM_LEDS1; i++) leds1[i] = CRGB::Black; FastLED.show(); FastLED.addLeds<WS2811, LED_PIN2, RGB>(leds2, NUM_LEDS2); for(i=0; i<NUM_LEDS2; i++) leds2[i] = CRGB::Black; FastLED.show(); target = 1 + (target + random(5,16)) % 28; points[1] = 0; } void melody_end(){ tone(11, musicScale[0]); delay(120); tone(11, musicScale[2]); delay(120); tone(11, musicScale[4]); delay(120); tone(11, musicScale[7]); delay(400); noTone(11); } void melody_again(int pitch){ tone(11, musicScale[pitch]); delay(30); noTone(11); }[9] LeaningSensor
![]()
個別RGB駆動LEDテープ を使った2作目として、過去に3軸加速度センサをオンボードに取り付けたSeeeduinoLiteがあったので、これを30cmの定規に貼り付けて、「傾きをコントロールする」というシステムを作った。 この日記(RRR日記[5])の「2025年1月27日(月)」のところに YouTube動画 とArduinoスケッチが置かれているが、ちょっとイジワルなのが感覚と逆の「大きく傾けると速度が低下して、ほぼ水平に近いところでは高速で移動する」という特性である。 これを理解するとクリアできる攻略法があるのだが、そこまで行かないで飽きてしまう生徒もいる。
[10] 8*8MatrixLED*4
・・・ということで、2日がかりになったものの、何とか10種類のシステムの情報を整理することが出来た。 明日にでも(今日は休業日らしいので)、このYouTube動画リンク集を「ロボット教室」の社長に送ってみよう。
![]()
新しくAmazonで仕入れた 8*8LEDマトリクス4連モジュール を活用した最新作である。 この日記(RRR日記[5])の「2025年2月3日(月)」のところに メイキング があり、「2025年2月10日(月)」のところに最終版のArduinoスケッチと、 YouTube動画 がある。 これはなかなかの自信作であり、明後日にある教室(ミドル+アドバンスの合体で計9人)でも話題になるのでは・・・と期待している。
そして、タブレットで作った「マイコンブロック」のソース画像も出来たところで、この10本の全てのソースを最小フォントでプリントした。 これも今週から、システムと一緒に持参して、興味があれば生徒に見せていくことにした。 単に電子工作システムで遊ぶだけでなく、その裏の「ソースコード」の凄さというものも実感してもらいたいのだ。
ここまで進めて、ぼちぼちJoyJoyに出掛ける準備をする時間になった。 今日は何となく手応えのあるこのテンションで、またまた頑張って完走を目指したい。2025年2月13日(木)
昨日のJoyJoyヒトカラでは6時間で64曲を完走した。 そして、昨日に10本(Arduinoテルミンを入れると11本)のサンプル動画情報が出揃ったので、ロボット教室の事務局に送ったところ、「以下のように駄目です」とのメイルが来た。
![]()
そう、僕はYouTube動画は一般公開していない(リンクされたところからだけ見える)し、「埋め込み」も許可していなかったのだった。 そこでYouTubeの設定を変更して、無事に以下のように見えることになった。
![]()
11個もズラッと並ぶと なかなか壮観 だが、これが呼び水になってくれるのかどうかは不明である。 そして、Arduinoのソースをたくさん載せたためか、既にこの日記のHTMLは248KBにもなっていた。 ここで区切りというのも微妙なのだが、これまでのルールに従って、続きは RRR日記(6) としていくことにしよう。 ただし、今日の日付けまではこちらなので、次に新しく書き始めるまでは、中身はからっぽである。
→ RRR日記(6)