Hi,
all you need to get started is the list of voices provided by the
device. The attached bash (mine) and Python scripts (provided
by Ryurick M. Hristev <ryurick.hris...@canterbury.ac.nz>) should
do the rest.
Regards.
On 1/1/22 8:45 PM, Kevin Cole wrote:
Hi,
I'm VERY new to Rosegarden, and MIDI devices in general. I'm on a
Pop!_OS 20.04 Linux box (effectively Ubuntu 20.04), running Rosegarden
19.12. Someone sold me a used Yamaha PSR-220 cheap, and it seems to
be in good working condition -- not that I've ever had a keyboard
before.
So. As I understand things, it would be nice to have an ".rgd" file
that corresponds to my keyboard. I couldn't find such a file, but
decided to see if I could create one. (It wasn't obvious to me that
the existing ones were gzipped, but I found that out when I tried to
edit one.)
A few questions then:
1. Is there already a PSR-220 file somewhere? If so, where?
2. If not, is there a good description of how to create one? Looking
at a gunzipped Yamaha-PSR-E223-YPT-220.rgd, it looks like there's an
awful lot of info that I'm not certain how to collect.
Thanks!
_______________________________________________
Rosegarden-user mailing list
Rosegarden-user@lists.sourceforge.net - use the link below to unsubscribe
https://lists.sourceforge.net/lists/listinfo/rosegarden-user
#!/usr/bin/python
# txt2rgd:
#
# DESCR: Converts from a text based voice list of an instrument/sound card
# to the XML mased *.rgd format understood by Rosegarden
#
# USAGE: txt2rgd <input file> <output file>
#
# where the input file contains the voice list in text format
# and the output file is the name/location of the *.rgd file
# to be generated
#
# The format of the imput file is any reasonable space or comma
# separated (CSV) list of parameters:
# a. comments start with '#' and continue to the end of the line
# and may occur anywhere
# b. blank lines (or containing whitespace and/or comments only)
# are ignored
# c. any line containing data must have 5 fields (+ comments, ignored):
# <user marker> <MSB> <LSB> <Prog#> <Voice> # optional comment
# where:
# <user marker> is a field used to help data entry,
# could be anything, e.g. voice number
# but must not contain whitespace, ',' or '#'
# this field is ignored in the output
# <MSB> MSB (Most Significant Byte) of the bank number
# range 0-127
# <LSB> LSB (Least Significant Byte) of the bank number
# range 0-127
# <Prog#> Program number of the voice witin the current bank
# range 1-128
# <Voice> The name of the voice as desired
# it is best to type it in as specified by the
# manufacturer or the GM/XG/GS standard if available
#
# MSB and LSB are 0 for the GM only instruments.
#
# Given the notes 2 & 3 some manual intervention on the output
# may be required to achieve best results.
#
# NOTES: 1. This script may use features available only in Python 2.2 and above
# 2. The device 'id' and 'name' tags are hardwired
# (apparently the id must be 0)
# 3. If possible we try to guess a bank name according to XG
# otherwise the bank name is autogenerated as "<MSB>-<LSB>"
#
# BUGS: 1. The name of the voice can't contain the ',' and '#'
#
# WARNING: 1. The output file will be overwritten if it already exists
#
# VER: 0.3 18 Feb 2003
#
# AUTH: Ryurick M. Hristev <ryurick.hris...@canterbury.ac.nz>
#
# LICENSE: This program is too trivial to license but if you need one
# pick any from: Public Domain, BSD, LGPL or GPL
# at your convenience.
import sys
import os
import string
import re
import gzip
if len(sys.argv) != 3 :
print "\n\tUsage: " + sys.argv[0] + " <in_text_filename> <out_rdb_filename>\n"
print "\tSee the comments inside the script for detailed usage.\n"
sys.exit(1)
in_file = sys.argv[1]
out_file = sys.argv[2]
# markers
line_no = 0
parse_err_no = 0
# will store the structure in a dict of dicts:
# {MSB: {LSB: {Prog#: Voice}}}
data = {}
# each input data line have: <user_field> MSB LSB Prog <Voice_name>
# (after whitespace stripping)
pattern = re.compile("^(?P<user>[^\s,]+)\s*(,|\s)\s*(?P<MSB>\d{1,3})\s*(,|\s)\s*(?P<LSB>\d{1,3})\s*(,|\s)\s*(?P<Prog>\d{1,3})\s*(,|\s)\s*(?P<Voice>.+)$")
# convert to XML friendly output
converter_table = { '&' : '&' ,
'<' : '<' ,
'>' : '>' ,
'"' : '"' ,
'\'' : ''' }
# generic bank names { MSB : { LSB : Name } }
# Sources: XG Specifications 1.26, XG Guidebook
# The '(?)' means that the docs do not specify an exact naming convention
# so it's my interpretation.
# Naming is very strongly biased towards XG
# From XG Extra Vol.1 No 6
# - The voices in banks 1 - 8 are essentially similar to their GM equivalent
# in bank 0, with only minor (though often highly effective) modifications.
# - The voices in banks 9 - 15 differ from their GM counterparts mainly by
# having different AEG (Amplifier Envelope Generator) settings.
# - The voices in banks 16 - 23 differ from their GM counterparts mainly by
# having different filter settings.
# - The voices in banks 24 - 31 differ from their GM counterparts mainly by
# having different FEG (Filter Envelope Generator) settings.
# - The voices in banks 32 - 39 differ from their GM counterparts mainly by
# layering two elements and then detuning the elements or changing their
# pitch relative to one another in various ways.
# - The voices in banks 40 - 47 differ from their GM counterparts mainly by
# layering two elements, with the new wave different from the basic one.
# - Banks 48 - 63 are reserved for future use.
# - The voices in banks 64 - 72 differ from their GM counterparts in that
# they use a different wavetable (though they are designed to create a
# similar kind of sound).
# - The voices in banks 96 - 101 often have little or no resemblance to their
# GM counterpart.
# - Banks 112 - 127 may be used by future XG instruments for the
# storage of user-created voices.
bank_names = { 0 : { 0 : 'GM' , # XG Spec
1 : 'KSP' , # XG Spec
2 : 'KSP 2' , # XG Guide (?)
3 : 'Stereo' , # XG Spec
4 : 'Stereo 2' , # XG Guide (?)
5 : 'Stereo 3' , # XG Guide (?)
6 : 'Single Element' , # XG Spec/XG Guide
8 : 'Slow Attack' , # XG Spec/XG Guide
9 : 'Fast Attack' , # XG Guide
10 : 'Long Release' , # XG Guide
11 : 'Short Release' , # XG Guide
12 : 'Fast Decay' , # XG Spec
13 : 'Slow Decay' , # XG Guide
14 : 'Double Attack' , # XG Spec
16 : 'Bright' , # XG Spec
17 : 'Bright 2' , # XG Spec (?)
18 : 'Dark' , # XG Spec
19 : 'Dark 2' , # XG Spec (?)
20 : 'Resonant' , # XG Spec
24 : 'Attack Transient' , # XG Spec/XG Guide
25 : 'Release Transient' , # XG Spec
26 : 'Sweep' , # XG Guide
27 : 'Rezo Sweep' , # XG Spec
28 : 'Muted' , # XG Spec
32 : 'Detune 1' , # XG Spec
33 : 'Detune 2' , # XG Spec
34 : 'Detune 3' , # XG Spec
35 : 'Octave Layered 1' , # XG Spec/XG Guide
36 : 'Octave Layered 2' , # XG Spec/XG Guide
37 : 'Fifth Layered 1' , # XG Spec/XG Guide
38 : 'Fifth Layered 2' , # XG Spec/XG Guide
39 : 'Bend Up/Down' , # XG Spec/XG Guide
40 : 'Tutti' , # XG Spec
41 : 'Tutti 2' , # XG Spec (?)
42 : 'Tutti 3' , # XG Spec (?)
43 : 'Velocity Switch' , # XG Spec
43 : 'Velocity Switch 2' , # XG Guide (?)
45 : 'Velocity Crossfade' , # XG Guide
46 : 'Velocity Crossfade 2' , # XG Guide (?)
64 : 'Other Wave' }, # XG Spec
64 : { 0 : 'SFX' }, # XG Spec
127 : { 0 : 'XG Rhythm Kits' } # XG Guide (?)
}
# Python Cookbook pg. 88
def multiple_replace(dict, text) :
regex = re.compile("|".join(map(re.escape, dict.keys())))
return regex.sub(lambda match: dict[match.group(0)], text)
IN_FH = open(in_file, 'r')
for line in IN_FH.readlines() :
line_no += 1
line = string.split(line, '#')[0]
line = string.strip(line)
if len(line) == 0 :
continue
matched_line = pattern.match(line)
# if there is no match then no group is generated (all or none)
try:
user = matched_line.group('user')
MSB = string.atoi(matched_line.group('MSB'))
LSB = string.atoi(matched_line.group('LSB'))
Prog = string.atoi(matched_line.group('Prog'))
Voice = matched_line.group('Voice')
except AttributeError:
print "\nInput error at line ", line_no, ", read:", line
print "Skipping line ... continuing parsing..."
parse_err_no += 1
continue
# make it XML friendly
Voice = multiple_replace(converter_table, Voice)
# Range check on MSB, LSB and Prog#
if MSB < 0 or MSB > 127 :
print "\nInput error at line ", line_no, ", read:", line
print "Second parameter (MSB) is out of range, must be between 0 and 127."
print "Skipping line ... continuing parsing..."
parse_err_no += 1
continue
if LSB < 0 or LSB > 127 :
print "\nInput error at line ", line_no, ", read:", line
print "Third parameter (LSB) is out of range, must be between 0 and 127."
print "Skipping line ... continuing parsing..."
parse_err_no += 1
continue
if Prog < 1 or Prog > 128 :
print "\nInput error at line ", line_no, ", read:", line
print "Fourth parameter is out of range, must be between 1 and 128."
print "Skipping line ... continuing parsing..."
parse_err_no += 1
continue
# Some confusion here: the program change number starts at 1
# while the program id starts at 0; substract 1
Prog -= 1
# create embedded dicts as required
try:
data[MSB][LSB][Prog] = Voice
except KeyError:
try:
data[MSB][LSB] = {Prog : Voice}
except KeyError:
data[MSB] = {LSB : { Prog : Voice}}
IN_FH.close()
if parse_err_no != 0 :
print "\nThere were ", parse_err_no, " error(s) in the input file."
print "Please correct the error(s) before proceeding forward."
print "No output was generated.\n"
sys.exit(1)
OUT_FH = gzip.open(out_file, 'w')
print >> OUT_FH, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
print >> OUT_FH, "<!DOCTYPE rosegarden-data>"
print >> OUT_FH, "<rosegarden-data>"
print >> OUT_FH, "<studio thrufilter=\"0\" recordfilter=\"0\">\n"
# WARNING: id here must be 0!
print >> OUT_FH, "<device id=\"0\" name=\"Unnamed\" type=\"midi\">\n"
# placeholder
print >> OUT_FH, "\t<librarian name=\"Unknown\" email=\"unknown\"/>\n"
# Add instruments, required ('midi' type instruments start at 2000)
# *** These should no longer be necessary
#for i in range(16) :
# print >> OUT_FH, "\t<instrument id=\"%u\" channel=\"%u\" type=\"midi\">" %(2000 + i, i)
# print >> OUT_FH, "\t\t<pan value=\"64\"/>"
# print >> OUT_FH, "\t\t<volume value=\"100\"/>"
# print >> OUT_FH, "\t\t<reverb value=\"0\"/>"
# print >> OUT_FH, "\t\t<chorus value=\"0\"/>"
# print >> OUT_FH, "\t\t<filter value=\"127\"/>"
# print >> OUT_FH, "\t\t<resonance value=\"0\"/>"
# print >> OUT_FH, "\t\t<attack value=\"0\"/>"
# print >> OUT_FH, "\t\t<release value=\"0\"/>"
# print >> OUT_FH, "\t</instrument>\n"
for MSB in data.keys() :
for LSB in data[MSB].keys() :
# dig out a name if possible, else default to bank name as MSB-LSB
if bank_names.has_key(MSB) and bank_names[MSB].has_key(LSB) :
print >> OUT_FH, "\t<bank name=\"%s\" msb=\"%u\" lsb=\"%u\">" % (bank_names[MSB][LSB], MSB, LSB)
else :
print >> OUT_FH, "\t<bank name=\"%03u-%03u\" msb=\"%u\" lsb=\"%u\">" % (MSB, LSB, MSB, LSB)
list = data[MSB][LSB].keys()
list.sort()
for prog in list :
print >> OUT_FH, "\t\t<program id=\"%u\" name=\"%s\"/>" % (prog, data[MSB][LSB][prog])
print >> OUT_FH, "\t</bank>\n"
print >> OUT_FH, "</device>"
print >> OUT_FH, "</studio>"
print >> OUT_FH, "</rosegarden-data>"
OUT_FH.close()
#!/bin/bash
#make input file for txt2rdg.py from program name lists
# usage: ./mkrgd MSB LSB Prog-num < prog-name-list > outfile
usage(){
echo "Usage: $0 <MSB> <LSB> <prog-num-start> <prog-num-end> <
<prog-list-file>"
}
if [[ $# -eq 4 ]]
then
for ((i=$3;i<=$4;i++))
do
read name
echo program-$i' '$1' '$2' '$i' '$name
done
else usage
fi
_______________________________________________
Rosegarden-user mailing list
Rosegarden-user@lists.sourceforge.net - use the link below to unsubscribe
https://lists.sourceforge.net/lists/listinfo/rosegarden-user