前書き
LV2は、効果音プラグインを作成するためのオープンスタンダードです。他のシステムでの使用に制限はありませんが、主にLinuxを対象としていると考えられています。それ以前は、LinuxにはLADSPAとDSSIの2つの同様の標準がすでにありました。それらの最初のものは主にオーディオ信号を処理することを目的としており、実際にはMIDIデータを処理できませんでした。それどころか、2つ目は、仮想シンセサイザーの標準として考案されました。
LV2という名前自体は、LADSPAバージョン2の略語であり、標準の新しい改良バージョンです。以前のバージョンとは異なり、オーディオデータ、midiストリームの処理、任意のユーザーインターフェイスの作成、およびホストアプリケーションとの任意のデータの交換が可能です。この規格は、拡張メカニズムもサポートしています。このおかげで、LV2は、「ファクトリ」プリセットのセット、状態の保存、ロギングなど、多くの追加機能を提供できます。理論的には、ユーザーは独自のアドオンを作成できます。例を含む詳細なドキュメントは、http://lv2plug.inにあります。
組織
確かに多くの人が人気のあるVST標準に精通しています。彼の場合、プラグインと関連リソースは通常、単一のダイナミックリンクライブラリ(DLLファイル)に含まれています。 LV2標準では、ほとんどの場合、複数のファイルが使用されます。この規格では、バンドルの概念を使用しています。この用語に相当するロシア語があるかどうかはわかりませんでした。バンドルは、このプラグインに関連するすべてのファイルが配置されるファイルシステム内のディレクトリです。ドキュメントの定義によると、「LV2バンドルは、最上位のmanifest.ttlファイルを含むディレクトリです。」ディレクトリには、プラグインの名前と一致するように名前を付けるのが通例です(amsynth.lv2やtriceratops.lv2など)が、任意の名前を使用できます。システム変数LV2_PATHで指定されたパスの場所をバンドルします(ホストアプリケーションの設定で直接定義されます)。複数のプラグインを一度に1つのバンドルに配置できます。
URI. , , . URI , . , , . URI: ; URI . http://example.org/. lv2ls.
manifest.ttl , , . - , ( ). , manifest.ttl Turtle. ttl-, manifest.ttl ( ). , LV2.
(UI). , ( ) . , . . - . UI , . , .
- . ( ) . :
AudioPort — . -. float.
ControlPort — , . — UI .
EventPort — ( MIDI-)
CVPort — (Control Voltage). «» : (VCO), (VCF), (VCA)
ttl-. — (index) (symbol), . . , , .
, , . ControlPort . -. , , .
LV2 — . ( , , ), , . . , (, memcpy()). - Utilities Forge. , .
LV2- . , ( LV2_Descriptor).
instantiate() - , . , .
connect_port() - . , . void *. , run().
activate() - . , , , connect_port().
run() - . , . , , .
deactivate() - activate(). , run() activate(). .
cleanup() - . .
extension_data() - , . URI , .
, midi-, . example URI http://example.org
, manifest.ttl, -.
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://example.org>
a lv2:Plugin, lv2:InstrumentPlugin ;
lv2:binary <example.so> ;
rdfs:seeAlso <example.ttl> .
, . URI . 5 , ( https://lv2plug.in/ns/lv2core/lv2core.html). , example.ttl, .
:
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
<http://example.org>
a lv2:Plugin, lv2:InstrumentPlugin ;
doap:name "Example" ;
lv2:requiredFeature urid:map ;
lv2:port [
a lv2:InputPort, atom:AtomPort ;
atom:bufferType atom:Sequence ;
atom:supports atom:Sequence, midi:MidiEvent ;
lv2:index 0 ;
lv2:symbol "in_midi" ;
lv2:name "Midi input" ;
], [
a lv2:AudioPort, lv2:OutputPort ;
lv2:index 1 ;
lv2:symbol "out" ;
lv2:name "Out"
] .
, , . . lv2:requiredFeature , ( optionalFeature). , , . requiredFeature instantiate(). , . / . ( , , , ).
13, . — midi ( lv2:InputPort lv2:OutputPort). lv2:AudioPort , atom:AtomPort , Atom ( , ControlPort , ).
, . lv2:index lv2:symbol. , connect_port(), . «» lv2:name. symbol . , .
:
#include <math.h>
#include <stdlib.h>
#include <stdbool.h>
#include <lv2/lv2plug.in/ns/lv2core/lv2.h>
#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
#include <lv2/lv2plug.in/ns/ext/atom/util.h>
#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
#include <lv2/lv2plug.in/ns/ext/midi/midi.h>
#define MURI "http://example.org"
enum Ports {
IN_MIDI,
OUT
};
typedef struct {
LV2_Atom_Sequence *midiPort;
float *outPort;
int rate;
bool soundOn;
int currentSample;
LV2_URID midiEvent;
} Plugin;
static LV2_Handle
instantiate(const LV2_Descriptor* descriptor,
double rate,
const char* bundle_path,
const LV2_Feature* const* features) {
Plugin *self = (Plugin *) malloc(sizeof(Plugin));
self->rate = rate;
self->currentSample = 0;
self->soundOn = false;
LV2_URID_Map* map = NULL;
for (int i = 0; features[i]; ++i) {
if (!strcmp(features[i]->URI, LV2_URID__map)) {
map = (LV2_URID_Map*)features[i]->data;
}
}
if (map == NULL) {
return NULL;
}
self->midiEvent = map->map(map->handle, LV2_MIDI__MidiEvent);
return (LV2_Handle)self;
}
static void connect_port(LV2_Handle instance,
uint32_t port,
void* data) {
Plugin *self = (Plugin *) instance;
switch (port) {
case IN_MIDI:
self->midiPort = (LV2_Atom_Sequence*) data;
break;
case OUT:
self->outPort = (float*) data;
break;
}
}
void processEvent(LV2_Atom_Event *event, Plugin *self) {
if (event->body.type != self->midiEvent) {
return;
}
const uint8_t* const msg = LV2_ATOM_BODY(&(event->body));
LV2_Midi_Message_Type type = lv2_midi_message_type(msg);
switch(type) {
case LV2_MIDI_MSG_NOTE_ON:
self->soundOn = true;
break;
case LV2_MIDI_MSG_NOTE_OFF:
self->soundOn = false;
break;
}
}
static void run(LV2_Handle instance, uint32_t sample_count) {
Plugin *self = (Plugin *) instance;
LV2_ATOM_SEQUENCE_FOREACH(self->midiPort, event) {
processEvent(event, self);
}
for (uint32_t i = 0; i < sample_count; i++) {
if (self->soundOn) {
self->outPort[i] = sinf(2 * M_PI * 440.0 * self->currentSample / self->rate);
} else {
self->outPort[i] = 0.0;
}
self->currentSample++;
}
}
static void cleanup(LV2_Handle instance) {
free(instance);
}
static const LV2_Descriptor descriptor = {
MURI,
instantiate,
connect_port,
NULL,
run,
NULL,
cleanup,
NULL
};
LV2_SYMBOL_EXPORT
const LV2_Descriptor*
lv2_descriptor(uint32_t index) {
switch (index) {
case 0:
return &descriptor;
default:
return NULL;
}
}
, , LV2_Descriptor lv2_descriptor(). URI , « ». , - , NULL. lv2_descriptor() - , . . , .
, Plugin. , LV2 . — LV2_Handle void *, - . — instatntiate(). , . , . map, URI . midi- . LV2_MIDI__MidiEvent .
, , . connect_port , ttl- . ( ) , . Plugin.
, run, . sample_count — , ( , , ). midi-, LV2_ATOM_TUPLE_FOREACH. , .
processEvent(). , midi-. , map . LV2_Atom_Event , LV2_ATOM_BODY. midi , «» . . , soundOn Plugin.
サウンドを形成する最も重要なセクションは、run()関数のループ内にあります。soundOn変数の状態は、出力ポートに書き込まれるもの(正弦波またはゼロ)を示します。(実際には、currentSampleを使用して現在の位置を保存するのは間違っています。遅かれ早かれオーバーフローし、正弦波にブレークが表示されます。ただし、デモンストレーションでは、同じように機能します)。