🔨

Electro-smith Daisy seedでライブラリのクラスを改造する

2021/05/05に公開

Daisyはデフォルトで提供されているクラスが充実していてそれだけで遊んでいても楽しいですが、以下の記事で書いたように、触っていると「もうちょっとパラメータを外から触れるようにしてほしい」と思うことがあります。

https://zenn.dev/yutannihilation/articles/366f6cc52cf9dc

さいわい、DaisySPのコードはMITライセンスで公開されているものなので、コピーして好きなようにいじることができます(改造したものを公開するときはattributionをつけるようにしましょう)。

プロジェクトをつくる

まずは新規のプロジェクトを作ります。最近は、DaisyExamplesレポジトリhelper.pyというスクリプトが用意されているのでこれを使いましょう。このスクリプトはあくまでDaisyExamplesレポジトリ内に新しいプロジェクトをつくるためのものなので、他の場所につくる場合は多少作業が必要ですが、そこは後述します。

python3 ./helper.py create path/to/<プロジェクト名> --board seed

スクリプトを実行すると、以下のような構造で空のプロジェクトができあがっています。

❯ tree -a .
.
├── .vscode
│   ├── .cortex-debug.peripherals.state.json
│   ├── .cortex-debug.registers.state.json
│   ├── STM32H750x.svd
│   ├── c_cpp_properties.json
│   ├── launch.json
│   └── tasks.json
├── Makefile
├── README.md
├── <プロジェクト名>.cpp
├── <プロジェクト名>.sln
└── vs
    ├── <プロジェクト名>-Debug.vgdbsettings
    ├── <プロジェクト名>-Release.vgdbsettings
    ├── <プロジェクト名>.vcxproj
    ├── <プロジェクト名>.vcxproj.filters
    ├── stm32.props
    └── stm32.xml

さっそくmakeでビルドしてみると...、エラーになります。

❯ make
Makefile:13: ../../libdaisy/core/Makefile: No such file or directory
make: *** No rule to make target '../../libdaisy/core/Makefile'.  Stop.

これは先程書いたように、デフォルトではDaisyExamplesの中で実行されるのが前提になっているためです。具体的には、Makefileのこの部分を書き換える必要があります。

# Library Locations
LIBDAISY_DIR = ../../libdaisy
DAISYSP_DIR = ../../DaisySP

すでにあるDaisyExamplesにパスを通すか、git submoduleとしてlibDaisyとDaisySPを追加してそのパスを指定するかしましょう。後者でやるなら、以下のようにしてからlib/libDaisylib/DaisySPMakefileに指定します。

mkdir lib
git submodule add git@github.com:electro-smith/libDaisy.git lib/libDaisy
git submodule add git@github.com:electro-smith/DaisySP.git lib/DaisySP

# ライブラリをビルド
(cd lib/libDaisy/ && make)
(cd lib/DaisySP/ && make)

ライブラリから必要なファイルをコピーしてきて編集

例として、クロックとして使えるユーティリティであるMetroを改造する場合を考えてみましょう。Metroはスピードを周波数で指定しますが、BPMで指定するようにしてみます。

元のコードはそれぞれ以下にあります。

これをコピーしてきて編集します。

cp lib/DaisySP/Source/Utility/metro.cpp metro.cpp
cp lib/DaisySP/Source/Utility/metro.h metro.h

やることはfreqbpmと書き換えて60で割るだけですが、metro.cppの方だけ貼っておきます。変数が1つdsp.hに定義されていたのでそこだけコピーしてますが、dsp.hごとコピーしてきてもいいかもです。

--- lib/DaisySP/Source/Utility/metro.cpp        2021-05-05 20:17:37.750956917 +0900
+++ metro.cpp   2021-05-05 20:49:21.337804387 +0900
@@ -1,15 +1,17 @@
 #include <math.h>
 #include "metro.h"
-#include "dsp.h"
+// dsp.h から必要な定義だけ持ってくる
+#define PI_F 3.1415927410125732421875f
+#define TWOPI_F (2.0f * PI_F)

-using namespace daisysp;
+using namespace my_metro;

-void Metro::Init(float freq, float sample_rate)
+void Metro::Init(float bpm, float sample_rate)
 {
-    freq_        = freq;
+    bpm_         = bpm;
     phs_         = 0.0f;
     sample_rate_ = sample_rate;
-    phs_inc_     = (TWOPI_F * freq_) / sample_rate_;
+    phs_inc_     = (TWOPI_F * bpm_ / 60.0f) / sample_rate_;
 }

 uint8_t Metro::Process()
@@ -23,8 +25,8 @@ uint8_t Metro::Process()
     return 0;
 }

-void Metro::SetFreq(float freq)
+void Metro::SetBpm(float bpm)
 {
-    freq_    = freq;
-    phs_inc_ = (TWOPI_F * freq_) / sample_rate_;
+    bpm_     = bpm;
+    phs_inc_ = (TWOPI_F * bpm_ / 60.0f) / sample_rate_;
 }

Makefileに追加

細かい点ですが、.cppファイルはMakefileのこの行に追加する必要があります。

# Sources
CPP_SOURCES = <プロジェクト名>.cpp metro.cpp

これで、以下のように自作のMetroを呼び出せるようになっているはずです。

#include "daisy_seed.h"
#include "daisysp.h"
#include "metro.h"

// ...

my_metro::Metro clock;

void AudioCallback(float **in, float **out, size_t size)
{
  // (余談1)
  for (size_t i = 0; i < size; i++) {
    if (clock.Process()) {
      // 何らかの処理
    }

    // ...
  }
}

int main(void)
{
  // ...

  // BPM120
  clock.Init(120.0f, hw.AudioSampleRate());
  
  while (1)
  {
    // (余談2)
  }
}

ビルドしてDaisyに送り込みましょう。

make && program-dfu

ちなみに余談ですが、Metro.Process()AudioCallback()のforループの中で動かす必要があります。上のコードで(余談1)や(余談2)の位置でLEDをチカチカさせるのに使おうとしたんですが、うまく動いてくれませんでした。内部の処理はサンプルレートの数だけ呼び出されるのはそのforループの中だけで、他の場所で使うなら自分で秒数を数える必要があるみたいです(DaisySeed.system.GetNow()とかを使うのかな?)。

最後に

C++も組み込みもDSPも初心者なので、なにか変なこと書いていればツッコミをいただけるとありがたいです...

Discussion