AIMP オーディオプレイヤーでリッピングしたノンストップ系の CD で途切れなく再生する設定

表題の通りの設定メモです。

AIMP Enjoy The Music!

デフォルト設定だと、ノンストップ系の音源で曲と曲の間がクロスフェードしてつながってしまうので、次のように設定すると想定通り繋がるようになります。(トータルフレームサイズが分かる) .flac 形式で確認していますが、おそらく他の形式でも大丈夫です。

  • 設定 -> 再生
  • 再生右下 “DSP 管理”
  • ミキシングタブ -> ☑トラックの自動切換え -> “何もしない”

また、トラック先読み設定にも☑しておくと良い結果になるかもしれません。

以上、ぱっと見当たらなくて困ったのでメモでした。(再導入するたびにあれ?ってなってる気がしますw

Zellij ターミナルワークスペースと WebAssembly プラグイン

自分は Linux をセットアップして最初に行う設定が tmux 関連だったりするのですが、新しい Windows 機の WSL2 にも秘伝の .tmux.conf を入れつつ、そうだ Zellij を試してみようと思い立ち、良い感じだったので紹介です。

Zellij

A terminal workspace with batteries included

Zellij (ぜりーじゅと読むそうです)は、いわゆるターミナルマルチプレクサーに種別されるソフトウェアで、セッション管理や、ターミナルエミュレータ上での画面分割やタブ制御を行うことができます。

Windows Terminal WSL2 上で動作している Zellij。

tmux よりも新しく作られていることもあり、デフォルトで自動起動スクリプトの出力やシステムクリップボード連携設定などが備わっており、また操作系も画面下部に表示することができるので非常に導入しやすいつくりとなっています。

導入

絶賛開発中 (記事執筆時点 0.36.0) の Rust でできたソフトウェアにて、導入は cargo install を使ってソースからビルドする方法が良いと思います。cargo なのでコマンド一発です。

Zellij User Guide – Installation

The easiest way to install Zellij is through a package for your OS.

If one is not available for your OS, you can download a prebuilt binary or even try Zellij without installing.

Otherwise, you can install it with Cargo.

(cargo を含む)Rust のツールチェインが未導入の場合は rustup で導入します。

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

導入後 PATH などが設定されますので、いったんシェルを落とし上げします。

cargo install で zellij を導入。

cargo install --locked zellij
$ ls -laF ~/.cargo/bin/ | grep zel
-rwxr-xr-x  1 hiromasa hiromasa 17380208  4月 20 17:31 zellij*

なお、cargo install で導入したバイナリーのアップデートは cargo-install-update を使うことで行うことができます。

# cargo-update の導入 (初回のみ)
cargo install cargo-install-update
# アップデート確認
cargo install-update -l
    Polling registry 'https://index.crates.io/'.....

Package         Installed  Latest   Needs update
espup           v0.2.9     v0.4.0   Yes
cargo-generate  v0.18.2    v0.18.2  No
cargo-update    v13.0.1    v13.0.1  No
powerline-rs    v0.2.0     v0.2.0   No
zellij          v0.36.0    v0.36.0  No
# Needs update が Yes なものをアップデート
cargo install-update -a

フォント

Zellij は標準で powerline フォントに対応しています。お使いのターミナルエミュレータのフォントに設定しましょう。自分は HackGen を使わせていただいています。(とてもきれいです…!)

https://github.com/yuru7/HackGen

白源 (はくげん/HackGen) は、プログラミング向け英文フォント Hack と、源ノ角ゴシックの派生フォント源柔ゴシックを合成したプログラミングフォントです。

設定フォントは「源ノ角ゴシックの派生フォント源柔ゴシックに、Powerline フォントを含む Nerd Fonts を合成した」 HackGen_NF_*.zipHackGenConsoleNF-Regular.ttfHackGen Console NF)を設定すると良いと思います。

なお、Windows Terminal だと Ricty 系の powerline フォントの罫線がずれるようです。

自動起動設定

zellij 起動確認後は、シェル設定ファイルに Zellij の自動起動設定を入れておくと便利です。

Zellij User Guide – Autostart on shell creation

Autostart a new zellij shell, if not already inside one. Shell dependent, fish:

自分は .bashrc ですので次のようになります。

echo 'eval "$(zellij setup --generate-auto-start bash)"' >> ~/.bashrc

ちなみに zellij setup --generate-auto-start bash の中身は次のようになっています。tmux だと自分で書かなければいけないので小粋な感じでぐー。

if [[ -z "$ZELLIJ" ]]; then
    if [[ "$ZELLIJ_AUTO_ATTACH" == "true" ]]; then
        zellij attach -c
    else
        zellij
    fi

    if [[ "$ZELLIJ_AUTO_EXIT" == "true" ]]; then
        exit
    fi
fi

というわけで、お好みで ZELLIJ_AUTO_ATTACH を設定すると、Windows Teminal などがダウンしたときにや誤ってタブを落とした時に自動復帰できて良いかと思います。

`.bashrc`

ZELLIJ_AUTO_ATTACH=true
eval "$(zellij setup --generate-auto-start bash)"

関連して Zellij の default key binding に Ctrl + s, s がバッファのサーチに割り当てられていて、自分はうっかりロックを忘れて tty に START/STOP を送ってしまうことがあったので、以下も一緒に入れて tty の制御コード受付を disable にしています。

if [[ -t 0 && $- = *i* ]]
then
    stty -ixon
fi
ZELLIJ_AUTO_ATTACH=true
eval "$(zellij setup --generate-auto-start bash)"

設定

Zellij はシングルバイナリーで動作するようになっていますが、設定ファイルを使うこともできます。デフォルト設定を最初に出力すると良いかと思います。

mkdir -p ~/.config/zellij/themes
mkdir -p ~/.config/zellij/layouts
mkdir -p ~/.config/zellij/plugins

Zellij User Guide – Configuration

Zellij uses KDL as its configuration language.

# 設定ファイル
zellij setup --dump-config > ~/.config/zellij/config.kdl

自分のデフォルト設定から変えている定義は次の通りです。ほとんど変えてないですねw

// ペインのフレームをボーダレスに
pane_frames false
// 起動時の操作モードをインターフェース locked に
default_mode "locked"
// 配色テーマ
themes {
    tokyo-night-dark {
        fg 169 177 214
        bg 26 27 38
        black 56 62 90
        red 249 51 87
        green 158 206 106
        yellow 224 175 104
        blue 122 162 247
        magenta 187 154 247
        cyan 42 195 222
        white 192 202 245
        orange 255 158 100
    }
}
theme "tokyo-night-dark"

テーマは次のドキュメントからいただいてきたものです。

Zellij User Guide – Themes

Themes can be specified either in the configuration file under the themes section, or directly in a separate file.

レイアウト

Zellij の肝のひとつですが、レイアウトファイルを使うことによりペインに好きなパーツを配置することができます。

# デフォルト画面レイアウトファイル
zellij setup --dump-layout default > ~/.config/zellij/layouts/default.kdl

pain の一番目に後述する自分の WebAssembly プラグインを追加していますが、ファイルの内容は次のようになります。

layout {
    pane size=1 borderless=true {
        plugin location="file:/home/hiromasa/.config/zellij/plugins/zellij-datetime.wasm"
    }
    pane size=1 borderless=true {
        plugin location="zellij:tab-bar"
    }
    pane
    pane size=2 borderless=true {
        plugin location="zellij:status-bar"
    }
}

この定義が起動時の画面レイアウトと対応する形のつくりになっています。タブバーや下部のステータスバーがデフォルトプラグインとして実装されているのが分かります。

レイアウトファイルの記述方法は以下のドキュメントが参考になります。

また Zellij 起動時の引数でレイアウトファイルを指定できるため、作業によってワークスペースを切り替えるようなことも可能です。

Zellij User Guide – Layouts

Layouts are text files that define an arrangement of Zellij panes and tabs.

You can read more about creating a layout

default_tab_template を使うと複数のタブで同じ設定を共有できます。

layout {
    tab name="t420s#1" focus=true
    tab name="t420s#2"
    default_tab_template {
        pane size=1 borderless=true {
            plugin location="file:/home/hiromasa/.config/zellij/plugins/zellij-datetime.wasm"
        }
        pane size=1 borderless=true {
            plugin location="zellij:tab-bar"
        }
        children
        pane size=2 borderless=true {
            plugin location="zellij:status-bar"
        }
    }
}

例えばですが、シングルボードコンピュータへの ssh 接続を行い、各種リソースを表示するようなレイアウトを作成した例 です。(後述していますがこの例で使っている RISC-V Linux な SBC では直接 Zellij が動作しないため、ホスト側から ssh で接続しています。RPi 4A など Arm 機ではリモート側で Zellij を起動できるでしょう)

layout {
    tab name="LPi4A" focus=true {
        pane split_direction="vertical" {
            pane size="60%" close_on_exit=true {
                command "sshpass"
                args "-p" "licheepi" "ssh" "sipeed@172.16.15.201"
            }
            pane size="40%" {
                pane size="25%" close_on_exit=true {
                    command "sshpass"
                    args "-p" "licheepi" "ssh" "sipeed@172.16.15.201" "-t" "watch" "-n5" "df" "-k"
                }
                pane size="16%" close_on_exit=true {
                    command "sshpass"
                    args "-p" "licheepi" "ssh" "sipeed@172.16.15.201" "-t" "watch" "sudo" "cat" "/sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq"
                }
                pane size="16%" close_on_exit=true {
                    command "sshpass"
                    args "-p" "licheepi" "ssh" "sipeed@172.16.15.201" "-t" "watch" "sensors"
                }
                pane size="43%" close_on_exit=true {
                    command "sshpass"
                    args "-p" "licheepi" "ssh" "sipeed@172.16.15.201" "-t" "htop"
                }
            }
        }
    }
    tab name="Host"
    default_tab_template {
        pane size=1 borderless=true {
            plugin location="zellij:tab-bar"
        }
        children
        pane size=2 borderless=true {
            plugin location="zellij:status-bar"
        }
    }
}

Ubuntu/Alactirry 上の動作:

Windows Terminal/WSL2 Ubuntu で動作させている様子(WSL2 Ubuntu 上の zellij-server が接続を維持してくれるのでターミナルを落としてしまっても復帰できる例):

キーバインドから特定のコマンドをペインで起動

例えば F12 押下で Python REPL を起動したいなんて時は config.kbl で次のようにします。

keybinds {
    normal {
        // uncomment this and adjust key if using copy_on_select=false
        // bind "Alt c" { Copy; }
        bind "F12" {
            Run "python" "-i" "/home/hiromasa/.local/share/micropython/imports.py" {
                cwd "/home/hiromasa"
                direction "Down"
            }
        }
    }

ワンボタンで計算機起動的。とても便利です。

ペイン内の操作シンク

クラスタ構成の同一構成複数サーバとかで同じコマンドを入力したい時につかうあれも OK です。tab 操作から sync を選択することで、ペイン全てに同じ操作ができます。

ペインとタブ間の Break 操作

Zellij 0.38 よりペインをタブに移動させたり、その逆ができるようになりました。便利です。

WebAssembly プラグイン拡張

Zellij プラグインは WebAssembly/WASI で作成することができます。勉強がてら 日時 date-time を表示するプラグインを Rust でかいてみました。

https://github.com/h1romas4/zellij-datetime

This plugin adds a date and time pane to [Zellij](https://zellij.dev/), a terminal multiplexer.

amd64 Ubuntu/Alacritty で動作している様子。

なお、システムインターフェースは WASI API の範囲となります。とりあえずまだ TIMEZONE がとれない気がした(たぶん)ので +9 JST 固定でビルドしました

(2023-06-21 追記)定義ファイルとマウスクリックでタイムゾーンを選択できるように改良しました。

WASI API 超えをしたい場合のアイディアメモ:(ホスト側で情報を取得するシェルスクリプトを動かして、結果をファイルかソケットでプロキシしていると思います)

https://github.com/yvt/zellij-cpulamp

Minimal CPU activity indicator plugin for Zellij

ファイルシステムへのセキュリティーポリシーなどがどうなっているかなど継続調査。WebAssembly ランタイムは Wasmer (執筆時点 2.3 系) が使われています。

https://zellij.dev/documentation/plugin-api-file-system.html

Plugin API – Reading from the Filesystem

(2023-06-19 追記) Zellij 0.37 からプラグインから Worker スレッドが作成できるようになりました…!

https://zellij.dev/documentation/plugin-api-workers.html

Plugin workers are a way to get around the fact that wasm/wasi threads are not stable yet. If a plugin has a potentially long operation to perform, it can declare a worker on startup and send and receive messages from it.

Wasm を使っているため .wasm ファイルは amd64 や Arm といった CPU の違いを気にせず同一バイナリーが使えますので便利です。 .wasm を配置してレイアウトファイルで読み込ませれば動作を開始します。(Wasmer は 3.2 から RISC-V 対応なので残念ながら現時点では RISC-V 機では動作しません)

今回は Rust を使いましたが、言語を選ばずプラグインがかけるのも良いです。

なお、まだ Rust と Zig 言語以外にはバインディングのリリースはなく、そのほかの言語では、引数をもらうのに WASI の stdin から JSON をパースする処理をかく必要がありそうです。Rust のバインディングも内部的にはこの構成になっています。この辺は、WebAssembly の Component Model (Interface Types) の仕様ができれば改善されることでしょう。

(2023-06-19 追記)Zellij は開発中のためプラグインの ABI (API) zellij-tile が現在のところまだ確定していません。このため Zellij の update が合った際は、Rust wasm32-wasi なプラグインは該当バージョンに合わせて dependencies を修正して .wasm ビルドしなおす必要があります。(これは近い将来に修正されると思われます)

(2023-08-28 追記)Zellij 0.38 で Wasm plugin API が protocol buffer 化され、下位互換が保たれるようになりました。API チェンジがない限り Zellij のバージョンが上がってもそれよりも下のバージョンでコンパイルされたプラグインが利用可能です。

https://zellij.dev/documentation/plugin-upgrading.html

Upgrading a Plugin

Since Zellij plugins using zellij-tile rely on shared data structures, currently one would need to compile a plugin against the corresponding zellij-tile package of the zellij version it is installed on.

The Zellij maintainers are aware this is an inconvenient and undesired scenario and are working on a more robust solution.

zellij-datetime リポジトリはその時点の最新版でリリースしていきます。もし筆者が Zellij のアップデートに気がついていなかったら、以下の部分のバージョンを Zellij コアのそれと合わせるように修正ビルドして .wasm を取得してみてください。

$ cat Cargo.toml | grep -A 2 dependencies
[dependencies]
zellij-tile = "^0.37"
zellij-tile-utils = "^0.37"

size=1 のペインを縦並びにするハック

デフォルトのペインの罫線表示を pane_frames false (非表示)とした場合、size=1 のペインを vertical で配置すると余計なボーダーが表示されてしまうのを、pane_frames trueとした上で、次のように個別に pane borderless=true として回避。

layout {
    tab name="thinkpad-t420s" focus=true {
        pane borderless=true
    }
    default_tab_template {
        pane size=1 split_direction="vertical" {
            pane size="75%" borderless=true {
                plugin location="zellij:tab-bar"
            }
            pane size="25%" borderless=true {
                plugin location="file:/home/hiromasa/.config/zellij/plugins/zellij-datetime.wasm" {
                    // Test multiple time zone definitions and default time zones.
                    timezone1 "PDT/-9"
                    timezone2 "UTC/0"
                    timezone3 "CEST/+2"
                    timezone4 "JST/+9"
                    default_timezone "JST"
                    // Test color settings.
                    background_color "#0080a0"
                    foreground_color "#ffffff"
                    pane_color "#383e5a"
                }
            }
        }
        children
        pane size=2 borderless=true {
            plugin location="zellij:status-bar" {
                supermode true
            }
        }
    }
}

次のスクショの 1行目のように、タブバーと時計を横並びに…などできる。(ただしウインドウサイズを変えるとボーダーがでてきたり、現在のところ少しバギー)

その他

もし Zellij が panic で起動しない場合はログを確認するのと、よく分からなければ次のテンポラリーファイルを削除して再起動してみます。(マルチユーザ使用の場合は気を付けて)

$ rm -Rf ~/.cache/zellij/*
$ rm -Rf /tmp/zellij*

Zellij 起動中に新しい Zellij を cargo install-update するとセッションがアップグレードできずに起動しない場合があります。 rm -Rf ~/.cache/zellij/* で修正できるはずです。

https://github.com/zellij-org/zellij/issues/2553

zellij does not start anymore #2553

ERROR  |zellij_utils::errors::not| 2023-06-17 18:54:54.876 [async-std/runti] [/home/amtoine/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/zellij-utils-0.36.0/src/errors.rs:585]: Panic occured:
             thread: async-std/runtime
             location: At /home/amtoine/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/rkyv-0.7.42/src/impls/core/mod.rs:267:17
             message: assertion failed: !result.is_null() 

おわりに

今回 Zellij のプラグインをかいていたら、昔よく VZ Editor のマクロをかいていたことを思い出しました。。Zellij 拡張も テトリスやスネークゲームなどもつくれそうな雰囲気です。

これだけ拡張性が高いと心強いですね。良ければお試しください。

関連

M5Stack RCA Module の I2S PCM5102A と Rust ESP32 xtensa-esp32-espidf ビルド

M5Stack RCA Module に搭載されている I2S (PCM5102APWR) を、Rust で生成した PCM 波形で発音させたメモです。

波形の生成は libymfm.wasm として WebAssembly 向けに作成している Rust 製のシーケンサーとサウンドチップエミュレーションをそのまま esp-idf に持ってきて xtensa-esp32-espidf ビルドしています。また、libymfm.wasm は C++ でつくられた FM 音源エミュレータの ymfm もリンクしています。

ESP32 Xtensa の Rust は初挑戦でしたが、確認した範囲で問題なくすんなり動作しました。ESP32 Xtensa Rust ツールチェインの準備やビルドは大変な印象がありましたが、昨今は esp-rs の各プロジェクトを組み合わせることで簡単に設定できるようになっています。

ちなみに ESP32-S3 開発ボードでも軽く動かしてみましたが問題なさそうです。ESP-S3 では PSRAM 80MHz Octa 設定にしているのが効いたか、波形生成は 1.4 倍程度高速でした。(Rust モジュールの細かなメモリ配置については未調査で課題としています)

ということで、この記事には以下の内容が含まれます。

  • I2S PCM5102APWR のイニシャライズ方法
  • ESP32 Xtensa Rust のビルド
実は M5Stack のスタックするモジュールを初めて買ったので、ボトムがないと寂しいことになると知らなかったのは内緒です。でも手軽に接続できてかっこいいです!

ソースコードは以下のリポジトリから見ることができます。詳しくはソースを見ていただくのが早いかもしれません。

https://github.com/h1romas4/m5stack-chipstream

This is a test to port C++’s ymfm and Rust’s vgmplay to ESP32(Xtensa).

Rust でつくられた VGM パーサーと SEGAPCM エミュレーションで I2S を発音させてるデモ:(残念ながら今回のビルドでは、ymfm の FM 音源エミュレーションは ESP32 で処理速度が間に合いませんでした

I2S PCM5102APWR のイニシャライズ

M5Stack Core2 に接続した RCA Module の PCM5102A の i2s_config と pin_config は以下のように設定すると良いようです。

// i2s_driver_install
i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate = sample_rate,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S),
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = dma_buf_count,
    .dma_buf_len = dma_buf_len,
    .use_apll = false,
    .tx_desc_auto_clear = true,
    .fixed_mclk = I2S_PIN_NO_CHANGE
};
ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_1, &i2s_config, 0, NULL));

// i2s_set_pin
i2s_pin_config_t i2s_pin_config = {
    .mck_io_num = GPIO_NUM_0,
    .bck_io_num = GPIO_NUM_19,
    .ws_io_num = GPIO_NUM_0,
    .data_out_num = GPIO_NUM_2,
    .data_in_num = I2S_PIN_NO_CHANGE
};
ESP_ERROR_CHECK(i2s_set_pin(I2S_NUM_1, &i2s_pin_config));

communication_format は必ず I2S_COMM_FORMAT_STAND_I2S を設定のこと。

うっかり I2S_COMM_FORMAT_STAND_MSB を設定するとなんとなく発音するものの、送信する PCM の先頭 1bit が無視されるような動きになってややはまり。(-32768, 32767 のフルスイング矩形波が正しく発音しなくて気が付きました…)

bits_per_sampleI2S_BITS_PER_SAMPLE_16BITchannel_formatI2S_CHANNEL_FMT_RIGHT_LEFT することで、int16_t のステレオ PCM 形式になります。

DMA バッファは、波形生成側のプリレンダのバッファリングに合わせて dma_buf_len を 1024 で dma_buf_count を 32個設定して動作させています。一度に扱うサンプル数としては int16_t ステレオにて 256 です。

今回のオーディオ系の実装ですが、波形生成を ESP32 の core 0 で、I2S への PCM の送信を core 1 と FreeRTOS タスクを分割して実行しています。なお、i2s_write にはほとんど時間がかからないようですので、波形生成は core 1 に持ってきても影響ないかもしれません。

タスク間の通信には、esp-idf の FreeRTOS Additions になっている RingBuffer の Byte buffers を介して PCM データの送受信を行っています。

FreeRTOS AdditionsRing Buffers

Byte buffers do not store data as separate items. All data is stored as a sequence of bytes, and any number of bytes can be sent or retrieved each time. Use byte buffers when separate items do not need to be maintained (e.g. a byte stream).

ESP32 Xtensa Rust のビルド方法

Xtensa の Rust ツールチェインは espup で導入するのが簡単でした。Windows の場合は WSL2 の Ubuntu 22.04 を使うと便利かもです。(sysroot は esp-idf 4.4.3 を使っています)

詳しくはリポジトリに GitHub Actions のビルドを入れていますの参考にしてください。

https://github.com/esp-rs/espup

Tool for installing and maintaining Espressif Rust ecosystem.

espup で Rust を導入すると ~/.rustup/toolchains/esp に Xtensa の Rust が導入されます。また .espressif/tools/xtensa-esp32-elf-clang に clang が入ります。

$ ls -laF ~/.rustup/toolchains/esp/bin/
合計 58120
drwxr-xr-x 2 hiromasa hiromasa     4096  2月 19 15:56 ./
drwxr-xr-x 7 hiromasa hiromasa     4096  2月 19 15:56 ../
-rwxr-xr-x 1 hiromasa hiromasa 21897920  2月 19 15:56 cargo*
-rwxr-xr-x 1 hiromasa hiromasa   954888  2月 19 15:56 cargo-clippy*
-rwxr-xr-x 1 hiromasa hiromasa  1796976  2月 19 15:56 cargo-fmt*
-rwxr-xr-x 1 hiromasa hiromasa 11781088  2月 19 15:56 clippy-driver*
-rwxr-xr-x 1 hiromasa hiromasa      759  2月 19 15:56 rust-gdb*
-rwxr-xr-x 1 hiromasa hiromasa     1933  2月 19 15:56 rust-gdbgui*
-rwxr-xr-x 1 hiromasa hiromasa     1072  2月 19 15:56 rust-lldb*
-rwxr-xr-x 1 hiromasa hiromasa    17264  2月 19 15:56 rustc*
-rwxr-xr-x 1 hiromasa hiromasa 11124040  2月 19 15:56 rustdoc*
-rwxr-xr-x 1 hiromasa hiromasa 11906968  2月 19 15:56 rustfmt*
$ ls -laF ~/.espressif/tools/xtensa-esp32-elf-clang/esp-15.0.0-20221201-x86_64-unknown-linux-gnu/esp-clang
合計 44
drwxr-xr-x 11 hiromasa hiromasa 4096  2月 19 15:57 ./
drwxr-xr-x  3 hiromasa hiromasa 4096  2月 19 15:57 ../
drwxr-xr-x  2 hiromasa hiromasa 4096  2月 19 15:57 bin/
drwxr-xr-x  4 hiromasa hiromasa 4096  2月 19 15:57 include/
drwxr-xr-x  8 hiromasa hiromasa 4096  2月 19 15:57 lib/
drwxr-xr-x  2 hiromasa hiromasa 4096  2月 19 15:57 libexec/
drwxr-xr-x  5 hiromasa hiromasa 4096  2月 19 15:57 riscv32-esp-elf/
drwxr-xr-x  9 hiromasa hiromasa 4096  2月 19 15:57 share/
drwxr-xr-x  5 hiromasa hiromasa 4096  2月 19 15:57 xtensa-esp32-elf/
drwxr-xr-x  5 hiromasa hiromasa 4096  2月 19 15:57 xtensa-esp32s2-elf/
drwxr-xr-x  5 hiromasa hiromasa 4096  2月 19 15:57 xtensa-esp32s3-elf/

ちなみに espup でツールチェインを入れると、esp-idf とは別のディレクトリ名に同バージョンの gcc が入るようです。(?)

さて、今回は Rust は波形生成をするライブラリの形で、C/C++ の Arduino Loop をメインとしたかったので、”esp-idf first” という構成としています。components 配下に Rust のプロジェクトをおいて、いつも通り idf.py build すると Rust ごとビルドしてくれます。

cargo generate で以下のテンプレートを cmake 指定してビルドスクリプトやディレクトリストラクチャーをつくっています。(引数を cargo にすると Rust 中心の構成になります)

https://github.com/esp-rs/esp-idf-template

cargo generate https://github.com/esp-rs/esp-idf-template cmake

esp-idf first のビルドでは components の下にいつも通りコンポーネントを配置し、CMakeLists.txt から external project として Rust を追加してビルドする動作するようにつくってくれます。

ビルドさえできてしまえば、あとは std 環境の Rust で、今回は外部ライブラリとしてバイナリーパーサ nom や JSON シリアライズ serde などを入れていますが、そのまま動作しました。

一点、Rust から返した *const i16 (16bit) の RAW ポインターを C 側から読むとメモリーの状態によって 1 byte (?) ズレるような動作がありました。いったん C からポインターを渡して Rust 側から書き込むことで修正していますが、Xtensa 特有なのか自分のミスなのかまだ詳しく原因を調査できていません。(Rust 構造体内の配列ポインターなのですがアライメント関連?)

というわけで、ESP32 で Rust std が呼べる。とても便利す。

Rust の特定モジュールを IRAM に載せたり、ヒープアロケータの SRAM/PSRAM コントロール(できる?)など未調査の部分もありますので、引き続きやってみたいと思います。

関連