RSS Twitter Facebook
DTM / シンセサイザー / VST / WebMusic 関係の技術情報を発信しています



2019/03/05 (2019年03月 のアーカイブ)

Web×Iot メイカーズチャレンジ東京ハッカソンが開催されました。

Twitter Facebook

先週末、3月2日(土)、3月3日(日) の2日間、Web × Iot メイカーズチャレンジ東京ハッカソンが開催され、メンターとして参加してきました。

Web×Iotメイカーズチャレンジ公式ページ

このイベントは2017年から始まった総務省主催のイベントで、30才未満のエンジニアを対象に IoT システム開発の若手エンジニア育成を目的として札幌から沖縄まで全国9ヶ所で順次開催されています。私がメンターをやったのは東京で開催されたハッカソンですが、既にその1ヵ月程前に事前の講習会とアイデアワークショップが2日間行われており、一応チーム分けと作りたいものが決まった所からのスタートです。

制御系は Web 技術ベースで IoT システムを構築できる「CHIRIMEN for Raspberry Pi」が基本になっています。

ハッカソンの実質の作業時間は1日半程度ですので、この時間内に動くものを完成させるのは簡単ではありません。各チーム想定外のトラブルに遭遇しつつの開発になります。

会場は虎ノ門、KDDI Digital Gate、綺麗な会場です。

スイッチサイエンスさん協力の元、ある程度の材料は会場内でも調達できるようになっています。

各チーム作業開始です。4~5人のチームが8チームあります。

半田付けは専用の半田付けコーナーが準備されています。

短時間でデモシステムを形にするため、段ボール工作スキルも必須です。

部品の一例、モーターとタイヤ。何ができるんですかね?

ペットボトルとか、とにかくあるものを使って形にしていきます。近くにある 100円ショップもかなり活躍した模様です。

おやつタイムに差し入れされた Web×Iot カステラ。文明堂ってこういうのも作ってくれるらしい。

途中でスペースXの打ち上げ時刻になったので突然実況を始める某氏。「スペースX は IoT だ!」と熱弁(まあそういう事にしておこう)。

調達してきた怪しげなセンサーを改造中。

2日目に入り開発も佳境に入ってきました。プログラムとセンサーとアクチュエータを繋ぎ合わせて行きますが、思ったように動かないという事も良くあります。

なかなか綺麗に描かれたプレゼン用ボード。成果をプレゼンするまでがハッカソンなのでプレゼン大事です。

さて、成果発表の時間になりました。

こちらのチームでは天候等の条件によって屋根が開く犬小屋。ちょっと予想外の動き。

これは猫背になっているのを検出して喝を入れる装置なのだけど、センサーの見た目がサイバーな感じでインパクトがあります。

体重計と連動してダイエットさせる冷蔵庫。

飲み過ぎを警告してくれる人形。

インテリジェントなトイレ。

寝落ちした時に優しく寝かせてくれるシステム。寝たら起こすのではなく、そのまま寝かせてしまうのがポイント。目が覚めた時にどうなっているかは知らない。

玄関の出入りをカウントしてGPSで追跡する装置。

金属センサーを繋いで地雷探知システムを作ったチームもありました。

審査委員長は村井純先生です。最優秀賞は「寝落ちした時に優しく寝かせてくれるシステム」でした。

最後は懇親会です。ハッカソンと言えばピザですね。

プラットフォームとなるシングルボード PC にセンサーやアクチュエーターを組み合わせて様々なシステムを簡単に構築できるというのが IoT と呼ばれるものの目指す所ではあるのですが、それでも1日半程度の時間でちゃんとデモができるものを組み立てるには、どこを切り捨ててどういう風に作るかの見極めが大事ですね。

このハッカソンでは、かなり大き目のメカ的な造作を動かしたいという話が多めで、その辺の工作に苦労しているチームが多かったようです。ソフトウェア関係等では、発表時間が迫ってきた所で突然動かなくなる等、ありがちなトラブルも色々あったようですが、まあ、そういうものですよね。トラブルも良い経験なのではないでしょうか。実際、最優秀賞を取った寝落ち検出システムも発表時間が迫るほどに不具合が多発して最終的にはかなり人力に頼りつつのデモとなりました。

なお、今回を含め全国各地で開催されたハッカソンの最優秀作品は、3月8日(金)にベルサール神田で行われるスマートIoT推進フォーラム総会で展示されます。

スマートIoT推進フォーラム総会

Posted by g200kg : 11:49 PM : PermaLink

2019/02/13 (2019年02月 のアーカイブ)

webaudio-macronodes

Twitter Facebook
これは GitHub に置いてあるリポジトリの説明です。

リポジトリ:webaudio-macronodes

Web Audio API のノードは全体的に機能の粒度が細かめで組み合わせの自由度は高いですが、反面何らかのエフェクターのようなものを構成するには複数のノードを組み合わせる必要があり、エフェクターの内部構造に関する知識を必要とします。

前回の記事で「[Web Audio API] 既存ノードを組み合わせたカスタムノードの作り方」というのを書いたのですが、これを使っていわゆるギターエフェクター的な機能を単一のノードにまとめる事ができます。

webaudio-macronodes は良くあるエフェクターのセットを Web Audio API のノードとして使えるようにまとめたものです。特長は、ノードの生成や接続、パラメータへのアクセスに関して、例えば GainNode や BiquadFilterNode のような通常の Web Audio API のノードとまったく同じ使い方ができる事です。


使い方

本体は webaudio-macronodes.js というファイルになるのでこれを読み込みます。GitHub pages から直接読み込むならば、次の行を HTML に追加します。
<script src="https://g200kg.github.io/webaudio-macronodes/webaudio-macronodes.js"></script>
これによって webaudio-macronodes が持っている 10 個程度のエフェクトが使えるようになります。


ノードの生成

コンストラクタを使って生成します。audioContext.createGain() のようなファクトリメソッドはサポートしていません。 例えば、

toneControl = new Macro_ToneControlNode(audioContext);
また、作成時にパラメータの初期値を渡す事もできます:

toneControl = new Macro_ToneControlNode(audioContext, {bass:10, mid:0 treble:-3});


ノードの接続

通常のノードと同様に connect() メソッドで接続して行きます。このノードに対する接続が Web Audio API の通常のノードと同様に connect() メソッドを使う事でできるのが webaudio-macronodes の肝の部分です。

例えば

sourceNode.connect(toneControl).connect(audioContext.destination);


パラメータへのアクセス

webaudio-macronodes の各ノードはそれぞれエフェクトのかかり方を調整するパラメータを持っています。これらのパラメータは基本的に AudioParam 型になっていますので、setValueAtTime() 等のオートメーション関数を使うなり、他のノードの信号を connect() するなり自由にアクセスできます。

例えば

now = audioContext.currentTime;
toneControl.bass.setValueAtTime(0,now);
toneControl.bass.linearRampToValueAtTime(12,now+10);


エフェクトの一覧

パラメータが取る値の範囲等の詳細はリポジトリを見てください。

リポジトリ : webaudio-macronodes
エフェクトパラメータ説明
Macro_ToneControlNodetreble
mid
bass
高音/中音/低音のトーンコントロール
Macro_GraphicEqNodeeq[]7 バンド・グラフィックイコライザー
Macro_ChorusNoderate
depth
一般的なコーラスエフェクト
Macro_PhaserNoderate
depth
一般的なフェイザーエフェクト
Macro_DeepPhaserNoderate
depth
resonance
フィードバック付きのフェイザーエフェクト
Macro_DelayNodedelayTime
feedback
mix
一般的なディレイエフェクト
Macro_PingPongDelayNodedelayTime
feedback
mix
左右チャンネルから交互に出るディレイエフェクト
Macro_BitCrusherNodebitsビット深度を減少させるビットクラッシャーエフェクト
Macro_OverDriveNodedrive
level
サチュレーションによる歪みを付加するオーバードライブエフェクト
Macro_FuzzNodefuzz
level
倍音の強い歪みを付加するファズエフェクト
Macro_AutoWahNodefrequency
Q
sense
音の大きさでフィルターを変化させるオートワウエフェクト

Posted by g200kg : 8:50 AM : PermaLink

2019/02/01 (2019年02月 のアーカイブ)

[Web Audio API] 既存ノードを組み合わせたカスタムノードの作り方

Twitter Facebook

少し前に GitHub にサンプルのリポジトリだけ置いてあったのですが、これは Web Audio API で幾つかのノードを組み合わせて独自のノードを作る方法の説明です。処理をすべてスクラッチから作るなら AudioWorkletNode を使えば良いのですが、単に既存のノードを組み合わせて1つのノードとして動作させるには少し裏技的な手法が必要になります。

GitHub : g200kg/webaudio-customnode

基本的に Web Audio API の各種ノードの機能はかなり細かく分けられていますので、一般的なエフェクターのようにまとまった機能のものを作るには幾つかのノードを組み合わせる必要があります。典型的な例がディレイエフェクトで、Web Audio API の DelayNode は信号を指定の時間遅らせるだけのものですから、エフェクター的ないわゆるディレイ効果を作るには、元音とのミックスや音を繰り返させるフィードバックの機能が欲しい所です。

上の図が今回作るサンプルのカスタムノード、MyDelayNode の構造です。入力端及び出力端は必ずしも GainNode でなくても良いのですが、単一の connect() メソッドでの接続ができるように信号が 1 本にまとまっている必要があります。

この MyDelayNode を標準の1つのノードと同等の方法で使えるようにします。つまり、
  1. var myNode = new MyNode(audiocontext, option) // コンストラクタでノードを作成できる。
  2. myNode.parameter.setValueAtTime(0,0) // 必要なパラメータにアクセスしオートメーションも使える。
  3. myNode.connect(otherNode) // ノードから出力を接続できる。
  4. otherNode.connect(myNode) // このノードに対して接続できる。
これを class にしたものが下のコードです。


class MyDelayNode extends GainNode {
  constructor(actx,opt){
    super(actx);
    // Internal Nodes
    this._delay = new DelayNode(actx, {delayTime:0.5});
    this._mix = new GainNode(actx, {gain:0.5});
    this._feedback = new GainNode(actx, {gain:0.5});
    this._output = new GainNode(actx, {gain:1.0});
    // Export parameters
    this.delayTime = this._delay.delayTime;
    this.feedback = this._feedback.gain;
    this.mix = this._mix.gain;
    // Options setup
    for(let k in opt){
      switch(k){
      case 'delayTime': this.delayTime.value = opt[k];
        break;
      case 'feedback': this.feedback.value = opt[k];
        break;
      case 'mix': this.mix.value = opt[k];
        break;
      }
    }
    this._inputConnect = this.connect;   // input side, connect of super class
    this.connect = this._outputConnect;  // connect() method of output
    this._inputConnect(this._delay).connect(this._mix).connect(this._output);
    this._inputConnect(this._output);
    this._delay.connect(this._feedback).connect(this._delay);
  }
  _outputConnect(to,output,input){
    return this._output.connect(to,output,input);
  }
}
ポイントはこのカスタムノードに対する接続の処理の仕方です。他のノードから connect() メソッドで接続するためには、このノード自体が AudioNode である必要があるので、既存ノードの子クラスとして作成します。
  • 1 行目、クラスは既存のノード、例えば GainNode の子クラスとして extends で派生させます。適当に作ったクラスでは他のノードからの接続ができません。この親クラスのノードが入力端のノードとなります。
  • 24、25 行目、このカスタムノードからの出力となる connect() メソッドは子クラス側でオーバーライドすれば良いのですが、その前に元の connect() メソッドを確保しておきます。ここで確保した inputConnect で、入力端子である親クラスの GainNode に入ってくる信号を取り出す事ができます。
  • 4 ~ 8 行目、内部で使用するノードです。
  • 9 ~ 12 行目、カスタムノードとして必要なパラメータを外に引っ張り出します。
MyDelayNode サンプル


もう1つの例、ピンポンディレイです。これはディレイ音が左右のチャンネルから交互に出るエフェクトで、DelayNode を分割して途中からタップを出し、左右のチャンネルに振り分けています。

入出力の接続に関しては前のサンプルと同様ですが、ここで問題になるのは、2 つある DelayNode の delayTime パラメータをどうやって制御するかという点です。単一の AudioParam 型パラメータから 2 つの DeleyNode を連動して動かす必要があります。これには ConstantSourceNode が使えます。ConstantSourceNode の offset パラメータを外部に対して delayTime パラメータとして露出させ、内部では connect() メソッドによるノード間の接続で DelayNode の delayTime に接続します。


class MyPingPongDelayNode extends GainNode {
  constructor(actx,opt){
    super(actx);
    // Internal Nodes
    this._delay1 = new DelayNode(actx, {delayTime:0.5});
    this._delay2 = new DelayNode(actx, {delayTime:0.5});
    this._merger = new ChannelMergerNode(actx);
    this._constant = new ConstantSourceNode(actx, {offset:0.5});
    this._mix = new GainNode(actx, {gain:0.5});
    this._feedback = new GainNode(actx, {gain:0.5});
    this._output = new GainNode(actx, {gain:1.0});
    // Export parameters
    this.delayTime = this._constant.offset;
    this.feedback = this._feedback.gain;
    this.mix = this._mix.gain;
    // Options setup
    for(let k in opt){
      switch(k){
      case 'delayTime': this.delayTime.value = opt[k];
        break;
      case 'feedback': this.feedback.value = opt[k];
        break;
      case 'mix': this.mix.value = opt[k];
        break;
      }
    }
    this._inputConnect=this.connect;   // input side, connect of super class
    this.connect=this._outputConnect;  // connect() method of output
    this._inputConnect(this._delay1).connect(this._delay2).connect(this._merger,0,0).connect(this._mix).connect(this._output);
    this._delay1.connect(this._merger,0,1);
    this._inputConnect(this._output);
    this._delay2.connect(this._feedback).connect(this._delay1);
    this._constant.connect(this._delay1.delayTime);
    this._constant.connect(this._delay2.delayTime);
    this._constant.start();
  }
  _outputConnect(to, output, input){
    return this._output.connect(to, output, input);
  }
}

MyPingPongDelayNode サンプル


このような手法で class の登録さえすれば、標準のノードと同様の使い方が可能な、独自のノードが使えるようになります。あまり複雑な構造になりすぎるとパフォーマンスに影響が出てきそうではありますが、内部のノードの JavaScript の処理をノードの接続で置き換えるのがポイントになります。

なお、Web Audio API の V2 では、複数のノードを組み合わせたものを格納できるコンテナとなるノードの検討がされるようです。これが使えるようになれば、もっと自然な方法でこういう事ができるようになると思いますが、現在の仕様内でもやり方を工夫すれば同様の事ができますので、覚えておけば役に立つ事もあるかも知れません。

Posted by g200kg : 4:13 AM : PermaLink

2019/01/30 (2019年01月 のアーカイブ)

SkinMan 1.01

Twitter Facebook

8 年くらい前にリリースしてそのままになっていたお絵描きプログラム「SkinMan」にバグレポートが来て、そもそも何をやってたか思い出せるかどうかが危ぶまれたのだが、何とか直ったようなので、今更だけどバージョンアップしておいた。

SkinMan 1.01

バグの内容はファイルを保存する度に少しずつファイルが大きくなって行くというもので、今 300MB くらいになってるんだけど... という事で申し訳ないです。

長年放置してたので言ってもしょうがないけど、オーディオ系 GUI 作成に特化しているから今でも割と使いやすいとは思っているのだけど。ただ Win 専用だし、書き直すなら Web アプリにしたい所なんだけど、ファイルの互換性を維持とか、ちょっと大変そうなんだよな。

Posted by g200kg : 2:18 AM : PermaLink

2019/01/28 (2019年01月 のアーカイブ)

MIDI 2.0 の件

Twitter Facebook

さて、例年どおり開催された世界最大の楽器ショー NAMM 関係のニュースもほぼ出そろったようです。
島村楽器さんの記事とかRockOnさんの記事はまとまっていて読みやすいですね。見れば大体どんなものが出てきたかがわかりますが、気になるものはあったでしょうか?

私としては Teenage Engineering のモジュラー組み立てキットとか気になりますが。

そしてもうひとつ NAMM の直前くらいからニュースが流れていた「MIDI 2.0」の話。38 年ぶりに MIDI の規格がバージョンアップするという事です。といっても、「今やってます、名前は決めました」というニュースなので、これを搭載した製品が出てくるにはもう少しかかると思いますが、NAMM ではプロトタイプのデモなども行われたようです。


MIDI-CI

MIDI 2.0 に先立って昨年「MIDI-CI (MIDI Capability Inquiry)」という規格を作っているというニュースが流れていましたが、これとの関係としては MIDI-CI は MIDI 2.0 の一部として入口部分にあたるものになっています。下の図が MMA のサイトなどで公開されていますが、MIDI デバイスはまず MIDI-CI によってお互いにネゴシエーションを行い、MIDI 2.0 に対応している場合はそれを使って更に設定などの情報をやり取りする、対応していない場合は今まで通りの MIDI 1.0 デバイスとして接続する、という事を表しています。

MIDI 2.0 の基本的なコンセプトとして、3つの「B」と3つの「P」なるものがあります。

3つの「B」の1つが「双方向 (Bi-Directional)」です。今までの MIDI が一方通行で送りっぱなし、送ったメッセージが届いたかどうか、そもそも送り先にデバイスが存在しているかどうかがわからなかったのに対して、MIDI 2.0 では相手のデバイスとの間で双方向のやり取りをする事になっています。そして2つ目の「B」が「下位互換 (Backward Compatible」、つまり MIDI 1.0 の機器を繋いでもちゃんと動作する事。まあ、これを大事にしていたから MIDI はここまで生き残れてきたので当然の戦略ですね。

そして3つ目が「両方やる (Both)」。新しい MIDI 2.0 部分だけ作って MIDI 1.0 部分は手付かずというわけではなく、MIDI 1.0 相当部分にも可能な改善は加えていくという意味のようです。

そして双方向通信を使ってお互いが MIDI 2.0 に対応しているかどうかというネゴシエーション以降になる 3 つの「P」の部分が「プロファイル・コンフィグレーション (Profile Configuration)」「プロパティ・エクスチェンジ (Property Exchange)」そして「プロトコル・ネゴシエーション (Protocol Negotiation)」ですね。

プロファイルは自分が何の機器であるかを端的に表す属性を示すもので、自分は「オルガン」だよ、とか「アナログシンセ」だよ、という風に宣言する事で例えば「オルガン」ならドローバーの設定などを共有したりできるようにするというものです。

そしてプロパティエクスチェンジは、どんなパラメータを持っているかの詳細をやり取りするもので、今までの MIDI 機器でいえば、繋ぐ機器に応じたMIDI コントローラのセットアップとか、MIDI デバイスの MIDI ラーンの機能を自動化する事を目的としています。なおこれは、MIDI-PE と略される事はあるみたいですが、MPE だと MIDI Polyphonic Expression の略で、後述する ROLI の Seaboard なんかがサポートしている ノート毎のアーティキュレーションを指す機能の事になります。ちょっとややこしい。

3つ目のプロトコル・ネゴシエーションはこの双方向通信によって MIDI 1.0 デバイスと MIDI 2.0 デバイスを振り分ける仕組み自体をさしています。


ボイスメッセージ関係

ここまでは、MIDI-CI の周辺になる双方向関係の部分ですが、今回のニュースでは今までの MIDI の不満点として良く言われるベロシティのレゾリューションの不足への対応やチャンネルの増設などにも触れられています。

1 本のケーブルで 16 チャンネルに対応するというのが MIDI 1.0 の規格でしたが MIDI 2.0 では、1 本のデータのストリーム内に MIDI 1.0 または 2.0 として使用できる 16 個のグループがあり、各グループが 16 チャンネルを持っている、つまりトータル 256 チャンネルという事になります。

Group
12345678910111213141516
MIDI 1.0MIDI 2.0MIDI 2.0MIDI 1.0MIDI 1.0MIDI 2.0MIDI 2.0MIDI 2.0MIDI 1.0MIDI 2.0MIDI 2.0MIDI 2.0MIDI 2.0MIDI 1.0MIDI 1.0MIDI 1.0


ベロシティやコントロールチェンジのレゾリューションも拡張されて軒並み上がってますね。これらは MIDI 1.0 にも相当する機能が存在していますが、レゾリューションが拡張されています。記憶ではたしか MIDI 1.0 でも高精度ベロシティプリフィックス (14bit) とか一応あったと思うのですが、全然使われてなかったようです。

MIDI 1.0MIDI 2.0
ベロシティ 7bitベロシティ 16bit
ポリ・チャンネルプレッシャー/ピッチベンド 7bitポリ・チャンネルプレッシャー/ピッチベンド 32bit
16384 RPN (14bit)
16384 NRPN (14bit)
16384 レジスタードコントローラ (32ビット)
16384 アサイナブルコントローラ (32ビット)
128コントロールチェンジ (7bit)128 コントロールチェンジ (32bit)


そして MIDI 1.0 とは互換の無い形で新規に追加されるメッセージもあります。8bit の型と 16bit のデータを持つアーティキュレーション情報、256 種のレジスタードコントローラと 256 種のアサイナブルコントローラ。今までのコントロールチェンジがチャンネル毎にまとめて対応なのに対して、これらはノートの 1 音 1 音毎に対応するもので、「5 次元キーボード」という触れ込みで話題だった ROLI の Seaboard がサポートしている MPE に相当するもの、VST で言えば Note Expression のようなものになると思います。

MIDI 2.0 Messages
アーティキュレーション タイプ (8bit) データ(16bit)
256 拡張レゾリューション・レジスタードコントローラ (32bit)
256 拡張レゾリューション・アサイナブルコントローラ
拡張レゾリューション・パーノート・ピッチベンド (32bit)

これだけのものを規格としてまとめるのはなかなか大変そうです。流石に 38 年経って古いといわれる MIDI 規格ですが、MIDI 2.0 も長く使える規格になって欲しいですね。


Posted by g200kg : 2:17 PM : PermaLink

2019/01/26 (2019年01月 のアーカイブ)

WebAudio.tokyo #7 が開催されました

Twitter Facebook

昨日 1 月 25 日に渋谷で 7 回目となる WebAudio.tokyo が開催されました。

内容は LT が 5 本 + 飛び込みです。ピアノロールの実装とかシェーダーを使った VJ エフェクトとかシャトルラン用プログラムとか。ヒューマンビートボックスと 88 鍵 キーボードでプログラムを書く話が印象的でした。

会場はこちら。渋谷ヒカリエ 17F レバレジーズ様です。

@afroscriptさんのヒューマンビートボックスに連動する3Dの豚のデモ。

飛び込み LT の@fudafootaさん。88鍵キーボードでプログラムを書く話。画面真っ白になってるけど。

88 鍵に文字を割り当てると キーボード(楽器のほう)でプログラムが書ける!! そして「hello, world」が曲になる!! いいねー



LT枠がなかなか埋まらないみたいだったのでつい埋めに行ってしまったけど、なぜかこのイベントは飛び込みで面白いものを持ってくる人が多い印象です。
ちなみに私からのネタは「ConstantSourceNodeの話」です。内容は WebAudioAPI には「ConstantSourceNode」という目立たない地味なノードがあるのだけど、これが実は Envelope Generator ノードとして動作するという話です。

この辺のノウハウ的な話は他にもいくつかあるので暇を見て「Web Audio API 解説」に追加しておこうと思います。なお、Web Audio API 解説もサンプルコードが古くなったりしていたので現状にあわせて更新中です。

デモで使ったプログラムはこちら :

ただし、現在 Release チャンネルの Chrome 71 では、ある操作をするとブラウザがハングアップします。Canary の Chrome 73 ではなおっているので降りてくるのを待ち状態。

Posted by g200kg : 4:34 PM : PermaLink

2019/01/16 (2019年01月 のアーカイブ)

サンプルコードは GitHub で Unlicense でどうかな?

Twitter Facebook

ちゃんと纏まっているアプリではなくて、ちょっとしたコードフラグメントやサンプルコードのようなものを GitHub に置く時、ライセンスの選択ってどうやってますか?

だいたいいつもテンプレートで選べるメジャーな選択肢の中では一番緩いと思われる MIT License にしていたのですが、MIT にも表示義務はあるので本当のところは、使い方の Tips 的なコードフラグメントに MIT License が付いていても気にする人は困る事があるだろうなとは思っていたんです。

クリエイティブ・コモンズの CC0 とか、ちょっとネタ的ではあるけど WTFPL(Do What The Fuck You Want To Public License) あたりが適当かなと思うのだけど、GitHub のテンプレートには無いから自分でコピーしてくるのが面倒くさい。

と思っていたのだけど、良いのがあるのを知らなかった。

それが、Unlicense

法的制約に則りつつパブリックドメインに可能な限り近づけるという クリエイティブ・コモンズ CC0 と目指すところは同じようです。

CC0 の方が洗練されているとか、色々意見もあるようだけど、サンプルコードなんかにはこれで良くないかな?
GitHub のライセンステンプレートの一番最後に載っていたのに今頃気づいた。

という事で今後は Unlicense を推します。
https://github.com/g200kg/audioworklet-in-one-file

Unlicense 全文

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.

In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org/>


Posted by g200kg : 9:22 PM : PermaLink

2019/01/15 (2019年01月 のアーカイブ)

[Web Audio API] AudioWorklet を 1 ファイルで書く方法

Twitter Facebook

AudioWorklet は Web Audio API でユーザーが自分独自のカスタムノードを作成できるようにするための仕組みで、プログラムのメインスレッドで走らせる AudioWorkletNode と音声処理スレッドで実行する AudioWorkletProcessor と呼ばれる部分で構成されます。

基本的な書き方としては AudioWorkletNode 側と AudioWorkletProcessor 側を別ファイルの ".js" で書くというスタイルが標準的で、これは別に悪い事ではないのですが前身となった ScriptProcessor が単に関数を定義するだけで使えたのに比べると、ちょっと試しに軽く使いたい時に、別ファイルを準備するのがちょっと面倒くさいと感じてしまいます (大規模なプログラムを書くつもりなら分けておいた方が良いと思うんですが)。

という事で、1ファイルでメインプログラムとプロセッサをまとめて書く方法を幾つか試してみます。


今回ベースにするコード

まずはベースとして前回の記事 ([WebAudio API] AudioWorklet の使い方) で書いた AudioWorklet のオーバードライブを使用します。

プロセッサ側のコードは次のようになっています。

overdrive-proc.js

class OverDrive extends AudioWorkletProcessor {
    static get parameterDescriptors () {
        return [{
            name: 'drive',
            defaultValue: 0,
            minValue: 0,
            maxValue: 1,
            automationRate: "k-rate"
        }];
    }
    process (inputs, outputs, parameters) {
        let input = inputs[0];
        let output = outputs[0];
        let drv = Math.pow(0.05,Math.abs(parameters.drive[0]));
        for (let channel = 0; channel < output.length; ++channel) {
            for (let i = 0; i < output[channel].length; ++i) {
                var d=input[channel][i];
                if(d<0)
                    output[channel][i]=-Math.pow(-d,drv);
                else
                    output[channel][i]=Math.pow(d,drv);
            }
        }
        return true;
    }
}
registerProcessor("OverDrive", OverDrive);

プロセッサのコードは "OverDrive" クラスを定義して、registerProcessor() で名前を付けて登録する、という処理になっています。 このプロセッサを使うメインプログラムとして取り合えず簡単なものを書いたものが次のコードです。


<!doctype html>
<html lang="en">
<body>
<audio id="soundsrc" src="./loop.wav" controls loop></audio><br/>
Drive : <input id="drive" type="range" min="0" max="1" step="0.01" value="0"/><br/>
<script>
window.onload = (async ()=>{
    audioctx = new AudioContext();
    await audioctx.audioWorklet.addModule('overdrive-proc.js');
    const src = new MediaElementAudioSourceNode(
        audioctx,{mediaElement:document.getElementById('soundsrc')}
    );
    const overdrive = new AudioWorkletNode(audioctx, 'OverDrive');
    const paramDrive = overdrive.parameters.get('drive');
    src.connect(overdrive).connect(audioctx.destination);

    document.getElementById("drive").addEventListener("input",(ev)=>{
        audioctx.resume();
        paramDrive.value=ev.target.value;
    })
});
</script>
</body>
</html>

このコードのテストページ


音源を <audio> タグでドキュメント内に置いて MediaElementAudioSourceNode で読み込む方法で、オーバードライブを経由して鳴らします。ちなみに window.onload で初期化をしていますが、プライバシーポリシー制限でユーザー操作が無い状態で作った AudioContext は "suspend" 状態ですので、スライダーを触ったら resume() するという事をやっています。

プロセッサのファイル "overdrive-proc.js" を読み込んでいるのが 9 行目、audioctx.audioWorklet.addModule() です。


取り合えずプロセッサ側のコードを文字列として埋め込んでみる

まずはファイル "overdrive-proc.js" の内容をそのまま文字列に入れておいて DataURI 経由でアクセスするという方法です。38 行目の処理、
await audioctx.audioWorklet.addModule('data:text/javascript,'+encodeURI(overdriveproc));
がそれです。'data:text/javascript,' に続けてプロセッサの文字列をエスケープしてくっ付ける事で、URL としてアクセスできるようにしています。

単純に文字列としてコードを入れるのは、昔の JavaScript だと文字列が改行で途切れないように全ての行末に「\」を付ける必要があったりしてあまり実用的ではなかったのですが、ES6 でテンプレート文字列が使えるようになってからは、コード全体を 「`」で囲むだけですのでこれでもまあいけなくはないです。

このコードのテストページ

<!doctype html>
<html lang="en">
<body>
<audio id="soundsrc" src="./loop.wav" controls loop></audio><br/>
Drive : <input id="drive" type="range" min="0" max="1" step="0.01" value="0"/><br/>
<script>
const overdriveproc =`
class OverDrive extends AudioWorkletProcessor {
    static get parameterDescriptors () {
        return [{
            name: 'drive',
            defaultValue: 0,
            minValue: 0,
            maxValue: 1,
            automationRate: "k-rate"
        }];
    }
    process (inputs, outputs, parameters) {
        let input = inputs[0];
        let output = outputs[0];
        let drv = Math.pow(0.05,Math.abs(parameters.drive[0]));
        for (let channel = 0; channel < output.length; ++channel) {
            for (let i = 0; i < output[channel].length; ++i) {
                var d=input[channel][i];
                if(d<0)
                    output[channel][i]=-Math.pow(-d,drv);
                else
                    output[channel][i]=Math.pow(d,drv);
            }
        }
        return true;
    }
}
registerProcessor("OverDrive", OverDrive);
`;
window.onload = (async ()=>{
    audioctx = new AudioContext();
    await audioctx.audioWorklet.addModule('data:text/javascript,'+encodeURI(overdriveproc));
    const src = new MediaElementAudioSourceNode(
        audioctx,{mediaElement:document.getElementById('soundsrc')}
    );
    const overdrive = new AudioWorkletNode(audioctx, 'OverDrive');
    const paramDrive = overdrive.parameters.get('drive');
    src.connect(overdrive).connect(audioctx.destination);

    document.getElementById("drive").addEventListener("input",(ev)=>{
        audioctx.resume();
        paramDrive.value=ev.target.value;
    })
});
</script>
</body>
</html>

特殊なタイプのスクリプトとして HTML 内に埋め込む方法

他のやり方としては HTML 内に特殊なスクリプトとして埋め込む方法があります。スクリプトのタイプをそのまま実行される JavaScript と解釈されないように特殊な名前で定義します。

こういうやり方は WebGL の GLSL シェーダーを HTML 内に書く時などにも使われていて 'x-shader/x-vertex' とか 'x-shader/x-fragment' などとする事が多いようです。'x-' を付けるのは標準のタイプではない、という意味になります。 ここでは、AudioWorklet 用の JavaScript なので、
<script type="x-audioworklet/javascript">
としました。中身は document.getElementById('id').innerHTML で取ってこれますので処理としては文字列とそれほど違いはないですね。

ここの表示ではプロセッサのコードが一応 JavaScript としてシンタックスハイライトが効いているので文字列の場合よりかなり読みやすいですが、これはエディタによると思います。VScode では未知のスクリプト扱いで駄目でした。

このコードのテストページ

<!doctype html>
<html lang="en">
<body>
<audio id="soundsrc" src="./loop.wav" controls loop></audio><br/>
Drive : <input id="drive" type="range" min="0" max="1" step="0.01" value="0"/><br/>

<script id="overdriveproc" type="x-audioworklet/javascript">
class OverDrive extends AudioWorkletProcessor {
    static get parameterDescriptors () {
        return [{
            name: 'drive',
            defaultValue: 0,
            minValue: 0,
            maxValue: 1,
            automationRate: "k-rate"
        }];
    }
    process (inputs, outputs, parameters) {
        let input = inputs[0];
        let output = outputs[0];
        let drv = Math.pow(0.05,Math.abs(parameters.drive[0]));
        for (let channel = 0; channel < output.length; ++channel) {
            for (let i = 0; i < output[channel].length; ++i) {
                var d=input[channel][i];
                if(d<0)
                    output[channel][i]=-Math.pow(-d,drv);
                else
                    output[channel][i]=Math.pow(d,drv);
            }
        }
        return true;
    }
}
registerProcessor("OverDrive", OverDrive);
</script>

<script>
window.onload = (async ()=>{
    audioctx = new AudioContext();
    await audioctx.audioWorklet.addModule(
        'data:text/javascript,'
        + encodeURI(document.getElementById('overdriveproc').innerHTML)
    );
    const src = new MediaElementAudioSourceNode(
        audioctx,{mediaElement:document.getElementById('soundsrc')}
    );
    const overdrive = new AudioWorkletNode(audioctx, 'OverDrive');
    const paramDrive = overdrive.parameters.get('drive');
    src.connect(overdrive).connect(audioctx.destination);

    document.getElementById("drive").addEventListener("input",(ev)=>{
        audioctx.resume();
        paramDrive.value=ev.target.value;
    })
});
</script>
</body>
</html>

普通の JavaScript のクラスとして宣言してしまう方法

前の 'x-audioworklet' あたりの方法がまあ妥当かと思いつつ、最後にもうひとつトリッキーなやりかたで、普通に JavaScript のクラスとして書いてしまう方法もなくはないです。

もちろんそのままプロセッサのコードを メインのコード内に書いても動かないので少し細工します。

まずプロセッサ用のクラスは 'AudioWorkletProcessor' クラスを継承して作成しますが、この AudioWorkletProcessor はメインスレッド側には存在しないため、そもそもエラーになるのでダミーを宣言しておきます。
class AudioWorkletProcessor{}
それから次の 'addAudioWorklet()' がメインスレッド側のクラスをプロセッサとして登録するための関数です。やっている事は宣言されているクラスオブジェクトから 'toString()' で文字列化して、後は文字列として埋め込む方法と違いはありません。

ただし引数として取るのはクラスオブジェクトそのものですので、プロセッサ登録のための 'registerProcessor()' は自前で追加しています。また登録される名前はクラス名そのものになります。ここではクラス 'OverDrive' で宣言しているのでノードを作成する時も
new AudioWorkletNode(context, 'OverDrive')
となります。

addAudioWorklet()の戻り値は 'addModule()' と同じく Promise ですので必要なら await で待つなりしてください。

このコードのテストページ

<!doctype html>
<html lang="en">
<body>
<audio id="soundsrc" src="./loop.wav" controls loop></audio><br/>
Drive : <input id="drive" type="range" min="0" max="1" step="0.01" value="0"/><br/>

<script>
// Register Class as an AudioWorklet Processor
class AudioWorkletProcessor{}
function addAudioWorklet(context, proc){
    var f=`data:text/javascript,${encodeURI(proc.toString())}; registerProcessor("${proc.name}",${proc.name})`;
    return context.audioWorklet.addModule(f);
}

// AudioWorklet Processor Class
class OverDrive extends AudioWorkletProcessor {
    static get parameterDescriptors () {
        return [{
            name: 'drive',
            defaultValue: 0,
            minValue: 0,
            maxValue: 1,
            automationRate: "k-rate"
        }];
    }
    process (inputs, outputs, parameters) {
        let input = inputs[0];
        let output = outputs[0];
        let drv = Math.pow(0.05,Math.abs(parameters.drive[0]));
        for (let channel = 0; channel < output.length; ++channel) {
            for (let i = 0; i < output[channel].length; ++i) {
                var d=input[channel][i];
                if(d<0)
                    output[channel][i]=-Math.pow(-d,drv);
                else
                    output[channel][i]=Math.pow(d,drv);
            }
        }
        return true;
    }
}

// Main Program
window.onload = (async ()=>{
    audioctx = new AudioContext();
    await addAudioWorklet(audioctx, OverDrive);
    const src = new MediaElementAudioSourceNode(
        audioctx,{mediaElement:document.getElementById('soundsrc')}
    );
    const overdrive = new AudioWorkletNode(audioctx, 'OverDrive');
    const paramDrive = overdrive.parameters.get('drive');
    src.connect(overdrive).connect(audioctx.destination);

    document.getElementById("drive").addEventListener("input",(ev)=>{
        audioctx.resume();
        paramDrive.value=ev.target.value;
    })
});
</script>
</body>
</html>

これならエディタで確実にシンタックスハイライトが効くというのが良い所です。
メインプログラムのスコープ内にクラスオブジェクトとして存在してしまっているのが無駄と言えば無駄ですが、許せる範囲でしょうか。

Posted by g200kg : 12:20 AM : PermaLink

2019/01/14 (2019年01月 のアーカイブ)

[WebAudio API] AudioWorklet の使い方

Twitter Facebook

さて、2019 年になって ScriptProcessor の後継とされる AudioWorklet が Chrome で動き始めてからもう一年くらい経ちましたかね。まだまだガリガリ使われているという感じでもないのは、以前の ScriptProcessor に比べると構造が複雑になってちょっととっつきにくくなったからでしょうか。少しハードルが上がった感はありますね。

それに現状は Web Audio API の仕様とブラウザの実装がまだ少し乖離している所が残っているので気を付けないとハマるかも知れません。

とは言っても ScriptProcessor がいつまであるかもわからないし、そろそろちゃんと使わないとですね。

という事でまずはサンプルを書いてみました。

AudioWorklet (OverDrive)


たいしたものではないですが、これはオーバードライブエフェクトを AudioWorklet で実装したものです。[Play]を押すと音楽が流れて [Drive] のツマミでかかり具合を調整できます。


ソースコード overdrive-proc.js


class OverDrive extends AudioWorkletProcessor {
    static get parameterDescriptors () {
        return [{
            name: 'drive',
            defaultValue: 0,
            minValue: 0,
            maxValue: 1,
            automationRate: "k-rate"
        }];
    }
    process (inputs, outputs, parameters) {
        let input = inputs[0];
        let output = outputs[0];
        let drv = Math.pow(0.05,Math.abs(parameters.drive[0]));
        for (let channel = 0; channel < output.length; ++channel) {
            for (let i = 0; i < output[channel].length; ++i) {
                var d=input[channel][i];
                if(d<0)
                    output[channel][i]=-Math.pow(-d,drv);
                else
                    output[channel][i]=Math.pow(d,drv);
            }
        }
        return true;
    }
}
registerProcessor("OverDrive", OverDrive);


AudioWorklet では、音声処理を実行するプロセッサはメインのプログラムとは別ファイルで用意するのが基本です。

この 'overdrive-proc.js' がプロセッサ部分になりますが、AudioWorkletProcessor クラスから派生した class を定義し、registerProcessor() で登録します。プロセッサはメインプログラムとは別のオーディオ処理専用のスレッドで実行され、変数を受け渡したりというメインプログラムの世界とは直接的なやりとりはできません。

ここで定義したクラス内の process() 関数が実際の処理を行います。引数の inputs が音声信号の入力の配列、outputs が音声信号の出力の配列、parameters はこのノードが持っているパラメータの値を表します。OverDrive のようなエフェクター的なノードの場合はデフォルト状態の入力が 1 本、出力が 1 本で良いです(ステレオ音声等のマルチチャンネルも入力の数としては 1 本です)。

なので inputs[0] および outputs[0] が処理すべき入出力になります。入出力はブロック (128 サンプル) 毎に渡されますので、各チャンネルの各サンプルを入力から取得して処理を行い出力に渡しています。

ちなみにオーバードライブ処理というと一般的には tanh 関数なんかが良く使われるのですが、ここで行っているのは

\(output = Math.pow(input, 定数)\)

という処理を正負対称にくっつけたものを使っています。ここで定数を 1.0 ~ 0.05、つまり実数乗根とすると 1.0 で歪み無し、小さくなる程すぐに 1.0 に漸近するカーブになるので歪みが大きくなります。更にパラメータの 0.0 ~ 1.0 に対して冪乗の定数は

\(Math.pow(0.05, パラメータ)\)

で 1.0 ~ 0.05 の範囲にマップしています。入力に対する出力カーブは次のようになります。
まあこのあたりは好みもあるので適当ですが。

後は process() の最後が return true になっている所に注目です。とりあえず今は process() は true を返す、という事で良いですがこれはまた後述。

それから、static get parameterDescriptors() いうメソッドがありますが、これはこの OverDrive ノードが持っているパラメータを表しています。Oscillator ノードの frequency や Gainノードの gain のようなものです。

ここではdrive という 0 ~ 1 の範囲のパラメータを一つだけ持っていて animationRate が "k-rate" である事が宣言されています。

animationRate は "k-rate" と "a-rate" があり、"a-rate" はパラメータが 1 回の process() の呼び出し内でサンプル単位で変化し(する可能性があり)、"k-rate" はブロック単位なので process() 呼び出し内では必ず固定値になります。"a-rate" だともう少しコード量が増えてしまうし、"k-rate" より処理が重くなります。


ソースコード メインプログラム (html)

さて、プロセッサは登録しましたがこれを使う側です。
async function Init(){
    audioctx = new AudioContext();
    buffer = await LoadSample(audioctx, "./loop.wav");
    await audioctx.audioWorklet.addModule("overdrive-proc.js");
    overdrive = new AudioWorkletNode(audioctx,"OverDrive");
    overdrive.drive = overdrive.parameters.get("drive");
    vol = new GainNode(audioctx,{gain:0.5});
    analyser = new AnalyserNode(audioctx);
    src = new AudioBufferSourceNode(audioctx, {buffer:buffer, loop:true});

    src.connect(overdrive).connect(vol).connect(analyser).connect(audioctx.destination);

    //....
}
初期化の部分だけですが、async/await で書いています。
  • AudioContext の作成
  • 音源のロード
の次に
  • addModule
で、さっき作った "overdrive-proc.js" を追加していますが、この API は Promise を返しますので await で終了を待っています。これでメインプログラム側で OverDrive が使えるようになります。 そして
  • overdrive = new AudioWorkletNode(audioctx,"OverDrive");
が実際に AudioWorklet のノードを作成している部分です。これにより普通のノードとして使用できる AudioWorklet ノードが作成され、自動的にユーザーからは見えないオーディオ処理スレッド側では対応する overdrive-proc.js の OverDrive 処理のインスタンスが生成されます。

次の
  • overdrive.drive = overdrive.parameters.get("drive");
は、OverDrive ノードが持っているパラメータにアクセスしやすくするために overdrive ノードのプロパティにくっつけている処理です。これで、GainNode.gain と同様に OverDrive.drive という形式でアクセスできます。これについてはまた後述。

最後の行で、ノードの接続は

src(BufferSource音源) => overdrive(AudioWorklet) => vol(メインボリューム用Gain) => analyser(波形表示用) => destination

と普通のノードと同様に connect() で接続しています。後は音源用のデータを fetch で読んだり、ツマミをいじった時に オーバードライブのパラメータを変更したり、出てきた音の波形を Canvas で描いたりしていますけど、今までの WebAudio のプログラムと違う所は無いと思います。


気を付けないといけない事

今はまだ仕様と実装が完全に一致していない部分があったりするので幾つか注意点があります。
  • プロセッサの process() の戻り値は、ノードのライフタイムを制御します。

    仕様上は false を返した場合、ノードに信号が入力されている間だけ動作を続け、接続が無くなると自動的にノードが破棄される、という事になっています。これはその内、実装に反映されるのではないかと思いますが、今は false を返すとノードが止まってしまうので必ず true を返して動かしっぱなしにする必要があります。

  • 作成した AudioWorkletNode のパラメータにアクセスするには
    overdrive.parameters.get("drive")
    のように parameters.get() を経由する必要があります。例えば OscillatorNode.frequency や DelayNode.detayTime のようには行かない所が、まあ事情はあると思いますがちょっと残念ではありますね。
    このサンプルではせめて、という事でメインプログラム側でノードを作った後、
    overdrive.drive = overdrive.parameters.get("drive")
    とやって overdrive.drive でアクセスできるようにしています。これがプロセッサ側のコードでできれば良いんですけどね。

    アクセスした先のオブジェクトは普通の AudioParam ですので、.value に値を代入したり、setValueAtTime() などのオートメーション関数を使ったり、他のノードの出力を接続して変調を掛けたりという事は通常通りできます。

  • この例では使っていないのですが、AudioWorklet のノードのパラメータに作成と同時に初期値を渡したい時、少し書式が違います。

    例えば GainNode を作成する時、
    new GainNode(audioctx, {gain:0.5})
    とやって作成時に gain を 0.5 に設定するなんて事ができますが、AudioWorklet ノードの場合、例えば OverDrive の drive を 0.5 に設定するなら、
    new AudioWorkletNode(audioctx,"OverDrive",{drive:0.5});
    ではなくて、
    new AudioWorkletNode(audioctx,"OverDrive",{parameterData:{drive:0.5}});
    のようになります。これが一応仕様上定められた書き方ですが、仕様書内の EXAMPLE コードでも書き方が違っている部分があったりするので混乱しやすい所です。

  • AudioWorklet だけの問題ではないですが、去年導入された Privacy Policy の影響で WebAudio で音を出すには最初にユーザーの操作が無くてはいけません。AudioWorklet は動き始める前に AudioContext を使って必要なモジュールの登録処理等が必要ですので、処理の順序を考えるのが少し面倒になった感があります。まあちゃんと考えれば良いのですけど。

  • その内修正は入ると思いますが、基本的に Web Audio API の仕様書内にある EXAMPLE はまだちゃんと整備されていないのでそのままでは動かないものが多いです。

    今のところ実際に動作するコードのサンプルとして一番信頼できるのは、Google の WebAudio 開発者である @hoch さんが ChromeLabs で公開している Audio Worklet のサンプルページ です。

という事で、まだ少し気を付けつつ、ではありますけど AudioWorklet もそろそろガシガシ使えるフェーズに入りつつあるんじゃないですかね。

Posted by g200kg : 3:40 AM : PermaLink

2019/01/09 (2019年01月 のアーカイブ)

Frizing でひっかかりやすい所

Twitter Facebook

IoT とか Make 界隈で良く使われている Frizing という回路図エディタがあります。

基本的に初心者向けであまり大規模なものには向かないのですが、「ブレッドボード図」が簡単に描けるというのが特色で、ちょっとした工作の解説用の図を作る時なんかに重宝されています。

多分ブレッドボード図を描くために使っている人が大多数なのではないかと思いますが、機能としてはブレッドボード図、普通の回路図、基板のアートワーク、ガーバーの出力、おまけにソフトウェアのコードまでを一括管理しようというなかなか遠大な目標を持つツールです。

まあブレッドボード図が必要ないのなら Eagle なり KiCad なりを使った方が良いとは思いますが。

現在バージョンは 0.9.3b のベータ版で少しバギーな部分を残したまま既に2年半くらいアップデートが無いので今後の開発がどうなるかは少し気になる所ではあります。

frizing.org

それで、この Frizing で在り合わせの部品でブレッドボード図を描くだけなら、なかなか快適ではあるのですが、それ以上の事、独自の部品を登録したり、回路図を描いたりという事をやろうとすると色々問題点が噴出して一気にハードルが上がるんですよね。去年の年末あたりから割合ちゃんと触る機会があって、かなり問題を回避するコツを掴んできましたのでポイントだけ紹介します。


そもそも描きたい図はどれなのか


ブレッドボード図と回路図を描くのであれば、回路図=>ブレッドボード図の順序で書いた方が効率が良いです。

ブレッドボード図と回路図は一応並行で進める事もできるのですが、片方で描いた配線が反対側では仮接続のような状態になりますので、それが邪魔で回路図が書きにくい事があります。


作った部品のピンが勝手にショートしてしまう


新しい部品を登録する時、似た部品を基にして画像だけを差し替えて作る、という手法が良く紹介されているのですが、例えば GND や電源が複数本あって内部で繋がっているというような部品を基にしてしまうとその内部接続をそのまま引き継いでしまい、部品エディタの操作で修復する方法が無さそうです(見つけられないだけ?)。

例えば PIC16F883 の場合 PIN 8 と 19 がどちらも GND で内部で繋がっています。これの画像だけを差し替えて新しい部品を作っても PIN 8 に配線しようとすると 19 にもつながってしまいます。

PIN 8 につないだのに PIN 19 にも繋がっている。

PIN 数多めの部品は複数電源やGNDが複数出ている事が多いのでベースにするのは小さめのIC等で後から部品エディタで PIN 数を増やした方が良さそうです。


フォントの大きさがおかしくなる


これは良く知られた問題ですが、ブレッドボード図でも回路図でも、Inkscape で 0.1 インチグリッドに載せて SVG で描けば差し替え用の画像が描けますが、フォントの大きさが狂ってしまいます。

Inkscape が出力する SVG ファイル中のフォントサイズ指定が 'px' になっているのが Frizing で解釈できないのが原因のようなので、SVG をエディタで開いて font-size の 'px' 指定を消せばなおります。それも面倒ならすべてのオブジェクトをパスに変換してしまえば避けられます。どちらが良いかは一長一短。


回路図を描く時、ジャンクションが変なところについてしまう


特にブレッドボード図を先に書いて回路図エディタで仮接続を頼りに線を引いていると下の図のように部品の足の所にジャンクション(大き目の黒丸)が付いてしまいがちです。

これはトポロジー的には下の図のようになっていて部品間ワイヤーが2本以上集まっている所にジャンクションが付くという仕様のせいです。

下の図のように枝分かれさせたい場所にあらかじめ折れ曲がり点を作っておき、部品の足からその点に向かってワイヤーを引くという手順を取ると回避できます。なおこの時、ブレッドボード側の仮配線を一度消さないと、仮配線が邪魔で部品の足から出るワイヤーを選択できない事があります。

部品を登録する時にピン割り付けが面倒くさい

既存部品の画像を差し替えて新しい部品を作る時、各ピンがSVG画像のどこに対応するかを毎回指定するのが面倒くさいのですが、これは SVG 内のピンに相当するオブジェクトのプロパティで ID に "connector0pin"、"connector1pin" などの名前を付けておくと自動的に割り付けられます。部品エディタで各端子を表す ID 名 "connectorN" に "pin" を付けた名前です。わかりにくいですね。

なお、この機能はある程度認知されているようですが、うまく動作しないという報告もそれなりにあります。実際にやってみると、今のところおかしな挙動には遭遇していないのですが。

基板パターン図用の画像が差し替えできない

基板パターン用に使う SVG 画像は表面、裏面に配置するパッドをそれぞれまとめて "copper0"、"copper1" という名前を付けたレイヤーに入れておく必要があります。

更にこれは "プレーンSVG" 形式で保存した場合の話で、デフォルトの "Inkscape SVG" 形式で保存する場合は、各パッドをレイヤーではなく"グループ" でまとめてグループの ID に "copper0"、"copper1" という名前を付けると大丈夫なようです。

以上 Frizing で遭遇した問題の workaround 的ノウハウでした。

そもそもこのツール、ブレッドボード図を描くというニッチな所に特化している感じではありますが、コードエディタが統合されていたり、曲がりなりにもオートルーターが搭載されていたり、ガーバーが出力できたり、目指している所はなかなか凄いです。

今後に期待したいと思います。

Posted by g200kg : 2:35 AM : PermaLink



...更に以前の記事...


g200kg