Chord Displayer

Started by Hardcoal, March 26, 2023, 21:22:09

Previous topic - Next topic


My method of work is to develop general environment of gadgets, and than decide what to do with them.
so i made gadgets for music apps.. and now im using it.

So this first thing I will probably release is this chord recognizer.

It display chords by its name and how its being played on a piano and a guitar..

all functions are already working.

I want to thank midimaster who helped me alot in all that regarding audio and notes..

I didnt use to care about looks for too much.
but since i got remarks about that, i started putting effort on looks as well.
and i admit.. its nice to see your app looks


I have to tell you, that the notation of the Cm-chord you display in your example screenshot is already wrong.

A Cm-Chord must have a C and a Eb and a G! Writing it with a D# is not allowed!

These Chords must use # (sharps): G D A E B F#  and Em Bm F#m C#m G#m D#m
These Chords must use b (flats): F Bb Eb Ab Db Gb and Dm Gm Cm Fm Bbm Ebm

You could implement a music theory AI to decide which chord uses which sign. But the more convenient way is to simply create a table where the app can look for each chord.

...back from Egypt


lol midi master.. I still didnt get to that..
That will be the last thing ill do when ill be ready to release the app..
so worry not.. im fully aware of that..

I already create a table for all chords, but i didnt get to that yet..

I just wanted to make first impression


The table could contain a bundle of chord definitions.
Each definition contains 3 or 4 notes.
Each note is a combination of a number for the altitude of the note and a letter for ev. presign:

"Cm =0 |2b|4 "

means Cm has three notes: The 0 represents C, the 2 represents E (two notes higher) and the b says that this E needs a flat presign. The 4 representsthe G.

Here is a stand alone runable example:

Graphics 600,400
SetColor 255,255,255

' the table:
Global Chord:String[10]
' chord note lines definition: 0=C, 1=D, 2=E, 3=F, 4=G, 5=A, 6=H
' # means "has a sharp", b means "has a flat"
="Cm =0 |2b|4 "
Chord[1]="Bb =6b|1 |3 "
Chord[2]="F#7=3#|5 |0#|2 "
' the current chord defintion:
Global CurrChord:String
    If KeyHit(KEY_C) Then Find("Cm")
    If KeyHit(KEY_B) Then Find("Bb")
    If KeyHit(KEY_F) Then Find("F#7")
    DrawText "Press <C> for Cm-chord, <B> for Bb-Major-chord or <F> for F#7-chord", 30,50
    'the 5 note lines:
    For Local i:Int=1 To 5
        DrawRect 100,200+i*20,300,1
    Flip 1
Until AppTerminate()
Function Find(Chordname:String)
    ' scans the table and return the chord definition
    For Local a:String=EachIn Chord
        Local middle:Int=Instr(a,"=")
        Local key:String=Trim(Left(a,middle-1))
        Print "scan table..." + key
        If key = Chordname
            Print "found definition: " + CurrChord
End Function
Function DrawActChord()
    ' paints the notes depending on the current definition
    'splits the defintion in the single notes:
    Local notes:String[]=CurrChord.Split("|")
    Local lastpos:Int  ' cares about octave shifting
    For Local a:String= EachIn notes
        Local pos:Int= a.ToInt
        ' cares about octave shifting
        If pos<lastpos
        DrawOval 200,311-pos*10,30,18
        SetScale 1,3
        DrawText Right(a,1), 180,300-pos*10    
        SetScale 1,1
End Function

The Forum-Editor has heavy problems with the blitzmax code snipplet so I post it again as attachment:

...back from Egypt


Awesome midi master..
I will not release this until it gets your approval anyway.
When i began developing it i was very busy with lots of aspects..
Now, im getting closer to the the finer details.

Im building things atm in a way that is accessible to any project so i don't need to repeat my code.
And if i improve one thing, it effects all projects.. For good and sometimes for bad.
But it can be balanced


Looks neat. Maybe this will be helpful in writing compositions with physical instruments.
Ebox Thin Client with Windows 95
EEE PC 701SD with Windows XP
Atari 1040STFM with GEM/TOS
Playstation 2 with FreeMcBoot Yabasic
Keyboard Famiclones with GBasic and FBasic
Xerox Sunrise 1800 with MSBasic and CP/M


Ok midiMaster.. Ive fixed it..

off course I will have to pass you the program for tests to know for certain everything is ok.
but this can wait.. i still have enough work around I want to do.

I must also thank Derron that caused me to put more effort and how my apps look.
Ive never considered before the importance of looks. but now i see how effective it is


I have this error RtMidiIn: message queue limit reached!! and i dont have any idea how to get rid of it..
or how to clean the queue...


MidiIn? Did you connect a MIDI Keyboard to the IN-Port of the computer? Which Keyboard?

A lot of keyboard send ACTIVE SENSING and TIME TICK messages. These are a lot of datas

You can check this with the tool MIDI-OX, which is still the best tool to investigate the MIDI ports

In your app you should check all the time the state of the midi in port and fetch away the messages.

Here is a code snipplet how I normaly handle the MIDI-IN-port. The open port sends messages as Byte[]-Arrays. Every 10msec I check, whether there are those messages and copy them all to my own message-list. Later I check this list and remove the messages I do not need, but return the messages of interest.

' function in the main code:

Function TimerTick()
    Global MidiInTimer:Int
    If (MidiInTimer<MilliSecs()) And (MIDI.InStarted=True) Then
        MidiInTimer = MilliSecs()+10
        If MyWindow()=Null
        ElseIf GameMode>0
End Function

Type MidiMessageTyp
    Field Typ:Int, Channel:Int, Note:Int, Volume:Int
End Type


    Global InMessages:TList = New TList

    Global MidiOut:TRtMidiOut, OutStarted:Int
    Global MidiIn :TRtMidiIn , InStarted:Int

    Function Receive()
        ' has to be called every 10msec!
        Local stamp:Double

        If InStarted=True Then
            message:Byte[] = midiIn.getMessage(stamp)
            nBytes:Int     = message.length

            Local Value:Int
            Local loc:MidiMessageTyp=New MidiMessageTyp  ' my message typ
            If nBytes > 0
                For Local i:Int = 0 Until nBytes
                    Value = message[i]
                    Select i
                        Case 0
                            loc.Typ     = Value/16
                            loc.Channel = Value Mod 16   
                        Case 1
                            loc.Note    = Value
                        Case 2
                            loc.Volume  = Value
                    End Select
                InMessages.AddLast loc  ' List for the messages
    End Function
    Function GetNextMessage:MidiMessageTyp(OnlyThisMessageTyp:Int=0)
        ' this also removes unwanted messages from the list
        If InMessages.count()>0 Then
            For Local loc:MidiMessageTyp = EachIn InMessages
                Akt:MidiMessageTyp = loc
                InMessages.Remove loc
                If OnlyThisMessageTyp=0
                    Print "fetch all types"
                    Return Akt
                ElseIf akt.Typ = OnlyThisMessageTyp
                    Print "found my type"
                    Return Akt
        Return Null
    End Function
End Type

...back from Egypt


This midi ox is cool.. i made something like that .. but not that advanced..

here is the latest image 


This is so annoying.. in RT midi.. I cant recognize if a midi is occupied by another program..
I tried every way.. including asking isportopen()
but the most annoying thing is that it does show an error message when trying to create new port.
but it does not bother to transmit the error message to the user from the DLL

so when i try opening the midi using  ' MidiIn = New TRtMidiIn.Create()' its and error but does not let you know.. by returning any flag
Method Prepare:String()
Local MSG:String, PortsExists_flg

MidiIn = New TRtMidiIn.Create()
MidiOut = New TRtMidiOut.Create()

   'Midi In

  Local PortsCount = Midi.midiIn.getPortCount()

   'If no Ports can be Open then
If PortsCount = 0 Then
Notify "No Midi Ports"
Return '<--End
Print "Number of Midi Ports " + PortsCount
End If

For Local I:Int = 0 Until PortsCount
Print "  Input port Named " + Midi.MidiIn.getPortName(I)
PortsExists_flg = True

MSG = MSG + "Opening " + Midi.MidiIn.getPortName() + "~n"

    If PortsExists_flg <> False Then Midi.MidiIn.openPort(0)  '< - 'This will not work if Mixcraft is open

   'Don't ignore sysex, timing, or active sensing messages.
Midi.MidiIn.ignoreTypes(False, False, False)

   'Midi Out
Return MSG
End Method


Hi Hardcoal,

thats an interesting point. I never tested what happens if a device is there but already used by another app. 

First of all: You can always open a RT-MIDI-instance, also if no port or no device exists or it is used by another app or it would be avaiable for you. So there is never an error massage when opening the RT-MIDI .
MidiOut:TRtMidiOut = New TRtMidiOut.Create()

After you created the instance you have to check whether there are avaiable ports. In my opinion the instance should now report only the not used ports with...
PortsCount = MidiIn.GetPortCount()
For Local i%=0 To PortsCount-1
   Print MidiIn.getPortName(i)
Did you find out, that RT-MIDI now also counts and lists the ports, that are used by another app? This would be a bug, or?

If so...I would expect that in the last step the RT-MIDI report something when trying to open such a port:
Report = MidiOut.openPort(i)
Can you please check if this is possible and if what happens...

There are two people responsible for RT-MIDI. One is Brucey, which wrote the wrapper to BlitzMax and the second is Gary P. Scavone which is the author of RT-MIDI class. Both would be very interested in a bug report, but first we should be 100% sure, that is it a bug and not a forgotten check from us.

Here ist the RT-MIDI-Homepage with also a tutorial: 

There I can see that they published Version 5, while Brucey is still on version 4

...back from Egypt


I now opened an issue on the GitHub-Account of RT-MIDI. It has the number #313

Here is the question:
 Hi, I'm working with RT-MIDI via a BlitzMax-Wrapper. At the moment we use V 4.0.0. The BlitzMax-Wrapper is made by Bruce A Henderson (
Today we come to the problem, that we have a MIDI-IN-Device, but it is used by another app. The question is now: How can we find out, that this Port is not aviable for us. Does RT-MIDI offer a way to detect this? by the way... is there a forum where we can ask qusetions like this to the RT-MIDI community?
we'll see what happens....

Years ago I found a bug on RT-MIDI and reported it to Brucey. He contacted Gary. And both were very helpful and immediately reacted with an update...
...back from Egypt


I am not sure but the blitzmax glue code is doing this:
void bmx_rtmidiout_openPort(RtMidiOut * m, int portNumber, BBString * portName) {
    char * n = bbStringToUTF8String(portName);
    try {
        m->openPort(portNumber, n);
    } catch (RtMidiError &error) {

while rtMidi does this:
void MidiOutCore :: openPort( unsigned int portNumber, const std::string &portName )
  if ( connected_ ) {
    errorString_ = "MidiOutCore::openPort: a valid connection already exists!";
    error( RtMidiError::WARNING, errorString_ );

so if something fails in rtmidi it "error()"s . error is defined in RtMidi.h and extends "exceptions". So normally it should throw an exception which is then passed into "bmx_rtmidi_throw"

which itself then does:

void bmx_rtmidi_throw(RtMidiError &error) {
    bbExThrow(CB_PREF(bah_rtmidi_TRtError__create)(bbStringFromCString(error.what()), (int)error.getType()));

Maybe you can add some 'printf("bla");' to these portions so to see what actually does "what" and where it does no longer pass along the error.

Edit: the current version of that rtMidi.mod surely needs an update anyways (char conversion casts missing for current NG).

Edit2: I modified the glue.cpp to compile - if you want to check with your changes -
As I do not have a midi input device it lists "0" here.

try {
m->openPort(portNumber, n);
printf("midi open port OK");
} catch (RtMidiError &error) {
printf("midi in openport error");
prints for me the OK part ... but not the caught exception.

I checked what the sample uses on "APIs":
For local i:int = EachIn midiIn.getCompiledApi()
print "api: " + i
only number 5 ... which equals to
bbdoc: A compilable but non-functional API.
End Rem

So in my case it asks the "dummy" api to open ports ...  which might explain why no error is thrown.



That would means he has to do something like this?

If PortsExists_flg
Catch Exception:Object
Print "Device open failed:"
Print Exception.ToString()
End Try

...back from Egypt