Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MIDI tempo and mode notes #6

Open
craigsapp opened this issue Mar 6, 2022 · 11 comments
Open

MIDI tempo and mode notes #6

craigsapp opened this issue Mar 6, 2022 · 11 comments

Comments

@craigsapp
Copy link

If you want to have nicer playback tempos, you can add tempos to the score like this:

**kern	**text	**kern	**text	**kern	**text
*staff3	*staff3	*staff2	*staff2	*staff1	*staff1
*Ivox	*	*Ivox	*	*Ivox	*
*I"Bassus	*	*I"Tenor	*	*I"Cantus	*
*clefC3	*	*clefC2	*	*clefG2	*
*M2/1	*	*M2/1	*	*M2/1	*
*k[]	*	*k[]	*	*k[]	*
*met(C|)	*	*met(C|)	*	*met(C|)	*
*MM280	*	*MM280	*	*MM280	*
=1	=1	=1	=1	=1	=1
1r	.	00r	.	1dd	Se-
1g	Se-	.	.	2dd	-lig
.	.	.	.	2dd	zu
=2	=2	=2	=2	=2	=2

Where *MM280 is the tempo marking, meaning 280 quarter notes per minute (units are always quarter notes).

Also for elegance, I would place the time signature, meter sign and tempo adjacent to each other in that order (I moved the key signature before the time signature):

**kern	**text	**kern	**text	**kern	**text
*staff3	*staff3	*staff2	*staff2	*staff1	*staff1
*Ivox	*	*Ivox	*	*Ivox	*
*I"Bassus	*	*I"Tenor	*	*I"Cantus	*
*clefC3	*	*clefC2	*	*clefG2	*
*k[]	*	*k[]	*	*k[]	*
*M2/1	*	*M2/1	*	*M2/1	*
*met(C|)	*	*met(C|)	*	*met(C|)	*
*MM280	*	*MM280	*	*MM280	*
=1	=1	=1	=1	=1	=1
1r	.	00r	.	1dd	Se-
1g	Se-	.	.	2dd	-lig
.	.	.	.	2dd	zu
=2	=2	=2	=2	=2	=2

Somewhat related, if you want to assign modalities to the pieces you can add modal qualifiers to the key designation:

*C:        == C major
*c:        == C minor

*c:dor    == C dorian
*c:phr    == C phrygian
*C:lyd    == C lydian
*C:mix   == C mixolydian

*c:aeo    == C aeolean (modal version of C minor)
*c:loc     == C locrian
*C:ion    == C ionian (modal version of C major)

Notice that the third scale degree of the mode matches the capitalization of the tonic (c implies E-flat is in the scale, and C implies E-natural). This allows using the nearest of major/minor scales when the exact mode is not needed.

So in this example piece, I would say that it is in G mixolydian, and I place the key assignment just under the key signature:

**kern	**text	**kern	**text	**kern	**text
*staff3	*staff3	*staff2	*staff2	*staff1	*staff1
*Ivox	*	*Ivox	*	*Ivox	*
*I"Bassus	*	*I"Tenor	*	*I"Cantus	*
*clefC3	*	*clefC2	*	*clefG2	*
*k[]	*	*k[]	*	*k[]	*
*G:mix	*	*G:mix	*	*G:mix	*
*M2/1	*	*M2/1	*	*M2/1	*
*met(C|)	*	*met(C|)	*	*met(C|)	*
*MM280	*	*MM280	*	*MM280	*
=1	=1	=1	=1	=1	=1
1r	.	00r	.	1dd	Se-
1g	Se-	.	.	2dd	-lig
.	.	.	.	2dd	zu
=2	=2	=2	=2	=2	=2
@WolfgangDrescher
Copy link
Owner

What is currently the best way to implement a MIDI player for Verovio? I tried some but they were not satisfying (synth-js, timidity, midi-player-js, Midi.js). So I guess https://github.com/rism-digital/midi-player still is the way to go with https://github.com/zz85/wild-web-midi as WebAssembly module. I would like to use another frontend for the player since I don't want to use jQuery. But as I understood it wild-web-midi would give me back a sound file, AudioBuffer or Blob that can be handled by any other player?

So in this example piece, I would say that it is in G mixolydian, and I place the key assignment just under the key signature:

I'm not so sure about the mode here since it (beatus vir) ends in c and thus looks to C ionian to me with a atypical beginning that seems to be in G mixolydian. Never the less: is there a way to mark spines with a distinction between let's say dorian and the plagal mode hypodorian to store some additional informations about the ambitus? It would be interesting to compare the expected range of the abitus with the effective max. and min. notes (from prange) within a voice. This could even be interesting for prange --score to display expected ambitus of the mode and effective ambitus of the voice somehow in the SVG.

Otherwise I would just use https://github.com/WolfgangDrescher/lassus-geistliche-psalmen/blob/master/meta/01-beatus-vir.yaml to store this information and parse it later on.

Btw. I found the Perl script to convert PMX files into SVG on https://wiki.ccarh.org/wiki/Music_253_Humdrum_homework. Is the code of the converter itself open sourced? I am thinking about a different (and maybe even interactive) way to display this graph since im not interested in average or median notes but more in the comparison to the ambitus of the mode and the range of the voice.

What can Humdrum do with the mode annotated like e.g. d:dor? Is there any command that can be useful for this or does it just have informational purpose?

WolfgangDrescher added a commit that referenced this issue Mar 6, 2022
@craigsapp
Copy link
Author

What is currently the best way to implement a MIDI player for Verovio? I tried some but they were not satisfying (synth-js, timidity, midi-player-js, Midi.js). So I guess https://github.com/rism-digital/midi-player still is the way to go with https://github.com/zz85/wild-web-midi as WebAssembly module. I would like to use another frontend for the player since I don't want to use jQuery. But as I understood it wild-web-midi would give me back a sound file, AudioBuffer or Blob that can be handled by any other player?

I looked around and there was nothing better than Wild Web MIDI adjusted by @lpugin for compiling with emscripten about 6 years ago. Since then I have look around every once in a while, but not noticed anything better (there probably is, but I have not come across it). The MIDI player used in the Piano Roll Project at Stanford looks promising:

 https://pianolatron.stanford.edu

 https://github.com/sul-cidr/pianolatron

but it is wrapped up in a lot of scripting languages, and I have not had time to see if it can be distilled into pure Javascript (I also would like to avoid using jQuery and other nonsense).

But as I understood it wild-web-midi would give me back a sound file, AudioBuffer or Blob that can be handled by any other player?

The front end should be independent of WWM, I played around with making my own last year, but it was too much of a bother, so I didn't bother much trying to remove jQuery from the interface.

If you are not planning on having a lot of files (such as less than a few thousand), and you have lots of storage on your web server, you can consider converting the MIDI to MP3 offline (I use timidity for this on JRP), and then use the MP3s for playback. This is what I do on the JRP and Tasso websites (WWM is not used and only the HTML 5 <audio> interface). For JRP there is about 4GB of MP3 files (for about 60 hours of performance time). For VHV where there is dynamically generated content, WWM is necessary since I do not want to run a web service to convert to MP3 on my server in realtime.

@craigsapp
Copy link
Author

I'm not so sure about the mode here since it (beatus vir) ends in c and thus looks to C ionian to me with a atypical beginning that seems to be in G mixolydian.

Yes, I must have been reading the C clefs wrong. The piece ends in a C major triad. Here is the keyscape for it:

Screen Shot 2022-03-06 at 8 53 51 AM

It matches most strongly to C major throughout its entirety. So categorizing at C ionian is best.

@craigsapp
Copy link
Author

What can Humdrum do with the mode annotated like e.g. d:dor? Is there any command that can be useful for this or does it just have informational purpose?

I don't do anything much with the modal refinement (keyscapes only look at major/minor and I have problems getting good results when trying to deal with modal assignments).

But they would be useful for some types of analysis. Cadences in dorian and phrygian are quite different due to the 6th scale degree being a minor second above the 5th.

I do use the tonic for calculating relative versus absolute keyscapes. In absolute mode green is C major, light blue is G, and yellow is F. For a relative view (functional harmony view), I transpose to C major/minor and then green means tonic, light blue means dominant and yellow means subdominant. But this does not consider the modal refinement since I color keys with a major third in a bright color and those with a minor third in a darker color.

@craigsapp
Copy link
Author

Btw. I found the Perl script to convert PMX files into SVG on https://wiki.ccarh.org/wiki/Music_253_Humdrum_homework. Is the code of the converter itself open sourced? I am thinking about a different (and maybe even interactive) way to display this graph since im not interested in average or median notes but more in the comparison to the ambitus of the mode and the range of the voice.

That specific method is private. There is a Javascript/Angular implementation for purely on the web, but I cannot find the link to it at the moment. The converter from Humdrum to SCORE PMX data is the Humdrum Extras tool called "prange" using the --score option:

http://extras.humdrum.org/man/prange

https://github.com/craigsapp/humextra/blob/master/cli/prange.cpp

@craigsapp
Copy link
Author

Otherwise I would just use https://github.com/WolfgangDrescher/lassus-geistliche-psalmen/blob/master/meta/01-beatus-vir.yaml to store this information and parse it later on.

prange also has textual output that can be used for this purpose:

[craig@agviq ~/git-cloud/verovio/tools] $ prange beatus_vir.krn
**keyno	**kern	**count
53	F	1
55	G	4
57	A	11
59	B	9
60	c	22
62	d	11
64	e	30
65	f	16
66	f#	2
67	g	36
69	a	18
71	b	22
72	cc	26
74	dd	15
76	ee	14
77	ff	3
79	gg	7
*-	*-	*-
!!tessitura:	26 semitones
!!mean:	67.0891 (g)
!!median:	67 (g)

Just the bassus:

$ extractx -s 1 /tmp/ss | prange 
**keyno	**kern	**count
53	F	1
55	G	4
57	A	11
59	B	9
60	c	15
62	d	5
64	e	13
65	f	6
66	f#	1
67	g	13
69	a	3
*-	*-	*-
!!tessitura:	16 semitones
!!mean:	61.8148 (d)
!!median:	62 (d)

The ambitus:

$ extractx -s 1 beatus_vir.krn | prange  | extractx -s 2| ridx -H | fmt | sed 's/ .* / /'
F a

(F3 to A4 for the Bassus part)

@craigsapp
Copy link
Author

Never the less: is there a way to mark spines with a distinction between let's say dorian and the plagal mode hypodorian to store some additional informations about the ambitus?

I am in discussion with several people on how to encode something like hypodorian in a key such as *d:dor. Best ideas at the moment are using the numeric system for church modes: *d:2t or prefixing the three-letter mode with an h: *d:hdor.

There is no formal system, but you could make one up such as *plagal or *hypo interpretations. With polyphonic music, it is hard to fit into these categories. Where are the melodies from in these psalm settings? The tenor part is from c to cc so it is in authentic, while the Bassus is close in form to the plagal/hypo. The cantus is from G4 to G5 is somewhat plagal as well.

@craigsapp
Copy link
Author

That specific method is private.

But I just tried the script and it is working (the converter from PMX to SVG is private, but the conversion itself is publicly accessible). From the command line:

prange --score input.krn | pmx2svg > output.svg

In theory it could be done directly on a website, but I would cache the plots before hand to avoid delays related to talking across web sites. A lazy way would be to hack the PMX data to add an option to prange to remove the median note (it is one of the lines. Prange has a -q option to show the quartile note in the plot (which is optional since I usually don't want to see it). The median and ambitus notes are in the form:

1 2 97.5 1 0 0 4 0 0 -2
1 2 97.5 8 0 0 4 0 0 -2
1 2 97.5 104 0 1 4 0 0 -1.66

Where the median one has the fourth number on the line greater than 100. Here is an example of removing the median notes by hand from the PMX data before converting to SVG:

Screen Shot 2022-03-06 at 9 58 54 AM

@WolfgangDrescher
Copy link
Owner

The MIDI player used in the Piano Roll Project at Stanford looks promising

I checked how they have done the playback. They are using midi-player-js (https://github.com/sul-cidr/pianolatron/blob/main/src/components/SamplePlayer.svelte#L342-L361) to generate real time JS events from the MIDI that then uses a custom "Piano" class (https://github.com/sul-cidr/pianolatron/blob/main/src/lib/tonejs-piano.js#L553-L570) in combination with the tone npm package. So it's not a single rendered audio file that just gets played, but multiple sound files (https://github.com/sul-cidr/pianolatron/tree/main/public/samples) that get played in a loop for each MIDI event.

If you are not planning on having a lot of files (such as less than a few thousand), and you have lots of storage on your web server, you can consider converting the MIDI to MP3 offline (I use timidity for this on JRP), and then use the MP3s for playback.

Yes I was also thinking about this, but I'm confident to somehow get the audio running directly in the browser.

$ extractx -s 1 beatus_vir.krn | prange | extractx -s 2| ridx -H | fmt | sed 's/ .* / /'

That is a nice command. Thank you!

I am in discussion with several people on how to encode something like hypodorian in a key such as *d:dor. Best ideas at the moment are using the numeric system for church modes: *d:2t or prefixing the three-letter mode with an h: *d:hdor.

This sound promising. Both variants look fine for me. Please give me a notice then it's implemented and ready to use or when you need someone to test the implementation (I'm not so deep into C++ yet to help with the implementation itself).

There is no formal system, but you could make one up such as *plagal or *hypo interpretations.

If *plagel get introduced a corresponding *authentic would also be needed.

With polyphonic music, it is hard to fit into these categories. Where are the melodies from in these psalm settings?

Ulenberg, Kaspar: Die Psalmen Davids: https://www.digitale-sammlungen.de/de/view/bsb11116252?page=53

How would you encode this considering the problematic of the needed bar lines in Verovio? Bars by printed staffs from the source or by lyrics? Since I don't have a GitHub repository yet for this my WIP draft is:

!!!COM: Caspar Ulenberg
!!!OTL: Beatus vir
!!!filter: kern2mens -NI
**kern	**text
*clefC3	*
*k[b-]	*
*M2/1	*
*met(C|)	*
=-	=-
1c	Se-
2c	-lig
2c	zu
2A	prei-
2B-	-sen
1A	ist
1G	der
1F	mann/
2r	.
=-	=-
1F	Der
2A	sich
2B-	ent-
2c	-helt
2c	von
2d	den
2c	got-
1B-	-lo-
1A	-sen/
2r	.
=-	=-
1G	Und
2A	wan-
2B-	-delt
2c	nicht
2A	im
2B-	rat
2c	der
1d	bö-
1c	-sen/
2r	.
=-	=-
1c	Trit
1f	auch
1e	nicht
2d	auff
2c	der
2c	sün-
2B-	-der
1c	ban/
2r	.
=-	=-
1c	Noch
2d	sitzt
2d	bey
2A	giff-
2d	-tig-
2c	-bö-
2A	-sen
1B-	rot
1A	ten/
2r	.
=-	=-
1c	Da
2A	man
2G	hon-
2F	-schimpf-
2A	-lich
2B-	weiß
2A	zu
1G	spot-
0Fl;	-ten.
==	==
*-	*-
```

@WolfgangDrescher
Copy link
Owner

Is there a documentation on how to read PMX? I can't figure out how to interpret it. E.g.:

1 2 97.5 1 0 0 4 0 0 -2
1 2 97.5 8 0 0 4 0 0 -2
1 2 97.5 104 0 1 4 0 0 -1.66

Or

#define SVG t 1 1 \n_99%svg%
t 2 10 14 1 1 0 0 0 0 -1.35
_00
8 1 0 0 0 200
8 2 0 -6 0 200
14 1 0 2
14 1 200 2
14 1 0 2 8
3 2 2
3 1 2 0 1
1 2 25 5 0 -1 4 0 0 0 99 0 0 6.5573
1 2 25 6 0 -1 4 0 0 0 99 0 0 8.6693
1 2 25 7 0 -1 4 0 0 0 99 0 0 17.1173
1 2 25 8 0 -1 4 0 0 0 99 0 0 19.9333
1 2 25 9 0 -1 4 0 0 0 99 0 0 12.8933
1 2 25 10 0 -1 4 0 0 0 99 0 0 12.1893
1 2 25 11 0 -1 4 0 0 0 99 0 0 4.4453
1 2 25 12 0 -1 4 0 0 0 99 0 0 7.2613
t 1 25 -3 1 1 0 0 0 0 -2
_00Cantus
1 2 25 5 0 0 4 0 0 -2
1 2 25 12 0 0 4 0 0 -2
1 2 25 108 0 1 4 0 0 -1.66
1 2 97.5 1 0 -1 4 0 0 0 99 0 0 9.58036
1 2 97.5 2 0 -1 4 0 0 0 99 0 0 8.54506
1 2 97.5 3 0 -1 4 0 0 0 99 0 0 19.9333
1 2 97.5 4 0 -1 4 0 0 0 99 0 0 12.6862
1 2 97.5 4 0 -1 4 0 0 0 99 0 0 3.36859
1 2 97.5 5 0 -1 4 0 0 0 99 0 0 19.9333
1 2 97.5 6 0 -1 4 0 0 0 99 0 0 8.54506
1 2 97.5 7 0 -1 4 0 0 0 99 0 0 3.36859
1 2 97.5 8 0 -1 4 0 0 0 99 0 0 3.36859
t 1 97.5 -3 1 1 0 0 0 0 -2
_00Tenor
1 2 97.5 1 0 0 4 0 0 -2
1 2 97.5 8 0 0 4 0 0 -2
1 2 97.5 104 0 1 4 0 0 -1.66
1 1 170 9 0 -1 4 0 0 0 99 0 0 3.50663
1 1 170 10 0 -1 4 0 0 0 99 0 0 7.02663
1 1 170 11 0 -1 4 0 0 0 99 0 0 15.24
1 1 170 12 0 -1 4 0 0 0 99 0 0 12.8933
1 2 170 1 0 -1 4 0 0 0 99 0 0 19.9333
1 2 170 2 0 -1 4 0 0 0 99 0 0 8.19997
1 2 170 3 0 -1 4 0 0 0 99 0 0 17.5866
1 2 170 4 0 -1 4 0 0 0 99 0 0 9.3733
1 2 170 4 0 -1 4 0 0 0 99 0 0 3.50663
1 2 170 5 0 -1 4 0 0 0 99 0 0 17.5866
1 2 170 6 0 -1 4 0 0 0 99 0 0 5.8533
t 1 170 -3 1 1 0 0 0 0 -2
_00Bassus
1 1 170 9 0 0 4 0 0 -2
1 2 170 6 0 0 4 0 0 -2
1 2 170 102 0 1 4 0 0 -1.66

@craigsapp
Copy link
Author

Send me your email to craigsapp at gmail.com and I will email some documentation.

The PMX is the data format for the SCORE notation editor (SCORE files are binary but there is a command in SCORE called PMX which outputs the data to an ASCII file). PMX means "Parameter MatriX", since it is like a 2D array of numbers (although the second dimension does not have a fixed length). Each line represents a particular object type, with the first number on the line indicating the object type. 1 means a note:

1 2 97.5 1 0 0 4 0 0 -2
  • P1 = 1: object is a note
  • P2 = 2; note is on staff 2 (second from the bottom)
  • P3 = 97.5; horizontal position (close to the middle of the page at 100).
  • P4 = 1; vertical position (1 = first ledger line below the staff)
  • P5 = 0; stem/accidentals (there is no stem nor accidental)
  • P6 = 0; notehead type (solid black note)
  • P7 = 4; duration (4 quarter notes)
  • P8 =0; stem length (if present: default of one octave)
  • P9 = 0; augmentation dots and stem flags (none)
  • P10 = -2: horizontal offset (move slightly to the left)

I teach SCORE for two weeks each year in my class at Stanford, so there is some more detailed information about SCORE and PMX data: https://wiki.ccarh.org/wiki/Music_253/CS_275a_Winter_2022_Syllabus#week5

Here is a cheat sheet for the meaning of note parameters:
https://wiki.ccarh.org/wiki/SCORE_note_parameters
PDF version: https://wiki.ccarh.org/images/f/fa/Code1.pdf

I have a C++ SCORE data parser: https://github.com/craigsapp/scorelib

And here is an example score + audio alignment that was created from PMX data: https://www.ccarh.org/beethoven/op131/mvmt4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants