-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for custom banks loaded in runtime #38
Comments
Original comment by James Alan Nguyen (Bitbucket: djtubig-malicex, GitHub: djtubig-malicex). Some of the above are already being used internally, but at the moment patch customization is limited to a build from source. I've just recently got back to working on this. |
Original comment by Jean Pierre Cimalando (Bitbucket: jpcima, GitHub: jpcima). I absolutely want this feature, and I have started to implement something. It is not difficult to do at all. The simplest way to send patches to the synth is by custom sysex. Is there a format we can agree with? I started with something like this.
|
Original comment by James Alan Nguyen (Bitbucket: djtubig-malicex, GitHub: djtubig-malicex). defs. sorry i've been slack at this repo haha. |
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand). SysEx-ish way looks like to let MIDI filed became like CMF files where it's pure MIDI file and FM patches are stored as SysEx. Looks interesting, and possibly you can use OPLI instrument format as a base which I have designed to support most features of all bank formats are known in history of OPL3 chip |
Original comment by James Alan Nguyen (Bitbucket: djtubig-malicex, GitHub: djtubig-malicex). yea maybe it's about time i've gotten off my arse and did something about this (along with IBK, BNK etc.). the sysex idea by @jpcima was something I've considered in the past but never got around to doing it. last time i tried something like this was in the form of a hook-in application using windows shared memory, which is non-portable. |
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand). The best idea is to make the small Control panel (or a separate app that communicating to driver) dialog that will setup running driver with next actions:
|
Original comment by Jean Pierre Cimalando (Bitbucket: jpcima, GitHub: jpcima). The basic idea was to keep the banks as concatenated sysex, yeah (.syx file). The roadmap right now is like this in my mind
Honestly I dont know much of all the OPL banks around and their formats. What I know is adlmidi provides various conversion functions to its own internal format, which has potential to reuse. |
Original comment by James Alan Nguyen (Bitbucket: djtubig-malicex, GitHub: djtubig-malicex). The issue with Control panel is that it would require the use of IPC or shared memory if communicating with the driver in real-time. That said I recall some older sound cards had this behaviour (eg: some variants of Crystal Audio which made a popup appear whenever the MIDI driver was opened). I'm a bit divided on whether to take an INI file or Registry approach for load-time configuration however. As for doing this via sysex, turns out there's already a similar issue ticket on that: https://bitbucket.org/djtubig-malicex/opl3-synth-driver/issues/17/playback-time-patch-register-modification |
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand). Also, OffTop: Can you document the patch bank format stored in your header files to let me import them by my OPL3 Bank Editor? |
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand). SysEx way is something different than change setup of the driver include of complete bank switching. Yes, you can use IPS and that must not be hard. And yeah, you can store the setup in registry and use IPS to poke driver reload it's config. That will be much simpler than passing entire settings via IPS. Anyway, old Jammie O'Connel's driver for Win3x and for Win9x had a configure tool (also a control panel dialog) which changes settings of running driver on a fly: banks, tremolo/vibrato bits, drum channels, etc. |
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand). Against of bank formats, I have made a table of support features: |
Original comment by James Alan Nguyen (Bitbucket: djtubig-malicex, GitHub: djtubig-malicex). Yeah I was reading your WOPL spec, I definitely agree WOPL is probably the format to go. Only question I have is, it doesn't appear to be that any format currently defined in the wild supports patch-level pan (eg: 2op-1 left, 2op-2 right) or retrigger time period (both used by patches from Voyetra's SuperSAPI! driver), and I haven't gotten around to implementing that either. There's probably no point documenting my format cause I think it was largely based on the windows default scheme which hugely resembles Junglevision with some minor differences - the banks I will most likely convert to use a more commonly used format (eg: WOPL). I do have a use case for pitch falling/rising rate (think 2xx or 1xx for XM effects, albeit nonstandard for MIDI), but i think i'm going to take it out as it doesn't appear to have much utility as a patch parameter (but it could exist as a custom MIDI-controllable effect, maybe). |
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand).
Good features to support them in WOPL too! And I would be happy to be able import Voyetra's instruments to play them everywhere rather Windows 3.1 DosBox environment or any old computer. I only need a documentation and sample banks to hack them. About of pitch falling/rising, I have a plan to provide that in libADLMIDI and libOPNMIDI to take more advantages from instruments, especially to implement something like SynthDrum with it's pitch falling. Why I asking about of documenting, I wanna take a valid result rather this: |
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand). P.S. As you see, WOPL at this moment have changed at least three versions are adding new features into the file formats, so, to implement file parser, you must handle versioing too, or you can use this STL-code as base: |
Original comment by Jean Pierre Cimalando (Bitbucket: jpcima, GitHub: jpcima). Now I just have a sysex parsing in the driver. And this converter. WOPL support will come later, I'm still on the basics. (in fact I'm stuck by a stupid sysex software which refuses to send for some reason, and such is the joy of using linux sometimes) |
Original comment by Jean Pierre Cimalando (Bitbucket: jpcima, GitHub: jpcima). Can you give me some guidance on how to convert WOPL to patchStruct, given data structures of libADLMIDI? To start, I am not sure how to navigate the imbrications std::map and std::pair, and I may not be doing it right. I think I can deduce what the 11 bytes are mapped to. what to do about finetune? it seems to have something with initializing B0-3. There is this mention in patchStruct.
|
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand). Look: ins.op[0].finetune = (int8_t)toSint16BE(idata + 32);
ins.op[1].finetune = (int8_t)toSint16BE(idata + 34); Those "fine tunes" are note offsets. Means half-tone offset from currently playing MIDI key. BUT, this fine tune is a frequency detune modifier: ins.adlins.voice2_fine_tune = 0.0;
int8_t voice2_fine_tune = int8_t(idata[37]);
if(voice2_fine_tune != 0)
{
if(voice2_fine_tune == 1)
ins.adlins.voice2_fine_tune = 0.000025;
else if(voice2_fine_tune == -1)
ins.adlins.voice2_fine_tune = -0.000025;
else
ins.adlins.voice2_fine_tune = ((voice2_fine_tune * 15.625) / 1000.0);
} It is used for DMX voices are pseudo-4-operator, there are two parallel 2-op voices. Also, again, the full detailed specification to understand the format itself: https://github.com/Wohlstand/OPL3BankEditor/blob/master/Specifications/WOPL-and-OPLI-Specification.txt |
Original comment by James Alan Nguyen (Bitbucket: djtubig-malicex, GitHub: djtubig-malicex). "note offsets" probably shoud be called "coarse tune" instead tbh. it's what every synth out there refers that to, albeit at the oscillator level. |
Original comment by Jean Pierre Cimalando (Bitbucket: jpcima, GitHub: jpcima). Ok I don't manage to find how to do this conversion of patches. There's too much I don't know atm. I take a break until I have the will to dig into details of the opl3. I have put it in a repo with the bits of libADLMIDI. It just needs to write the conversion wopl->internal patch. |
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand).
Thanks for correction, I'll re-factor that And yeah, I think, to make your work easier, I'll try to make on a quick hand a super-simple WOPL file parser which will give you the set of structures are easy to understand rather you will dig a hard-understandable ADLDATA generator. Original ADLMIDI's code structure sucks and even I did some big job in libADLMIDI to refactor/rework many parts of original code, it still have many things to polish and refactor to resolve most of existing crap in the code. |
Original comment by Jean Pierre Cimalando (Bitbucket: jpcima, GitHub: jpcima). I have a file parser for WOPL, and I have instances of WOPL_Inst to work on. I understand not all but still a decent part about this format. It's more the patchStruct of the maliceX driver which is foreign to me, which speaks all in register names. I speak of the structure patchStruct of this file: |
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand). Some fields are super-specific to libADLMIDI, for example, ms_sound_kon and ms_sound_koff, there are millisecond delays of while tone is sustaining and while it sounding after key off. That thing is used by libADLMIDI's channel manager which uses that to avoid breaking of long-playing notes and other things. |
Original comment by Jean Pierre Cimalando (Bitbucket: jpcima, GitHub: jpcima). So far I figured out this much. Next is to find how E682 bytes are ordered in adlmidi, and I don't know what rhythmMap is.
|
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand). Don't put attention to rythmMap, it's an ugly workaround I will remove and I will replace with per-instrument flags where AdLib drums are in use. That was a set of flags to tell which legacy AdLib drum type is set on the voice. For example, kick, snare, hi-hat, tom, or cymbell. |
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand). Just now I have made WOPL parser in pure C you can easily pick up: https://github.com/Wohlstand/OPL3BankEditor/tree/master/src/FileFormats/wopl It reads raw data taken from a file and gives you a structure with data you can take and use. For convenience, I named operator's fields with both register title and addresses. And I have documented every thing inside of structures and enums to let you understand data easily. How to use: At first, read the full bank file into byte array in memory, and then: int errCode;
WOPLFile * wopl = WOPL_LoadBankFromMem(raw_filedata, raw_filedata_length, &errCode);
if(!wopl)
{
// use errCode and WOPL_ErrorCodes enum to identify error
// and nuke everything!
}
//...
// Put data from `wopl` into your internal structures and arrays
//...
//Clear the WOPL data away after you completed to parse it
WOPLFree(wopl);
If using my new structures and your code, will be: void ConvertFromWOPL(patchStruct &dst, const WOPLInstrument &src)
{
dst.bOp = PATCH_1_2OP;
if(inst_flags & WOPL_Ins_Pseudo4op)
dst.bOp = PATCH_2_2OP;//When both 4-op and Pseudo4op flags defined, Pseudo-4op has higher priority
else if(inst_flags & WOPL_Ins_4op)
dst.bOp = PATCH_1_4OP;
// Be careful! Some instruments may have 'WOPL_Ins_IsBlank' flag which
// means instrument with no sound. I use it in multi-bank to detect when
// I must to fall to root bank (bank-0 in melodic sets) when selected patch
// is absence.
dst.bAtC0[0] = src.fb_conn1_C0;
dst.bAtC0[1] = src.fb_conn2_C0;
for (unsigned opnum = 0; opnum < 4; ++opnum) {
dst.op[opnum].bAt20 = src.operators[opnum].avekf_20;
dst.op[opnum].bAt40 = src.operators[opnum].ksl_l_40;
dst.op[opnum].bAt60 = src.operators[opnum].atdec_60;
dst.op[opnum].bAt80 = src.operators[opnum].susrel_80;
dst.op[opnum].bAtE0 = src.operators[opnum].waveform_E0;
}
// Use src.note_offset1 as modifier of MIDI note number for 2-op and 4-op instrument (+12 or -12 means modify tone in one octave up or down)
// Use src.note_offset1 in pseudo-4op mode to modify tone of second voice
// Use src.second_voice_detune to micro-detune the tone of
// second voice in pseudo-4op mode
// Use src.percussion_key_number to tell which note drum instrument must
// play in fact with no matter to "slot" where it is stored. Means, which note it
// will play when it's a melodic instrument?
// src.midi_velocity_offset can be just ignored, it is storing but not used
// So, ignore it
// src.delay_on_ms and src.delay_off_ms values are physical sounding time
// of the given timbre. It is used inside of internal ADLMIDI's channel
// management system to find the best channel in condition of "age"
// of playing note in some physical channel. You can just ignore them
// as you are using different channels management system which
// doesn't relying on those values.
}
|
Original comment by Jean Pierre Cimalando (Bitbucket: jpcima, GitHub: jpcima). @Wohlstand thanks for doing this I'll include it. I have a status update, despite being so sick lately and not very productive. I want to ask a few things too. (sorry for making a long post) To finish with this issue I had started making a simple windows control panel. It has the WOPL file loader, and it transmits them to the midi port as sysex events. For the WOPL I just extracted the code parts from libADLMIDI and I condensed it into a "miniwopl", not terribly elegant but functional. On my converter wopl2syx: I added a conversion mode to C header file. So I was able to compare a Fatman 2Op's wopl with this driver's patch hardcoded in source. I have at least some confidence now I got most of the conversion right. I am tempted of forking the project to revamp the voice management. Not only, I would like to add multi-chip, but I have encountered problems of sticky notes. (only with 4op patches) @Wohlstand how hard would it be to add realtime MIDI input to libADLMIDI? if it's a trivial thing, I might as well switch to your driver. To somewhat emulate multi-chip I start multiple instances and dispatch the midi channels between them. |
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand).
P.S. @jpcima , be healthful and don't ill 😉 :fox: |
Original comment by Jean Pierre Cimalando (Bitbucket: jpcima, GitHub: jpcima).
Actually I believe there is such a thing in windows as MIDI "driver specific messages". If that is the case, there shouldn't be need for a separate ipc channel. Anyway if I was going to talk to the MIDI driver, I told myself why not just use the sysex protocol which is already there.
So it can actually take real-time input, I utterly missed that. It's great.
If you will target ALSA at some point, it may be interesting to make it a softsynth plugin. Especially in Juce, from which you get the standalone Linux, and all the plugin formats.
I can't really call myself musician, but I really enjoy fm. A nice sy77 is often near me but it replaces by no means the good old fm chips. :)
I don't mind about desecrating perfection of Nuked OPL with my heretic work. If it sounds same to my ear without bit purity, fine enough. Who knows, I may be even tempted at one point to add >1bit panning. |
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand).
Okay, this makes sense! I accidentally though you gonna to make that through MIDI-out chain as regular SysEx event came from MIDI files. This thing is seems different one. So, as I told, use this interface only to poke driver to reload it's config from registry (where you will write changed settings from your CP) and reload the bank file from the disk or restore default embedded.
I have added it aproximately-recently, so, now you know that 😉
Оnce I gone from Windows, on Linux I use FluidSynth to play music/notes in MIDI-related software, and now I'm able to implement the daemon which will use ALSA as source of MIDI events and play them through libADLMIDI.
Not a problem ;3
Full panning is implementable, but in cost of 2x channels use and volumes balancing which is made in Jamie O'Connel's driver for Win3x/9x and in some other things like "OPL Synth Emulation" in GZDoom and kode54's ADLMIDI fork made as foobar plugin, and I gonna to backport some his features include panning stereo into my libADLMIDI. In case of emulation, just use more virtual chips to don't pay by FM channels. |
Original comment by Jean Pierre Cimalando (Bitbucket: jpcima, GitHub: jpcima). @Wohlstand After I convert my code to the new wopl library, I find that parameters of operator 1-2 are swapped, and so are operators 3-4. I exactly used the code fragment you have written. Are you sure of the operator order in your library code? it seems opposite of the previous wopl code I used, and also opposite of the internal driver format.
|
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand). Operators order at me is: #define CARRIER1 0
#define MODULATOR1 1
#define CARRIER2 2
#define MODULATOR2 3 And yeah, in So, in your case you will need to have a map: size_t opmap[4] = {
WOPL_OP_MODULATOR1, //operators 1 and 2
WOPL_OP_CARRIER1,
WOPL_OP_MODULATOR2, //operators 3 and 4
WOPL_OP_CARRIER2
};//Please re-order them to match your internal operators order
for (unsigned opnum = 0; opnum < 4; ++opnum) {
size_t srcop = opmap[opnum];
dst.op[opnum].bAt20 = src.operators[srcop].avekf_20;
dst.op[opnum].bAt40 = src.operators[srcop].ksl_l_40;
dst.op[opnum].bAt60 = src.operators[srcop].atdec_60;
dst.op[opnum].bAt80 = src.operators[srcop].susrel_80;
dst.op[opnum].bAtE0 = src.operators[srcop].waveform_E0;
} Also, please take an update of |
Original comment by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand). P.S. When you compiling libADLMIDI to use it in the driver, you may want to disable embedded MIDI sequencer - which is needed to pass MIDI files and play them through |
Original report by Wohlstand (Bitbucket: Wohlstand, GitHub: Wohlstand).
Hello!
I have some bunch of IBK files made by me, and I was used the Jamie O’Connel’s custom FM MIDI Driver to play&record music with it, but it has a poor support of drums (I can't implement full support of them).
That driver (Win 3x / Win 9x):
http://wohlsoft.ru/docs/Sounds/SMBX_OPL/SMBX_OPL_Sounds_src/Software%20for%20FM%20chip/fmsynthJamieOConnel.zip
SMB Timbre tool (DOS 16 bit) which I used to make my mods:
http://wohlsoft.ru/docs/Sounds/SMBX_OPL/SMBX_OPL_Sounds_src/Software%20for%20FM%20chip/sbtimbre.zip
My banks: http://wohlsoft.ru/docs/Sounds/SMBX_OPL/SMBX_OPL_Sounds_src/Software%20for%20FM%20chip/IBK%20banks/
EDIT:
Half year ago I found the ADLMIDI program (which I redesigned into the library) which implements an MIDI-Player over OPL chip emulator and uses a big set of various bank formats. It works light and is not overloads CPU, tested on Pentium IV machine. This also supports overclocking 18-channels 2-op and 6-channels 4-op limits by running multiple copies of chip emulators and then MIDI-player shares notes between all running chip emulators.
Now I got skills and then I quickly made a bank editor for Junglevision format:
https://github.com/Wohlstand/OPL3BankEditor (also on bitbucket too: https://bitbucket.org/Wohlstand/opl3bankeditor)
That format is supported both 2-operator, 4-operator instruments. Then later I have been added more bank formats: DMX, TMB, IBK, BNK (AdLib and HMI), and AIL (also I working on other formats too).
I think, I'll make a new file format which must support:
**EDIT2: ** The new WOPL format has been created
It is already supported by OPL3 Bank Editor and by libADLMIDI:
The full specification is here: https://github.com/Wohlstand/OPL3BankEditor/blob/master/Specifications/WOPL-and-OPLI-Specification.txt
Feel free to use it on your side!
The text was updated successfully, but these errors were encountered: