pengantar
LV2 adalah standar terbuka untuk membuat plugin efek suara. Ini diyakini terutama ditujukan untuk Linux, meskipun tidak ada batasan untuk penggunaannya di sistem lain. Sebelumnya, sudah ada dua standar serupa di Linux - LADSPA dan DSSI. Yang pertama ditujukan terutama untuk memproses sinyal audio dan secara praktis tidak dapat bekerja dengan data MIDI. Sebaliknya, yang kedua dipahami sebagai standar untuk penyintesis virtual.
Nama LV2 itu sendiri adalah singkatan dari LADSPA versi 2 , ini adalah versi standar yang baru dan lebih baik. Tidak seperti pendahulunya, ini memungkinkan Anda untuk memproses data audio, streaming midi, membuat antarmuka pengguna apa pun, dan bertukar data apa pun dengan aplikasi host. Standar juga mendukung mekanisme ekstensi. Berkat ini, LV2 dapat menawarkan sejumlah fitur tambahan: satu set preset "pabrik", status penyimpanan, logging. Secara teori, pengguna dapat membuat add-on mereka sendiri. Dokumentasi terperinci dengan contoh ada di http://lv2plug.in
Organisasi
Pasti banyak yang familiar dengan standar VST yang populer. Dalam kasusnya, plugin dan sumber daya terkait biasanya terdapat dalam satu pustaka tautan dinamis (file DLL). Lebih dari satu file hampir selalu digunakan dalam standar LV2. Standar menggunakan konsep bundel . Saya belum dapat menemukan apakah ada padanan bahasa Rusia untuk istilah ini. Bundel adalah direktori dalam sistem file tempat semua file yang terkait dengan plugin ini ditempatkan. Menurut definisi dari dokumentasi: "Bundel LV2 adalah direktori yang berisi file manifest.ttl di tingkat atas . "Merupakan kebiasaan untuk menamai direktori sehingga namanya sama dengan nama plugin, misalnya amsynth.lv2 atau triceratops.lv2, tetapi nama apa pun diperbolehkan. Lokasi jalur bundel yang ditentukan dalam variabel sistem LV2_PATH (baik ditentukan secara langsung dalam pengaturan aplikasi host). Beberapa plugin dapat ditempatkan dalam satu bundel sekaligus.
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.
Bagian terpenting yang membentuk suara terletak di dalam loop dalam fungsi run (). Status variabel soundOn menunjukkan apa yang akan ditulis ke port keluaran: gelombang sinus atau nol. (Sebenarnya, menggunakan currentSample untuk menyimpan posisi saat ini salah. Cepat atau lambat akan meluap dan jeda akan muncul di gelombang sinus. Tapi untuk demonstrasi akan bekerja begitu saja).