Chord Displayer

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

Previous topic - Next topic

Hardcoal

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







Things I've done:   https://itch.io/profile/hardcoal  [I keep improving them btw]

Midimaster

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.

...on the way to China.

Hardcoal

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

Things I've done:   https://itch.io/profile/hardcoal  [I keep improving them btw]

Midimaster

#3
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:

Code: BASIC
SuperStrict
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"
Chord
="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 
Find("Cm")
Repeat
    Cls
    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
    Next  
    
    DrawActChord    
    
    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
            CurrChord=Mid(a,middle+1,-1)
            Print "found definition: " + CurrChord
            Return 
        EndIf 
    Next 
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
            pos=pos+7
        EndIf 
        lastpos=pos
        
        
        DrawOval 200,311-pos*10,30,18
        
        SetScale 1,3
        DrawText Right(a,1), 180,300-pos*10     
        SetScale 1,1
    Next 
    
End Function 



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


[url=&quot;https://www.syntaxbomb.com/index.php?action=dlattach;attach=5752;type=preview;file&quot;]ChordDisplayer.bmx[/url]
...on the way to China.

Hardcoal

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
Things I've done:   https://itch.io/profile/hardcoal  [I keep improving them btw]

lucidapogee

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

Hardcoal

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

Things I've done:   https://itch.io/profile/hardcoal  [I keep improving them btw]

Hardcoal

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...
Things I've done:   https://itch.io/profile/hardcoal  [I keep improving them btw]

Midimaster

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

http://www.midiox.com/


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.

Code: BASIC
' function in the main code:

Function TimerTick()
    Global MidiInTimer:Int
    
    If (MidiInTimer<MilliSecs()) And (MIDI.InStarted=True) Then
        MidiInTimer = MilliSecs()+10
        MIDI.Receive
        If MyWindow()=Null
            MIDI.InMessages.clear()
        ElseIf GameMode>0
            GameWindow.FetchMidiMessage
        Else 
            MIDI.InMessages.clear()
        EndIf
    EndIf
End Function 



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



Type MIDI

    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
                Next    
                InMessages.AddLast loc  ' List for the messages
            EndIf
        EndIf
    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
                EndIf 
            Next
        EndIf
        Return Null
    End Function
    
End Type
    
    


...on the way to China.

Hardcoal

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

here is the latest image 


Things I've done:   https://itch.io/profile/hardcoal  [I keep improving them btw]

Hardcoal

#10
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
Code: BASIC
	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
			Else
				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
			Next

			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


Things I've done:   https://itch.io/profile/hardcoal  [I keep improving them btw]

Midimaster

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 .
Code: BASIC
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...
Code: BASIC
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:
Code: BASIC
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:

https://www.music.mcgill.ca/~gary/rtmidi/ 

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


...on the way to China.

Midimaster

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

https://github.com/thestk/rtmidi/issues/313

Here is the question:
Quotepre-information:
 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 (https://github.com/bmx-ng).
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...
...on the way to China.

Derron

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


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

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:
Code: BASIC

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 - https://gist.github.com/GWRon/c8df9f3e1097a149a013ccc07cbd6f14
As I do not have a midi input device it lists "0" here.

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

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

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

bye
Ron

Midimaster

That would means he has to do something like this?

Code: BASIC
...
If PortsExists_flg
	Try
		Midi.MidiIn.openPort(0) 
	Catch Exception:Object
		Print "Device open failed:"
		Print Exception.ToString()
	End Try
	
EndIf 
...
...on the way to China.