Hi! Here is a script I've been working on, which converts Hydrogen drumkits to BEAST drumkits. This is a nice way to improve drums in BEAST because
- BEAST currently ships only one drumkit, Hydrogen has a few more - some Hydrogen drumkits sound really good (i.e. Hydrogen GMkit sounds better than our retrokit) - since debian (ubuntu) packages Hydrogen drumkits, there should be no license issues when importing/redistributing these - conversion needs not be done by the end user - we can simply ship the resulting .bsewave files I've attached my script, usage information is at the start of the script. I've attached a simple test drum loop, without the actual waves included, so you can load GMkit into it and play with different kits. GMkit is a good start. Limitations: There is one thing that we need in BEAST before shipping imported kits: the author, license, ... information is parsed by the script, but is not imported, since bsewave xinfos for these do not exist. So I cannot import this information, but we probably want to have it, so BEAST should have xinfos and read xinfos and display it somewhere in the UI. Since the Hydrogen kits are multilayered (different gain implies different wave files), and since BEAST afaik doesn't support this, I use only the settings for the loudest layer - but that should be OK. Also there are a few settings I ignore during import, but as long as the resulting kits sound good, that should be OK too. Cu... Stefan -- Stefan Westerfeld, http://space.twc.de/~stefan
#!/usr/bin/env python # Hydrogen to BEAST drumkit converter # # Usage: # # apt install hydrogen hydrogen-drumkits # # himport.py # list all available hydrogen drumkits # himport.py GMkit # import one drumkit (from list) # himport.py all # import all drumkits # # Author: Stefan Westerfeld <[email protected]> # # CC0 Public Domain: http://creativecommons.org/publicdomain/zero/1.0 import xml.etree.ElementTree as ET import os import sys import subprocess def die (message): print >> sys.stderr, "himport.py: " + message exit (1) def system_or_die (command): print "+++ %s" % command return_code = subprocess.call (command, shell=True) if return_code != 0: die ("executing command '%s' failed, return_code=%d" % (command, return_code)) def get_channels (filename): return int (subprocess.check_output (["soxi", "-c", filename])) def get_format (filename): return subprocess.check_output (["soxi", "-t", filename]).strip() def list_kits(): all_kits = [] for kit in os.listdir ("/usr/share/hydrogen/data/drumkits"): try: os.stat ("/usr/share/hydrogen/data/drumkits/%s/drumkit.xml" % kit) all_kits.append (kit) except: pass return all_kits class HydrogenKit: pass class HydrogenNote: pass def normalize_tag (child): name = child.tag # some (newer) drumkit tags use namespace: http://www.hydrogen-music.org/drumkit if name[0] == "{": uri, tag = name[1:].split("}") if uri == "http://www.hydrogen-music.org/drumkit": return tag else: die ("normalize_tag: found unsupported namespace %s" % uri) else: # no namespace means we're reading an old file without namespaces return name def parse_kit (dir_name): kit_name = dir_name kit_dir = "/usr/share/hydrogen/data/drumkits/" + kit_name kit = kit_dir + "/drumkit.xml" tree = ET.parse (kit) root = tree.getroot() instrument_index = 0 hydrogen_kit = HydrogenKit() hydrogen_kit.notes = [] for child in root: if normalize_tag (child) == "name": hydrogen_kit.name = child.text elif normalize_tag (child) == "author": hydrogen_kit.author = child.text elif normalize_tag (child) == "info": hydrogen_kit.info = child.text elif normalize_tag (child) == "license": hydrogen_kit.license = child.text elif normalize_tag (child) == "instrumentList": for il_child in child: # import each instrument if normalize_tag (il_child) == "instrument": note_filename = None note_volume = 1 note_gain = 1 for ichild in il_child: if normalize_tag (ichild) == "volume": note_volume = float (ichild.text) if normalize_tag (ichild) == "layer": for lchild in ichild: if normalize_tag (lchild) == "min": layer_min = float (lchild.text) if normalize_tag (lchild) == "max": layer_max = float (lchild.text) if normalize_tag (lchild) == "filename": layer_filename = lchild.text if normalize_tag (lchild) == "gain": layer_gain = float (lchild.text) if layer_min <= 0.9999 and layer_max >= 0.9999: # only import layer with the loudest sample note_filename = layer_filename note_gain = layer_gain if normalize_tag (ichild) == "filename": # legacy: old hydrogen drumkits have no layers, only one global filename note_filename = ichild.text if note_filename: hydrogen_note = HydrogenNote() hydrogen_note.index = instrument_index hydrogen_note.filename = note_filename hydrogen_note.volume = note_volume hydrogen_note.gain = note_gain try: os.stat (kit_dir + "/" + note_filename) hydrogen_kit.notes.append (hydrogen_note) except: # we don't die here because there are broken drumkits that reference non-existent files print "WARNING: soundfile %s missing, not importing that instrument" % (kit_dir + "/" + note_filename) else: print "note_filename", note_filename print "note_volume", note_volume print "note_gain", note_gain print "WARNING: instrument from kit %s could not be imported, missing required fields" % kit_name instrument_index += 1 else: print "UNPARSED INSTRUMENT LIST CHILD:", il_child.tag else: print "UNPARSED CHILD:", child.tag return hydrogen_kit def do_import (dir_name): kit_name = dir_name kit_dir = "/usr/share/hydrogen/data/drumkits/" + kit_name kit = kit_dir + "/drumkit.xml" bsewave = kit_name + ".bsewave" hydrogen_kit = parse_kit (dir_name) # figure out number of channels for this drumkit channels = 0 for note in hydrogen_kit.notes: note.channels = get_channels (kit_dir + "/" + note.filename) channels = max (channels, note.channels) if channels != 1 and channels != 2: die ("unsupported channels: %d" % channels) system_or_die ("rm -f '%s'" % bsewave) system_or_die ("bsewavetool create '%s' %d" % (bsewave, channels)) system_or_die ("bsewavetool xinfo '%s' --wave play-type=plain-wave-%d label='%s'" % (bsewave, channels, kit_name)) for note in hydrogen_kit.notes: full_filename = kit_dir + "/" + note.filename print "importing note %d, filename %s, channels %d" % (note.index, note.filename, note.channels) # mono to stereo conversion is necessary if some notes are mono and others # are stereo in order to get a working bsewave in this case, we convert all # mono files to stereo automatically convert_to_stereo = (note.channels == 1 and channels == 2) if convert_to_stereo: tmp_filename = "himport.tmp%d.wav" % os.getpid() system_or_die ("sox '%s' -c 2 '%s'" % (full_filename, tmp_filename)) full_filename = tmp_filename # convert every file that is not flac to flac convert_to_flac = get_format (full_filename) != "flac" if (convert_to_flac): tmp_flac_filename = "himport.tmp%d.flac" % os.getpid() system_or_die ("sox '%s' '%s'" % (full_filename, tmp_flac_filename)) full_filename = tmp_flac_filename system_or_die ("bsewavetool add-chunk '%s' -m=%d '%s'" % (bsewave, note.index + 36, full_filename)) system_or_die ("bsewavetool xinfo '%s' -m=%d volume=%f" % (bsewave, note.index + 36, note.volume * note.gain)) if convert_to_stereo: os.unlink (tmp_filename) if convert_to_flac: os.unlink (tmp_flac_filename) if len (sys.argv) == 2: if sys.argv[1] == "all": for kit in list_kits(): do_import (kit) else: do_import (sys.argv[1]) else: print "usage: himport.py <kit_name>" print print "where kit_name is one of the drumkits in /usr/share/hydrogen/data/drumkits:" for kit in list_kits(): print " " + kit
; BseProject (bse-version "0.10.1") (container-child "BseWaveRepo::Wave-Repository" (modification-time "2017-05-12 10:35:40") (creation-time "2017-05-12 10:35:40") (license "Creative Commons Attribution 2.5 (http://creativecommons.org/licenses/by/2.5/)") (author "Stefan Westerfeld") (container-child "BseWave::GMkit" (load-wave "/home/stefan/src/sandbox/himport/GMkit.bsewave" "GMkit")) (container-child "BseWave::retrokit" (load-wave "/usr/local/beast/share/beast/samples/retrokit.bsewave" "retrokit"))) (container-child "BseSoundFontRepo::Sound-Font-Repository" (modification-time "2017-05-12 10:35:40") (creation-time "2017-05-12 10:35:40") (license "Creative Commons Attribution 2.5 (http://creativecommons.org/licenses/by/2.5/)") (author "Stefan Westerfeld")) (container-child "BseSong::Titel" (bpm 120) (musical_tuning OD_12_TET) (auto_activate 1) (loop-right 3072) (loop-left 0) (loop-enabled #t) (denominator 4) (numerator 4) (tpqn 384) (modification-time "2017-05-12 10:35:56") (creation-time "2017-05-12 10:35:56") (license "Creative Commons Attribution 2.5 (http://creativecommons.org/licenses/by/2.5/)") (author "Stefan Westerfeld") (container-child "BsePart::Part-1" (n-channels 2) (insert-notes 0 (0x00000 0x180 36) (0x000c0 0x0c0 42) (0x00180 0x180 38) (0x00240 0x0c0 42) (0x00300 0x0bf 36) (0x003c0 0x0bf 36) (0x00480 0x180 38) (0x00540 0x0c0 42)) (insert-notes 1 (0x00000 0x0bf 42) (0x00180 0x0c0 42) (0x00300 0x0c0 42) (0x003c0 0x0c0 42) (0x00480 0x0c0 42))) (container-child "BseBus::Master-1" (master-output #t) (right-volume 1) (left-volume 1) (bus-input (link 1 "Track-01"))) (container-child "BseTrack::Track-01" (n-voices 16) (wave (link 2 "Wave-Repository:GMkit")) (insert-part 0 (link 1 "Part-1")) (insert-part 1536 (link 1 "Part-1")))) (container-child "BseCSynth::BQS Reverb" (auto_activate 0) (modification-time "2017-05-12 10:38:53") (creation-time "2005-08-07 14:23:05") (license "Provided \\\"as is\\\", WITHOUT ANY WARRANTY (http://beast.testbit.eu/LICENSE-AS-IS)") (author "Stefan Westerfeld") (container-child "BseSubIPort::SubIPort-1" (pos-y 1.04) (pos-x -4.21)) (container-child "BseSubOPort::SubOPort-1" (pos-y 1.07) (pos-x 2.13) (source-input "input-1" (link 1 "FreeVerb-1") "left-audio-out") (source-input "input-2" (link 1 "FreeVerb-1") "right-audio-out")) (container-child "BseFreeVerb::FreeVerb-1" (width 100) (dry-level 1) (wet-level 0.34796237945556641) (damping 40) (room-size 0.8399999737739563) (pos-y 1.07) (pos-x -1.09) (source-input "left-audio-in" (link 1 "SubIPort-1") "output-1") (source-input "right-audio-in" (link 1 "SubIPort-1") "output-2")))
_______________________________________________ beast mailing list [email protected] https://mail.gnome.org/mailman/listinfo/beast
