前回の続きです。
前回は1か月分以上の進捗をまとめて書いたので、だいぶ長文になってしまいました。今回は、前回から一週間分の進捗です。
前回、PCM関連のToDoとして挙げた以下の項目を実装してみました。
・4点補間機能を持つPure Dataオブジェクト([tabread4~]など)への対応
・ステレオPCM対応
・PCM bank選択用変数
補間機能対応
サンプリングしたデータを読み出す際、Pure Dataではサンプルと次のサンプルの途中の値を補間によって求めることができます。名前が「4~」で終わっているオブジェクトはたいていこの目的で用意されています。
logue SDKではライブラリで提供されている1次補間(\(tx_0 + (1-t)x_1\))を使うことが多かったのですが、Pure Dataの補間アルゴリズムは3次(cubic)補間です。これは所与の4点を通る補間で、そのために本来のサンプルデータの他に、先頭に1サンプル、末尾に2サンプルのデータが追加で必要になります。
実用上で補間機能が重要になるのはウエーブテーブル音源を作る場合で、そのために用意されたオブジェクト[tabosc4~]では、サンプル数は「2の階乗+3」でなければなりません。
通常の(長い)サンプルを再生する場合は、補間の影響はそこまでシビアではありませんが、補間を使う方がS/N比は改善します。特に、サンプルをよりゆっくりと再生したり、一部を切り出して使うなどする場合は補間することが望ましいです。
追加のサンプルを前後に加えることはPure Dataだけでもできなくはないのですが、サンプルをテーブルに書き込む時に自動で追加できれば便利です。というわけで、サンプルのバンクやインデックス番号を指定する機能と同じ枠組みを使って、補間用サンプルの追加の機能をオン・オフするスイッチを作りました。
このスイッチは”guard”という名前で、[guard 1( というメッセージを”テーブル名_set”という変数(この変数はバンクやインデックスの指定と共通)に@hv_param付きでsendする([send tablename_set @hv_param])と、補間用サンプル追加の機能がオンになります。[guard 0( はこの機能をオフにします。デフォルトはオフです。
機能がオンの時、PCMデータはテーブルの先頭ではなく2番目から書き込まれます。先頭にはPCMデータの最後の値が書き込まれます。また、テーブルの末尾にはPCMデータの先頭の2つの値がコピーされます。

“テーブル名_size”には、テーブルに書き込まれるサンプルのデータ長が送られますが、guardがオンでもオフでも、データ長の値は変わりません。しかし、テーブル自体にはデータ長+3個のデータが書き込まれ、実際のデータはテーブルの先頭ではなく2番目から始まります。データ長の情報はあくまで元のサンプルデータそのもののサイズであり、テーブルのサイズではないということです。
ステレオPCM対応
drumlogueではモノラル/ステレオ両方のPCMデータが利用できます。本体に内蔵されているサンプルも、一部はステレオになっています。
一方、Pure DataにはステレオのPCMデータを扱うための標準的な方法はありません。基本的には、左右それぞれのチャネルについて1つずつテーブルが必要になります。
今回は1つのテーブルでモノラル・ステレオ両方に対応可能とするため、左右のチャネルのデータを1つのテーブルで持つことにします。その方法としてはインターリーブ(LRLR・・・と並べる)またはノンインターリーブ(LLL・・・RRR・・・と並べる)が考えられますが、上述のような補間処理がありうることを考えると、インターリーブ方式は選択できません。従って、ノンインターリーブ(あるいはPlanarとも呼ばれます)方式を採用しました。
モノラル/ステレオの切り替えは、これも”テーブル名_set”という変数へのsendで行います。属性”chan”を使って[chan 2( というメッセージを”テーブル名_set”変数にsendすると、チャネル数を2にするという指定になり、テーブルはステレオのPCM用(前半がLチャネル、後半がRチャネルに使われる)として扱われます。
使えるチャネル数は1または2で、デフォルトは1です。チャネル数が1で、かつテーブルに書き込むデータがステレオの場合は、各サンプルフレームについて(L+R)/2 がテーブルに書き込まれます。逆に、チャネル数が2でテーブルに書き込むデータがモノラルの場合は、同一のデータがLとRに書き込まれます。
このテーブルを再生するときは、前半(Lチャネル)と後半(Rチャネル)それぞれに読み出すオブジェクトを用意して、それらを同期させる必要があります。下図はそれを行うパッチの例です。

黄色の枠で囲っている部分の中に、[loadbang]でキックされる初期設定があります。[bank 4, guard 0, chan 2 ( となっていますので、テーブルsample_sはバンク番号4を使用、補間機能は不使用、テーブルのデータは2チャンネル(ステレオ)用となります。従って、bankとindexで指定されたサンプルは、上述の通り、モノラルの場合はテーブルに2回(前半と後半に)コピーされ、ステレオの場合は前半にLチャネル、後半にRチャネルがコピーされます。
[dac~]の直上に[tabread~]が二つあります。これは左がLチャネル、右がRチャネル用です。
その上に位置する[line~]がテーブルからの読み出し位置を制御しています。[line~]の出力値は0からサンプル数([r sample_size @hv_param]で受信)で、最大値に達するまでの時間はサンプル数/48 msecです(サンプリングレートが48KHzのため)。
左の[tabread~]は[line~]の出力をそのまま読み出し位置として受け取り、先頭からサンプル数-1までのサンプルを再生します。右の[tabread~]は、[line~]の出力にサンプル数分のオフセットが加算された読み出し位置になっていますので、サンプル数番目から最後までのサンプルを再生します。
ステレオ+補間機能対応
ステレオで、かつ補間機能を使う場合には、LチャネルとRチャネルのそれぞれで読み出し位置の修正が必要になります。Lチャネルは読み出し位置にオフセットを1加え、Rチャネルはそれにさらにサンプル数+Lチャネルの追加3サンプルをオフセットとして加えます。
下図はステレオ+補間機能+再生速度設定(1/2倍~2倍)を付けたPure Dataパッチの例です。動画は、これをhvccで変換したdrumlogue用ユニットを動作させています。

Pure Dataの補間機能付きオブジェクト(tabread4~など)およびステレオサンプルの読み込みにも対応しました。再生速度変更はPure Dataで行っています。 pic.twitter.com/FeSCq7hxYQ
— boochowp (@boochowp) December 27, 2025
PCM bank選択用変数
上のビデオにも出ていますが、バンクを番号ではなく名前で選択できるように、コードを追加しました。使い方は前回の記事で紹介したインデックス選択と同様で、”テーブル名_s”という名称(末尾に”_s”が付く)でテーブルを作成すると、”テーブル名_menuBank”という特別な変数が利用できるようになります。
この変数を@hv_param付きでreceiveすると、通常のパラメータと同様にノブで選択された値を受信できますが、その際、ノブには数値ではなくバンクの名称が表示されます。ディスプレイに表示されるパラメータ名は”BK:”+テーブル名の先頭4文字になります。
上に載せたパッチでは、”sample_bankMenu”というパラメータが使われているので、これに対応してノブにバンク名が表示されています。drumlogueでは、使えるバンクは0~6の7つで、それぞれ”CH(closed hihat)”、”OH(open hihat)”、”RS(rim shot)”、”CP(clap)”、”MISC”、”USER”、”EXP”という名称になっています。USERとEXPはユーザが自分のサンプルを登録することができます。(上のビデオで使っているドラムループは私がUSERバンクに追加したものです。)
テーブルをresizeできない件
今回悩まされたのは、テーブルのサイズを変更する部分です。どこの不具合なのか分かりませんが、サイズを現在よりも大きくしても、それがうまくhvccコンテキストに反映されません。内部的には新しいサイズでrealloc()、実際にはmalloc()+古い配列をfree()になると思われます。確認した範囲ではrealloc()に失敗している形跡は無いのですが、新しく確保されたメモリブロックにデータを書き込んでも、再生時には増えた分のデータが無音になってしまうという謎の症状になっています。
つまり、最初にPure Dataの[table]オブジェクトでテーブルを作成する際に指定したサイズを超えると、そのデータは再生できません。バウンダリチェックにひっかかっているのだとすると、そもそもサイズの情報が更新されていないのではないか、と考えたのですが、hvccのAPI経由でテーブルのサイズを取得すると正しい値が返ってきます。
drumlogueではなくPC上で、hvccコンテキストを動作させてみるコードを書いてみたのですが、そちらは正常に動作しました。なのでdrumlogueの環境の問題という可能性も残っていますが、なかなか追跡する手段が無いので、現状、この問題は棚上げにしています。つまり、テーブルをサンプルデータのサイズに合わせてリサイズすることはできず、最初に宣言したサイズよりも大きなサンプルは途中で切られてしまう、ということです。
【2026/1/2追記】
この件、いろいろ調べたところ、hvccが生成するコードは[tabread~]などを使ったテーブル読み出しにおいて、読み出し位置がテーブルの最大値を超えないように
読み出し位置=min(読み出し位置、テーブルサイズ)
という管理をしているのですが、このテーブルサイズがhv_table_setLength()では更新されないことが分かりました。以下はhvccのtabread~.pdの定義と思われますが、これを見るとテーブルのサイズはloadbangを契機に取得しており、テーブルがリサイズされても再取得はされないように見えます。

そのため、テーブルをリサイズしたあとは、対象のテーブルを読んでいるオブジェクトに対して、リサイズしたことを何らかの形で伝えてやる必要があります。
とりあえず、今は下図のように、[tabread~]に対して読み出し対象のテーブルをsetしなおす(同じテーブルを再度読み出し対象として指定する)と、うまくサイズが更新されることが分かっています。

ただ、これをPure Dataパッチの中に書かなければならないのは変なので、これ相当の処理をCのコードで実現する方が良さそうな気がしています。
他に方法がないか、まだ調査中です。
【追記終わり】
パラメータの値の表示スペースが狭い件
サンプルデータの名称をメニュー画面に表示させていますが、文字列は概ね8文字程度のスペースしかありません(等幅フォントではないので、もっと多く表示できる場合もあります)。これは数値ならまあ問題ないですが、サンプル選択に使うにはちょっと不便です。サンプル名は結構長いものが多く、先頭8文字が全く同じということも珍しくありません。
そのため、サンプル名の表示では、先頭3文字と末尾3文字を表示させて、途中は”..”で省略するようにしました。drumlogueのSP1/SP2では、長いサンプル名が表示できるので問題ないのですが、1画面に4パラメータという仕様上、やむを得ず苦肉の策となりました。


コメント