SysEx Librarian/Editor


#1

Ok, let us first look what we have at the moment:

Similar Open Source Projects

SynthEd:
http://synthed.sourceforge.net/

Website says:
"SynthEd is inactive"
Uses Python as programming language.

JSynthLib:
http://www.jsynthlib.org/

Still Beta, not many synth supported, programming skills required
to add support for new devices.
Slow development.
Java is not well accepted in the Audio scene!

SysExxer:
http://sysexxer.sourceforge.net/

Is not a real librarian/editor but a SysEx-Tool.
Based on KDE, so Linux-dependant.

Closed Source

Emagic SoundDiver

Discontinued.

Seems to have the best user interface so far.

MIDIQuest
http://www.squest.com/index.html

Has a bad repute regarding stability, performance and user interface.


Main goals

[list]
[] Open Source, written in C++ using JUCE[/]
[] Supported platforms: Mac OS X, Linux and MS Windows[/]
[] It should be possible for non-developers to add librarian/editor support for a MIDI device,
my idea is to use XML-files for that, one for librarian and one for editor support (the latest JSynthLib version includes an XML-format which may be a good point to start).
Device families for similar devices (e.g. Emu Proteus 2000 family with Proteus 2000, XL-1, Mo Phatt, etc.).
Maybe one driver with switchable options or so.[/
]
[] Generic driver for simple dump/send support for not supported devices (?).[/]
[] Easy user interface without unnecessary optical stuff[/]
[] Configurable per text files (more can be added later)[/]
[] No automatic scanning (that sucks IMHO on SoundDiver),
devices should be added manually[/
]
[] Function for pre-listening the sound, maybe including a small
virtual piano (like this one) or a very small & basic sequencer[/
]
[] Check for doubled settings (mostly sounds as the main use will be as a generic synth librarian/editor)[/]
[] A simple SysEx string editor should be included for hardcore users ;-)[/]
[] Good copy & paste support (e.g. for using init sounds as a base for new sounds)[/][/list]

Requisites for this idea to become real

[list]
[] I need at least one additional developer for Linux and one for Mac OS X as I’m Windows-based at the moment. :oops:[/]
[] Much help from users with many synths willing to add support for new devices and with the patience to test them[/]
[] Time :wink: [/]
[] Much help from the JUCE community since my C++ experience is really basic. At work I’m using Java so I’m not that much into the pointer and memory-management stuff. :oops: [/]
[] Someone with experience in XML to help me designing the XML format for librarian and editor support.[/][/list]

Now I’m ready for you comments. :wink:


#2

Some comments would be nice. :wink:

Or is here nobody else with hardware synths? :shock:

I think I’ll start trying to program an editor for one of my simple synthesizers like the Yamaha FB-01 and develop the XML-format in parallel.

Are there any XML-experts here?
I want to avoid to create a format which needs major changes later on because of design errors.
E.g. I need some sort of loops and switchable parameters for some family-device-members.


#3

Only thing I know about synths is almost how to play them. :stuck_out_tongue:

But XML is easy, very generic data container. Just develop a class and structure linking designed to be called upon how the xml is built and output, it is very simple once you figure out how.


#4

yeah, xml is really a godsend when it comes to data storage, it makes saving and reloading data so simple. I’ll probably get a bit carried away here writing this, and it may be absolutely obvious to you - let me apologise in advance if any of this sounds patronising, i’m a teacher at heart and like to try to make things clear. I just thought it might be useful to explain how I personally go about things.

If my program needs to have any of its data stored in a file, that data is always linked in some way to some type of object. A program will invariably have many object types in, so that means there are usually a bunch of different types of object, each holding data on a different thing.

Thanks to the object oriented nature of C++, and with a bit of programming discipline, you usually have all your data inherently grouped in logical structures due to the design of your object classes. For example, you might have a ‘control’ object, which can hold data on a particular parameter controller (e.g. a Midi CC parameter on your device). That could be a member of an array of these objects, which is in turn a member of a ‘control list’ object, which is in turn a member of a higher level ‘device’ object.

The brilliant thing about this sort of object level structuring is that you can give each class the ability to export and import information about itself really easily. I always give my classes the following two functions:

XmlElement* xml();

void processXml (XmlElement* tag); 

The first one is designed to return all of the object’s data as an XmlElement (a pointer to a dynamically allocated object, because that’s how all the XmlElement operations tend to get done). The second takes a given XmlElement object and extracts all the data from it, filling the object with the new information.

If you work in these functions at every level of your object structure, you can quickly see how such things make life easy. To illustrate, let me go back to the example above; a very basic ‘controller’ object type is shown below, using the xml functions:

class Controller
{
   int number;
   String name;
   // etc..

   ...

   XmlElement* xml ()
   {
      // create a new XmlElement (our 'tag')...
      XmlElement* tag = new XmlElement (T("Controller"));

      // give it our data as attributes...
      tag->setAttribute (T("name"), name);
      tag->setAttribute (T("num"), number);
      // etc...
      return tag;
   }

   void processXml (XmlElement* tag)
   {
      if (tag) // check the tag isn't null
      {
         if (tag->hasTagName (T("Controller")) )
         {
             // get the data from the attributes...
             name = tag->getStringAttribute (T("name"));
             number = tag->getIntAttribute (T("num"));
         }
      }
   }

   ...
};

That shows roughly how to give your class a way of storing and recalling its data to and from an outside Xml source. Now, we’ve programmed this in, every Controller object has this ability, and can return a single tag (containing all of its data) with a single function call. Let us imagine that we have a ControllerList class too, which just holds an array of these…

class ControllerList
{
   OwnedArray <Controller> list;
   String listName;
   // etc..

   ...

   XmlElement* xml ()
   {
      // create a new XmlElement tag to hold our list...
      XmlElement* tag = new XmlElement (T("ControllerList"));
      // set attributes from this object's data...
      tag->setAttribute (T("name"), name);

      // step thru each contained object and add its tag...
      for (int i=0; i<list.size (); i++)
      {
         tag->addChildElement (list[i]->xml());
      }

      // etc...
      return tag;
   }

   void processXml (XmlElement* tag)
   {
      if (tag)
      {
         if (tag->hasTagName (T("ControllerList"))
         {
            listName = tag->getStringAttribute (T("name"));

            // step thru each contained tag and create objects...
            tag = tag->getFirstChildElement ();
            while (tag)
            {
               // create a new object for this tag...
               Controller* control = new Controller;
               // give the object the tag to read itself...
               control->processXml (tag);
               // add the new object to our array!
               list.add (control);

               tag = tag->getNextChildElement ();
            }
         }
      }
   }

   ...
};

Remember that this is already a logically structured class hierarchy; the ControllerList ‘has’ data on a bunch of Controllers, because it makes sense to do so. Thus, in our Xml version of the data, it also makes sense to keep our Controller tags inside a ControllerList tag. Because we know that we’ll have the Controllers inside the list, we can tell the list to call the xml() function we’ve already made in order to get the data- it’s really simple! (although that sentence was a bit convoluted :hihi: )

I won’t show another block of code because it’d be pretty similar to those above, but a higher level ‘device’ class would do the same again; it would generate an XmlElement containing attributes describing the device itself, (e.g. model, manufacturer, inputs, outputs, etc), and it could contain a ControllerList (or several) amongst myriad other things. You can hopefully see that adding ALL the data from the ControllerList is now as simple as simply adding a single child tag element obtained from the ControllerList::xml() function.

Of course, your actual program will likely follow a different design to the very basic example i’ve shown here, but i hope it’s helped to illustrate a simple approach to getting data in and out of your objects. Just be aware of the structure your types will have, and give any classes that hold important data the means to speak via xml.

I spent a large part of the summer writing a .midnam editor (similar to cherryPicker) entirely in Juce. I got the majority of it working, reading and writing the midnam files. If you’ve not heard of them, do a google for midnam or cherry picker; basically it’s the mac’s desired format for holding MIDI device name info. It’s not really specifically geared for sysex stuff, but it might be of interest to you. You could of course expand upon the format to include your own sysex librarian data. In its current form it holds program names, bank names, channel bank groups (called NameSets) control name lists and note name lists. There’s a lot of crap in the format too, and different ways that certain devs have used it. My midnam classes seem okay at reading most of the variants.

I need to tidy them up quite a lot, but if you’re interested in making use of the MidNam format for this, i’ll put my midnam class library up for you to have a look at. It took a lot of learning and effort to make it, and then it turned out to be a complete waste of my time (the project it was for was changed to use a different format!)… but it does mean that i now have a working midnam library reader/writer (in juce, naturally). I also wrote a neat little ‘librarian’ thing which scans the midnam directories, reading the files and automatically building up a little ‘device library’ so you can pick your devices by manufacturer.

There are a great many .midnam files around on the internet, but they are largely unused by PC musicians; this is because the only PC software that supports them is protools! On the mac, they’re used by OSX’s midi device manager (i believe), MOTU DP and of course PT. I was hoping that I could help make it a bit more of a cross platform thing, but got a bit fed up when my work seemed in vain!

Let me know if you’d like to know more.

Crumbs, what a rant that was!


#5

Thanks, I will have a look at the midnam file format. :slight_smile:


#6

Having the class constructor have an override to take an xmlelement would work nicely.


#7

yes indeed, that’s something else i do- although it’s usually just a case of putting a call to processXml in that constructor.

xml is a miracle :slight_smile: as is juce


#8

No kidding; Juce is what gui creation should of been like from the beginning…


#9

I have checked my schedule and unfortunately I have no time for this project for at least the next six months.

I’m too much into synth-diy right now and as I’m working in front of computers the whole day it is more fun to do something without computers in my spare time. :cry:

Damn it, I want more hours for a day! :wink:


#10