Wohlstand / libADLMIDI

A Software MIDI Synthesizer library with OPL3 (YMF262) emulator
GNU Lesser General Public License v3.0
174 stars 17 forks source link

Stabilizing Real-Time MIDI API #53

Closed Wohlstand closed 5 years ago

Wohlstand commented 6 years ago

Continue a discussion with @jpcima about of Real-Time MIDI API and ADLJack which implements a real-time MIDI Synthesizer over libADLMIDI https://github.com/Wohlstand/libADLMIDI/issues/48

jpcima commented 6 years ago

Synthesizer, not sequencer. The sequencer is fmidi which is another separate thing.

Anyway it's a toy program at this point, and it should remain simple like it is. If I start a synthesizer project with full features and gui, it should be a different project, and an audio plugin in VST+LV2 formats.

So if you want to discuss about that and talk features, you're welcome.

Wohlstand commented 6 years ago

Synthesizer, not sequencer.

Fixed :wink:

Anyway it's a toy program at this point, and it should remain simple like it is.

At the same time, it's very useful to polish RT-API by many ways, or use it with MIDI Editors on Linux to directly use libADLMIDI/OPNMIDI and preview result in real time rather dealing with FluidSynth and previewing the result after saving of MIDI file.

If I start a synthesizer project with full features and gui, it should be a different project, and an audio plugin in VST+LV2 formats.

The toy is perfect for experiments, tests and previews, but is a good idea to make something like full-featured synthesizer thing with giving ability to control librarie's setup in real time and using it wider for many other things together with player plugins (I have tiny VLC plugin at me in utils/ folder) or in-game usages (GZDoom and SDL Mixer X).

jpcima commented 6 years ago

giving ability to control librarie's setup in real time

At the very least there should be like in yamaha synths, an edition memory which keeps a copy of the active bank, and which can be manipulated by gui. And there is the save/load possibility between the disk and the edition buffer. (and of course modification by MIDI via the cc and sysex messages)

jpcima commented 6 years ago

I will need some elements which I believe, at first sight, are not present in the current API.

1.

I would need a way to obtain the parameters and to meddle with them in real time, while the instrument is playing live. Either in the form of registers, or human comprehensible names (algorithm, waveform, detune, vibrato...). And there ideally be a way to access more directly the banks in the active memory. There are only APIs which operate on WOPL input.

2.

These functions to manipulate should be callable in hard real time context. Which means, they should guarantee no kind of wait, locking, or malloc, in their operation. It would be great if the API would document functions which are RT-Safe guaranteed, and these which are not.

EDIT: I want to absolutely avoid the idea of dynamic banks. I need only one bank slot inside, no more. Dynamic banks implies std::vector and dynamic allocation, meaning not RT-Safe.

Wohlstand commented 6 years ago

EDIT: I want to absolutely avoid the idea of dynamic banks. I need only one bank slot inside, no more. Dynamic banks implies std::vector and dynamic allocation, meaning not RT-Safe.

About of dynamic banks now: everything that was built by Joel in original ADLMIDI was designed for linking to embedded banks, and dynamic banks thing originally was added for CMF playing support are containing custom FM instruments. I used that store to load custom WOPL banks into them. About of threading-safety, library is completely NOT thread-safe because of the case while adl_play() or adl_generate is running, parallel call of any setup function may cause a crash. On my phone I had the crash crap inside of emulator because of parallel register writing and sound generation in the process. Therefore mutex is needed when calling generator and configurer functions from different threads.

Anyway, is a good idea to refactor "running" bank storage and have some cache which will hold a "currently-using" bank rather to directly access adldata's base. Anyway, don't forget that it's not GM-only, it's multi-bank, which means when you have GS or XG bank, you need an extra space to store extra instruments data. And yeah, on side of OPNMIDI I'll refactor this too as initially I just ported existing construction to work with a different chip. About of embedded MIDI Sequencer (which loads MIDI file data and plays it), I have plan to split it away to let it be used independently as it has a very good features include looping support, positioning, etc. I mean, even now is a macro which disables MIDI sequencer, but also, I wanna make an ability to use that MIDI player with any other MIDI device.

jpcima commented 6 years ago

library is completely NOT thread-safe

Thread safety does not matter, I really talk about RT safety. User interface will message RT via FIFO queue, and RT will call all the adl setup. (except initialization and destruction) But because RT will do all the setup, functions must be RT safe. (again no lock, no wait, no malloc)

when you have GS or XG bank, you need an extra space to store extra instruments data

Can one just possibly allocate all the maximum required storage in advance, that will be required by XG and GS? What I mean is: I don't want std::vector to go away, I need bank storage allocated at fixed capacity for all the lifetime of playback.

Wohlstand commented 6 years ago

Can one just possibly allocate all the maximum required storage in advance, that will be required by XG and GS?

Aggressive memory usage is bad solution for mobile platforms, or even on desktop which I totally hate with various Electron apps because of freaking Chromium core inside of NodeJS which sucks because of memory usage. Anyway, instead to have something of fixed-size, let's don't resize it every time, but when loading bank of lesser size, fill all tail instruments with null data instead of chopping.

What I mean is: I don't want std::vector to go away, I need bank storage allocated at fixed capacity for all the lifetime of playback.

The bank data storage is changing only in case when you loading another bank and it is kept fixed for any other setup. So, as I told, I'll don't resize/clear the vector when loading different banks. That also going be faster in case memory (re)allocation also takes some time.

jpcima commented 6 years ago

Ok understood. Back to the main point:

I will have a VST instrument plugin. The user interface will look similar to OPL3BankEditor's front panel. You can change a control by turning a knob. It will make immediate impact on the timbre which is played.

How do I send this update to ADLMIDI in current API, other than resending the entire bank to the library. I just want one value changed, I will not update the whole bank each time a knob is turned.

How do I make updates like this?

Wohlstand commented 6 years ago

How do I send this update to ADLMIDI in current API, other than resending the entire bank to the library. I just want one value changed, I will not update the whole bank each time a knob is turned.

For now is no API to change single instrument playback yet. In OPL3 Bank Editor I have made the note player which silly iterates channels in a ring and which sucks as it is can't be used for music playing because of a case of note-off turns off all playing notes with no matter how long you hold each key. As the base, look wopl_file.h file where is instrument structure. I plan to replace WOPL parser in libADLMIDI with that C-codded parser to unify the way to parse WOPL file to don't have two different file parsers of same format to maintain.

I think, it's needed to give the structure of OPL data which will be passed through a function where you can refer the bank by MIDI lsb/msb, by patch number, and by type (melodic or percussion) to override specific instrument in the currently running bank data. That stuff is for custom banks, not for embedded as embedded data is const. However, this crap I gonna to rework to COPY bank data from database into dynamical array to keep it be always-allocated and be easy-patchable from the side.

And the good idea to use not just a silly instrument tester, but full featured MIDI synthesizer that also can be used to put the MIDI file to run a live-testing of currently-editing bank.

jpcima commented 6 years ago

Let's make this its own issue maybe then.

Wohlstand commented 6 years ago

Made! https://github.com/Wohlstand/libADLMIDI/issues/59

jpcima commented 6 years ago

There's progress on the softsynth plugin. It does the very basic functionality at this point. In the future it can substitute for adljack, because it has a standalone executable, that can make it interesting to you.

While I wait for the instrument edition feature you have assigned to yourself, I will make progress on secondary stuff like making the GUI.

jpcima commented 6 years ago

I got my hands today on something amusing. You ever heard about OPL7? :laughing:

Wohlstand commented 6 years ago

OPL7? Never heard... But seems it's some neat thing :laughing:

jpcima commented 6 years ago

OPL7 is essentially a 6-op OPL3 extended with the whole set of 32 algorithms of the DX7. (no I haven't drunk a glass of wine too much in case you wondered :smile: ) I invented OPL7. It's to be found here.

Let me told you how this happened. :smile: I searched through a set of old hard drives I had in a drawer, and I found again this old project of mine. I had absolutely erased of my memory I had ever started a thing like this. It's because some years a go I stopped all personal programming stuff to focus on the thesis.

In fact in this project I did this: I picked a core of OPL3 emulation (I believe it was nuked, but not sure); and I re-engineered the monolith that it is, into logical decoupled blocks (operator, envelope, etc). And I augmented the capacity of it, into something like a DX7.

This thing has graph files in it that describe all the set of algorithms, and I use these definitions for a code generation which writes C code of all the algorithms. Technically this thing COULD play 6-op FM algos on OPL3 core. But it has a few bugs, and I never continued it after and forgot.

Well, now you know OPL7 :smile:

jpcima commented 6 years ago

I made some progress on a GUI for the ADLMIDI plugin. This is a picture. It's not the most fantastic of things for now.. but well, it's something.

adlplug

Wohlstand commented 6 years ago

Looking neat! God job, @jpcima :fox_face: :+1:

jpcima commented 6 years ago

It looks significantly better now. I arranged the layout better and more compactly. It has received a keyboard and can play directly on it. And I did other tiny stuff. If I can be of some help to write patch edition features, ask me.

adlplug

Wohlstand commented 5 years ago

Anything needed also? Or this task can be closed as completed?

jpcima commented 5 years ago

I consider it to be complete, as I'm concerned libADLMIDI has everything I want.

Wohlstand commented 5 years ago

Then, I'll close this as the main goal of this task has been completed! :fox_face: :+1: