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 = { '&'  : '&amp;'  ,
                    '<'  : '&lt;'   ,
                    '>'  : '&gt;'   ,
                    '"'  : '&quot;' ,
                    '\'' : '&apos;' }

# 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

Reply via email to