rism-digital / verovio

🎵 Music notation engraving library for MEI with MusicXML and Humdrum support and various toolkits (JavaScript, Python)
https://www.verovio.org
GNU Lesser General Public License v3.0
663 stars 184 forks source link

MIDI instrument number to name mapping function #812

Closed craigsapp closed 10 months ago

craigsapp commented 6 years ago

In libmei/atttypes.h, the enumeration of General MIDI instrument names starts at 1 (with undefined assigned to 0):

https://github.com/rism-ch/verovio/blob/28ec3b58dde0f20fd20a43ff1e9328ccd05ad681/libmei/atttypes.h#L673-L851

enum data_MIDINAMES {
    MIDINAMES_NONE = 0,
    MIDINAMES_Acoustic_Grand_Piano,
    MIDINAMES_Bright_Acoustic_Piano,
    MIDINAMES_Electric_Grand_Piano,
    MIDINAMES_Honky_tonk_Piano,

However the instrDef@midi.instrnum values start at 0 rather than one:

http://music-encoding.org/guidelines/v3/data-types/data.midinames.html

So, line 674 above should instead be:

    MIDINAMES_NONE = -1

Related to https://github.com/music-encoding/music-encoding/issues/510

craigsapp commented 6 years ago

But now I see that the percussion instruments are appended to the end of the list:

    MIDINAMES_Gunshot,                (last general MIDI instrument, patch change 127)
    MIDINAMES_Acoustic_Bass_Drum,     (first general MIDI percussion, key number 35)
    MIDINAMES_Bass_Drum_1,
    MIDINAMES_Side_Stick,

So the enumeration is not intended to match the General MIDI patch-change numbers. But I now am wondering how I can give the General MIDI instrument number to a function and have it return the string name of the instrument? (different from dealing with percussion, which would be some sort of other function to map percussion key numbers to percussion names, perhaps).

There is a list in libmei/atttypes.h:

https://github.com/rism-ch/verovio/blob/b592c1ac8e14080a087cf37b4fc9a66c2fa67b06/libmei/attconverter.cpp#L1417-L1548

But this cannot be accessed directly (since the enumerations of the instruments could change compared to the General MIDI instrument number values).

lpugin commented 6 years ago

What is the request? You still want MIDINAMES_NONE to be -1?

craigsapp commented 6 years ago

No. Instead It would be nice to have a function that takes a General MIDI instrument number (integer indexed from 0) and have it return the MEI string name. data_MIDINAMES includes both General MIDI instruments as well as the percussion key numbers on channel 10, so having data_MIDINAMES exactly match the values of General MIDI instrumens is not necessary.

Here is how I am currently doing that:

https://github.com/rism-ch/verovio/blob/451772efde9e88b1dcc36b9bd5048cfa5197bea0/src/iohumdrum.cpp#L2547-L2563

This is naughty:

     data_MIDINAMES idval = (data_MIDINAMES)(gmpc + 1); 
     idef->SetMidiInstrname(idval); 

Since the data_MIDINAMES enumeration could change, causing a bug in this code. I am taking a General MIDI instrument number (from 0 to 127) and casting it to be of type data_MIDINAMES enumeration.

Probably what I really want is a function similar to:

     idef->SetMidiInstrname(idval);

which inputs a General MIDI instrument number instead of a data_MIDINAMES enumeration value. (These are similar but not identical). And/or a function which converts a General MIDI instrument number (from 0 to 127) into a data_MIDINAMES enumeration value (from 1-128).

craigsapp commented 6 years ago

Somewhat related:

In iomusxml.cpp there is a function that converts a MusicXML instrument name into an MEI instrument name:

https://github.com/rism-ch/verovio/blob/171584406a9c978fcd5cbe113265ab47bb53d639/src/iomusxml.cpp#L505

In general this will not work since the StrToMidinames function assumes that the input is an MEI instrument name string:

https://github.com/rism-ch/verovio/blob/171584406a9c978fcd5cbe113265ab47bb53d639/libmei/attconverter.cpp#L1604-L1784

But the MusicXML midi-name is an unstructured string rather than an allowed token list:

https://usermanuals.musicxml.com/MusicXML/Content/EL-MusicXML-midi-name.htm

https://usermanuals.musicxml.com/MusicXML/Content/CT-MusicXML-midi-instrument.htm

rettinghaus commented 5 years ago

@craigsapp what is with this issue?

craigsapp commented 5 years ago

The source of the issue was that I was having problems adding MIDI instrument names to MEI data within verovio, but that problem seems to have been fixed. So the issue can be closed (but see note further below).

Example conversion of a Humdrum score with a soprano clarinet specified:

**kern
*Iclars
*clefG2
*M4/4
=1
1c
==
*-

Which converts to MEI with the instrument name and number, as I was originally wanting to do probably when I submitted the issue:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="http://music-encoding.org/schema/4.0.0/mei-all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="http://music-encoding.org/schema/4.0.0/mei-all.rng" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<mei xmlns="http://www.music-encoding.org/ns/mei" meiversion="4.0.0">
    <meiHead>
        <fileDesc>
            <titleStmt>
                <title />
            </titleStmt>
            <pubStmt />
        </fileDesc>
        <encodingDesc>
            <appInfo>
                <application isodate="2019-04-11T15:51:28" version="2.1.0-dev-5f615b1">
                    <name>Verovio</name>
                    <p>Transcoded from Humdrum</p>
                </application>
            </appInfo>
        </encodingDesc>
        <workList>
            <work>
                <title />
            </work>
        </workList>
    </meiHead>
    <music>
        <body>
            <mdiv xml:id="mdiv-0000001760370640">
                <score xml:id="score-0000000667141761">
                    <scoreDef xml:id="scoredef-0000000072552127" midi.bpm="400">
                        <staffGrp xml:id="staffgrp-0000001271639941">
                            <staffDef xml:id="staffdef-0000001331175392" clef.shape="G" clef.line="2" meter.count="4" meter.unit="4" n="1" lines="5">
                                <label xml:id="label-0000000229569068" />
                                <instrDef xml:id="instrdef-0000000925153403" midi.instrnum="71" midi.instrname="Clarinet" />
                            </staffDef>
                        </staffGrp>
                    </scoreDef>
                    <section xml:id="section-L1F1">
                        <measure xml:id="measure-L5" right="end" n="1">
                            <staff xml:id="staff-L5F1N1" n="1">
                                <layer xml:id="layer-L5F1N1" n="1">
                                    <note xml:id="note-L6F1" dur="1" oct="4" pname="c" accid.ges="n" />
                                </layer>
                            </staff>
                        </measure>
                    </section>
                </score>
            </mdiv>
        </body>
    </music>
</mei>

I just updated the humdrum-to-MEI converter to use 0-offset indexing for the instrument numbers (change for MEI 4.0):

https://music-encoding.org/guidelines/v4/elements/instrdef.html

Screen Shot 2019-04-11 at 3 56 36 PM

http://fmslogo.sourceforge.net/manual/midi-instrument.html


So in regards to @lpugin's question:

What is the request? You still want MIDINAMES_NONE to be -1?

I would now say yes, since the instrument numbers now start at 0 rather than 1.

In other words my code for setting the instrument number/name is now:

    int gminst = 71;
    idef->SetMidiInstrnum(gminst);
    data_MIDINAMES idval = (data_MIDINAMES)(gminst + 1);
    idef->SetMidiInstrname(idval);

It would be preferable to not have to do the +1 to get the name of the instrument.