SPRESENSE(SORA-Q)が月面撮影成功!!歴史的偉業を達成! [SPRESENSE]
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SLIMそのものは、50m地点でスラスタ一つが脱落。異常モードに入り、本来、高度1.8mで放出する予定だった LEV-1、LEV-2 を高度 5m で放出。それでも両方とも問題なく動作したのはすごいですね。
スラスタを失ったSLIMは横滑りしながら月面に着陸し、横方向の勢いがあまってしまい写真のように逆立ちをしてしまったようです。
太陽光パネルが西方向を向いており、月面の太陽は東側。SLIMは電源を確保できずに現在停止しています。月の昼間はだいたい17日くらい。1月末には天頂に来て、2月上旬には西に来るので、そのときに復活できるかもしれません。
一方、LEV-1もすごいですね。飛び跳ねて移動するというのもユニークですが、あの小さなロボットで地球との通信も実現しています。通信はテレメトリにUHF帯、データ送信用にS帯を使っているようです。
残念ながら、LEV-1 の画像取得はうまくいっていないようですが、それでもLEV-1、LEV-2で複数のロボットが連携して月面で動作させたのは世界初!ロボット大国の面目躍如ですね。
最後に特筆すべきは、SLIMのピンポイント着陸でしょう。画像照合しながら完全自立制御。スラスタが落ちなければ3−5m精度だったようです。
今までは 数kmから十数km レベルです。というのも、月面着陸直前の探査機の速度は時速200km程度。それを急減速させて着陸させるので、それくらいの誤差が出るのは当然だと思われました。それをピッタリ狙った駐車場つけることができたのが今回の偉業です。
最近は失敗続きの日本の宇宙開発ですが、ひさしぶりに大きなインパクトの偉業を成し遂げました。今後、日本の月面開発が活発化しそうですね。
「SPRESENSEでデジタルエフェクターを作ろう!」ーモジュレーション系エフェクターの実現ー [SPRESENSE]
空間系エフェクターは歪み系と空間系エフェクターの中間に接続されるエフェクータです。今回はトレモロ、ビブラート、コーラスを実装したいと思います。モジュレーション系エフェクターは正弦波で変調を加えているのが大きな特徴です。
信号処理の理論は次の本を参考にしました。音声音響信号処理の基本が身につきますのでおすすめです。
音声音響信号処理の基礎と実践 - フィルタ,ノイズ除去,音響エフェクトの原理 - (次世代信号情報処理シリーズ 2)
- 出版社/メーカー: コロナ社
- 発売日: 2021/04/08
- メディア: 単行本
■ トレモロの信号処理
トレモロはモジュレーション系エフェクターの中で最も簡単な処理です。正弦波によって音の強弱をかえていきます。うねりのような効果が得られますが、エフェクターとしての効果は控えめな印象です。
dは通常0.3~0.6の値、周波数は数Hzに設定することが多いようです。曲にあわせて調整が必要になります。
■ トレモロの実装
正弦波の処理は時間がかかるのでCoretex M4F の浮動小数点アクセラレータを使うため、ARMが提供している数値ライブラリを用いました。実装では正弦波の周波数を4Hzで、dは0.4を設定しました。
(コードの中では、SAMPLE_SIZE と sample_size が混在していますが同じものです。そのうち整理します)
#define ARM_MATH_CM4 #define __FPU_PRESENT 1U #include <cmsis/arm_math.h> void signal_process(int16_t* mono_input, int16_t* stereo_output, const uint32_t sample_size) { /* clean up the output buffer */ memset(stereo_output, 0, sizeof(int16_t)*sample_size*2); /* memory pool for 1.5sec (= 720*100*(1/48000)) */ static const int lines = 100; static int16_t src_buf[sample_size*lines]; /* 2*720*100=144kBytes */ /* shift the buffer data in src_buf and add the latest data to top of the bufer */ memcpy(&src_buf[0], &src_buf[sample_size], SAMPLE_SIZE*sizeof(int16_t)*(lines-1)); memcpy(&src_buf[(lines-1)*sample_size], &mono_input[0], SAMPLE_SIZE*sizeof(int16_t)); static int theta = 0; const int mod_freq = 4; /* Hz */ const float omega = 2.*PI*mod_freq/AS_SAMPLINGRATE_48000; const int sin_total_samples = AS_SAMPLINGRATE_48000/mod_freq; const float d = 0.4; /* amplitude of moduration */ const int src_buf_end_point = lines*SAMPLE_SIZE-1; for (int n = SAMPLE_SIZE-1; n >= 0; --n) { float modulation = 1.-d*arm_sin_f32(omega*theta); mono_input[(SAMPLE_SIZE-1)-n] = (int16_t)(modulation*src_buf[src_buf_end_point-n]); if (++theta >= sin_total_samples) theta = 0; } /* copy the signal to output buffer */ for (int n = SAMPLE_SIZE-1; n >= 0; --n) { stereo_output[n*2] = stereo_output[n*2+1] = mono_input[n]; } return; }
製品の中には正弦波の他に三角波、矩形波を加える機能をもっているものもあるようです。プログラミングできますので、そのような機能をもたせてみるのも楽しいかもしれません。
■ ビブラートの信号処理
ビブラートはディレイと同じように音を遅延させる処理になります。ただし、その遅延量は、正弦波で与えます。トレモロと異なりエフェクトの効果は抜群でギターの音がシンセのような音に変わります。ただ、演奏するには慣れが必要そうです。
dは遅延量を設定するもので、0~2d の間で変化します。ここはサンプルで指定しますが、時間に換算するには、サンプリング周波数の逆数をサンプル数でかけた数になります。正弦波の周波数は小数点以下から数Hzに設定することが多いようです。
■ ビブラートの実装
ビブラートの変数 d には 96サンプル、2ミリ秒の遅延を与えます。音の遅延は0~4ミリ秒で変化します。また、周波数はトレモロと同じく4Hzに設定しました。
#define ARM_MATH_CM4 #define __FPU_PRESENT 1U #include <cmsis/arm_math.h> void signal_process(int16_t* mono_input, int16_t* stereo_output, const uint32_t sample_size) { /* clean up the output buffer */ memset(stereo_output, 0, sizeof(int16_t)*sample_size*2); /* memory pool for 1.5sec (= 720*100*(1/48000)) */ static const int lines = 100; static int16_t src_buf[SAMPLE_SIZE*lines]; /* 2*720*100=144kBytes */ /* shift the buffer data in src_buf and add the latest data to top of the bufer */ memcpy(&src_buf[0], &src_buf[SAMPLE_SIZE], SAMPLE_SIZE*sizeof(int16_t)*(lines-1)); memcpy(&src_buf[(lines-1)*SAMPLE_SIZE], &mono_input[0], SAMPLE_SIZE*sizeof(int16_t)); const float mod_freq = 4; /* Hz */ const float omega = 2.*PI*mod_freq/AS_SAMPLINGRATE_48000; const int sin_total_samples = AS_SAMPLINGRATE_48000/mod_freq; const int d = 96; /* delay samples = delay time / Sampling Rate */ static int theta = 0; const int src_buf_end_point = lines*SAMPLE_SIZE-1; for (int n = SAMPLE_SIZE-1; n >= 0; --n) { float modulation = 1. + arm_sin_f32(omega*theta); int offset = (int)(modulation*d); mono_input[(SAMPLE_SIZE-1)-n] = src_buf[src_buf_end_point-n-offset]; if (++theta == sin_total_samples) theta = 0; } /* copy the signal to output buffer */ for (int n = SAMPLE_SIZE-1; n >= 0; --n) { stereo_output[n*2] = stereo_output[n*2+1] = mono_input[n]; } return; }
■ コーラスの信号処理
コーラスの信号処理は、ビブラートの変形になります。エコーと同じく遅延した音をもとの音に重ね合わせます。コーラスの効果は、ビブラートのような機械的な音ではなく、自然な音が出せるのでいろいろな場面で使えそうです。
コーラスはその名の通り、声の重ね合わせを表現するので、遅延料 d は数十ミリ秒と比較的大きな値を与え、周波数は小数点以下の値を与えることが多いようです。
■ コーラスの実装
コーラスの遅延量 d には480サンプル、100ミリ秒を与えます。また周波数は 0.1Hz に設定しました。かなり自然な音に仕上がっていると思います。
#define ARM_MATH_CM4 #define __FPU_PRESENT 1U #include <cmsis/arm_math.h> void signal_process(int16_t* mono_input, int16_t* stereo_output, const uint32_t sample_size) { /* clean up the output buffer */ memset(stereo_output, 0, sizeof(int16_t)*sample_size*2); /* memory pool for 1.5sec (= 720*100*(1/48000)) */ static const int lines = 100; static int16_t src_buf[SAMPLE_SIZE*lines]; /* 2*720*100=144kBytes */ /* shift the buffer data in src_buf and add the latest data to top of the bufer */ memcpy(&src_buf[0], &src_buf[SAMPLE_SIZE], SAMPLE_SIZE*sizeof(int16_t)*(lines-1)); memcpy(&src_buf[(lines-1)*SAMPLE_SIZE], &mono_input[0], SAMPLE_SIZE*sizeof(int16_t)); const float mod_freq = 0.1; /* Hz */ const float omega = 2.*PI*mod_freq/AS_SAMPLINGRATE_48000; const int sin_total_samples = AS_SAMPLINGRATE_48000/mod_freq; const int d = 720*2; /* delay samples = 720(samples/frame) * delay time (msec) / 15 (msec/frame) */ const int p = 480; /* p must be less than d */ static int theta = 0; const int src_buf_end_point = lines*SAMPLE_SIZE-1; for (int n = SAMPLE_SIZE-1; n >= 0; --n) { int offset = (int)(d + p*arm_sin_f32(omega*theta)); mono_input[(SAMPLE_SIZE-1)-n] = (src_buf[src_buf_end_point-n] + src_buf[src_buf_end_point-n-offset])/2; if (++theta == sin_total_samples) theta = 0; } /* copy the signal to output buffer */ for (int n = SAMPLE_SIZE-1; n >= 0; --n) { stereo_output[n*2] = stereo_output[n*2+1] = mono_input[n]; } return; }
■ 処理の確認
実際の効果を確認してみました。ビブラートの音はかなりエグいですね。SFやホラーの曲で使うと効果がありそうです。プロの演奏を見るとすごく効果的に使っているので関心します。ギターど素人の私には到底ムリなレベル。ペダルでオンオフ、効果の変化を与えられると効果的に使えそうですね。ギター練習しようかな…
今後、簡単に使えるように Arduino のライブラリにまとめていきたいと思います!
モジューレーション系エフェクターをSPRESENSEで試してみました。あとは簡単に使えるように全てのエフェクトをArduinoライブラリにまとめたいと思います。SPRESENSE一台で全てのエフェクトがかけられるといいな pic.twitter.com/iV4Lo6hd3I
— よしのたろう 純国産ボード”スプレッセンス”応援団長(仮) (@Taro_Yoshino) June 11, 2022
「SPRESENSEでデジタルエフェクターを作ろう!」ープリアンプの製作編
「SPRESENSEでデジタルエフェクターを作ろう!」ー低遅延入出力の実現ー
「SPRESENSEでデジタルエフェクターを作ろう!」ーコンプレッサーとディストーションの実現ー
「SPRESENSEでデジタルエフェクターを作ろう!」ー空間系エフェクターの実現ー
「SPRESENSEでデジタルエフェクターを作ろう!」ーモジュレーション系エフェクターの実現ー
音声音響信号処理の基礎と実践 - フィルタ,ノイズ除去,音響エフェクトの原理 - (次世代信号情報処理シリーズ 2)
- 出版社/メーカー: コロナ社
- 発売日: 2021/04/08
- メディア: 単行本
SPRESENSEではじめるローパワーエッジAI (Make: PROJECTS)
- 出版社/メーカー: オライリージャパン
- 発売日: 2022/02/28
- メディア: 単行本(ソフトカバー)
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE 拡張ボード CXD5602PWBEXT1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
「SPRESENSEでデジタルエフェクターを作ろう!」ー空間系エフェクターの実現ー [SPRESENSE]
空間系エフェクターは最後段に接続されるエフェクターです。今回はディレイ(エコー)やリバーブを実装したいと思います。
信号処理の理論は次の本を参考にしました。音声音響信号処理の基本が身につきますのでおすすめです。
音声音響信号処理の基礎と実践 - フィルタ,ノイズ除去,音響エフェクトの原理 - (次世代信号情報処理シリーズ 2)
- 出版社/メーカー: コロナ社
- 発売日: 2021/04/08
- メディア: 単行本
■ ディレイ(エコー)の信号処理
ディレイは、その名の通り遅れた音を重畳する処理です。遅れた音を重ね合わせることで講堂で演奏しているような反響効果を得ることができます。
講堂のようなところで演奏すると音は、複数の壁に反響してやってきます。遅れてやってくる音はディレイ値で指定しますが、跳ね返ってくる中で減衰をしますので減衰量も指定します。減衰量は遅延量が大きいものほど大きくなります。数式では次のように表現できます。
専門的な用語になりますが、ディレイはFIR(Finite Impulse Response)フィルターで構成されています。
■ ディレイ(エコー)の実装
ディレイの実装にはメモリが必要になります。例えばディレイの量を600ミリ秒とすると、その分キャプチャした音を蓄積しておく必要があります。サンプリングレート48000Hzで1フレーム720サンプルなので1フレームあたり15ミリ秒なので40フレーム必要になります。
プログラムは前回紹介したコードの中の singnal_process 関数部分に実装しています。プログラムでは蓄積用メモリに100フレーム(1500ミリ秒分)分準備しておきました。遅延量はフレーム単位(15ミリ秒単位)で行ったほうがきれいに音が出ます。
今回は反響音はディレイ値、300ミリ秒、600ミリ秒の二つを指定します。減衰量は演算量を減らすためにそれぞれ1/2、1/4に設定しています。
void signal_process(int16_t* mono_input, int16_t* stereo_output, const uint32_t sample_size) { /* clean up the output buffer */ memset(stereo_output, 0, sizeof(int16_t)*sample_size*2); /* memory pool for 1.5sec (= 720*100*(1/48000)) */ static const int lines = 100; static int16_t src_buf[SAMPLE_SIZE*lines]; /* 2*720*100=144kBytes */ /* shift the buffer data in src_buf and add the latest data to top of the bufer */ memcpy(&src_buf[0], &src_buf[SAMPLE_SIZE], SAMPLE_SIZE*sizeof(int16_t)*(lines-1)); memcpy(&src_buf[(lines-1)*SAMPLE_SIZE], &mono_input[0], SAMPLE_SIZE*sizeof(int16_t)); /* set constatns for echo effect */ static const uint32_t D1_in_ms = 300; /* milli sec */ static const uint32_t D2_in_ms = 600; /* milli sec */ static const uint32_t offset1 = D1_in_ms * 48000 / 1000; static const uint32_t offset2 = D2_in_ms * 48000 / 1000; const int src_buf_end_point = lines*SAMPLE_SIZE-1; for (int n = SAMPLE_SIZE-1; n >= 0; --n) { /* set h1 = 1/2, h2 = 1/4 to reduce calculation costs */ mono_input[(SAMPLE_SIZE-1)-n] = src_buf[src_buf_end_point-n] + src_buf[src_buf_end_point-n-offset1]/2 + src_buf[src_buf_end_point-n-offset2]/4; } /* copy the signal to output buffer */ for (int n = SAMPLE_SIZE-1; n >= 0; --n) { stereo_output[n*2] = stereo_output[n*2+1] = mono_input[n]; } return; }
処理時間は約240マイクロ秒。ほぼ無視できるレベルです。メモリは消費しますが、計算量はたいしたことないので想定通りです。
■ リバーブの信号処理
リバーブは出力したディレイと同じく音の反響処理になります。ディレイは原音が反響して返ってくる処理に対し、リバーブは処理後の音が返ってくるのが異なります。ちょうど、”やまびこ”のようなイメージをもつといいかも知れません。
リバーブは反響して遅れてやってきた音を原音に加えるのはディレイと同じなのですが、遅れた音は原音ではなく出力後の音を加えます。数式で表すと次のようになります。
専門的な用語になりますが、リバーブはIIR(Infinite Impulse Response)フィルターで構成されています。
■ リバーブの実装
リバーブの実装にはメモリが必要になります。蓄積する音は、ディレイと異なり、キャプチャした原音ではなく、処理後の音になるので注意してください。
プログラムは前回紹介したコードの中の singnal_process 関数部分に実装しています。プログラムでは蓄積用メモリに100フレーム(1500ミリ秒分)分準備しておきました。遅延量はフレーム単位(15ミリ秒単位)で行ったほうがきれいに音が出ます。
遅延量は600ミリ秒。減衰量は演算量を減らすために1/2に設定しました。
void signal_process(int16_t* mono_input, int16_t* stereo_output, const uint32_t sample_size) { /* clean up the output buffer */ memset(stereo_output, 0, sizeof(int16_t)*sample_size*2); /* memory pool for 1.5sec (= 720*100*(1/48000)) */ static const int lines = 100; static int16_t out_buf[SAMPLE_SIZE*lines]; /* 2*720*100=144kBytes */ /* set constatns for echo effect */ static const uint32_t D_in_ms = 600; /* milli sec */ static const uint32_t offset = D_in_ms * 48000 / 1000; const int src_buf_end_point = lines*SAMPLE_SIZE-1; for (int n = SAMPLE_SIZE-1; n >= 0; --n) { /* set alpha = 1/2 to reduce calculation costs */ mono_input[(SAMPLE_SIZE-1)-n] = mono_input[(SAMPLE_SIZE-1)-n] + out_buf[src_buf_end_point-n-offset]/2; } /* shift the buffer data in src_buf and add the latest data to top of the bufer */ memcpy(&out_buf[0], &out_buf[SAMPLE_SIZE], SAMPLE_SIZE*sizeof(int16_t)*(lines-1)); memcpy(&out_buf[(lines-1)*SAMPLE_SIZE], &mono_input[0], SAMPLE_SIZE*sizeof(int16_t)); /* copy the signal to output buffer */ for (int n = SAMPLE_SIZE-1; n >= 0; --n) { stereo_output[n*2] = stereo_output[n*2+1] = mono_input[n]; } return; }
こちらも処理時間は約240マイクロ秒。ほぼ無視できるレベルですね。
■ 処理の確認
実際の効果を確認してみました。なかなかそれらしい音を出せました。ディレイやリバーブのエフェクターの価格を見るとそれぞれ2万円くらいなので、SPRESENSEで実現できればコストパフォーマンスは高そうです。音作りも自分で自由にできるので、ギターの楽しみ方が広がりそうですね。
SPRESENSE で空間系エフェクターを作ってみました。ディレイ(エコー)とリバーブです。久しぶりのギター、むっちゃ指が痛かった pic.twitter.com/tVCsBeor2v
— よしのたろう 純国産ボード”スプレッセンス”応援団長(仮) (@Taro_Yoshino) May 21, 2022
次は、モジュレーション系のエフェクターであるビブラートやトレモロを実装してみたいと思います。
(^^)/~
「SPRESENSEでデジタルエフェクターを作ろう!」ープリアンプの製作編
「SPRESENSEでデジタルエフェクターを作ろう!」ー低遅延入出力の実現ー
「SPRESENSEでデジタルエフェクターを作ろう!」ーコンプレッサーとディストーションの実現ー
「SPRESENSEでデジタルエフェクターを作ろう!」ー空間系エフェクターの実現ー
「SPRESENSEでデジタルエフェクターを作ろう!」ーモジュレーション系エフェクターの実現ー
音声音響信号処理の基礎と実践 - フィルタ,ノイズ除去,音響エフェクトの原理 - (次世代信号情報処理シリーズ 2)
- 出版社/メーカー: コロナ社
- 発売日: 2021/04/08
- メディア: 単行本
SPRESENSEではじめるローパワーエッジAI (Make: PROJECTS)
- 出版社/メーカー: オライリージャパン
- 発売日: 2022/02/28
- メディア: 単行本(ソフトカバー)
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE 拡張ボード CXD5602PWBEXT1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
「SPRESENSEでデジタルエフェクターを作ろう!」ーコンプレッサーとディストーションの実現ー [SPRESENSE]
ギターのエフェクターは次のように接続するのが一般的なようです。今回は、コンプレッサーとディストーションをまず実現したいと思います。ボリュームはアンプの抵抗定数変更やマイク入力のゲイン変更で行うことできます。
信号処理は次の本を参考にしました。音声音響信号処理の基本が身につきますのでおすすめです。
音声音響信号処理の基礎と実践 - フィルタ,ノイズ除去,音響エフェクトの原理 - (次世代信号情報処理シリーズ 2)
- 出版社/メーカー: コロナ社
- 発売日: 2021/04/08
- メディア: 単行本
■ コンプレッサーの信号処理
コンプレッサーは設定した閾値から観測信号を圧縮する処理を行います。変換を表すグラフと式を次に表します。グラフを見ても明らかなように、小さい音を持ち上げて大きな音を抑制する働きをします。
■ ノイズゲートの信号処理
ノイズを除去するフィルターとしてノイズゲートというものもあります。これはある閾値以下の値はノイズとして処理するものです。あまりやり過ぎると音が不自然になるのでこれはなくてもよいかも知れません。
■ ディストーションの信号処理
ディストーションはある閾値以上の音をクリップしてしまう処理です。ロックでは非常に重要なエフェクトですね。信号処理でゲインを与えるというやり方はありますが、情報が欠落してしまうのであまりおすすめしません。
ゲインを上げたい時はアンプのボリューム抵抗で入力信号を増幅するか、マイク入力のゲインをあげたほうがロックらしい音が得られます。
ディストーションは、あるレベルで信号をクリップしてしまうので、音のレベルを小さくしてしまうのに注意してください。出来る限りアンプの増幅度をあげてアナログ的にクリップしたほうがいいかも知れません。ここは、アナログとデジタルをうまく使い分けたいところです。
■ コンプレッサー、ノイズゲート、ディストーションの実装
これらの処理を実装してみました。前回紹介したコードの中の singnal_process 関数部分だけを掲載します。
void signal_process(int16_t* mono_input, int16_t* stereo_output, uint32_t sample_size) { /* clean up the output buffer */ memset(stereo_output, 0, sizeof(int16_t)*sample_size*2); /* volume (gain setting) */ static int16_t gain = 0; /* 0dB */ theFrontEnd->setMicGain(gain); /* compressor & distotin*/ static int16_t comp_th = 0x7fff/128; static int16_t noise_g = 0x0005; static int16_t dist_th = 0x7fff/32; static float alpha = 0.5; for (int n = SAMPLE_SIZE-1; n >= 0; --n) { /* compressor process */ if (abs(mono_input[n]) > comp_th) { int32_t value = mono_input[n]; mono_input[n] = value/abs(value)*(abs(value)*alpha + (1.-alpha)*comp_th); } /* noise gate process */ if (abs(mono_input[n]) < noise_g) { mono_input[n] = 0; } /* distortion process */ if (abs(mono_input[n]) > dist_th) { int32_t value = mono_input[n]; mono_input[n] = value/abs(value)*dist_th; } } /* copy the signal to output buffer */ for (int n = SAMPLE_SIZE-1; n >= 0; --n) { stereo_output[n*2] = stereo_output[n*2+1] = mono_input[n]; } return; }
処理時間を計測してみると最大で1.6ミリ秒でした。トータル15ミリ秒ありますので、まだ処理を追加できそうですね。SPRESENSEの計算能力恐るべしです。
■ 効果を確認
実際の効果を確認してみました。なかなかそれらしい音を出せました。全部プログラムで処理しているので、自分好みの音を自分で作れるのが醍醐味ですね。
”Spresense デジタルエフェクター”
— よしのたろう 純国産ボード”スプレッセンス”応援団長(仮) (@Taro_Yoshino) May 3, 2022
コンプレッサーとディストーションを真面目に実装。
効果を確認。なかなか良い感じ。 pic.twitter.com/xpdNsyhwbK
次は空間系エフェクターの実装をしてみたいと思います。(⌒▽⌒)/~
「SPRESENSEでデジタルエフェクターを作ろう!」ープリアンプの製作編
「SPRESENSEでデジタルエフェクターを作ろう!」ー低遅延入出力の実現ー
「SPRESENSEでデジタルエフェクターを作ろう!」ーコンプレッサーとディストーションの実現ー
「SPRESENSEでデジタルエフェクターを作ろう!」ー空間系エフェクターの実現ー
「SPRESENSEでデジタルエフェクターを作ろう!」ーモジュレーション系エフェクターの実現ー
音声音響信号処理の基礎と実践 - フィルタ,ノイズ除去,音響エフェクトの原理 - (次世代信号情報処理シリーズ 2)
- 出版社/メーカー: コロナ社
- 発売日: 2021/04/08
- メディア: 単行本
SPRESENSEではじめるローパワーエッジAI (Make: PROJECTS)
- 出版社/メーカー: オライリージャパン
- 発売日: 2022/02/28
- メディア: 単行本(ソフトカバー)
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE 拡張ボード CXD5602PWBEXT1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
「SPRESENSEでデジタルエフェクターを作ろう!」ー低遅延入出力の実現ー [SPRESENSE]
ヤマハが行った実験によると、30ミリ秒だと遅延があると認識され、50ミリ秒以上の遅延では演奏が困難になるらしいです。
SPRESENSEはデフォルトで48000bpsで音をキャプチャしているので、最大1440サンプル(1440/48000=0.03)を1フレームとして処理できそうです。ただ、より遅延感をなくすために、その半分の720サンプル単位で処理できるようにしたいと思います。
SPRESENSEは短時間で入力から出力するためライブラリが用意されています。FrontEnd ライブラリとOutputMixerライブラリです。実装をしてみたスケッチを示します。
このスケッチのポイントは、"frontend_pcm_cb"関数です。この関数はFrontEndライブラリがデータを取得すると呼ばれるコールバック関数です。この関数の中で、入力処理→信号処理→出力処理を行います。入力がモノラル信号で出力はステレオ信号になることに注意してください。
その他見るべきポイントは" setup"関数と"loop"関数です。setup関数で FroneEndライブラリとOuputMixerライブラリの初期化。loop関数でエラー状態の監視を行っています。
#include <FrontEnd.h> #include <OutputMixer.h> #include <MemoryUtil.h> #include <arch/board/board.h> #define SAMPLE_SIZE (720) FrontEnd *theFrontEnd; OutputMixer *theMixer; const int32_t channel_num = AS_CHANNEL_MONO; const int32_t bit_length = AS_BITLENGTH_16; const int32_t sample_size = SAMPLE_SIZE; const int32_t frame_size = sample_size * (bit_length / 8) * channel_num; bool isErr = false; void frontend_attention_cb(const ErrorAttentionParam *param) { Serial.println("ERROR: Attention! Something happened at FrontEnd"); if (param->error_code >= AS_ATTENTION_CODE_WARNING) isErr = true; } void mixer_attention_cb(const ErrorAttentionParam *param){ Serial.println("ERROR: Attention! Something happened at Mixer"); if (param->error_code >= AS_ATTENTION_CODE_WARNING) isErr = true; } static bool frontend_done_cb(AsMicFrontendEvent ev, uint32_t result, uint32_t detail){ UNUSED(ev); UNUSED(result); UNUSED(detail); return true; } static void outputmixer_done_cb(MsgQueId requester_dtq, MsgType reply_of, AsOutputMixDoneParam* done_param) { UNUSED(requester_dtq); UNUSED(reply_of); UNUSED(done_param); return; } static void outputmixer0_send_cb(int32_t identifier, bool is_end) { UNUSED(identifier); UNUSED(is_end); return; } static void frontend_pcm_cb(AsPcmDataParam pcm) { static uint8_t mono_input[frame_size]; static uint8_t stereo_output[frame_size*2]; static const bool time_measurement = true; if (time_measurement) { static uint32_t last_time = 0; uint32_t current_time = micros(); uint32_t duration = current_time - last_time; last_time = current_time; Serial.println("duration = " + String(duration)); } frontend_signal_input(pcm, mono_input, frame_size); signal_process((int16_t*)mono_input, (int16_t*)stereo_output, sample_size); mixer_stereo_output(stereo_output, frame_size); return; } void frontend_signal_input(AsPcmDataParam pcm, uint8_t* input, uint32_t frame_size) { /* clean up the input buffer */ memset(input, 0, frame_size); if (!pcm.is_valid) { Serial.println("WARNING: Invalid data! @frontend_signal_input"); return; } if (pcm.size > frame_size) { Serial.print("WARNING: Captured size is too big! -"); Serial.print(String(pcm.size)); Serial.println("- @frontend_signal_input"); pcm.size = frame_size; } /* copy the signal to signal_input buffer */ if (pcm.size != 0) { memcpy(input, pcm.mh.getPa(), pcm.size); } else { Serial.println("WARNING: Captured size is zero! @frontend_signal_input"); } } void signal_process(int16_t* mono_input, int16_t* stereo_output, uint32_t sample_size) { /* clean up the output buffer */ memset(stereo_output, 0, sizeof(int16_t)*sample_size*2); /* copy the signal to output buffer */ for (int n = SAMPLE_SIZE-1; n >= 0; --n) { stereo_output[n*2] = stereo_output[n*2+1] = mono_input[n]; } return true; } void mixer_stereo_output(uint8_t* stereo_output, uint32_t frame_size) { /* Alloc MemHandle */ AsPcmDataParam pcm_param; if (pcm_param.mh.allocSeg(S0_REND_PCM_BUF_POOL, frame_size) != ERR_OK) { Serial.println("ERROR: Cannot allocate memory @mixer_stereo_output"); isErr = false; return; } /* Set PCM parameters */ pcm_param.is_end = false; pcm_param.identifier = OutputMixer0; pcm_param.callback = 0; pcm_param.bit_length = bit_length; pcm_param.size = frame_size*2; pcm_param.sample = frame_size; pcm_param.is_valid = true; memcpy(pcm_param.mh.getPa(), stereo_output, pcm_param.size); int err = theMixer->sendData(OutputMixer0, outputmixer0_send_cb, pcm_param); if (err != OUTPUTMIXER_ECODE_OK) { Serial.println("ERROR: sendData -" + String(err) + "- @mixer_stereo_output"); isErr = true; } } void setup() { Serial.begin(115200); /* Initialize memory pools and message libs */ initMemoryPools(); createStaticPools(MEM_LAYOUT_RECORDINGPLAYER); /* setup FrontEnd and Mixer */ theFrontEnd = FrontEnd::getInstance(); theMixer = OutputMixer::getInstance(); /* begin FrontEnd and OuputMixer */ theFrontEnd->begin(frontend_attention_cb); theMixer->begin(); Serial.println("Setup: FrontEnd and OutputMixer began"); /* activate FrontEnd and Mixer */ theFrontEnd->setCapturingClkMode(FRONTEND_CAPCLK_NORMAL); theFrontEnd->activate(frontend_done_cb); theMixer->create(mixer_attention_cb); theMixer->activate(OutputMixer0, outputmixer_done_cb); delay(100); /* waiting for Mic startup */ Serial.println("Setup: FrontEnd and OutputMixer activated"); /* Initialize FrontEnd */ AsDataDest dst; dst.cb = frontend_pcm_cb; theFrontEnd->init(channel_num, bit_length, sample_size, AsDataPathCallback, dst); Serial.println("Setup: FrontEnd initialized"); /* Set rendering volume */ theMixer->setVolume(0, 0, 0); /* Unmute */ board_external_amp_mute_control(false); theFrontEnd->start(); Serial.println("Setup: FrontEnd started"); } void loop() { if (isErr == true) { board_external_amp_mute_control(true); theFrontEnd->stop(); theFrontEnd->deactivate(); theMixer->deactivate(OutputMixer0); theFrontEnd->end(); theMixer->end(); Serial.println("Capturing Process Terminated"); while(1) {}; } }
入出力までの時間の計測結果は次のようになりました。縦軸はマイクロ秒です。多少ばらつきはありますが、狙った通り15ミリ秒で処理ができているようです。
エフェクトは、"signal_process"関数内に記述していけば出力に反映されます。次から入力信号にエフェクトをかけてみたいと思います!
(^^)/~
「SPRESENSEでデジタルエフェクターを作ろう!」ープリアンプの製作編
「SPRESENSEでデジタルエフェクターを作ろう!」ー低遅延入出力の実現ー
「SPRESENSEでデジタルエフェクターを作ろう!」ーコンプレッサーとディストーションの実現ー
「SPRESENSEでデジタルエフェクターを作ろう!」ー空間系エフェクターの実現ー
「SPRESENSEでデジタルエフェクターを作ろう!」ーモジュレーション系エフェクターの実現ー
SPRESENSEではじめるローパワーエッジAI (Make: PROJECTS)
- 出版社/メーカー: オライリージャパン
- 発売日: 2022/02/28
- メディア: 単行本(ソフトカバー)
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE 拡張ボード CXD5602PWBEXT1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
「SPRESENSEでデジタルエフェクターを作ろう!」ーSPRESENSEアンプ編ふたたびー [SPRESENSE]
詳しくは過去記事を参照していただきたいのですが、軽く今までのおさらいをしたいと思います。
「SPRESENSEでデジタルエフェクターを作ろう!」ープリアンプの製作編ー
https://makers-with-myson.blog.ss-blog.jp/2021-04-19
「SPRESENSEでデジタルエフェクターを作ろう!」ーSPRESENSEアンプ編ー
https://makers-with-myson.blog.ss-blog.jp/2021-05-03
ギターのエフェクターを作るには、まずエレキギターの信号をプリアンプで増幅しなければなりません。
このプリアンプはオペアンプを使って制作しました。抵抗やコンデンサーの定数の決定方法は、過去の記事を参照してください。
実装は次のようにしました。
ここで、エレキギターの音をSPRESENSEに入力するまで出来たのですが、この後のソフトウェアの処理が問題でした。ここにあるように遅延がひどい!
今日の Maker Faire Kyoto 2021 に出しそびれてしまった作品
— よしのたろう 純国産ボード”スプレッセンス”応援団長(仮) (@Taro_Yoshino) May 1, 2021
「SPRESENSE DIGITAL EFFECTER」
なかなか香ばしい作品になったのでご賞味ください
私の下手くそな素人ギターも良い味わいを加えてくれています ? pic.twitter.com/QGHqHBzwg6
それもそのはず、わかりやすく処理のパイプラインを書くと次のように組んでいました。これをどうしようかと考えていてペンディングとなりました。
この度、検討を再開していてFrondEndライブラリがMixerと直結できることが分かりました。パイプラインの図で書くと次のようになります。これだとほぼ遅延はありません。理論的には15ミリ秒程度の遅延に収まるはずです。人には感知できないレベルです。
FrontEndライブラリを使って実際にデジタルエフェクターを作ってみたのがこちらです。
#SPRESENSE でデジタルエフェクター作ってみた
— よしのたろう 純国産ボード”スプレッセンス”応援団長(仮) (@Taro_Yoshino) April 17, 2022
アンプもエフェクターも不要!しかも、エフェクトは無限大 ウクレレもエフェクターかけれるぞ!
今後、メトロノーム機能も搭載予定?
では、下手くそな演奏をお楽しみください。 pic.twitter.com/RkdFONGBoA
かなりいい感じじゃないですか?これならエフェクターとして十分使えそうです。今後、これらのコードの解説をしていきたいと思います!次回をお楽しみにー♪(近日中にアップ予定)
SPRESENSEではじめるローパワーエッジAI (Make: PROJECTS)
- 出版社/メーカー: オライリージャパン
- 発売日: 2022/02/28
- メディア: 単行本(ソフトカバー)
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE 拡張ボード CXD5602PWBEXT1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
Tensorflow対応 SPRESENSE Arduinoボードパッケージをネットからインストール [SPRESENSE]
SPRESENSEのTensorflow対応Arduinoボードパッケージを公開してみた
https://qiita.com/TaroYoshino/items/25255ada7a4fb6e4a788
独自のArduinoボードパッケージにするポイントは、spresense-arduino-compatible のMakefileと出力した package_spresense_tensorflow_index.jsonを変更します。
Makefile の変更
15行目の NAME_SUFFIX を変更します。
NAME_SUFFIX ?= tensorflow
package_spresense_tensorflow_index.jsonの変更
outディレクトリに出力したファイルをgithubにアップロードして、Arduino Library、SDK、tools がダウンロードできるように package_spresense_tensorflow_index.json のURLを変更します。
・ArduinoパッケージURLの変更
"maintainer": "Spresense tensorflow Community", "name": "SPRESENSE_tensorflow", "platforms": [ { "architecture": "spresense", "archiveFileName": "spresense-v2.4.1_tensorflow.tar.gz", "boards": [ { "name": "Generic Spresense Module" } ], "category": "SPRESENSE", "checksum": "SHA-256:4cd54064112ffcd92a886c372c48b4c6b81dd4f2c7b259f31d948c5eddf91f47", "help": { "online": "https://developer.sony.com/develop/spresense/developer-tools/get-started-using-arduino-ide/set-up-the-arduino-ide" }, "name": "Spresense tensorflow Board", "size": 952491, "toolsDependencies": [ { "name": "spresense-sdk", "packager": "SPRESENSE_tensorflow", "version": "2.4.1" }, { "name": "spresense-tools", "packager": "SPRESENSE_tensorflow", "version": "2.4.1" }, { "name": "gcc-arm-none-eabi", "packager": "SPRESENSE", "version": "5.4.1" } ], "url": "https://github.com/YoshinoTaro/spresense-arduino-tensorflow/raw/main/staging/packages/spresense-v2.4.1_tensorflow.tar.gz", "version": "2.4.1" }
・SDKパッケージのURL変更
{ "name": "spresense-sdk", "systems": [ { "archiveFileName": "spresense-sdk-v2.4.1_tensorflow.tar.gz", "checksum": "SHA-256:54f269c49daca0382b7d5dc5bdbf04442511b9b9be2663fdae14231fd7993667", "host": "i686-mingw32", "size": 23265440, "url": "https://github.com/YoshinoTaro/spresense-arduino-tensorflow/raw/main/staging/packages/spresense-sdk-v2.4.1_tensorflow.tar.gz" }, { "_comment": "Allow x64-Linux build", "archiveFileName": "spresense-sdk-v2.4.1_tensorflow.tar.gz", "checksum": "SHA-256:54f269c49daca0382b7d5dc5bdbf04442511b9b9be2663fdae14231fd7993667", "host": "x86_64-pc-linux-gnu", "size": 23265440, "url": "https://github.com/YoshinoTaro/spresense-arduino-tensorflow/raw/main/staging/packages/spresense-sdk-v2.4.1_tensorflow.tar.gz" }, { "archiveFileName": "spresense-sdk-v2.4.1_tensorflow.tar.gz", "checksum": "SHA-256:54f269c49daca0382b7d5dc5bdbf04442511b9b9be2663fdae14231fd7993667", "host": "i386-apple-darwin11", "size": 23265440, "url": "https://github.com/YoshinoTaro/spresense-arduino-tensorflow/raw/main/staging/packages/spresense-sdk-v2.4.1_tensorflow.tar.gz" } ], "version": "2.4.1" },
・toolsパッケージの変更
{ "name": "spresense-tools", "systems": [ { "archiveFileName": "spresense-tools-v2.4.1_tensorflow.tar.gz", "checksum": "SHA-256:7d90721932a9f4ce27ceddead0d14b8b53f9e14168e3f1c8844a7425a770e5c3", "host": "i686-mingw32", "size": 59772452, "url": "https://github.com/YoshinoTaro/spresense-arduino-tensorflow/raw/main/staging/packages/spresense-tools-v2.4.1_tensorflow.tar.gz" }, { "_comment": "Allow x64-Linux build", "archiveFileName": "spresense-tools-v2.4.1_tensorflow.tar.gz", "checksum": "SHA-256:7d90721932a9f4ce27ceddead0d14b8b53f9e14168e3f1c8844a7425a770e5c3", "host": "x86_64-pc-linux-gnu", "size": 59772452, "url": "https://github.com/YoshinoTaro/spresense-arduino-tensorflow/raw/main/staging/packages/spresense-tools-v2.4.1_tensorflow.tar.gz" }, { "archiveFileName": "spresense-tools-v2.4.1_tensorflow.tar.gz", "checksum": "SHA-256:7d90721932a9f4ce27ceddead0d14b8b53f9e14168e3f1c8844a7425a770e5c3", "host": "i386-apple-darwin11", "size": 59772452, "url": "https://github.com/YoshinoTaro/spresense-arduino-tensorflow/raw/main/staging/packages/spresense-tools-v2.4.1_tensorflow.tar.gz" } ], "version": "2.4.1" }
以上の対応をすれば、Arduino IDEでボードパッケージ(package_spresense_tensorflow_index.json)のURLを指定すれば、ダウンロードができるようになります。注意するポイントとしては、GitHubのRawデータのURLを設定するところです。かなり備忘録に近い内容になってしまいましたが、独自のArduinoパッケージを作るヒントになれば幸いです。
SPRESENSEではじめるローパワーエッジAI (Make: PROJECTS)
- 作者: 太田 義則
- 出版社/メーカー: オライリージャパン
- 発売日: 2022/02/28
- メディア: 単行本(ソフトカバー)
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE LTE拡張ボード CXD5602PWBLM1JUL
- 出版社/メーカー: Spresense
- メディア:
Tensorflow対応のSPRESENSE Arduinoパッケージを作ってみた話 [SPRESENSE]
Tensorflow lite/micro 用の SPRESENSE Arduino パッケージを作ってみた
https://qiita.com/TaroYoshino/items/be62c70ad78a9a80308a
今回はその経緯についてちょっと触れたいと思います。今後、作業をしてみる人の参考になるかもしれません。
コンパイルを通すのに一苦労
外部ライブラリをArduinoパッケージに取り込むのは、jpegライブラリを追加したときに経験済なので、ある程度どのあたりを変えたらよいのかというあたりはついていました。
参考
SPRESENSE Arduino で libjpeg を使いたい
https://ja.stackoverflow.com/questions/73301/spresense-arduino-%e3%81%a7-libjpeg-%e3%82%92%e4%bd%bf%e3%81%84%e3%81%9f%e3%81%84
しかし、Tensorflow lite/microの場合は大変でした。もっとも大変だったのはヘッダーファイルのエクスポートです。とても整理されたコードとは思えず、依存関係が複雑でかつフォルダが細分化されているので、必要なすべてのヘッダーファイルを絞り出すのにかなりの時間がかかりました。
サンプルコードがとにかく分かりにくい
一通りコンパイルは通ったので、動作確認のためにサンプルコードをArduinoに移植しようとしたのですが、サンプルなのにすごく分かりにくい。プラットフォームに依存しないように作ろうとしたことはわかりますが定数まで、分離する必要あるんですかね?
サンプルという以上は普通はわかりやすい例を出すものですが、とてもそのような事を意識したようには思えません。HelloWorldサンプルがなぜ正弦波を予測して出力するものなのか意味わかりません。Androidは非常にわかりやすく作られていましたが、Tensorflowはひどいですね。やはり組み込みはおまけなのでしょうか?
サンプルはビルドしたけどTensorflowの学習済モデルが動かない
どうにかこうにか、HelloWorldサンプルをArduinoに移植し、SPRESENSEのイメージを作るところまではいきました。しかし、それを書き込んでみると、Tensorflowの学習済モデルが動いている気配がない。量子化係数がnanを返しているので、インタープリターが動いていない。
理由がわからず試行錯誤を繰り返しましたが、サンプルコードをコンパイルするときのコンパイルオプションに問題があるのだろうとあたりをつけて、SDKのサンプルをコンパイルしているときのオプションをログを出力して細かく見ました。差分をArduinoIDEのplatform.txtに設定してみたら、これがビンゴ!無事に動くようになりました。
かなり癖のあるTensorflow lite/micro ですがTensorflowの最新フレームワークが使えるのは大きな魅力です。これから少しずつ使いこなしていきたいと思います。
(^^)/
TensorFlowとKerasで動かしながら学ぶ ディープラーニングの仕組み 畳み込みニューラルネットワーク徹底解説 (Compass Books)
- 作者: 中井 悦司
- 出版社/メーカー: マイナビ出版
- 発売日: 2019/11/28
- メディア: Kindle版
深層学習&深層強化学習による電子工作 TensorFlow編 (たのしくできる)
- 出版社/メーカー: 東京電機大学出版局
- 発売日: 2021/06/21
- メディア: 単行本(ソフトカバー)
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SPRESENSEでジェスチャー使って音楽を操作する「MusicGripper」の裏話 [SPRESENSE]
実は、もともとはSPRESENSEを使ったスマートグラスのようなものを作りたいと思っていましたが、0.96インチの液晶ディスプレイが、SPRESENSEで何をやっても動かない。簡単に動くと思ったのですがこれは大誤算。
レンズや鏡も準備していたのですが、これは困った。動かない原因はおいおい調べるとして、Advent Calendar のトリとしては立派なコンテンツをあげたいところ。このままでは間に合いません。
いろいろ考えた結果、前からアイディアとしてあたためていたジェスチャーで音楽を操作することを試してみることにしました。
しかし、順調にものごとが進むことは稀で、ここでもいくつかの壁がありました。一つ目はBLEと音楽再生の平行処理です。BLEの接続は前から調べていたのでなんとかなりますが、今回は音楽再生とBLE受信の平行処理をしなければなりません。
いろいろ検討したのですが、さすがPythonです。ここは ”Threading” というテクニックを使って解決しました。Pythonは正直苦手なんですが、これは直感的で使いやすいですね。
import time import threading n = 0 def func1(): global n while n < 10: print ('hello') time.sleep(0.5) thread1 = threading.Thread(target=func1) thread1.start() while n < 10: n = n+1 print('n='+str(n)) time.sleep(1) thread1.join()
二番目に苦労したのが、再生速度を変える方法です。これがかなり面倒くさい。再生速度を早くするには、オリジナルの音源のサンプル数を間引きしてプレーヤーに渡す必要があります。一方で、再生速度を遅くするにはサンプルの間を補間する必要があります。
聞くだけでも面倒な処理ですが、ここもPythonの数値ライブラリが活躍してくれました。これらの話を、今後このブログでしていきたいと思います。
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
Prament BMI160 モジュール 6DOF 6 軸角速度ジャイロ + 重力加速度センサ IICSPI
- 出版社/メーカー: Prament
- メディア: エレクトロニクス
SPRESENSE/Arduino でAVI動画を記録 [SPRESENSE]
YoshinoTaro/AviLibrary_Arduino Contribute to YoshinoTaro/AviLibrary_Arduino development by creating an account on GitHub. github.com |
SPRESENSEやArduinoなどの省電力のボードマイコンはカメラ用のISPが内蔵されていないので動画を撮影することはできません。でも、SPRESENSEのカメラのようにカメラモジュールがJPEG出力している場合は、Motion JPEG(AVI)で動画を簡単に記録できます。
void setup() { Serial.begin(115200); theCamera.begin(); while (!theSD.begin()) { Serial.println("insert SD card"); } // Setup the still picture parameters of Spresense Camera // The image format must be JPEG theCamera.setStillPictureImageFormat( CAM_IMGSIZE_QUADVGA_H, CAM_IMGSIZE_QUADVGA_V, CAM_IMAGE_PIX_FMT_JPG); theSD.remove(filename); aviFile = theSD.open(filename, FILE_WRITE); // Initialize AVI library theAvi.begin(aviFile, CAM_IMGSIZE_QUADVGA_H, CAM_IMGSIZE_QUADVGA_V); theAvi.startRecording(); } void loop() { static uint32_t start_time = millis(); CamImage img = theCamera.takePicture(); // Add a frame. The image format must be JPEG theAvi.addFrame(img.getImgBuff(), img.getImgSize()); uint32_t duration = millis() - start_time; if (duration > recording_time_in_ms) { // Stop AVI library theAvi.endRecording(); theAvi.end(); theCamera.end(); while (true) {} } }
このライブラリは実時間で記録することもできますが、FPSを指定して再生速度を指定することができます。特にタイムラプスの使用を想定しているので、数分毎に撮影した画像を指定したFPSで再生することができます。
void setup() { Serial.begin(115200); theCamera.begin(); while (!theSD.begin()) { Serial.println("insert SD card"); } // Setup the still picture parameters of Spresense Camera // The image format must be JPEG theCamera.setStillPictureImageFormat( CAM_IMGSIZE_QUADVGA_H, CAM_IMGSIZE_QUADVGA_V, CAM_IMAGE_PIX_FMT_JPG); theSD.remove(filename); aviFile = theSD.open(filename, FILE_WRITE); // Initialize AVI library theAvi.begin(aviFile, CAM_IMGSIZE_QUADVGA_H, CAM_IMGSIZE_QUADVGA_V); // Start the timelapse specifying fps theAvi.startTimelapse(target_fps); } void loop() { static uint32_t start_time = millis(); CamImage img = theCamera.takePicture(); // Add a timelapse frame. The image format must be JPEG theAvi.addTimelapseFrame(img.getImgBuff(), img.getImgSize()); uint32_t duration = millis() - start_time; if (duration > recording_time_in_ms) { // Stop AVI library theAvi.endTimelapse(); theAvi.end(); theCamera.end(); while (true) {} } }
特にSPRESENSEの場合は、低消費電力で動かすことができるので、数分おきに起動して撮影を繰り返すことでバッテリーでも長時間撮影が可能です。省電力カメラのサンプルも用意しています。夜空の撮影や、旅程の記録、自宅の監視用にぜひぞうぞ。
void setup() { // Initialize LowPower Library LowPower.begin(); RTC.begin(); // get boot cause to check whether this boot is caused by the alarm bootcause_e bc = LowPower.bootCause(); Serial.begin(115200); theCamera.begin(); while (!theSD.begin()) { Serial.println("insert SD card"); } // Setup the still picture parameters of Spresense Camera // The image format must be JPEG theCamera.setStillPictureImageFormat( CAM_IMGSIZE_QUADVGA_H, CAM_IMGSIZE_QUADVGA_V, CAM_IMAGE_PIX_FMT_JPG); // In the case of power on / reset, the parameters are set to the initial value. if (bc != DEEP_RTC && bc != DEEP_OTHERS) { theSD.remove(filename); rec_frame = 0; movi_size = 0; file_size = 0; } else { EEPROM.get(rec_frame_address, rec_frame); // memory the recorded frame count EEPROM.get(movi_size_address, movi_size); // memory the size of the video EEPROM.get(file_size_address, file_size); // memory the file size } aviFile = theSD.open(filename, FILE_WRITE); // Initialize AVI library theAvi.begin(aviFile, CAM_IMGSIZE_QUADVGA_H, CAM_IMGSIZE_QUADVGA_V); // set parameters got from EEPROM theAvi.setTotalFrame(rec_frame); theAvi.setFileSize(file_size); theAvi.setMovieSize(movi_size); // Start the timelapse specifying fps theAvi.startTimelapse(target_fps); } void loop() { CamImage img = theCamera.takePicture(); // Add a timelapse frame. The image format must be JPEG theAvi.addTimelapseFrame(img.getImgBuff(), img.getImgSize()); // end the recording immediately theAvi.endTimelapse(); theAvi.end(); theCamera.end(); // memory the updated parameters EEPROM.put(rec_frame_address, theAvi.getTotalFrame()); EEPROM.put(movi_size_address, theAvi.getMovieSize()); EEPROM.put(file_size_address, theAvi.getFileSize()); // finish the task when the total frame is over the specified number if (theAvi.getTotalFrame() > recording_total_frames) { while (true) {} } // power off until the time specified by sleep_time LowPower.deepSleep(sleep_time); }
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE カメラモジュール CXD5602PWBCAM1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE 拡張ボード CXD5602PWBEXT1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware