Song autosaving

From Buzztrax

Jump to: navigation, search

We'd like to lower the risk of loosing the change in case of the program crashing, the computer to malfunction or whatsoever.

Regular Autosaving

A common way is to do regular saves in the background. Therefore the app would start a timer when the song has been changes. When the timer reaches 0, it does the autosaving.

Besides the application would use a lock (e.g. gconf-key or file) when starting and remove it when closing down properly. When the application starts and it find the lock still there, it would check for autosaved versions of the last file and ask the user, if he like to restore it. If the user chooses no, they are removed. If the users chooses yes, the autosave is loaded (and file-name changed to proper file-name). User can verify and save or discard the autosave.

This might have some disadvantages. Its hard to make atomic saving, if we don't block the ui. Blocking the ui would be bad. Also it might disturb the playback. A quick check shows that saving Aenathron.bmx as xml takes 0.08 sec only and as zipped xml it takes 0.1 sec. This will be different for songs with lots of waveforms.

Edit Log

A better way is to write all edit actions to a log file. Whenever the song has been saved successfully, the log is reset as well. If one opens a file and there is a non-empty log, ask the user if the log should be replayed. This is linked to the full undo, redo feature as a undo/redo stack is our log in a serialized form. For a new song this looks like

        -o- undo/redo/save pointer = NULL
         `- new/loaded song

After a bit of work, it might look like this:

action7 -o
action6 -o- undo/redo pointer
action5 -o
action4 -o
action3 -o- save pointer
action2 -o
action1 -o 
         `- new/loaded song

What can we read from this

  • we have done one undo (can do one redo)
    • if undo_ptr.has_next() we can do undo
    • if redo_ptr.has_pref() we can do redo
  • if save_ptr && save_ptr.has_next() song is changed (can save)

There are three basic operations:

create - create and add a new object

grep "_new(const BtSong" *.c

modify - change an object


destroy - remove and dispose an object



Some object exists implicitely in a song - they are always there and there is just one of them:

  • setup
  • sequence
  • song-info
  • wavetable

Other objects can exist not at all or multiple times:

  • machine (in setup)
  • wire (in setup)
  • pattern (in machine)
  • wire-pattern (in wire)
  • wave (in wavetable)
  • wave-level (in wave)

the log (v1)



 // create two objects
 BtMachine.create(plugin-name=simsyn, id=...
 BtWire.create(src-machine=..., dst-machine=...)
 // move the machine somewhere (difficult as xpos,ypos are no object properties)
 BtMachine.modify(object-id, properties.xpos=xxx, properties.ypos=yyy)

For elements where we can have multiple ones, the first param needs to be the object id.

the log (v2)



 // create two objects
 setup.create(plugin-name=simsyn, id=...
 setup.create(src-machine=..., dst-machine=...)
 // move the machine somewhere (difficult as xpos,ypos are no object properties)
 setup/machine/simsyn.modify(properties.xpos=xxx, properties.ypos=yyy)
 // add a pattern
 // set a value in a pattern (again here is no property for it)
 setup/machine/simsyn/beep.modify(tick=10, group=global, param=freq, value=100.0)


libbuzztrax-core can provide the session-log (as a singleton). Certain functions would write the log-entries. The session-log would provide helpers to e.g. build the object path. The persistence class has static serialization/deserialization functions. The session-log object has the replay method. The replay method can check the object-path, lookup the object and then dispatch to the respective class. Could we implement something like GstChildProxy to provide the object lookup.

Would be cool if we could reuse the persistence interface implementation.

Object Path

To do a lookup for a given path "setup/machine/simsyn/beep" we'll always start at song. This looks at the first item "setup" and dispatches or errors out (e.g. if path is "oops/peng/..."). Setup in turn looks at the 2nd item "machine", it can resolv machines itself, thus looks at the 3rd item and resolves.

When objects are created we'll assign them the path, so that when logging, we have the path ready. When we build the song, we tell setup, that its path is "setup" and wavetable that its path is "wavetable". When we add a machine to setup, setup tells the machine that the path is "setup/machines/simsyn", that is "<own-path>/machines/<machine-id>".

  // recursively called until it return NULL for unknown/failure or an object
  GObject *bt_object_path_lookup(GObject *object, gchar *path);
  // ideally each object gets the path assigned upon object creation
  bt_object_path_set(GObject *object, gchar *path);

Open items

  • add timestamps? could be used to do a realtime replay for e.g. demos
  • how to identify objects and how to generalize how they can be looked up
Support Us


GStreamer Logo
Become a Friend of GNOME
Linux Sound Logo
GNU Library Public Licence
GNU Free Documentation License 1.2