My Music Editor

Started by Hardcoal, May 24, 2021, 23:57:24

Previous topic - Next topic

Hardcoal

So midi master.. according to what I see.. all the notes that are overlapping each other turn into a Bemol Sign
On the BB Chord Case they are not overlapping each other , so why do i need to use Bemol.

anyway.. dont think i understood the note tables you posted..

that's why i postponed it for later.

anyway.. Now im starting to implement some things.. than ill post a picture and see what you think
Code

Midimaster

I may look like you found the rule for b or #. But the example of the Bb-chord already shows you, that this cannot be the rule.

As I said, there are 12 tables which define, when or whether a midi note is displayed as with a # or a b sign.

This what I call "translation table". At the moment you try to use the midi-number to find the notation position. But the correct way is to first "translate" the midi-number into a notation-number. then you will find the correct position.

For chords this way is a 100% working solution.

1.step: Calculate
MidiMod%=(midinumber mod 12)
Octave%=(midinumber/12)


2.step: Now use one of the 12 tables to get the NotationMod:
NotationMod%= Table[TableNumber%, MidiMod]
NotationPos%=NotationMod*Octave

now you now the vertical pos of the note in the score


3.step
the system can give you additional informations now:

Select (NotationMod mod 3)
    Case 0
        ' note needs a b sign
    Case 1
        ' note needs no sign
    Case 2
        ' note needs a # sign
end select


The only question that remains is: "Which table do I use?"

The answer is: "depends on...!". In the case of chord this question is easy, because you only have to find the table which contains all three midinumbers of your chord.

In 12 hours I will send you the 12 tables and a demo code. (now it is round midnight)
...back from Egypt

Hardcoal

Awesome.. Ill wait for the demo..

I told you im gonna listen to you eventually lol :)
Code

Hardcoal

Progress Report..

Now I got 4 Lines Notes Ladder Editing.
Up till now it was only one Line Note Editing



Making this a true professional any note sheet notation Editor.. well thats too much for me for the moment..
Ill just make this work right and ill be pleased
Code

Midimaster

#34
Ok, here is the demo for the 12->21 converter. This enables to write the chords automatically correct.

You can use some of the fundamental  functions:
Code (BlitzMax) Select
Function EasyTranslate:Int[] ( MidiChord:Int[] )
' converts a midi noted chord into a notation noted chord
' the array can contain 1 to 4 notes


Function Name$(Note%)
' returns the name of a notation note
' notemod    0   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21
' --> name  Cb   C  C#  Db   D  D#  Eb   E  E#  Fb   F  F#  Gb   G  G#  Ab   A  A#  Bb   B  B#


Function DrawNotes (Chord:Int[])
'converts a notation chord into dispayed notes
Local ZeroY%=300, Distance%=10, BassDistance%=25
' flexible in scaling:
' ZeroY defines the absolute Y-position of  both systems
' Distance defines the absolute y-diff from one note to the next higher note
' BassDistance defines the relative distance of bass system to treble system


Function PaintHelpingLines(X%, Note%, Clef%=TREBLE, ZeroY%, Distance%, BassDistance%)
'paints all necessary helplines of a notation note


Function NoteToMidi:Int(Note%)
' converts a notation note back to a midi note
' notemod      0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20
        ' --> to midi -1   0   1   1   2   3   3   4   5   4   5   6   6   7   8   8   9  10  10  11  12



You can add your chords for testing here:
Code (BlitzMax) Select

Function NewChord()
number=number +1
Select Number
Case 1
chord=[58,62,65]    'Bb
Case 2
...
Case 9
chord=[57,60,64]    'Am
Case 10
chord=....






The whole runnable example


Start the app and click with the mouse to check some chords....

Code (BlitzMax) Select

Graphics 600,600
SetBlend alphablend
Notation.StartUp


Global MidiChord%[]=[60,63,67]    'Am

Global  ShowChord:Int[]=Notation.EasyTranslate(MidiChord)
Global Number
Notation.ChordInfo(MidiChord, ShowChord)

SetClsColor 255,255,255
Repeat
Cls
Notation.DrawNotes Showchord
If MouseHit(1)
NewChord
EndIf
Flip 1
Until AppTerminate()



Function NewChord()
number=number +1
Midichord=[58+number,59+number,60+number]    'Bb

ShowChord = Notation.EasyTranslate(MidiChord)
Notation.ChordInfo(MidiChord, ShowChord)
End Function













Type Notation
Global MidiTable:Int[]= [-1,0,1, 1,2,3, 3,4,5 ,4,5,6, 6,7,8,  8,9,10,  10,11,0]
Global Names:String[]= "Cb,C,C#,Db,D,D#,Eb,E,E#,Fb,F,F#,Gb,G,G#,Ab,A,A#,Bb,B,B#".split(",")
    Global NoteImages:TImage
Global TableMidi:Int[15,7], TableNote:Int[15,7]



Function StartUp()
'builds the 13 tables from this 2 "exotic" strings
Local t1$="0|2|4|5|7|9|11|0|2|4|6|7|9|11|0|2|4|5|7|9|10|1|2|4|6|7|9|11|0|2|3|5|7|9|10|1|2|4|6|8|9|11|0|2|3|5|7|8|10|1|3|4|6|8|9|11|0|1|3|5|7|8|10|1|3|4|6|8|10|11|0|1|3|5|6|8|10|11|1|3|5|6|8|10|1|3|5|6|8|10|11|"
Local t2$="1|4|7|10|13|16|19|1|4|7|11|13|16|19|1|4|7|10|13|16|18|2|4|7|11|13|16|19|1|4|6|10|13|16|18|2|4|7|11|14|16|19|1|4|6|10|13|15|18|2|5|7|11|14|16|19|1|3|6|10|13|15|18|2|5|7|11|14|17|19|1|3|6|10|12|15|18|21|3|6|10|12|15|18|2|5|8|11|14|17|19|"
t1=t1 + "1|3|5|6|8|10|0|1|3|4|6|8|10|11|"
t2=t2 + "2|5|8|11|14|17|-1|3|6|9|12|15|18|21|"

'cis h#    1| 3| 5| 6| 8|10| 0|
'14.       2| 5| 8|11|14|17|-1|

'Cb  fb    1| 3| 4| 6| 8|10|11|
'13.       3| 6| 9|12|15|18|21|

Local v1$[]=t1.split("|")
Local v2$[]=t2.split("|")
For Local I%=0 To 14
For Local j%=0 To 6
TableMidi[i,j]=v1[i*7+j].toint()
TableNote[i,j]=v2[i*7+j].toint()
Next
Next
'ShowTable
' load some note images for testing:
NoteImages=LoadAnimImage("noten.png",60, 140,0,15)
End Function



Function ShowTable()
' shows all 13 tables for debugging purposes only:
For Local I%=0 To 14
Print "------------Table Nr " + (i+1) + "--------------------"
Local a$= "", b$= ""
For Local j%=0 To 6
a=a+ " " + TableMidi[i,j]
b=b+ " " + TableNote[i,j]
Next
Print a
Print b
Next
End Function



Function NoteToMidi:Int(Note%)
' converts a notation note back to a midi note
' notemod      0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20
        ' --> to midi -1   0   1   1   2   3   3   4   5   4   5   6   6   7   8   8   9  10  10  11  12
Local Octave:Int=note/21
Local NoteMod:Int=Note Mod 21
Local Midi:Int=MidiTable[NoteMod] + 12*Octave
Print Note + " " + Names[NoteMod] + " => midi " + (Midi)
Return midi
End Function




Function EasyTranslate:Int[] (MidiChord:Int[])
' converts a midi noted chord into a notation noted chord
' the array can contain 1 to 4 notes
Local TakeTable%=Notation.WhichTable(MidiChord)
Print "Chord found in table " + TakeTable
Return Translate(TakeTable,MidiChord)
End Function





Function WhichTable:Int(MidiChord:Int[])
'trys to find out which of the 13 tables is the best
'returns a number between 0 and 12
Local Result%
For Local i:Int=0 To 12
Result= CheckTable(i , MidiChord)
If Result=True Then Return i
Next
Return -1
End Function




Function CheckTable:Int(Nr%, MidiChord:Int[])
'checks whether all notes of a chord are member of this table
For Local i:Int=0 To MidiChord.Length-1
Local SearchNote% = MidiChord[i] Mod 12
Local Result%=False
For Local j%=0 To 6
If TableMidi[Nr,j] = SearchNote
Result=True
Exit
EndIf
Next
If Result=False Return False
Next
Return True
End Function


Function Translate:Int[] (Nr%, MidiChord:Int[])
' converts a midi noted chord into a notation noted chord
' the array can contain 1 to 4 notes
Local ReturnChord:Int[MidiChord.Length]
If Nr=-1
' no real chord! use as minimum same sign
Return UseAllSameSign(MidiChord)


EndIf
For Local i:Int=0 To MidiChord.Length-1
Local SearchNote% = MidiChord[i] Mod 12
Local Octave%=MidiChord[i] / 12
For Local j%=0 To 6
If TableMidi[Nr,j] = SearchNote
'Print "found at " + j
ReturnChord[i]= TableNote[Nr,j] +21*Octave
Exit
EndIf
Next
Next
Return ReturnChord
End Function




Const FLAT:Int=0, SHARP:Int=1

Function UseAllSameSign:Int[] (MidiChord:Int[], Iteration:Int=0)
' tricky new function to process also "strange chords"
' if all 14 tables fail this algo trys to prevent notes on the same line
Local SameTableNote[][]= New Int[][4]
'                  [C   D   E  F     G     A     B]
SameTableNote[0]=  [1,3,4,6,7,10,12,13,15,16,18,19]
SameTableNote[1]=  [1,2,4,5,7,10,11,13,14,16,17,19]
SameTableNote[2]=  [1,3,4,6,9,10,12,13,15,16,18,21]
SameTableNote[3]=  [-1,2,4,5,7,8,11,13,14,16,17,19]

Local ReturnChord:Int[MidiChord.Length]

Print "STRANGE CHORD! now Using UseAllSameSign() function with Iteration-Round=" + Iteration
' first use one of the tables:
For Local i:Int=0 To MidiChord.Length-1
Local SearchNote% = MidiChord[i] Mod 12
Local Octave%=MidiChord[i] / 12
ReturnChord[i]= SameTableNote[Iteration][SearchNote] +21*Octave
Next

'now test, whether this table also produce notes on the same line:
For Local i:Int=0 To MidiChord.Length-1
For Local j:Int=i+1 To MidiChord.Length-1
If YPos(ReturnChord[i]) = YPos(ReturnChord[j])
'If yes... try the other table
If Iteration < 3 'for not producing a endless loop
Return UseAllSameSign(MidiChord,Iteration+1)
Else
Print "Needs Mix Methode"
Return UseMixedSigns (MidiChord, ReturnChord)
EndIf
EndIf
Next
Next
Return ReturnChord
End Function


Function UseMixedSigns:Int[] (MidiChord:Int[], BadChord:Int[])
' ultimate chance for chords: Try to mix # with b or send BadChord
Local list:TList=New TList
Local SearchNote% = MidiChord[0] Mod 12
Local Octave%=MidiChord[0] / 12
Local locChord:Int[]
AddChordsToListe(List, locChord, SearchNote, Octave)

For Local i:Int=1 To MidiChord.Length-1
Local SearchNote% = MidiChord[i] Mod 12
Local Octave%=MidiChord[i] / 12

For Local Chord:Int[]=EachIn list
If chord.length=i
AddChordsToListe(List, Chord, SearchNote, Octave)
List.Remove Chord
EndIf
Next
Next
Print "EXTREM STRANGE CHORD! now Using UseMixedSigns() function"
For Local Chord:Int[]=EachIn list
Return chord
Next
Print "!!!!! BAD CHORD! no solution found !!!!"
Return BadChord
End Function


Function AddChordsToListe(List:TList, Chord[], SearchNote, Octave)
Print "Adding " + Searchnote
Local last%=Chord.length
Local NewChord_I:Int[last+1]
Local NewChord_II:Int[last+1]
For Local i%=0 To last-1
NewChord_I [i] = Chord[i]
NewChord_II[i] = Chord[i]
Next
Select SearchNote
Case 0
NewChord_I[last]=-1+21*Octave
NewChord_II[last]=1+21*Octave
Case 1
NewChord_I[last]=2+21*Octave
NewChord_II[last]=3+21*Octave
Case 2
NewChord_I[last]=4+21*Octave
Case 3
NewChord_I[last]=5+21*Octave
NewChord_II[last]=6+21*Octave
Case 4
NewChord_I[last]=7+21*Octave
NewChord_II[last]=9+21*Octave
Case 5
NewChord_I[last]=8+21*Octave
NewChord_II[last]=10+21*Octave
Case 6
NewChord_I[last]=11+21*Octave
NewChord_II[last]=12+21*Octave
Case 7
NewChord_I[last]=13+21*Octave
Case 8
NewChord_I[last]=14+21*Octave
NewChord_II[last]=15+21*Octave
Case 9
NewChord_I[last]=16+21*Octave
Case 10
NewChord_I[last]=17+21*Octave
NewChord_II[last]=18+21*Octave
Case 11
NewChord_I[last]=19+21*Octave
NewChord_II[last]=21+21*Octave
EndSelect
If ContainsSameLine(NewChord_I)=False
Print "ADD **** Chord_I to list" + StringFromChord(newchord_I)
List.Addlast NewChord_I
EndIf

If NewChord_II[Last]>0
If ContainsSameLine(NewChord_II)=False
List.AddLast NewChord_II
Print "ADD **** Chord_II to list" + StringFromChord(newchord_II)
EndIf
EndIf
End Function


Function ContainsSameLine:Int(Chord:Int[])
For Local i:Int=0 To Chord.Length-1
For Local j:Int=i+1 To Chord.Length-1
If YPos(Chord[i]) = YPos(Chord[j])
Return True
EndIf
Next
Next
Return False
End Function

Function StringFromChord$(Chord:Int[])
Local s$
For Local i%=0 To chord.length-1
s=s + chord[i] + "-"
Next
Return s
EndFunction










Function Name$(Note%)
' returns the name of a notation note
' notemod    0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20
' --> name  Cb   C  C#  Db   D  D#  Eb   E  E#  Fb   F  F#  Gb   G  G#  Ab   A  A#  Bb   B  B#
Return Names[note Mod 21]
End Function


Const TREBLE:Int=0, BASS:Int=1

Function YPos:Int(note%, Clef%=TREBLE)
'returns the relative screen-y position of a notation note
Local pos:Int
If clef=TREBLE
Return Int(note/3) -28
EndIf
Return Int(note/3) -16
End Function


Function NeedsHelpingLines:Int (Note%, Clef%=TREBLE)
' finds out whether this note needs helplines
Local pos:Int=Ypos(note,Clef)
If pos<8 Or pos>18
Return True
EndIf
Return False
End Function


Function PaintHelpingLines(X%, Note%, Clef%=TREBLE, ZeroY%, Distance%, BassDistance%)
'paints helplines of a note
If NeedsHelpingLines(Note, Clef)=False Return
Local pos:Int=Ypos(note,Clef)
BassDistance=BassDistance*Clef
SetColor 1,1,1
If pos<8
For Local i%=7 To pos Step -2
DrawRect X-8,ZeroY-((i-Bassdistance)*distance),40,1
Next 
Else
For Local i%=19 To pos Step 2
DrawRect x-8,ZeroY-((i-Bassdistance)*distance),40,1
Next 
EndIf
SetColor 255,255,255
End Function 

Function DrawNotes (Chord:Int[])
'converts a notation chord into dispayed notes
Local ZeroY%=300, Distance%=10, BassDistance%=25, Imagedistance%=70
' flexible in scaling:
' ZeroY defines the absolute Y-position of  both systems
' Distance defines the absolute y-diff from one note to the next higher note
' BassDistance defines the relative distance of bass system to treble system
' ImageDistance is related to the "noten.png" (fine adjusting)
' this "noten.png" is best for distances from 8 to 11

'5 score-lines in bass and treble
SetColor 1,1,1
DrawText "Midi : " + MidiChord[0]+ "-" +  + MidiChord[1]+ "-" + MidiChord[2],100,100
For Local i%=0 To 4
DrawRect 100,ZeroY-((i*2+9)*distance),400,1
DrawRect 100,ZeroY-((i*2+9-BassDistance)*distance),400,1
Next
SetColor 255,255,255

'the keys:
DrawImage NoteImages,120,ZeroY-12*Distance-ImageDistance,0
DrawImage NoteImages,120,ZeroY-(12-Bassdistance)*Distance-ImageDistance,1

' paint the chord
Local signCounter%=0
For Local i:Int=0 To Chord.Length-1
'Print "found chord note " + Chord[i] + " " + ypos(chord[i])
PaintHelpingLines(250+i*25, Chord[i], 0, ZeroY, Distance, BassDistance)
Select Chord[i] Mod 3
Case 0
DrawImage NoteImages,220+signCounter,ZeroY- ypos(Chord[i])*Distance-ImageDistance,10
signCounter=signCounter+15
Case 2
DrawImage NoteImages,220+signCounter,ZeroY- ypos(Chord[i])*Distance-ImageDistance,9
signCounter=signCounter+15
End Select
DrawImage NoteImages,250+i*25,ZeroY- ypos(Chord[i])*Distance-ImageDistance,5
Next


'paint it in bass system too
Local AsBass%=1
signCounter=0
For Local i:Int=0 To Chord.Length-1
'Print "found chor note " + Chord[i] + " " + ypos(chord[i])
PaintHelpingLines(350+i*25, Chord[i], AsBass, ZeroY, Distance, BassDistance)
Select Chord[i] Mod 3
Case 0
DrawImage NoteImages,320+signCounter,ZeroY- (ypos(Chord[i],AsBass)-BassDistance)*Distance-ImageDistance,10
signCounter=signCounter+15
Case 2
DrawImage NoteImages,320+signCounter,ZeroY- (ypos(Chord[i],AsBass)-BassDistance)*Distance-ImageDistance,9
signCounter=signCounter+15
End Select
DrawImage NoteImages,350+i*25,ZeroY- (ypos(Chord[i],AsBass)-BassDistance)*Distance-ImageDistance,6
Next
End Function


Function ChordInfo(Chord:Int[],ShowChord:Int[])
Print "************************************"
Print "S C A N   R E S U L T S:"
Local ChordInfo$ = "      Midi-Chord was: "
Local NoteInfo$  = "       Note-Chord is: "
Local NameInfo$  = "           Names are: "
Local TrebleInfo$= "Draw at treble lines: "
Local BassInfo$  = " Draw at bass lines : "
For Local I%=0 To Chord.length-1
ChordInfo= ChordInfo + + Chord[i]  + " "
NoteInfo= NoteInfo + ShowChord[i] + " "
NameInfo= NameInfo + Notation.name(ShowChord[i]) + " "
TrebleInfo= TrebleInfo + Notation.Ypos(ShowChord[i]) + " "
BassInfo= BassInfo + Notation.Ypos(ShowChord[i],1) + " "
Next
Print ChordInfo
Print NoteInfo
Print NameInfo
Print TrebleInfo
Print BassInfo
End Function

End Type

Rem
Table-Explanation:
-----------------------------------
Scale      New       Midi-Notes
Order      sign      Notation-notes
-----------------------------------

-----------------------------------
no sign
Table     C  D  E  F  G  A  B
-----------------------------------
C         0| 2| 4| 5| 7| 9|11|
0.        1| 4| 7|10|13|16|19|
-----------------------------------
all #
Tables    C  D  E  F  G  A  B
-----------------------------------
G   f#    0| 2| 4| 6| 7| 9|11|
1.        1| 4| 7|11|13|16|19|
-----------------------------------
D   c#    1| 2| 4| 6| 7| 9|11|
3.        2| 4| 7|11|13|16|19|
-----------------------------------
A   g#    1| 2| 4| 6| 8| 9|11|
5.        2| 4| 7|11|14|16|19|
-----------------------------------
E   d#    1| 3| 4| 6| 8| 9|11|
7.        2| 5| 7|11|14|16|19|
-----------------------------------
H   a#    1| 3| 4| 6| 8|10|11|
9.        2| 5| 7|11|14|17|19|
-----------------------------------
fis e#    1| 3| 5| 6| 8|10|11|
12.       2| 5| 8|11|14|17|19|
-----------------------------------
cis h#    1| 3| 5| 6| 8|10| 0|
14.       2| 5| 8|11|14|17|-1|
-----------------------------------
-----------------------------------
all b
Tables    C  D  E  F  G  A  B
-----------------------------------
F   hb    0| 2| 4| 5| 7| 9|10|
2.        1| 4| 7|10|13|16|18|
-----------------------------------
Bb  eb    0| 2| 3| 5| 7| 9|10|
4.        1| 4| 6|10|13|16|18|
-----------------------------------
Eb  ab    0| 2| 3| 5| 7| 8|10|
6.        1| 4| 6|10|13|15|18|
-----------------------------------
Ab  db    0| 1| 3| 5| 7| 8|10|
8.        1| 3| 6|10|13|15|18|
-----------------------------------
Db  gb    0| 1| 3| 5| 6| 8|10|
10.       1| 3| 6|10|12|15|18|
-----------------------------------
Gb  cb    1| 3| 5| 6| 8|10|11|
11.       3| 6|10|12|15|18|21|
-----------------------------------
Cb  fb    1| 3| 4| 6| 8|10|11|
13.       3| 6| 9|12|15|18|21|
-----------------------------------

end rem


you need the Noten.png from the attachment to run the example.

...back from Egypt

Hardcoal

Thanks midimaster.. I've already experimented with it.. and i will implement it in my code.
I had to fix some Errors of Local I, but other than that it seems to work fine.

I'm not in a mood for programming atm, but ill go back to that soon.
or maybe ill implement it later.. and continue developing other things.
the only way i survive in coding.. is doing what i feel and not doing what i need
Code

Hardcoal

MM=MidiMaster..

I tried to implement you method ..into my code.. I didnt succeed too well..
so I went with my system for now..
but I kept you code in Rem State.. in my project..
So I can continue my work..
Ill Contact you again.. soon when Ill solve one more thing and will see what to do
:)

The good news.. Is that Im serious about this App

Code

Hardcoal

#37


Im trying to understand the logic of the notation..
and ill try again to implement your code midimaster..

Im looking at muse score app to see how it preforms notations

and ill try again to implement your code..

does your code also support this 3 half tone chords arrangement?

Ive managed to make progress with MidiMaster Code, but im not their yet.

I also Started to like Muse Score..
Finally it makes sense to me

Code

Midimaster

#38
Also in MuseScore you can see, that the base is not a 12-objects-per-octave-system, but a 21-objects-per-octave-system.

If you want to create a "note object " the property "altitude" of the note needs to be in this 21-system. Re-calculating the midinote from the 21-systems is easy.

but using a 12-system means not beeing able to calculate the correct score position. So there is no way without 21-system.

This is the base, But this is not the final solution for all the problems you will get with notation. So drawing the notes is again a challenge with a lot of rules and still more exeptions.

Using the 21-system means you are able to write all the examples you showed in your screenshot. But these screenshots are no typical notations.

In a perfect notation it would look like this:


Lets talk about the 5 chord in your screenshot:

1. This is allowed. You can combine D,F and G# in one chord. But if it is possible you would always use D-F-Ab. (See my example)

2. perfect

3. This is not allowed what should it be? two C# together with one D? I would write is as one C# with one D.

4. This is not allowed. You never see a Eb and a E written in this way. The solution if you need 3 notes closed together is always to notate it as: D-Eb-Fb (See my example). That is a typical solution where you would use Fb as a replacement for E.

5. Are there 3 note in your example? Not allowed. If this are 2 notes my example would be the solution. If you need again three notes: F-F# and G you would write them always as E#-F#and G

The property, whether a note is notated "normal" or "a little bit to the right" is not a fixed porperty of the note, but a decision of the drawing algorithm in the last moment before displaying it. Often adding one single new note to a chord changes to behavior of all the other members of this chord.

But one rule is: "never use the same altitude position twice". So C-C#-D would always become H#-C#-D.

Or a more complex example: G-G#-A would become a elegant F##-G#-A. The fact that a notation app like muse-score allowes your 5th example still does not mean that it is allowed or elegant.  It is a little bit like writing a typo in WORD: "God Luck" will be printed, but is still a typo.


...back from Egypt

Midimaster

#39
To your question:

Quotedoes your code also support this 3 half tone chords arrangement?
Yes every 21-system does support this! This does not means that the result is alllowed or elegant.

My example code demostrate five things:

1.
How a 21-system works.

2.
How a midi-note is re-calculated from a 21-system-note.

3.
How to calculte a "best match" 21-system-note from a midi note or a midi-chord.

4.
How to display 21-system-notes

5.
How to calcuate the help lines



What is not implemented:

1. It has no algorithm to decide, when placing a note "a little bit to the right". But his can easyly be done by comparing all note of the chord with the given function YPos() (=note/3). If then two results have only a difference of 1 this means you will need this "right displacement" anywhere.

2. It has no a double ## or double bb algorithm. So some of the "3 half tone chords" would look similar unelegant like they do in musescore. (example: G-G#-A)

3. It has no "is it bass or treble"-flag. So this needs to be done.



Write questions if you need more explanation of the example code.


   



...back from Egypt

Hardcoal

#40
I tried putting midi notes 60,61,62 which is C C# and D and your system didnt display it properly..



anyway what I'm gonna do is to post my last version of progress and maybe slowly improve it.. by your advices..

I was busy fixing other stuff in the little time I put on this.
but now, after fixing other errors, I'm free again to concentrate on proper notes displaying.
I promise you ill improve it until it get to what you think its proper, just need patient.

I started making this type of notes Display



And ye .. I know its not correct key. Ill fix.

Code

Midimaster

#41
The helping line problem does not come from my code.

In the case of 60-61-62 my app crashs, because it is no "senseful chord". The function Notation.WhichTable(MidiChord) returns a "-1", what means: "No table fits!"
In this cases we have to add  "last resort" to pruduce anything but no crash.

I added a new function, which finds those senseless chords and tries to display them in a "all b" or "all #" style. As this can produce notes on the same line, it tries to test both approaches and select the "best". If nothing helps it return a ugly chord with notes onthe same line.

I updated the "Whole Runnable Code" example in my post #34:
https://www.syntaxbomb.com/index.php/topic,8467.msg347050592.html#msg347050592

There the  "Whole Runnable Code" example has now a new function...

Function UseAllSameSign:Int[] (MidiChord:Int[], FlatOrSharp:Int=FLAT)

...which tries to display anything in each situation.

This is the algo:
Code (BlitzMax) Select
Function UseAllSameSign:Int[] (MidiChord:Int[], FlatOrSharp:Int=FLAT)
Global Iteration%
' tricky new function to process also "strange chords"
' if all 14 tables fail this algo trys to prevent notes on the same line
Local FlatTableNote[]  =  [1,3,4,6,9,10,12,13,15,16,18,21]
Local SharpTableNote[] = [-1,2,4,5,7,8,11,13,14,16,17,19]
Local ReturnChord:Int[MidiChord.Length]

Print "STRANGE CHORD! now Using UseAllSameSign() function with FlatOrSharp=" + FlatOrSharp + " Iteration-Round=" + Iteration
' first use one of the tables:
For Local i:Int=0 To MidiChord.Length-1
Local SearchNote% = MidiChord[i] Mod 12
Local Octave%=MidiChord[i] / 12
If FlatOrSharp=SHARP
ReturnChord[i]= SharpTableNote[SearchNote] +21*Octave
Else
ReturnChord[i]= FlatTableNote[SearchNote] +21*Octave
EndIf
Next

'now test, whether this table also produce notes on the same line:
For Local i:Int=0 To MidiChord.Length-1
For Local j:Int=i+1 To MidiChord.Length-1
If YPos(ReturnChord[i]) = YPos(ReturnChord[j])
'If yes... try the other table
If Iteration=0
Iteration=1  'for not producing a endless loop
Return UseAllSameSign(MidiChord,1-FlatOrSharp)
EndIf
EndIf
Next
Next
Iteration=0
Return ReturnChord
End Function


Additional I added 2 more tables for the very seldom used scales "C#-Major" with 6#, and "Cb-Major" with 6b. This serves in cases where a F#-Major-Song use the B#-Chord. Or a Gb-Major-Song use the Cb-Chord.


Because my example still has no algo for the "a little bit to the right"problem it now displays the chords in a "arpeggio" style. This makes it easier to check the algo is workuing correct.

Scrennshot shows a 60-61-62-chord
...back from Egypt

Hardcoal

now thats awesome.. im gonna try to use it.. thanks
Code

Midimaster

#43
Ok I again could refine the algo for chords with two improvements.

If all 14 scale-tables fail, now there are 4 additional tables for methods "all with #" and "all with b". both methods now have two steps "less signs" or "a lot of signs":

Code (BlitzMax) Select

Function UseAllSameSign:Int[] (MidiChord:Int[], Iteration:Int=0)
' tricky new function to process also "strange chords"
' if all 14 tables fail this algo trys to prevent notes on the same line
Local SameTableNote[][]= New Int[][4]
'                  [C   D   E  F     G     A     B]
SameTableNote[0]=  [1,3,4,6,7,10,12,13,15,16,18,19]
SameTableNote[1]=  [1,2,4,5,7,10,11,13,14,16,17,19]
SameTableNote[2]=  [1,3,4,6,9,10,12,13,15,16,18,21]
SameTableNote[3]=  [-1,2,4,5,7,8,11,13,14,16,17,19]


The app iterates through al 4 variants and decides for the best.

In the case where all 4 still deliver notes on the same line a new algo UseMixedSigns() starts. It tries to build the chord without any scale-rules only with using any combination of # and b that is theorectical possible:

Code (BlitzMax) Select

Function UseMixedSigns:Int[] (MidiChord:Int[], BadChord:Int[])
' ultimate chance for chords: Try to mix # with b or send BadChord
Local list:TList=New TList
Local SearchNote% = MidiChord[0] Mod 12
Local Octave%=MidiChord[0] / 12
Local locChord:Int[]
AddChordsToListe(List, locChord, SearchNote, Octave)

For Local i:Int=1 To MidiChord.Length-1
Local SearchNote% = MidiChord[i] Mod 12
Local Octave%=MidiChord[i] / 12

For Local Chord:Int[]=EachIn list
If chord.length=i
AddChordsToListe(List, Chord, SearchNote, Octave)
List.Remove Chord
EndIf
Next
Next
Print "EXTREM STRANGE CHORD! now Using UseMixedSigns() function"
For Local Chord:Int[]=EachIn list
Return chord
Next
Print "!!!!! BAD CHORD! no solution found !!!!"
Return BadChord
End Function


Function AddChordsToListe(List:TList, Chord[], SearchNote, Octave)
Print "Adding " + Searchnote
Local last%=Chord.length
Local NewChord_I:Int[last+1]
Local NewChord_II:Int[last+1]
For Local i%=0 To last-1
NewChord_I [i] = Chord[i]
NewChord_II[i] = Chord[i]
Next
Select SearchNote
Case 0
NewChord_I[last]=-1+21*Octave
NewChord_II[last]=1+21*Octave
Case 1
NewChord_I[last]=2+21*Octave
NewChord_II[last]=3+21*Octave
Case 2
NewChord_I[last]=4+21*Octave
Case 3
NewChord_I[last]=5+21*Octave
NewChord_II[last]=6+21*Octave
Case 4
NewChord_I[last]=7+21*Octave
NewChord_II[last]=9+21*Octave
Case 5
NewChord_I[last]=8+21*Octave
NewChord_II[last]=10+21*Octave
Case 6
NewChord_I[last]=11+21*Octave
NewChord_II[last]=12+21*Octave
Case 7
NewChord_I[last]=13+21*Octave
Case 8
NewChord_I[last]=14+21*Octave
NewChord_II[last]=15+21*Octave
Case 9
NewChord_I[last]=16+21*Octave
Case 10
NewChord_I[last]=17+21*Octave
NewChord_II[last]=18+21*Octave
Case 11
NewChord_I[last]=19+21*Octave
NewChord_II[last]=21+21*Octave
EndSelect
If ContainsSameLine(NewChord_I)=False
Print "ADD **** Chord_I to list" + StringFromChord(newchord_I)
List.Addlast NewChord_I
EndIf

If NewChord_II[Last]>0
If ContainsSameLine(NewChord_II)=False
List.AddLast NewChord_II
Print "ADD **** Chord_II to list" + StringFromChord(newchord_II)
EndIf
EndIf
End Function


In the case where also this algo looses, it decides to take the "Bad-Chord", what means a design of the chord with notes on the same line. This happens f.e. on midichord=67-68-69

I updated for you the The whole runnable example in my post #34. So you have a full running example. It starts with a harmless C-min chord but if you click you can step through all ugly chromatic chord like 59-60-61 then 60-61-62 and so on....

...back from Egypt

Hardcoal

Thanks MidiMaster.. have a nice weekend
Thanks for all the effort youre putting..

From time to time I leave the notation punctuality and concentrating on other aspect of this app.. (when i feel good)
so I wont give it all up.

Now Im working on live notation recording for a while

Code