2018年06月20日

Open.Theremin@波形を観測しながらデモ演奏を行う



将来的には各種出力波形のプロジェクションと共に演奏を行ってみたい。
posted by Yasuski at 17:07| open.Theremin

2018年06月19日

Open.Theremin@波形の解析

AnalogDiscovery2でキャプチャしたOpen.ThereminOnTeensy3.6の出力波形を解析する。

まずは基本波形から。

vlcsnap-2018-06-19-18h43m05s445.png

基本波は普通のサイン波で、これの整数倍の波形をWavetableに登録している。例外的なものとしてサイン波のハーフウエーブ・三角波・ノコギリ波・ランダム等を登録しているが、波形の鋭いエッジがノイズ源となってしまうこともあるために、あまり選択する場面がない。

vlcsnap-2018-06-19-18h43m31s710.png

このシステムは基本的に加算合成方式で波形の編集を行うが、波形が登録されたアドレスにはプリセット14種類、編集が可能な記録バンクが16ch分用意されている。 この波形はプリセットされたもので、任意の波形を選択してレベルを調整したものを加算合成している。

vlcsnap-2018-06-19-18h43m48s632.png

参照波の位相差を利用して、このような波形を合成することが出来る。

vlcsnap-2018-06-19-18h44m16s192.png

プリセット波形のレベル調整は、処理を軽くするためにデータの右シフトという荒っぽい方法で行っているが、何れも開発の極初期段階に直感的にセレクトしたものなので、使用頻度等のフィードバックが得られ次第リファインしたいところ。

vlcsnap-2018-06-19-18h44m23s807.png

このような非常に高い音程の波形を使用する場合、妙な変調が耳についてしまうことがある。

vlcsnap-2018-06-19-18h44m30s567.png

音量の再検討等の運用面のバランスを考慮した改修が必要。

vlcsnap-2018-06-19-18h45m15s972.png

この波形のように、三角波を合成した場合、エッジの効いた「ヒゲ」の部分がノイズに聞こえてしまうことがある。

vlcsnap-2018-06-19-18h23m23s353.png

こちらは、Overloadモードの波形。 これはクリップする前の状態で、Transitionモードとあまり差異はない。

vlcsnap-2018-06-19-18h23m32s088.png

VolumeAntで増幅度を変化させると、このように波形がクリップしてくる。 この段階ではハーモニックエキサイター風のハーフウエーブ・ディストーションに近い波形が出現する。

vlcsnap-2018-06-19-18h23m38s648.png

更に増幅度を上げていくと、波形下側が完全にクリップする。

vlcsnap-2018-06-19-18h24m05s878.png

transitionのオフセット値を調整して、波形の粗密度を設定する。

vlcsnap-2018-06-19-18h24m29s838.png

波形の上下が完全に潰れた形。 波形のエッジをデジタル/アナログLPFで削ぐことで、ノイズ感を減少させている。

vlcsnap-2018-06-19-18h24m45s648.png

上側がクリップした波形。 このような歪の大きな波形が左手の加減によってシームレスに生成されていく。

vlcsnap-2018-06-19-18h25m25s440.png

増幅度を下げると、クリアな音質を得ることが出来る。

vlcsnap-2018-06-19-18h25m54s417.png

上下がクリップした波形とサイン波の下弦の部分を加算合成するとこういった波形になる。

vlcsnap-2018-06-19-18h26m08s662.png

音程が高くなるにつれて、トータルの音声レベルが低下していく。 また、出力コンデンサーに蓄積したDC成分が増大するにつれて、ダイナミックレンジが狭まっていく。 コンデンサーの放電を行うためには発音を中断しなければならない。

vlcsnap-2018-06-19-18h26m19s827.png

各オシレーター出力のピークにオフセットを掛けることで、更に複雑な動的制御が可能となる。

vlcsnap-2018-06-19-18h26m34s997.png

これは、ディストーション・サウンドの典型的な波形。

vlcsnap-2018-06-19-18h26m58s107.png

そこに、いきなりサイン波のスリットが入ってくる。

ここからは、Transitionモードの波形を紹介していく。

vlcsnap-2018-06-19-18h36m48s866.png

VolumeAntと相関させる形でオシレーターのピークポイントをズラすことで、倍音構成に動的な変化を与えている。 コードを演奏する場合は、これにピッチのグラデュエーション効果が加わる。

vlcsnap-2018-06-19-18h36m57s306.png

極限値なので判り難いが、和音の構成を操作している。

vlcsnap-2018-06-19-18h37m12s093.png

発振周波数が高くなった場合の反応。

vlcsnap-2018-06-19-18h37m20s623.png

和音の構成が、、、

vlcsnap-2018-06-19-18h38m10s468.png

段階的に変化していく。

vlcsnap-2018-06-19-18h38m40s293.png

TransitionControlはオシレーターに配分された和音の構成が順送りされる仕組みだが、オシレーター側の設定次第で中間地帯に高い音を仕込むことが可能。

vlcsnap-2018-06-19-18h39m07s340.png

Transitionの最終ポイントで波形は固定される。

vlcsnap-2018-06-19-18h39m25s627.png

TransitionModeを単音で運用した場合、倍音の比率や位相の異なる波の干渉に因って出音の音色が変化する。

vlcsnap-2018-06-19-18h39m42s585.png

音色は段階的に変化したあと、、、

vlcsnap-2018-06-19-18h40m09s085.png

Transitionの最終ポイントに配置されたオシレーターの発音に固定される。

vlcsnap-2018-06-19-18h41m21s318.png

MCUの処理が追いつかなくなると、このように波形の不連続面が発生し、それはノイズとして認識されてしまう。

vlcsnap-2018-06-19-18h42m19s197.png

極端に低いピッチを設定した場合、波形に段差が発生する。

vlcsnap-2018-06-19-18h42m27s890.png

これは、TransitionModeに於いて、PitchMemoryModeで編集した和音構成を展開した時に生じる典型的な例。

vlcsnap-2018-06-19-18h42m35s995.png

このザラつきも、ノイズとして認識される。

vlcsnap-2018-06-19-18h42m43s610.png

処理に割かれるリソースが過大な現状ではadatの送信はかなり厳しいと言わざるをえないが、とりあえずはch数を減らした状態で送信実験を行ってみる予定。
posted by Yasuski at 20:12| open.Theremin

Open.Theremin@OverloadModeを実装する

Overloadモードがようやく稼動状態になった。



音声が出力されなかった原因は複数あって、、、

1)DAC周りの回路の配線ミスで、BIAS電圧が供給されていなかった。
2)そのうえ、GNDの接続も適当で、断線している箇所があった。
3)OpAmpのSOIC/.DIP変換基板が不良品で、接続されるべき端子が浮いていた。

と、1/3は自分の所為ではなかったのだが、全体的にアホっぽい原因なのが情けない。

で、ノイズをキャンセルするために追加した機能の所為でタスクがオーバーフローした結果、ポリフォニック動作時に出力がノイズまみれになるという本末転倒な事態が発生していた。

この問題を回避するために、WCKの立ち上がり毎に分けていたDACへのデータ送信を旧来のWavetableを参照してミックスを行った後に一括処理するスタイルに戻したところ、ノイズは消滅し、余録として起動時のピポパ音も復活させることが出来た。

Overloadモードは波形をクリップさせる手前、事前にレベルのプリセットが必要で、作業の1/4は最適値を探りだすために費やされることになった。

posted by Yasuski at 12:18| open.Theremin

2018年06月17日

Open.Theremin@TransitionModeを実装する



VolumeControl系のデータの扱いに関しての備忘録。

音量のコントロールには、Wavetableに記録したサインカーヴを使用する。 データのステップは12bitを基本とし、全体の音量を12bit/トランジションの音量を11bitの分解能で制御する。

周波数ディテクターは、オシレーター出力のデューティーサイクルを16bit幅の周波数カウンターでカウントしているが、Volume側ではその14bit分を扱う。 採取されたValueは最終的に3bit右シフトを行って11bit/FSに加工する。 

WS001303.JPG

この11bitステップで音声コントロール用に準備した1/4波長プラス側サイン波のWavetableを参照した後、結果にLPFを挿入してデータのフラつきを防ぐ。 これが、基本的な音声出力カーヴとなる。

WS001305.JPG

現在設定しているVolume側のウエイト値、EMA_b は暫定で0.33を設定しているが、今後は実際に運用を行って最適化を進めていく。

WS001296.JPG

一方、Transitionコントロールを行うための波形は、中央にピークを持たせたプラス側半波長のサイン波で、ステップ数/分解能が共に11bitのWavetableを参照する。 

WS001307.JPG

このデータを基本として、対象となる各オシレーター分の読み出しアドレスにオフセットを掛けたものを準備する。 オフセットの初期値はオシレーター毎に400ステップを設定しているが、これはロータリーエンコーダーよって変更が可能とした。 トータル音声出力のレベル設定は、先に出力した12bit幅の音声出力カーヴとローカルの音声出力パターンを掛けあわせたものを圧縮した12bit/FSで行う。 この出力にも最終段階でLPFによる丸めを個別に行っている。

WS001310.JPG

こちらは、トランジションモードの出力波形とVolume側のオシレーターの相関を記録した映像。



今後、レベル設定の最適化を行う必要があるが、実用上問題がない範囲で可変域を制限した方がよさそうだ。

その後、帰還型のLPFの時定数を変更して、よりデータの変動を抑える方向にチューニングを行った。



モノラル出力の場合には有効性が証明されたが、タスクが重いポリフォニック・モードでは時間切れでノイズが発現してしまうようだ。

以下のサイトをLPF製作の参考にした。

https://www.norwegiancreations.com/2015/10/tutorial-potentiometers-with-arduino-and-filtering/
posted by Yasuski at 23:33| open.Theremin

2018年06月16日

Open.Theremin@新型機がようやく稼動状態になった

開発がスタックしていたオリジナル基板を実装した新型テルミンについて。 

各種時定数の最適化に時間が掛かったが、ロータリーエンコーダーの極性の反転、Volumeコントロールを行う部分のデータ・ハンドリングの修正、誤記のチェック、オーバークロック化に対するカウンタ分周値の最適化等の対症療法を行った結果、曲がりなりにも音が出るようになった。



現時点で、発音されるはずの起動音が聞こえない、メモリー登録時のアラートも同様に聞こえない、断線気味のPitchアンテナ端子による回路の不安定化、調整がクリティカル過ぎるVolumeオシレーター、サブDACの動作不良、、、等々、実際の運用に直結する問題が解決できておらず、現場への投入は無理と判断している。

時間を掛けて調整を行えばなんとか楽器っぽく動作するレベルに落ち着いているが、特にVolumeの扱いが難しい。

追記:

波形を確認したが、ノイズっぽいのはなんとかならんのか、、、。
posted by Yasuski at 16:13| open.Theremin

Open.TherminOnTeensy@pin configurations

#define log10f_fast(x) (log2f_approx(x)*0.3010299956639812f)

#define BAUD 115200 // uncomment if serial is implimented

#define LATCH01 27
#define LATCH01_ON (CORE_PIN27_PORTSET = (1<<15))
#define LATCH01_OFF (CORE_PIN27_PORTCLEAR = (1<<15))

#define LATCH02 20
#define LATCH02_ON (CORE_PIN20_PORTSET = (1<<5))
#define LATCH02_OFF (CORE_PIN20_PORTCLEAR = (1<<5))

#define LATCH03 21
#define LATCH03_ON (CORE_PIN21_PORTSET = (1<<6))
#define LATCH03_OFF (CORE_PIN21_PORTCLEAR = (1<<6))

#define LATCH04 18
#define LATCH04_ON (CORE_PIN18_PORTSET = (1<<3))
#define LATCH04_OFF (CORE_PIN18_PORTCLEAR = (1<<3))

#define LATCH05 16
#define LATCH05_ON (CORE_PIN16_PORTSET = (1<<0))
#define LATCH05_OFF (CORE_PIN16_PORTCLEAR = (1<<0))

#define LATCH06 15
#define LATCH06_ON (CORE_PIN15_PORTSET = (1<<0))
#define LATCH06_OFF (CORE_PIN15_PORTCLEAR = (1<<0))

#define LATCH07 17
#define LATCH07_ON (CORE_PIN17_PORTSET = (1<<1))
#define LATCH07_OFF (CORE_PIN17_PORTCLEAR = (1<<1))

#define CKW 25
#define CKW_ON (CORE_PIN25_PORTSET = (1<<5))
#define CKW_OFF (CORE_PIN25_PORTCLEAR = (1<<5))

#define DATA01 26
#define DATA01_ON (CORE_PIN26_PORTSET = (1<<14))
#define DATA01_OFF (CORE_PIN26_PORTCLEAR = (1<<14))

#define LED 7 // LED on D7
#define LED_ON (CORE_PIN7_PORTSET = (1<<2))
#define LED_OFF (CORE_PIN7_PORTCLEAR = (1<<2))

#define LEDgrn 8 // LED on D8
#define LEDgrn_ON (CORE_PIN8_PORTSET = (1<<3))
#define LEDgrn_OFF (CORE_PIN8_PORTCLEAR = (1<<3))

#define LEDred 9 // LED on D9
#define LEDred_ON (CORE_PIN9_PORTSET = (1<<3))
#define LEDred_OFF (CORE_PIN9_PORTCLEAR = (1<<3))

#define LED2grn 38 // LED on D38
#define LED2grn_ON (CORE_PIN38_PORTSET = (1<<11))
#define LED2grn_OFF (CORE_PIN38_PORTCLEAR = (1<<11))

#define LED2red 37 // LED on D37
#define LED2red_ON (CORE_PIN37_PORTSET = (1<<10))
#define LED2red_OFF (CORE_PIN37_PORTCLEAR = (1<<10))

#define LED2 39 // LED on D39
#define LED2_ON (CORE_PIN39_PORTSET = (1<<17))
#define LED2_OFF (CORE_PIN39_PORTCLEAR = (1<<17))

#define buttonPin01 11 // Button Pin on D11
#define button_State1 (CORE_PIN11_PINREG & (1<<6))

#define buttonPin02 36 // Button Pin on D36
#define button_State2 (CORE_PIN36_PINREG & (1<<9))

#define buttonPin03 3 // Button Pin on D3
#define button_State3 (CORE_PIN3_PINREG & (1<<12))

#define buttonPin04 4 // Button Pin on D4
#define button_State4 (CORE_PIN4_PINREG & (1<<13))

#define buttonPin05 5 // Button Pin on D5
#define button_State5 (CORE_PIN5_PINREG & (1<<7))

#define buttonPin06 6 // Button Pin on D6
#define button_State6 (CORE_PIN6_PINREG & (1<<4))

#define LEDvol 28 // LED on D28
#define LEDvol_ON (CORE_PIN28_PORTSET = (1<<16))
#define LEDvol_OFF (CORE_PIN28_PORTCLEAR = (1<<16))

#define LEDvol2 13
#define LEDvol2_ON (CORE_PIN13_PORTSET = (1<<5))
#define LEDvol2_OFF (CORE_PIN13_PORTCLEAR = (1<<5))

#define SAMPCLK_STATE (CORE_PIN19_PINREG & (1<<2)) //sampling clock in
#define SAMPCLK_MASK (CORE_PIN19_CONFIG = PORT_PCR_IRQC(0)) //masking sampling clock in
#define SAMPCLK_ACTV (CORE_PIN19_CONFIG = PORT_PCR_IRQC(9)) //activate sampling clock in

#define SCK01 30
#define SCK01_ON (CORE_PIN30_PORTSET = (1<<19))
#define SCK01_OFF (CORE_PIN30_PORTCLEAR = (1<<19))

#define SD01 32
#define SD01_ON (CORE_PIN32_PORTSET = (1<<11))
#define SD01_OFF (CORE_PIN32_PORTCLEAR = (1<<11))

#define CS01 29
#define CS01_ON (CORE_PIN29_PORTSET = (1<<18))
#define CS01_OFF (CORE_PIN29_PORTCLEAR = (1<<18))

#define LDAC01 33
#define LDAC01_ON (CORE_PIN33_PORTSET = (1<<24))
#define LDAC01_OFF (CORE_PIN33_PORTCLEAR = (1<<24))

#define DAC16 40
#define DAC16_ON (CORE_PIN40_PORTSET = (1<<28))
#define DAC16_OFF (CORE_PIN40_PORTCLEAR = (1<<28))

#define DAC16_2 31
#define DAC16_2_ON (CORE_PIN31_PORTSET = (1<<10))
#define DAC16_2_OFF (CORE_PIN31_PORTCLEAR = (1<<10))
posted by Yasuski at 07:45| open.Theremin

2018年06月15日

Open.TherminOnTeensy@音声出力波形を観測する

テルミンのWCK強制同期回路の試作は失敗した模様。

インターラプトが動作するポイントにマーカーを追加して、動作不良の原因を探ることになるが、そもそもLoopの設定がアカン感じがする。

何故か単音しか出力されず、出力に妙な変調が掛かったり、インジケータのLEDが動作不能だったりするが、とりあえず左手でVolumeをコントロールできるまでにはなった。

Screen Shot 2018-06-14 at 19.51.05.png

とにかくチューニングがクリティカル過ぎてお話にならないが、アンテナ長分のオフセット設定は計算通りにはいかず、収納する筐体に因ってもズレが発生するのが大問題。

Screen Shot 2018-06-14 at 20.09.52.png

やはり、24bitの計算は無理っぽいので、16bit対応でひとまずまとめることにするかオーバークロックにトライするかで迷うところだが、まずはオーバークロックで無理を確認する。

Screen Shot 2018-06-14 at 20.11.10.png

その後、240MHzにオーバークロックして発音を確認。 相変わらず何かが変だけど、とりあえず音の制御は出来ている。



WCKを32kHzで廻すのもアリ。そもそもがハイファイを目指す意味が無さそうだし。
posted by Yasuski at 20:51| open.Theremin

2018年06月12日

Open.Theremin@attatchInterruptの項目を追加する

DACの読み出しを行うタイミングの制御に関して思いついたこと。

プログラムの基本的な構造は、アンテナのセンシングやロータリーエンコーダーの読み出しを行っているメインループと、そこにWCKが立ち上がるタイミングでDACへのデータ送信を行った後に波形データを読みだす一連のタスクが割り込む仕組み。

現状ではadat等のWCKの表裏でデータを扱うタスクの実現は難しく、当初はその対策としてメインループの先頭に音声データをDACに出力する仕掛けを追加していたが、データラインの波形を観測した結果、インターラプトからの復帰後に行われる筈のデータ転送のタイミングが安定していないことが判った。

当初、ズレの原因は波形読み出しのタスクの重さから発生する遅延の影響と仮定していたのだが、インターラプトから復帰するタイミングによってズレのバラつきが生じる可能性に思い至った。

この変動するズレの問題を解決するには、WCKの立ち下がりで発動する別のインターラプトを追加して強制的にDACにアクセスするタイミングを固定する方法が有効と思える。(波形を読み出す際のタスクの重さは無視して)とりあえずはこの機能を実装してみたところ、コンパイルだけは通せたのが現時点での状況。

前述したように、この機能はタスクの重さに因って破綻する可能性があり、まずは実験による観測が必要だ。

adatフォーマットの取り扱いに関しては、外部にFPGAによるデータラッチを追加し、WCKの裏面でバースト信号を受信/表でフォーマットに準拠したタイミングで送信を行わせている。

基板にadat系のチップを実装してはいるが、データのハンドリングを行うFPGAの有効性は実証されていない。データ送りのタスクに要する時間(1サイクルでクロックを40余りも消費する)の制限は大きく、実質的には4〜5chの送信が限界と思われる。

既に波形の観測によってattatchInterruptによる遅延が可視化されているが、タイミング的にはWCKに同期していれば問題はない。ただし、この遅れによってシステム全体の処理に要する時間的余裕が削られてしまうのはマズいので、直接的にピンの状態を参照する仕掛けを探る必要がある。

ダイレクトアクセスのアドレスとして既にSAMPCLK_STATEを仕込んであるので、ひとまずattatchInterruptにこれをアサインしてみよう。
posted by Yasuski at 10:42| open.Theremin