Merge pull request #78 from jpcima/midnam

Add the MIDNAM API, and the LV2 extension
This commit is contained in:
JP Cimalando 2020-02-26 16:07:57 +01:00 committed by GitHub
commit 3bfa573dc2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 15057 additions and 4 deletions

View file

@ -48,6 +48,9 @@ else()
target_link_libraries(sfizz-sndfile INTERFACE ${SNDFILE_LIBRARIES})
endif()
add_library(sfizz-pugixml STATIC "src/external/pugixml/src/pugixml.cpp")
target_include_directories(sfizz-pugixml PUBLIC "src/external/pugixml/src")
# If we build with Clang use libc++
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT ANDROID)
set(USE_LIBCPP ON CACHE BOOL "Use libc++ with clang")

View file

@ -13,7 +13,7 @@ set (LV2PLUGIN_TTL_SRC_FILES
)
add_library (${LV2PLUGIN_PRJ_NAME} SHARED ${PROJECT_NAME}.c ${LV2PLUGIN_TTL_SRC_FILES})
target_link_libraries (${LV2PLUGIN_PRJ_NAME} ${PROJECT_NAME}::${PROJECT_NAME})
target_include_directories(${LV2PLUGIN_PRJ_NAME} PRIVATE .)
target_include_directories(${LV2PLUGIN_PRJ_NAME} PRIVATE . external/ardour)
sfizz_enable_lto_if_needed (${LV2PLUGIN_PRJ_NAME})
# Remove the "lib" prefix, rename the target name and build it in the .lv build dir

64
lv2/atomic_compat.h Normal file
View file

@ -0,0 +1,64 @@
/*
SPDX-License-Identifier: ISC
Sfizz LV2 plugin
Copyright 2019-2020, Paul Ferrand <paul@ferrand.cc>
This file was based on skeleton and example code from the LV2 plugin
distribution available at http://lv2plug.in/
The LV2 sample plugins have the following copyright and notice, which are
extended to the current work:
Copyright 2011-2016 David Robillard <d@drobilla.net>
Copyright 2011 Gabriel M. Beddingfield <gabriel@teuton.org>
Copyright 2011 James Morris <jwm.art.net@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
Compiling this plugin statically against libsndfile implies distributing it
under the terms of the LGPL v3 license. See the LICENSE.md file for more
information. If you did not receive a LICENSE.md file, inform the current
maintainer.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#if defined(__cplusplus)
#include <atomic>
#elif defined(_MSC_VER)
#include <intrin.h>
typedef volatile int atomic_int;
inline int atomic_exchange(atomic_int *d, int x)
{
return _InterlockedExchange((volatile long *)d, (long)x);
}
inline void atomic_store(atomic_int *d, int x)
{
_InterlockedExchange((volatile long *)d, (long)x);
}
inline int atomic_load(const atomic_int *d)
{
return _InterlockedOr((volatile long *)d, 0);
}
#else
#include <stdatomic.h>
#endif

View file

@ -0,0 +1,282 @@
/*
Copyright 2016 Robin Gareus <robin@gareus.org>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _ardour_lv2_extensions_h_
#define _ardour_lv2_extensions_h_
#include "lv2/core/lv2.h"
/**
@defgroup lv2inlinedisplay Inline-Display
Support for displaying a miniaturized generic view
directly in the host's Mixer Window.
@{
*/
#define LV2_INLINEDISPLAY_URI "http://harrisonconsoles.com/lv2/inlinedisplay"
#define LV2_INLINEDISPLAY_PREFIX LV2_INLINEDISPLAY_URI "#"
#define LV2_INLINEDISPLAY__interface LV2_INLINEDISPLAY_PREFIX "interface"
#define LV2_INLINEDISPLAY__queue_draw LV2_INLINEDISPLAY_PREFIX "queue_draw"
#define LV2_INLINEDISPLAY__in_gui LV2_INLINEDISPLAY_PREFIX "in_gui"
/** Opaque handle for LV2_Inline_Display::queue_draw() */
typedef void* LV2_Inline_Display_Handle;
/** raw image pixmap format is ARGB32,
* the data pointer is owned by the plugin and must be valid
* from the first call to render until cleanup.
*/
typedef struct {
unsigned char *data;
int width;
int height;
int stride;
} LV2_Inline_Display_Image_Surface;
/** a LV2 Feature provided by the Host to the plugin */
typedef struct {
/** Opaque host data */
LV2_Inline_Display_Handle handle;
/** Request from run() that the host should call render() at a later time
* to update the inline display */
void (*queue_draw)(LV2_Inline_Display_Handle handle);
} LV2_Inline_Display;
/**
* Plugin Inline-Display Interface.
*/
typedef struct {
/**
* The render method. This is called by the host in a non-realtime context,
* usually the main GUI thread.
* The data pointer is owned by the plugin and must be valid
* from the first call to render until cleanup.
*
* @param instance The LV2 instance
* @param w the max available width
* @param h the max available height
* @return pointer to a LV2_Inline_Display_Image_Surface or NULL
*/
LV2_Inline_Display_Image_Surface* (*render)(LV2_Handle instance, uint32_t w, uint32_t h);
} LV2_Inline_Display_Interface;
/**
@}
*/
/**
@defgroup lv2automate Self-Automation
Support for plugins to write automation data via Atom Events
@{
*/
#define LV2_AUTOMATE_URI "http://ardour.org/lv2/automate"
#define LV2_AUTOMATE_URI_PREFIX LV2_AUTOMATE_URI "#"
/** an lv2:optionalFeature */
#define LV2_AUTOMATE_URI__can_write LV2_AUTOMATE_URI_PREFIX "canWriteAutomatation"
/** atom:supports */
#define LV2_AUTOMATE_URI__control LV2_AUTOMATE_URI_PREFIX "automationControl"
/** lv2:portProperty */
#define LV2_AUTOMATE_URI__controlled LV2_AUTOMATE_URI_PREFIX "automationControlled"
#define LV2_AUTOMATE_URI__controller LV2_AUTOMATE_URI_PREFIX "automationController"
/** atom messages */
#define LV2_AUTOMATE_URI__event LV2_AUTOMATE_URI_PREFIX "event"
#define LV2_AUTOMATE_URI__setup LV2_AUTOMATE_URI_PREFIX "setup"
#define LV2_AUTOMATE_URI__finalize LV2_AUTOMATE_URI_PREFIX "finalize"
#define LV2_AUTOMATE_URI__start LV2_AUTOMATE_URI_PREFIX "start"
#define LV2_AUTOMATE_URI__end LV2_AUTOMATE_URI_PREFIX "end"
#define LV2_AUTOMATE_URI__parameter LV2_AUTOMATE_URI_PREFIX "parameter"
#define LV2_AUTOMATE_URI__value LV2_AUTOMATE_URI_PREFIX "value"
/**
@}
*/
/**
@defgroup lv2license License-Report
Allow for commercial LV2 to report their
licensing status.
@{
*/
#define LV2_PLUGINLICENSE_URI "http://harrisonconsoles.com/lv2/license"
#define LV2_PLUGINLICENSE_PREFIX LV2_PLUGINLICENSE_URI "#"
#define LV2_PLUGINLICENSE__interface LV2_PLUGINLICENSE_PREFIX "interface"
#define LV2_PLUGINLICENSE__interface2 LV2_PLUGINLICENSE_PREFIX "interface2"
typedef struct _LV2_License_Interface {
/* @return -1 if no license is needed; 0 if unlicensed, 1 if licensed */
int (*is_licensed)(LV2_Handle instance);
/* @return a string copy of the licensee name if licensed, or NULL, the caller needs to free this */
char* (*licensee)(LV2_Handle instance);
/* @return a URI identifying the plugin-bundle or plugin for which a given license is valid */
const char* (*product_uri)(LV2_Handle instance);
/* @return human readable product name for the URI */
const char* (*product_name)(LV2_Handle instance);
/* @return link to website or webstore */
const char* (*store_url)(LV2_Handle instance);
/* interface2 ext: preferred location to install the license file, the caller needs to free this */
char* (*preferred_license_file_path)(LV2_Handle instance);
/* interface2 ext: currently used license file (if any, may be NULL), the caller needs to free this */
char* (*current_license_file_path)(LV2_Handle instance);
/* interface2 ext: free() allocated strings (licensee, license_file_paths) */
void (*free)(char*);
} LV2_License_Interface;
/**
@}
*/
/**
@defgroup lv2bypass Plugin-provided bypass
A port with the designation "processing#enable" must
control a plugin's internal bypass mode.
If the port value is larger than zero the plugin processes
normally.
If the port value is zero, the plugin is expected to bypass
all signals unmodified.
The plugin is responsible for providing a click-free transition
between the states.
(values less than zero are reserved for future use:
e.g click-free insert/removal of latent plugins.
Generally values <= 0 are to be treated as bypassed.)
lv2:designation <http://ardour.org/lv2/processing#enable> ;
@{
*/
#define LV2_PROCESSING_URI "http://ardour.org/lv2/processing"
#define LV2_PROCESSING_URI_PREFIX LV2_PROCESSING_URI "#"
#define LV2_PROCESSING_URI__enable LV2_PROCESSING_URI_PREFIX "enable"
/**
@}
*/
/**
@defgroup lv2routing plugin port/routing control
This is a "feature" to simplify per port meta-data of
http://lv2plug.in/ns/ext/port-groups/port-groups.html#source
Plugins using this feature provide a strong hint that the host
should always connect all audio output-ports.
This allows mono->stereo plugins to override strict_io rules.
@{
*/
#define LV2_ROUTING_URI "http://harrisonconsoles.com/lv2/routing"
#define LV2_ROUTING_PREFIX LV2_ROUTING_URI "#"
#define LV2_ROUTING__connectAllOutputs LV2_ROUTING_PREFIX "connectAllOutputs"
/**
@}
*/
/**
@defgroup lv2midnam MIDI Naming
@{
*/
#define LV2_MIDNAM_URI "http://ardour.org/lv2/midnam"
#define LV2_MIDNAM_PREFIX LV2_MIDNAM_URI "#"
#define LV2_MIDNAM__interface LV2_MIDNAM_PREFIX "interface"
#define LV2_MIDNAM__update LV2_MIDNAM_PREFIX "update"
typedef void* LV2_Midnam_Handle;
/** a LV2 Feature provided by the Host to the plugin */
typedef struct {
/** Opaque host data */
LV2_Midnam_Handle handle;
/** Request from run() that the host should re-read the midnam */
void (*update)(LV2_Midnam_Handle handle);
} LV2_Midnam;
typedef struct {
/** Query midnam document. The plugin
* is expected to return a null-terminated XML
* text which is a valid midnam desciption
* (or NULL in case of error).
*
* The midnam \<Model\> must be unique and
* specific for the given plugin-instance.
*/
char* (*midnam)(LV2_Handle instance);
/** The unique model id used ith the midnam,
* (or NULL).
*/
char* (*model)(LV2_Handle instance);
/** free allocated strings. The host
* calls this for every value returned by
* \ref midnam and \ref model.
*/
void (*free)(char*);
} LV2_Midnam_Interface;
/**
@}
*/
/**
@defgroup lv2bankpatch MIDI Bank/Patch Notifications
LV2 extension to allow a synth to inform a host about the
currentl used MIDI bank/program.
@{
*/
#define LV2_BANKPATCH_URI "http://ardour.org/lv2/bankpatch"
#define LV2_BANKPATCH_PREFIX LV2_BANKPATCH_URI "#"
#define LV2_BANKPATCH__notify LV2_BANKPATCH_PREFIX "notify"
typedef void* LV2_BankPatch_Handle;
/** a LV2 Feature provided by the Host to the plugin */
typedef struct {
/** Opaque host data */
LV2_BankPatch_Handle handle;
/** Info from plugin's run(), notify host that bank/program changed */
void (*notify)(LV2_BankPatch_Handle handle, uint8_t channel, uint32_t bank, uint8_t pgm);
} LV2_BankPatch;
/**
@}
*/
#endif

View file

@ -47,12 +47,16 @@
#include <lv2/log/logger.h>
#include <lv2/log/log.h>
#include <ardour/lv2_extensions.h>
#include <math.h>
#include <sfizz.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "atomic_compat.h"
#define DEFAULT_SFZ_FILE "/home/paul/Documents/AVL_Percussions/AVL_Drumkits_Percussion-1.0-Alt.sfz"
#define SFIZZ_URI "http://sfztools.github.io/sfizz"
#define SFIZZ_PREFIX SFIZZ_URI "#"
@ -90,6 +94,7 @@ typedef struct
LV2_URID_Unmap *unmap;
LV2_Worker_Schedule *worker;
LV2_Log_Log *log;
LV2_Midnam *midnam;
// Ports
const LV2_Atom_Sequence *control_port;
@ -145,6 +150,7 @@ typedef struct
int max_block_size;
int sample_counter;
float sample_rate;
atomic_int must_update_midnam;
} sfizz_plugin_t;
enum
@ -310,6 +316,9 @@ instantiate(const LV2_Descriptor *descriptor,
if (!strcmp((**f).URI, LV2_LOG__log))
self->log = (**f).data;
if (!strcmp((**f).URI, LV2_MIDNAM__update))
self->midnam = (**f).data;
}
// Setup the loggers
@ -684,6 +693,11 @@ run(LV2_Handle instance, uint32_t sample_count)
// Render the block
sfizz_render_block(self->synth, self->output_buffers, 2, (int)sample_count);
if (self->midnam && atomic_exchange(&self->must_update_midnam, 0))
{
self->midnam->update(self->midnam->handle);
}
}
static uint32_t
@ -770,6 +784,8 @@ sfizz_lv2_update_file_info(sfizz_plugin_t* self, const char* file_path)
lv2_log_note(&self->logger, "[sfizz] Number of masters: %d\n", sfizz_get_num_masters(self->synth));
lv2_log_note(&self->logger, "[sfizz] Number of groups: %d\n", sfizz_get_num_groups(self->synth));
lv2_log_note(&self->logger, "[sfizz] Number of regions: %d\n", sfizz_get_num_regions(self->synth));
atomic_store(&self->must_update_midnam, 1);
}
static LV2_State_Status
@ -1066,12 +1082,44 @@ work_response(LV2_Handle instance,
return LV2_WORKER_SUCCESS;
}
static char *
midnam_model(LV2_Handle instance)
{
char *model = malloc(64);
if (!model)
return NULL;
sprintf(model, "Sfizz LV2:%p", instance);
return model;
}
static char *
midnam_export(LV2_Handle instance)
{
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
char *model = midnam_model(instance);
if (!model)
return NULL;
char *xml = sfizz_export_midnam(self->synth, model);
free(model);
return xml;
}
static void
midnam_free(char *string)
{
free(string);
}
static const void *
extension_data(const char *uri)
{
static const LV2_Options_Interface options = {lv2_get_options, lv2_set_options};
static const LV2_State_Interface state = {save, restore};
static const LV2_Worker_Interface worker = {work, work_response, NULL};
static const LV2_Midnam_Interface midnam = {midnam_export, midnam_model, midnam_free};
// Advertise the extensions we support
if (!strcmp(uri, LV2_OPTIONS__interface))
@ -1080,6 +1128,8 @@ extension_data(const char *uri)
return &state;
else if (!strcmp(uri, LV2_WORKER__interface))
return &worker;
else if (!strcmp(uri, LV2_MIDNAM__interface))
return &midnam;
return NULL;
}

View file

@ -5,7 +5,7 @@
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
@prefix opts: <http://lv2plug.in/ns/ext/options#> .
@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
@prefix pg: <http://lv2plug.in/ns/ext/port-groups#> .
@prefix pprop: <http://lv2plug.in/ns/ext/port-props#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@ -14,6 +14,10 @@
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
@prefix work: <http://lv2plug.in/ns/ext/worker#> .
@prefix midnam: <http://ardour.org/lv2/midnam#> .
midnam:interface a lv2:ExtensionData .
midnam:update a lv2:Feature .
<#config>
a pg:Group ;
@ -65,6 +69,9 @@
lv2:optionalFeature lv2:hardRTCapable, opts:options ;
lv2:extensionData opts:interface, state:interface, work:interface ;
lv2:optionalFeature midnam:update ;
lv2:extensionData midnam:interface ;
patch:writable <@LV2PLUGIN_URI@:sfzfile> ;
lv2:port [

View file

@ -30,7 +30,7 @@ target_sources(sfizz_static PRIVATE ${SFIZZ_SOURCES} sfizz/sfizz_wrapper.cpp)
target_include_directories (sfizz_static PUBLIC .)
target_include_directories (sfizz_static PUBLIC external)
target_link_libraries (sfizz_static PUBLIC absl::strings absl::span)
target_link_libraries (sfizz_static PRIVATE sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile)
target_link_libraries (sfizz_static PRIVATE sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml)
add_library (sfizz::parser ALIAS sfizz_parser)
add_library (sfizz::sfizz ALIAS sfizz_static)
@ -47,7 +47,7 @@ if (SFIZZ_SHARED)
target_sources(sfizz_shared PRIVATE ${SFIZZ_SOURCES} sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp)
target_include_directories (sfizz_shared PRIVATE .)
target_include_directories (sfizz_static PRIVATE external)
target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile)
target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml)
target_compile_definitions(sfizz_shared PRIVATE SFIZZ_EXPORT_SYMBOLS)
set_target_properties (sfizz_shared PROPERTIES OUTPUT_NAME sfizz PUBLIC_HEADER "sfizz.h;sfizz.hpp")
set_property (TARGET sfizz_shared PROPERTY SOVERSION ${PROJECT_VERSION_MAJOR})

24
src/external/pugixml/LICENSE.md vendored Normal file
View file

@ -0,0 +1,24 @@
MIT License
Copyright (c) 2006-2019 Arseny Kapoulkine
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

74
src/external/pugixml/src/pugiconfig.hpp vendored Normal file
View file

@ -0,0 +1,74 @@
/**
* pugixml parser - version 1.10
* --------------------------------------------------------
* Copyright (C) 2006-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
* Report bugs and download new versions at https://pugixml.org/
*
* This library is distributed under the MIT License. See notice at the end
* of this file.
*
* This work is based on the pugxml parser, which is:
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
*/
#ifndef HEADER_PUGICONFIG_HPP
#define HEADER_PUGICONFIG_HPP
// Uncomment this to enable wchar_t mode
// #define PUGIXML_WCHAR_MODE
// Uncomment this to enable compact mode
// #define PUGIXML_COMPACT
// Uncomment this to disable XPath
// #define PUGIXML_NO_XPATH
// Uncomment this to disable STL
// #define PUGIXML_NO_STL
// Uncomment this to disable exceptions
// #define PUGIXML_NO_EXCEPTIONS
// Set this to control attributes for public classes/functions, i.e.:
// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
// Tune these constants to adjust memory-related behavior
// #define PUGIXML_MEMORY_PAGE_SIZE 32768
// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
// Uncomment this to switch to header-only version
// #define PUGIXML_HEADER_ONLY
// Uncomment this to enable long long support
// #define PUGIXML_HAS_LONG_LONG
#endif
/**
* Copyright (c) 2006-2019 Arseny Kapoulkine
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

12948
src/external/pugixml/src/pugixml.cpp vendored Normal file

File diff suppressed because it is too large Load diff

1490
src/external/pugixml/src/pugixml.hpp vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -94,6 +94,16 @@ SFIZZ_EXPORTED_API int sfizz_get_num_masters(sfizz_synth_t* synth);
* @return int the number of curves
*/
SFIZZ_EXPORTED_API int sfizz_get_num_curves(sfizz_synth_t* synth);
/**
* @brief Export a MIDI Name document describing the the currently loaded
* SFZ file.
*
* @param synth The synth
* @param model the model name used if a non-empty string, otherwise generated
*
* @return char* a newly allocated XML string, which must be freed after use
*/
SFIZZ_EXPORTED_API char* sfizz_export_midnam(sfizz_synth_t* synth, const char* model);
/**
* @brief Returns the number of preloaded samples for the current SFZ file.
*

View file

@ -12,6 +12,7 @@
#endif
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
#endif
#include "absl/strings/string_view.h"
#include <cstddef>
#include <cstdint>
@ -62,6 +63,11 @@ namespace config {
modulated filter. The lower, the more CPU resources are consumed.
*/
constexpr int filterControlInterval { 16 };
/**
Default metadata for MIDIName documents
*/
const absl::string_view midnamManufacturer { "The Sfizz authors" };
const absl::string_view midnamModel { "Sfizz" };
} // namespace config
// Enable or disable SIMD accelerators by default

View file

@ -11,6 +11,7 @@
#include "MidiState.h"
#include "ScopedFTZ.h"
#include "StringViewHelpers.h"
#include "pugixml.hpp"
#include "absl/algorithm/container.h"
#include "absl/strings/str_replace.h"
#include <algorithm>
@ -577,6 +578,91 @@ int sfz::Synth::getNumCurves() const noexcept
return numCurves;
}
std::string sfz::Synth::exportMidnam(absl::string_view model) const
{
pugi::xml_document doc;
absl::string_view manufacturer = config::midnamManufacturer;
if (model.empty())
model = config::midnamModel;
doc.append_child(pugi::node_doctype).set_value(
"MIDINameDocument PUBLIC"
" \"-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN\""
" \"http://www.midi.org/dtds/MIDINameDocument10.dtd\"");
pugi::xml_node root = doc.append_child("MIDINameDocument");
root.append_child(pugi::node_comment)
.set_value("Generated by Sfizz for the current instrument");
root.append_child("Author");
pugi::xml_node device = root.append_child("MasterDeviceNames");
device.append_child("Manufacturer")
.append_child(pugi::node_pcdata).set_value(std::string(manufacturer).c_str());
device.append_child("Model")
.append_child(pugi::node_pcdata).set_value(std::string(model).c_str());
{
pugi::xml_node devmode = device.append_child("CustomDeviceMode");
devmode.append_attribute("Name").set_value("Default");
pugi::xml_node nsas = devmode.append_child("ChannelNameSetAssignments");
for (unsigned c = 0; c < 16; ++c) {
pugi::xml_node nsa = nsas.append_child("ChannelNameSetAssign");
nsa.append_attribute("Channel").set_value(std::to_string(c + 1).c_str());
nsa.append_attribute("NameSet").set_value("Play");
}
}
{
pugi::xml_node chns = device.append_child("ChannelNameSet");
chns.append_attribute("Name").set_value("Play");
pugi::xml_node acs = chns.append_child("AvailableForChannels");
for (unsigned c = 0; c < 16; ++c) {
pugi::xml_node ac = acs.append_child("AvailableChannel");
ac.append_attribute("Channel").set_value(std::to_string(c + 1).c_str());
ac.append_attribute("Available").set_value("true");
}
chns.append_child("UsesControlNameList")
.append_attribute("Name").set_value("Controls");
}
{
pugi::xml_node cns = device.append_child("ControlNameList");
cns.append_attribute("Name").set_value("Controls");
for (const CCNamePair& pair : ccNames) {
pugi::xml_node cn = cns.append_child("Control");
cn.append_attribute("Type").set_value("7bit");
cn.append_attribute("Number").set_value(std::to_string(pair.first).c_str());
cn.append_attribute("Name").set_value(pair.second.c_str());
}
}
///
struct string_writer : pugi::xml_writer {
std::string result;
string_writer()
{
result.reserve(8192);
}
void write(const void* data, size_t size) override
{
result.append(static_cast<const char*>(data), size);
}
};
///
string_writer writer;
doc.save(writer);
return std::move(writer.result);
}
const sfz::Region* sfz::Synth::getRegionView(int idx) const noexcept
{
return (size_t)idx < regions.size() ? regions[idx].get() : nullptr;

View file

@ -111,6 +111,10 @@ public:
* @return int
*/
int getNumCurves() const noexcept;
/**
* @brief Export a MIDI Name document describing the loaded instrument
*/
std::string exportMidnam(absl::string_view model = {}) const;
/**
* @brief Get a raw view into a specific region. This is mostly used
* for testing.

View file

@ -49,6 +49,11 @@ int sfizz_get_num_curves(sfizz_synth_t* synth)
auto self = reinterpret_cast<sfz::Synth*>(synth);
return self->getNumCurves();
}
char* sfizz_export_midnam(sfizz_synth_t* synth, const char* model)
{
auto self = reinterpret_cast<sfz::Synth*>(synth);
return strdup(self->exportMidnam(model ? model : "").c_str());
}
size_t sfizz_get_num_preloaded_samples(sfizz_synth_t* synth)
{
auto self = reinterpret_cast<sfz::Synth*>(synth);