pgpl/tests/plugin.cpp

426 lines
13 KiB
C++
Raw Normal View History

2025-10-03 20:16:11 +02:00
#include "clap/clap.h"
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <pgpl.h>
#include <stdio.h>
#include <string>
#include <time.h>
#include <vector>
static std::string *font_path;
struct Note {
int32_t key;
int32_t note_id;
int32_t channel;
float phase;
};
class PluginGUI {
public:
PluginGUI() {
strcpy(counter_buffer, "0");
counter = 0;
font = pgpl_font_create_from_file(font_path->c_str(), 256);
pgpl_gui_theme_configure(&theme, pgpl_color_create(255, 255, 255, 255), 48,
font);
gui = pgpl_gui_create("PGPL Test", 320, 320, 0, 0, &theme, this);
pgpl_gui_widget_add(gui, create_main_view());
pgpl_gui_run_and_show(gui, false);
}
~PluginGUI() { pgpl_font_destroy(NULL, font); }
inline PGPL_WindowThread *get_window() { return gui->window_thread; }
private:
static void on_counter_button_click(void *data) {
PluginGUI *app = (PluginGUI *)data;
app->counter++;
snprintf(app->counter_buffer, sizeof(app->counter_buffer), "%d",
app->counter);
if (app->counter % 10 == 0) {
pgpl_gui_theme_configure(
&app->gui->theme,
pgpl_color_create(rand() % 256, rand() % 256, rand() % 256, 255), 48,
app->gui->theme.font);
}
}
PGPL_GuiWidget *create_main_view() {
PGPL_GuiButtonWidget *counter_button;
PGPL_GuiTextWidget *counter;
column_layout = pgpl_gui_container_layout_create(
PGPL_GUI_CONTAINER_LAYOUT_DIRECTION_VERTICAL);
counter_button =
pgpl_gui_button_widget_create("Click me!", on_counter_button_click);
counter = pgpl_gui_text_widget_create(counter_buffer);
counter->parent.font_size = PGPL_GUI_FONT_SIZE_TITLE;
counter->parent.background = false;
counter->parent.border = false;
pgpl_gui_container_layout_widget_add(column_layout, &counter->parent);
pgpl_gui_container_layout_widget_add(column_layout,
&counter_button->parent);
return (PGPL_GuiWidget *)column_layout;
}
PGPL_GuiContainerLayout *column_layout;
PGPL_Gui *gui;
PGPL_GuiTheme theme;
PGPL_Font *font;
int counter;
char counter_buffer[128];
};
class PluginState {
public:
void process_event(const clap_event_header_t *event) {
if (event->space_id == CLAP_CORE_EVENT_SPACE_ID) {
if (event->type == CLAP_EVENT_NOTE_OFF ||
event->type == CLAP_EVENT_NOTE_CHOKE) {
const clap_event_note_t *note_event = (const clap_event_note_t *)event;
for (size_t i = 0; i < this->notes.size(); i++) {
Note &note = this->notes[i];
if (note_event->note_id == note.note_id ||
note_event->note_id == -1) {
this->notes.erase(this->notes.begin() + i--);
}
}
} else if (event->type == CLAP_EVENT_NOTE_ON) {
const clap_event_note_t *note_event = (const clap_event_note_t *)event;
this->notes.push_back(
{note_event->key, note_event->note_id, note_event->channel, 0.0f});
}
}
}
void render_audio(float *outputL, float *outputR, uint32_t length) {
for (uint32_t index = 0; index < length; index++) {
float sum = 0.0f;
for (size_t i = 0; i < this->notes.size(); i++) {
Note &note = this->notes[i];
sum += sinf(2.0f * 3.14159f * 440.0f *
exp2f((note.key - 81.0f) / 12.0f) * note.phase) *
0.2f;
note.phase += 1.0f / sample_rate;
note.phase -= floorf(note.phase);
}
outputL[index] = sum;
outputR[index] = sum;
}
}
inline void set_sample_rate(uint32_t sample_rate) {
this->sample_rate = sample_rate;
}
inline void create_window() { gui = new PluginGUI(); }
inline void destroy_window() { delete gui; }
inline PGPL_WindowThread *get_window() {
if (gui) {
return gui->get_window();
}
return nullptr;
}
private:
uint32_t sample_rate;
std::vector<Note> notes;
PluginGUI *gui;
};
static const char *features[] = {
CLAP_PLUGIN_FEATURE_INSTRUMENT,
CLAP_PLUGIN_FEATURE_SYNTHESIZER,
CLAP_PLUGIN_FEATURE_STEREO,
NULL,
};
static const clap_plugin_descriptor_t plugin_descriptor = {CLAP_VERSION_INIT,
"pluto.pgpl.test",
"PGPL Plugin Test",
"Patrick_Pluto",
"",
"",
"",
"1.0.0",
"Testing",
features};
static const clap_plugin_note_ports_t note_ports = {
[](const clap_plugin_t *, bool is_input) -> uint32_t {
return is_input ? 1 : 0;
},
[](const clap_plugin_t *, uint32_t index, bool isInput,
clap_note_port_info_t *info) -> bool {
if (!isInput || index)
return false;
info->id = 0;
info->supported_dialects = CLAP_NOTE_DIALECT_CLAP;
info->preferred_dialect = CLAP_NOTE_DIALECT_CLAP;
strncpy(info->name, "Note Input", sizeof(info->name));
return true;
},
};
static const clap_plugin_audio_ports_t audio_ports = {
[](const clap_plugin_t *, bool is_input) -> uint32_t {
return is_input ? 0 : 1;
},
[](const clap_plugin_t *, uint32_t index, bool isInput,
clap_audio_port_info_t *info) -> bool {
if (isInput || index)
return false;
info->id = 0;
info->channel_count = 2;
info->flags = CLAP_AUDIO_PORT_IS_MAIN;
info->port_type = CLAP_PORT_STEREO;
info->in_place_pair = CLAP_INVALID_ID;
strncpy(info->name, "Audio Output", sizeof(info->name));
return true;
},
};
static const clap_plugin_gui_t gui = {
[](const clap_plugin_t *, const char *api, bool is_floating) -> bool {
#ifdef __WIN32__
if (strcmp(api, CLAP_WINDOW_API_WIN32) == 0 && !is_floating) {
#else
if (strcmp(api, CLAP_WINDOW_API_X11) == 0 && !is_floating) {
#endif
return true;
}
return false;
},
[](const clap_plugin_t *, const char **api, bool *is_floating) -> bool {
#ifdef __WIN32__
*api = CLAP_WINDOW_API_WIN32;
#else
*api = CLAP_WINDOW_API_X11;
#endif
*is_floating = false;
return true;
},
[](const clap_plugin_t *plugin, const char *api, bool is_floating) -> bool {
#ifdef __WIN32__
if (strcmp(api, CLAP_WINDOW_API_WIN32) == 0 && !is_floating) {
#else
if (strcmp(api, CLAP_WINDOW_API_X11) == 0 && !is_floating) {
#endif
PluginState *state = (PluginState *)plugin->plugin_data;
state->create_window();
return true;
}
return false;
},
[](const clap_plugin_t *plugin) {
PluginState *state = (PluginState *)plugin->plugin_data;
pgpl_window_thread_destroy(state->get_window());
state->destroy_window();
},
[](const clap_plugin_t *plugin, double scale) -> bool {
PluginState *state = (PluginState *)plugin->plugin_data;
pgpl_window_thread_set_scale(state->get_window(), scale);
return true;
},
[](const clap_plugin_t *plugin, uint32_t *width, uint32_t *height) -> bool {
PluginState *state = (PluginState *)plugin->plugin_data;
pgpl_window_thread_get_size(state->get_window(), width, height);
return true;
},
[](const clap_plugin_t *) -> bool { return true; },
[](const clap_plugin_t *, clap_gui_resize_hints_t *) -> bool {
return false;
},
[](const clap_plugin_t *, uint32_t *, uint32_t *) -> bool { return true; },
[](const clap_plugin_t *plugin, uint32_t width, uint32_t height) -> bool {
PluginState *state = (PluginState *)plugin->plugin_data;
pgpl_window_thread_set_size(state->get_window(), width, height);
return true;
},
[](const clap_plugin_t *plugin, const clap_window_t *window) -> bool {
PluginState *state = (PluginState *)plugin->plugin_data;
#ifdef __WIN32__
pgpl_window_thread_reparent_to(state->get_window(),
(void *)window->win32);
#else
pgpl_window_thread_reparent_to(state->get_window(), (void *)&window->x11);
#endif
return true;
},
[](const clap_plugin_t *plugin, const clap_window_t *window) -> bool {
PluginState *state = (PluginState *)plugin->plugin_data;
#ifdef __WIN32__
pgpl_window_thread_set_transient(state->get_window(),
(void *)window->win32);
#else
pgpl_window_thread_set_transient(state->get_window(),
(void *)&window->x11);
#endif
return true;
},
[](const clap_plugin_t *plugin, const char *title) {
PluginState *state = (PluginState *)plugin->plugin_data;
pgpl_window_thread_set_title(state->get_window(), title);
},
[](const clap_plugin_t *plugin) -> bool {
PluginState *state = (PluginState *)plugin->plugin_data;
pgpl_window_thread_show(state->get_window());
return true;
},
[](const clap_plugin_t *plugin) -> bool {
PluginState *state = (PluginState *)plugin->plugin_data;
pgpl_window_thread_hide(state->get_window());
return true;
},
};
static const clap_plugin_t plugin_class = {
&plugin_descriptor,
nullptr,
[](const clap_plugin *) -> bool { return true; },
[](const clap_plugin *plugin) {
delete (PluginState *)plugin->plugin_data;
delete plugin;
},
[](const clap_plugin *plugin, double sample_rate, uint32_t,
uint32_t) -> bool {
PluginState *state = (PluginState *)plugin->plugin_data;
state->set_sample_rate(sample_rate);
return true;
},
[](const clap_plugin *) {},
[](const clap_plugin *) -> bool { return true; },
[](const clap_plugin *) {},
[](const clap_plugin *) {},
[](const clap_plugin *plugin,
const clap_process_t *process) -> clap_process_status {
PluginState *state = (PluginState *)plugin->plugin_data;
uint32_t event_amount = process->in_events->size(process->in_events);
uint32_t event_index = 0;
uint32_t next_event_frame = event_amount ? 0 : process->frames_count;
for (uint32_t i = 0; i < process->frames_count;) {
while (event_index < event_amount && next_event_frame == i) {
const clap_event_header_t *event =
process->in_events->get(process->in_events, event_index);
if (event->time != i) {
next_event_frame = event->time;
break;
}
state->process_event(event);
event_index++;
if (event_index == event_amount) {
next_event_frame = process->frames_count;
break;
}
}
state->render_audio(process->audio_outputs[0].data32[0] + i,
process->audio_outputs[0].data32[1] + i,
next_event_frame - i);
i = next_event_frame;
}
return CLAP_PROCESS_CONTINUE;
},
[](const clap_plugin *, const char *id) -> const void * {
if (0 == strcmp(id, CLAP_EXT_NOTE_PORTS))
return &note_ports;
if (0 == strcmp(id, CLAP_EXT_AUDIO_PORTS))
return &audio_ports;
if (0 == strcmp(id, CLAP_EXT_GUI))
return &gui;
return nullptr;
},
[](const clap_plugin *) {},
};
static const clap_plugin_factory_t plugin_factory = {
[](const clap_plugin_factory *) -> uint32_t { return 1; },
[](const clap_plugin_factory *,
uint32_t index) -> const clap_plugin_descriptor_t * {
return index == 0 ? &plugin_descriptor : nullptr;
},
[](const clap_plugin_factory *, const clap_host_t *host,
const char *plugin_id) -> const clap_plugin_t * {
if (!clap_version_is_compatible(host->clap_version) ||
strcmp(plugin_id, plugin_descriptor.id)) {
return nullptr;
}
clap_plugin_t *plugin = new clap_plugin_t(plugin_class);
plugin->plugin_data = new PluginState();
return plugin;
},
};
extern "C" const clap_plugin_entry_t clap_entry = {
CLAP_VERSION_INIT,
[](const char *plugin_path) -> bool {
pgpl_init();
srand(time(NULL));
font_path = new std::string(plugin_path + std::string(".ttf"));
return true;
},
[]() {
delete font_path;
pgpl_deinit();
},
[](const char *factory_id) -> const void * {
return strcmp(factory_id, CLAP_PLUGIN_FACTORY_ID) ? nullptr
: &plugin_factory;
},
};