sfztools / sfizz Goto Github PK
View Code? Open in Web Editor NEWSFZ parser and synth c++ library, providing a JACK standalone client
Home Page: https://sfz.tools/sfizz/
License: BSD 2-Clause "Simplified" License
SFZ parser and synth c++ library, providing a JACK standalone client
Home Page: https://sfz.tools/sfizz/
License: BSD 2-Clause "Simplified" License
Please include instructions about how to compile this on MacOS.
As a byproduct of queued changes to the MIDI handling and state tracking code, there will be a list of CCs used in the SFZ file loaded. This list could be transmitted to hosts through the MIDNAM interface.
This is also a good time to visit a possible solution for #23 : if no CC is active here, we advertise CC7, CC10 and CC11 as volume, panning and expression and track them globally.
MIDNAM pull request: #78
reported by @alcomposer : when you play 64 notes of a one-shot generator, the polyphony is filled and you can't play anymore unless resetting the plugin.
Should the voices be cleared once the envelope has reached the end?
also can we permit new notes to steal a slot more aggressively, in case the polyphony is filled?
<group>
lovel=0
hivel=127
<region>
trigger=release
sample=*noise
loop_mode=one_shot
ampeg_attack=1
ampeg_decay=1
ampeg_release=0
ampeg_sustain=0
We need some research and behavior check but definitely now we're not properly handling the difference between these 2 opcodes since they're always checked together.
The sfizz.lv2 plugin segfaults, when the an LV2 host (here: Carla) restores the state:
Log from #lad on freedode
[19:57:38] <strogon14> falktx: I have a LV2 plugin (http://sfztools.github.io/sfizz), that crashes with segfault, when I load a Carla project containing the plugin (even when there's no other plugin in the project). There's no error in the log output. How can I debug this?
[...]
[20:53:21] <falktx> I get a crash
[20:53:52] <falktx> sfizz_load_file(sfizz_synth_t*, char const*) (synth=0x0, path=0x1ae3f00 "") at /home/falktx/Projects/Source/Other/sfizz/src/sfizz/sfizz_wrapper.cpp:41
[20:54:07] <falktx> synth being null is weird
[...]
[20:59:34] <falktx> haha!
[20:59:47] <falktx> the crash happens because the synth pointer is only created during activate()
[20:59:54] <falktx> carla restores the state before calling that
[...]
[21:02:59] <falktx> not sure if lv2 states that state-restore must be called with an activated plugin.. I do not think that is the case
[21:03:38] <falktx> tbh sfizz does this in a bad way. it basically reloads the sfz file each time the plugin is re-activated
[...]
[21:05:55] <falktx> strogon14: patch file that works for me here https://kx.studio/Paste/raw/29xpr
I attach an implementation of filters which matches ARIA near perfectly. (when implemented there)
It uses some very generic resonant filters, it showed to be quite easy to reproduce them.
Current:
sfz-filters
sfzfilters.dsp.gz
Reference:
https://sfzformat.com/opcodes/fil_type
https://sfzformat.com/opcodes/fil2_type
Status:
New ARIA extensions:
Not implemented by ARIA:
Implemented by us, not in specification:
This is the wavetable generator with IFFT for saw, square, triangle.
It's based on BSD-licensed kissfft.
https://gist.github.com/jpcima/9e43f08ed3a39e0363115c1ed449740e
In sfizz, it will need to implement adequate resampling so these waves can be generated with good quality.
After running the make file I don't see any binaries in the client folder or an .lv2 plugin.
Currently the API documentation is built at each commit in develop branch which is not correct, the correct behavior should be to build the API at each release and eventually separate the documentation per versions.
Need to evaluate eventually also if to remove the build date from pages footer template.
When I run sfizz_jack
I get:
Flags
- Oversampling: 1x
- Preloaded Size: 8192
Positional arguments:
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_M_construct null not valid
[1] 3300 abort (core dumped) sfizz_jack
This is on the develop branch, commit 994fe17
Some initial analysis of effect routing is added in the wiki.
https://github.com/sfztools/sfizz/wiki/Effect-routing
This is a quick-made and terrible dot graph of what routing can be supposed to be. (not checked 100%)
I'm packaging sfizz for NixOS, and since nix doesn't condone downloads during the build, the FetchContent_Populate commands won't work without hacky patching.
Would you consider making the downloads optional?
Why didn't you choose to include them as dependencies?
When loading project, and starting to play, some notes aren't playing. Something like sounds are loaded in request, and hitting first time, they aren't loaded yet?
Are sounds played from memory or disk?
Using version 0.2.0, downloaded linux-amd64, and using lv2
Dropzone and Rapture SFZv2 supports embedding of samples.
See: sfz/sfz.github.io#23
Note that parsing this data is necessarily context-dependent; the parser needs the knowledge that it's handling a binary-valued opcode and treat accordingly.
If it were to handle this case, the current parser would need adapting.
ifstream
with binary mode' '
in advance (would corrupt the coding)=
for opcode's end, but rather the $
final marker as indicated in the reference postWe can implement the tuning file support thanks to the surge tuning library.
https://github.com/surge-synthesizer/tuning-library
This library is able to accept one or a pair of .scl and .kbm, and it can output and array of MIDI note frequencies to match these settings. The advantage of this particular library is the MIT license.
As LV2 is concerned, an example of manifest can be taken from rncbc's synthv1.lv2.
Parameters are as follows:
From issue #19:
Yes, the crossfade calculation are not dynamic right now and they're only checked when the note start. After reading around, this is indeed not what's expected so I'll look into it!
I'm opening this as a separate issue because the main bugs from that issue have been solved.
It would be good to list currently implemented opcodes, similar to this page from Linux Sampler: https://www.linuxsampler.org/sfz/
There's barely any sound and weird things are happening, probably due to filters in the latest develop.
Probably better to fix it upstream. Using multiple relative default_path
opcodes does not seem so well formed: should we take the relative path with respect to the current sample path or the sfz file parent path? What about includes?
Let's fill in some info regarding effects as we figure out the implementation and controls, on wiki as well as here.
bitred
decim
autopan
Yesterday I compiled the sfizz lv2 from the develop
branch and tried using Virtual Playing Orchestra. There are bugs; I'm not sure if they are due to VPO using non-standard sfZ opcodes, or if sfizz is missing features that VPO depends on.
Any of the VPO sfz files with PERF
in the file name causes sfizz to crash immediately upon playing a single note.
The pizzicato articulations aren't played correctly, they make a very brief sound and then go silent.
There are possibly other issues, these are just the ones I noticed while doing a cursory test.
If multiple instances are loading some identical sample "foo.wav", we have solutions to avoid duplicating the data into each instance.
Create a static storage that permits to share samples according to file ID.
The ownership of the samples can be managed using reference counts.
Currently we are providing tar.gz
archives for both sfizz library and LV2 plugin.
On Windows we have an InnoSetup script to build an installer setup, via Travis-CI, for the LV2 plugin; it would be good to have one also on macOS.
On macOS does exists a procedure to create .pkg
installer package files using pkgbuild, productbuild and pkgutil tools; there are also some frontend utilities to simplify the process. I started using macos-installer-builder (here an article from the author) but it seems there is also another interesting one called Packages.
The purpose is to create a final bash script to use in Travis-CI, so no GUI based builders.
As a side note, similar on InnoSetup on Windows, macOS packages can be publicly signed and an Apple Developer account is required to do it. On latest macOS versions signing is mandatory (see "notarization").
Another additional option could be to distribute the resulting package using a dmg
image, usually built using hdiutil. This could be also achieved by using create-dmg, "a shell script to build fancy DMGs", so it can be executed inside the CI build.
sfizz/src/sfizz/sfizz_wrapper.cpp
Line 238 in f02f96c
opcodeList[totalLength] = '\0';
should be:
opcodeList[totalLength + unknownOpcodes.size() - 1] = '\0';
Otherwise the added commas are not accounted for before the termination is added.
Implement curvecc
for MIDI controller changes.
I have added an analysis in the wiki.
I have studied effects of bend_smooth
, and I share some results.
In SFZ specs, smoothcc
under Modulators is a "see also" to opcode bend_smooth
.
The experiment is to load a sine tone and modulate midi pitch bend with a ramp lfo.
The results were observed on spectrogram in real-time.
Then, it was matched to a faust program which plays a sine modulated with identical LFO and range.
The smoothing filter is one-pole LPF which is defined by a time-constant tau
. (cf. faust basics.lib
)
pole = exp(-1.0/(tau*Fs))
These are approximate results measured visually.
tau
is observed starting at 0, varying +3 ms by bend_smooth
unit.
#bend_smooth tau measured (ms)
10 30
20 70
30 90
40 120
50 160
60 180
70 220
80 230
90 270
100 300
In the stereo output, it appears as if width and position are mixed.
Below is mono position, vs. stereo width: a similar computation.
It suggests position and width processed to have been mistaken for each other, in the case of stereo.
Lines 296 to 305 in 7f1d825
Lines 340 to 348 in 7f1d825
I posted a simple convolution-based resampler.
https://github.com/jpcima/simple-resampler
It has an example in the repo, to try it on sound files.
It has these properties:
Oversampling set to 1 plays long sustained notes properly. However when set to x2,x4 or x8 the notes get cut off.
Example of SFZ that this is effecting (not limited to): https://github.com/sgossner/VSCO-2-CE/blob/SFZ/SViolinVib.sfz
Currently the channel
argument for the sfizz_send_*
functions uses 1-based counting of MIDI channels.
I suggest changing this to zero-based counting, i.e. 0-15, since this is how the channel is represented in the MIDI status byte and how most software stores and passes it internally.
IMO, 1-based counting should only be used when presenting a MIDI channel number to the user.
I would be interested in seeing Polyphonic Aftertouch as well. Devices such as the Roli Seaboard support it, as do any devices that support MPE. (The list is growing every day).
I understand that this would be a long term goal.
Originally posted by @alcomposer in #7 (comment)
A sfz file contains the following region line (edited for brevity)
sample=..\G&S CW-Drum Kit-1\SnareFX\SNR-OFF-V08-CustomWorks-6x13.wav
sfizz_jack fails to load given sample because of "&" in the sample name.
Applying
parser.diff.txt
to sfizz/sfizz/Parser.h solves the issue. Not widly tested as yet but doesn't seem to have unintended consequences.
Hope this helps.
I'm not sure whether this is strictly compatible with the SFZ spec, but it would be nice if Channel Volume (CC7) and/or Expression (CC11) had default behavior of controlling volume. Of course if the SFZ file specifies their behavior, it should override the default.
Relevant opcode:
The main use case is SFZ files for drum sets, where one would want to route different drum sounds to different output channels, so they can be routed to different track in the DAW, e.g. for treating them with different effects or do custom panning.
Hi all,
first of all, thanks a lot for working on this! I've recently discovered sfizz thanks to Michael Wellis' excellent Ardour template for VPO, and I started using it for all my SFZ needs, especially considering LinuxSampler was crashing a bit too often on my old laptop.
I was wondering if you could add a command line option to sfizz_jack
to provide a custom name to use for the Jack client: at the moment it defaults to an hardcoded sfizz
auto defaultName = "sfizz";
jack_status_t status;
client = jack_client_open(defaultName, JackNullOption, &status);
which can be problematic when you open multiple clients at the same time, or want to be able to quickly create connections with other applications. This looks like a simple task so I can take care of that as well and prepare a pull request, if you let me know which branch you'd like me to target.
Thanks!
Aka mono(phonic) aftertouch.
Relevant opcodes:
(Does SFZ not allow channel aftertouch to be mapped to filter resonance?)
NB: the sfizz.h
header currently lists a function ambiguously named sfizz_send_aftertouch
. I suggest naming this sfizz_send_channel_pressure
or at least adding this as an alias, so there is no confusion that this is about handling channel pressure (aka monophonic aftertouch), not poly pressure (aka poly aftertouch).
Hi!
I'm currently trying to get sfizz running under Unity and I'm planning to use in Python for another project in the future. To help with rendering to buffers from other languages I added a couple of helpers in sfizz_wrapper.c
to avoid implementing the float smashing in the bindings language.
The code isn't very good, but this is my temporary code for now:
void sfizz_render_block_noninterleaved(sfizz_synth_t* synth, float* channels, int num_channels, int num_frames)
{
auto self = reinterpret_cast<sfz::Synth*>(synth);
// Only stereo output is supported for now
ASSERT(num_channels == 2);
UNUSED(num_channels);
self->renderBlock({{channels, channels + num_frames}, static_cast<size_t>(num_frames)});
}
void sfizz_render_block_interleaved(sfizz_synth_t* synth, float* channels, int num_channels, int num_frames)
{
float tmpbuffers[2][32768]; // Is this somehow solvable with preallocated buffers?
auto self = reinterpret_cast<sfz::Synth*>(synth);
// Only stereo output is supported for now
ASSERT(num_channels == 2);
UNUSED(num_channels);
if (num_frames > 32768)
num_frames = 32768;
self->renderBlock({{tmpbuffers[0], tmpbuffers[1]}, static_cast<size_t>(num_frames)});
for (int i = 0; i < num_frames; i++)
for (int j = 0; j < num_channels; j++)
channels[i * num_channels + j] = tmpbuffers[j][i];
}
Not sure if it happens in the JACK client or the SFZ engine, but compared to other sfz players I notice a definitive lack of notes played when using a few SFZs I have around.
This is one such case https://musical-artifacts.com/artifacts/460
Is there a polyphony limit? If so, it seems very low.
As ridiculous as this may sound, you can optimize clamp by rewriting it as a 2-line function.
It's a craziness I was aware of for a bit of time, you can easily reproduce it.
https://godbolt.org/z/NuNaq9
As you will be able to observe, gcc will generate a branch in the slow case, and just an instruction pair in others.
I built and installed on Debian 10 and the file
/usr/local/lib/pkgconfig/sfizz.pc
was correct (verified)
However the sfizz.pc
in the release sfizz-v0.2.0-linux-amd64.tar.gz
has the following lines
prefix=/home/travis/build/sfztools/sfizz/build/sfizz-v0.2.0-linux-amd64
exec_prefix=/home/travis/build/sfztools/sfizz/build/sfizz-v0.2.0-linux-amd64
which should be
prefix=/usr/local
exec_prefix=/usr/local
Below is a number generator, fast and branchless, in few instructions.
It has a comparison with standard library generators.
https://godbolt.org/z/r_TccA
As a note, the std generator is particularly bad when using clang as the compiler, for some reason.
I wish to eliminate the detection of features such as HAVE_INTRIN_H
.
Usually, in gcc at least, there already exist some predefined macros which permit to detect CPU features without resorting to such checks.
If configure-time checks are gone, sfizz is more easily included from systems which do not use cmake.
For instance, DPF uses only GNU make, and I would like to provide some make module that plugin makers include to embed it easier into the plugin.
Also, in the part below, it's not advisable to override a user's flags with a fixed architecture.
It disables the ability of creating portable ARM packages.
(moreover I don't think -march=native and -mtune or -mfpu together to make sense)
add_compile_options (-DHAVE_ARM_NEON_H)
add_compile_options (-mfpu=neon)
add_compile_options (-march=native)
add_compile_options (-mtune=cortex-a53)
OS: Arch Linux x86_64
Host: TM1701
Kernel: 5.4.2-arch1-1
Uptime: 1 day, 2 hours, 29 mins
Packages: 2445 (pacman), 21 (flatpak)
Shell: bash 5.0.11
Resolution: 1920x1080
DE: GNOME 3.34.2
WM: Mutter
WM Theme: Adwaita
Theme: Adwaita [GTK2/3]
Icons: Adwaita [GTK2/3]
Terminal: gnome-terminal
CPU: Intel i7-8550U (8) @ 4.000GHz
GPU: NVIDIA GeForce MX150
GPU: Intel UHD Graphics 620
Memory: 4294MiB / 15899MiB
Ardour crashes when trying to play a loaded SFZ library with Sfizz: introduced in commit: f012d45
Ardour terminal output:
Removed 0 out of 8 regions.
Loading file for Xylo_Medium_C4_ff_01_far.wav in the background
ardour-6.0.pre0.2674: /home/alexmitchell/Documents/github/sfizz/external/abseil-cpp/absl/types/span.h:357: void absl::lts_2019_08_08::Span<T>::remove_prefix(absl::lts_2019_08_08::Span<T>::size_type) [with T = float; absl::lts_2019_08_08::Span<T>::size_type = long unsigned int]: Assertion `size() >= n' failed.
Aborted (core dumped)
#0 0x00007ffff2cf2f25 in raise () at /usr/lib/libc.so.6
#1 0x00007ffff2cdc897 in abort () at /usr/lib/libc.so.6
#2 0x00007ffff2cdc767 in _nl_load_domain.cold () at /usr/lib/libc.so.6
#3 0x00007ffff2ceb526 in () at /usr/lib/libc.so.6
#4 0x00007fffd14cfe21 in absl::lts_2019_08_08::Span<float>::remove_prefix(unsigned long) ()
at /usr/local/lib/lv2/sfizz.lv2/sfizz.so
#5 0x00007fffd14cf6ae in sfz::ADSREnvelope<float>::getBlock(absl::lts_2019_08_08::Span<float>) ()
at /usr/local/lib/lv2/sfizz.lv2/sfizz.so
#6 0x00007fffd14c676b in sfz::Voice::processStereo(sfz::AudioSpan<float, 2u>) () at /usr/local/lib/lv2/sfizz.lv2/sfizz.so
#7 0x00007fffd14c6289 in sfz::Voice::renderBlock(sfz::AudioSpan<float, 2u>) () at /usr/local/lib/lv2/sfizz.lv2/sfizz.so
#8 0x00007fffd14903c1 in sfz::Synth::renderBlock(sfz::AudioSpan<float, 2u>) () at /usr/local/lib/lv2/sfizz.lv2/sfizz.so
#9 0x00007fffd148bce5 in sfizz_render_block () at /usr/local/lib/lv2/sfizz.lv2/sfizz.so
#10 0x00007fffd148ac88 in run () at /usr/local/lib/lv2/sfizz.lv2/sfizz.so
#11 0x00007ffff78fef6b in lilv_instance_run(LilvInstance*, uint32_t) (instance=0x55555ac0d050, sample_count=1024)
at /usr/include/lilv-0/lilv/lilv.h:1704
#12 0x00007ffff7910537 in ARDOUR::LV2Plugin::run(unsigned int, bool) (this=0x555561225780, nframes=1024, sync_work=false)
at ../libs/ardour/lv2_plugin.cc:3176
#13 0x00007ffff790e26f in ARDOUR::LV2Plugin::connect_and_run(ARDOUR::BufferSet&, long, long, double, ARDOUR::ChanMapping const&, ARDOUR::ChanMapping const&, unsigned int, long)
(this=0x555561225780, bufs=..., start=1035, end=1035, speed=0, in_map=..., out_map=..., nframes=1024, offset=0)
at ../libs/ardour/lv2_plugin.cc:2836
#14 0x00007ffff765c33f in ARDOUR::PluginInsert::connect_and_run(ARDOUR::BufferSet&, long, long, double, unsigned int, long, bool) (this=0x5555612562e0, bufs=..., start=1035, end=1035, speed=0, nframes=1024, offset=0, with_auto=true)
at ../libs/ardour/plugin_insert.cc:1038
#15 0x00007ffff765d9b5 in ARDOUR::PluginInsert::run(ARDOUR::BufferSet&, long, long, double, unsigned int, bool)
(this=0x5555612562e0, bufs=..., start_sample=1035, end_sample=1035, speed=0, nframes=1024)
at ../libs/ardour/plugin_insert.cc:1243
#16 0x00007ffff76fe4c2 in ARDOUR::Route::process_output_buffers(ARDOUR::BufferSet&, long, long, unsigned int, bool, bool)
(this=
0x55555e65a6a0, bufs=..., start_sample=1035, end_sample=1035, nframes=1024, gain_automation_ok=false, run_disk_reader=false) at ../libs/ardour/route.cc:537
#17 0x00007ffff76ff5e1 in ARDOUR::Route::run_route(long, long, unsigned int, bool, bool)
(this=0x55555e65a6a0, start_sample=0, end_sample=0, nframes=1024, gain_automation_ok=false, run_disk_reader=false)
at ../libs/ardour/route.cc:728
#18 0x00007ffff7712c0c in ARDOUR::Route::no_roll_unlocked(unsigned int, long, long, bool)
(this=0x55555e65a6a0, nframes=1024, start_sample=0, end_sample=0, session_state_changing=false)
--Type <RET> for more, q to quit, c to continue without paging--
at ../libs/ardour/route.cc:3912
#19 0x00007ffff75cd9c3 in ARDOUR::MidiTrack::no_roll_unlocked(unsigned int, long, long, bool) (this=
0x55555e65a6a0, nframes=1024, start_sample=0, end_sample=0, state_changing=false) at ../libs/ardour/midi_track.cc:344
#20 0x00007ffff7712ad4 in ARDOUR::Route::no_roll(unsigned int, long, long, bool)
(this=0x55555e65a6a0, nframes=1024, start_sample=0, end_sample=0, session_state_changing=false)
at ../libs/ardour/route.cc:3882
#21 0x00007ffff72b67fc in ARDOUR::Graph::process_one_route(ARDOUR::Route*) (this=0x555558224fd0, route=0x55555e65a6a0)
at ../libs/ardour/graph.cc:670
#22 0x00007ffff72bb7dc in ARDOUR::GraphNode::process() (this=0x55555e65ab78) at ../libs/ardour/graphnode.cc:80
#23 0x00007ffff72b6a5b in ARDOUR::GraphNode::run(int) (this=0x55555e65ab78, chain=1)
at /home/alexmitchell/Documents/github/ardour/libs/ardour/ardour/graphnode.h:62
#24 0x00007ffff72b430c in ARDOUR::Graph::run_one() (this=0x555558224fd0) at ../libs/ardour/graph.cc:442
#25 0x00007ffff72b4673 in ARDOUR::Graph::helper_thread() (this=0x555558224fd0) at ../libs/ardour/graph.cc:470
#26 0x00007ffff72baff3 in boost::_mfi::mf0<void, ARDOUR::Graph>::operator()(ARDOUR::Graph*) const
(this=0x7fffe00d20f8, p=0x555558224fd0) at /usr/include/boost/bind/mem_fn_template.hpp:49
#27 0x00007ffff72ba7f3 in boost::_bi::list1<boost::_bi::value<ARDOUR::Graph*> >::operator()<boost::_mfi::mf0<void, ARDOUR::Graph>, boost::_bi::list0>(boost::_bi::type<void>, boost::_mfi::mf0<void, ARDOUR::Graph>&, boost::_bi::list0&, int)
(this=0x7fffe00d2108, f=..., a=...) at /usr/include/boost/bind/bind.hpp:259
#28 0x00007ffff72b9c7c in boost::_bi::bind_t<void, boost::_mfi::mf0<void, ARDOUR::Graph>, boost::_bi::list1<boost::_bi::value<ARDOUR::Graph*> > >::operator()() (this=0x7fffe00d20f8) at /usr/include/boost/bind/bind.hpp:1294
#29 0x00007ffff72b93a7 in boost::detail::function::void_function_obj_invoker0<boost::_bi::bind_t<void, boost::_mfi::mf0<void, ARDOUR::Graph>, boost::_bi::list1<boost::_bi::value<ARDOUR::Graph*> > >, void>::invoke(boost::detail::function::function_buffer&) (function_obj_ptr=...) at /usr/include/boost/function/function_template.hpp:158
#30 0x0000555555b7c4e9 in boost::function0<void>::operator()() const (this=0x7fffe00d20f0)
at /usr/include/boost/function/function_template.hpp:763
#31 0x00007fffe1309f05 in ARDOUR::AlsaAudioBackend::alsa_process_thread(void*) (arg=0x55555a174100)
at ../libs/backends/alsa/alsa_audiobackend.cc:1095
#32 0x00007ffff39584cf in start_thread () at /usr/lib/libpthread.so.0
#33 0x00007ffff2db62d3 in clone () at /usr/lib/libc.so.6
Hello. Thanks for a fun project, well done!
I'm trying out the develop branch, after building successfully I check libsfizz.so
and it does not contain any of the symbols found in libsfizz.a
.
I'm not sure if C++ differs from C when it comes to linking, but I think the symbols of an archive (static library) are only included when linking if they are actually used by whatever you are linking. I'm not sure if this is the problem, but maybe.
I think you can use -Wl,--whole-archive static_libs.a -Wl,--no-whole-archive
to include everything. There's also a cmake recipe for creating archives and shared objects from the same set of objects:
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) # Handy when building on windows
add_library(library_objects OBJECT ${source_files})
set_target_properties(library_objects PROPERTIES POSITION_INDEPENDENT_CODE 1) # Linux
add_library(library_static STATIC $<TARGET_OBJECTS:library_objects>)
target_link_libraries(library_static PUBLIC we_need_this_too.a)
if(BUILD_SHARED)
add_library(library_shared SHARED $<TARGET_OBJECTS:library_objects>)
target_link_libraries(library_shared PUBLIC we_need_this_too.a)
endif()
Linking with the other static libraries (absl) should then work for the symbols used by any of the objects, but I'm not sure. May be totally wrong here.
It's not clear which code style must be adopted for contributing.
It would be nice to settle for one, and introduce a clang-format configuration for the whole source.
The most frequent that I can see is reminiscent of Stroustrup's style from C++ guidelines.
But there's some files, like lv2/sfizz.c
, which mix different kinds of "*" and "{" placement, and make it extremely confusing.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.