Text Format Music, software by Henstepl - A batch software to notate and process music with only a text editor

henstepl

Cool Like Florian Schneider
True & Honest Fan
kiwifarms.net
Joined
Oct 6, 2020
My syndrome gives me an intellectual constraint - I'm the smartest man in only certain given rooms, and there are many activities that repulse me. But there's one activity that compels me: it is the writing of my Text Format Music software.

What is Text Format Music?
Text Format Music (TFM) is the plaintext musical notation I devised years ago and undertook as a software project ever since. Let's see an example:
Code:
][ MIDI channel 0 and 1 with title
][
[]T:  Laughing_With\
[]ch \\-B\\\\\\\\\\\
[]ch \\-A\\\\\\\\\\\
[]ch \\-A\\\\\\\\\\\
[]ch \\-A\\\\\\\\\\\
[]ch \\-A\\\\\\\\\\\
[]ch \\-A\\\\\\\\\\\
[]ch \\-A\\\\\\\\\\\
[]ch \\-A\\\\\\\\\\\
[]ch \\-A\\\\\\\\\\\
][
][ midi instrument 54 (decimal), vocals, on channel 1
][
[]in \54\
[]in \--\
[]in \--\
[]in \--\
[]in \--\
[]in \--\
[]in \--\
[]in \--\
[]in \--\
][
][ tempo and first notes: flattened notes are lowercase and naturals uppercase. Get SHARPS in []pi instead.
][
[]Q:  \\  77
[]PI  \\d3--F3--\\e3------\\C3a2\\b2\\
[]PI  \\F3--C3--\\b2--C3C3\\a2F2\\G2\\
[]PI  \\b2--a2--\\G2--a2G2\\F2d2\\e2\\
[]rh  \\naaanaaa\\naaanana\\nana\\na\\
[]PI  \\----F2--\\e2--e2C2\\----\\--\\
[]PI  \\F2--C2--\\b1------\\C2a1\\b1\\
[]PI  \\b1--F1--\\e1--a1G1\\F1d1\\e1\\
[]rh  \\naaanaaa\\naaanana\\nana\\na\\
][
][ And yes, that's rhythm: "--nanana naaaaaaa"! Proportional to the duration of the note.
][
[]ly        No-one laughs at God in a hospital,                                     No-one's\
[]ly                                            no-one laughs at God in a war.             \\
[]PI  \\----d3d3d3--d3d3e3C3b2a2--b2b2--\\------b2-b2-b2----a2-b2-C3C3C3------------b2-a2-\\\
[]rh  \\----nananaaananananananaaananaaa\\------naanaanaaaaanaanaanananaaaaaaa------naanaa\\\
[]PI  \\F3--------------F3--------------\\e3----------------------C3----------C3----------\\\
[]PI  \\d3--------------C3--------------\\b2----------------------a2----------G2----------\\\
[]PI  \\b2--------------a2--------------\\G2----------------------------------------------\\\
[]rh  \\naaaaaaaaaaaaaaanaaaaaaaaaaaaaaa\\naaaaaaaaaaaaaaaaaaaaaaanaaaaaaaaaaanaaaaaaaaaaa\\\
[]PI  \\F2--------------F2--------------\\e2----------------------e2----------e2----------\\\
[]PI  \\----------------C2--------------\\b1----------------------------------------------\\\
[]PI  \\b1--------------F1--------------\\e1----------------------a1----------G1----------\\\
[]rh  \\naaaaaaaaaaaaaaanaaaaaaaaaaaaaaa\\naaaaaaaaaaaaaaaaaaaaaaanaaaaaaaaaaanaaaaaaaaaaa\\\
][
][ Lyrics display at the very time they're printed in a jukebox-compatible MIDI player.
][
[]ly  laughing at God when they're starving or freezing or               \
[]ly                                    so very poor.                    \
[]PI \a2b2C3b2C3b2a2--a2b2C3b2C3b2a2--\\b2--a2a2a2----------------------\\
[]rh \nananananananaaanananananananaaa\\naaananana----------------------\\
[]PI \C3------C3------C3------C3------\\a2------------------------------\\
[]PI \a2------a2------G2------G2------\\F2------------------------------\\
[]PI \F2------F2------e2------e2------\\d2------------------------------\\
[]rh \naaaaaaanaaaaaaanaaaaaaanaaaaaaa\\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\
[]PI \C2------------------------------\\a1------------------------------\\
[]PI \--------------------------------\\--------------------------------\\
[]PI \F1----------C2--e1----------C2--\\d1------------------------------\\
[]rh \naaaaaaaaaaanaaanaaaaaaaaaaanaaa\\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\

And it is readily composable in any text editor, and usable with the TFM software itself, which I coded from the ground up. At this time, it converts my files to valid MIDI - that is, to sound, with limited sheetmusic support (input, compile with -DLY) And it's comfy to use!

TFM was born from the concept of "squares" - pairs of characters provably sufficient for many kinds of music - and the vertical parallel by which the file is read by columns, not by rows. Each column corresponds to the same time in music, and a "block" of columns is ended with a "broken square" ][, akin to a master linebreak.

My software is written in C, with a Flex lexer generator file featuring lots of code you'll never need to see, paired with simplified regex rules anyone could extend. Due to the unique syntax, I find my software to be immensely easier than anything else for its purpose, and for all the effort I've got ahead of me, I'd invite folks to contribute if I weren't nervous about code from undoxxed individuals.

How can I try TFM?
You can download this Linux shellscript to get it and here's some input files. Complete source code is tfm.l, main.c, multi.c and all you need is Flex and a compiler, but the Linux shellscript will walk you through it entirely. Last I checked it compiled on Windows - maybe get the flex output instead of the tfm.l input - but it's all CLI right now, until I finish the C# Notepad clone I've forked for a GUI.
 
I don't want to be the rainer-on of parades, but this seems incredibly difficult to use. Am I understanding it right that each "rhythm section" controls the lines above it, and everything in each "rhythm block" has the same rhythm?

So if we have this (in the treble clef):
1667794414198.png


Does that have to become:
Code:
[]PI  \\A5G4\\\
[]rh  \\nana\\\
[]PI  \\E4--\\\
[]rh  \\naaa\\\
Or what?
 
  • Thunk-Provoking
Reactions: Toolbox
I don't want to be the rainer-on of parades, but this seems incredibly difficult to use. Am I understanding it right that each "rhythm section" controls the lines above it, and everything in each "rhythm block" has the same rhythm?

So if we have this (in the treble clef):
View attachment 3813048

Does that have to become:
Code:
[]PI  \\A5G4\\\
[]rh  \\nana\\\
[]PI  \\E4--\\\
[]rh  \\naaa\\\
Or what?
If you've ever seen tracker software that runs vertical instead of horizontal (like every other DAW) it's basically that in text format.
 
I mean, it's pretty cool and all. I always find things like this interesting, but what's the point it writing out a large text file that looks really annoying to type out, typing '\\ ------' over and over and over again seems pretty shitty to be honest, that then gets converted to a midi file when there's a million and one programs out there for editing and working with midi files using a myriad of different workflows?
 
I don't want to be the rainer-on of parades, but this seems incredibly difficult to use. Am I understanding it right that each "rhythm section" controls the lines above it, and everything in each "rhythm block" has the same rhythm?

So if we have this (in the treble clef):
View attachment 3813048

Does that have to become:
Code:
[]PI  \\A5G4\\\
[]rh  \\nana\\\
[]PI  \\E4--\\\
[]rh  \\naaa\\\
Or what?

Those notes would be A3, G3, E3. The number rolls over at the C note.

Apart from that, that is mostly correct, however I would make the upper []rh into an []RH. This actually makes it a sub-channel, so it can be added or removed between blocks.


[]]rh \...\ Channel A
[=]rh \...\ Channel B
][
[]rh \...\ Channel A
[]RH \...\ Subchannel B1
[]rh \...\ Channel B


Channels themselves are specified early on with []ch type lines, and you can see alignment characters in the beginning of each line. This and []RH subchannels are more visible in the Beethoven file.

I mean, it's pretty cool and all. I always find things like this interesting, but what's the point it writing out a large text file that looks really annoying to type out, typing '\\ ------' over and over and over again seems pretty shitty to be honest, that then gets converted to a midi file when there's a million and one programs out there for editing and working with midi files using a myriad of different workflows?

These text formats exist, and for some people they're the only way to do music and everything else is point-and-click bullshit.

But there's a reason it's more comfortable than you let off. Because of the vertical separation, the writing of each note-aspect (such as pitch) automatically puts you in position to write the same aspect of the next note. It's unstaggered - you can do the rhythm of the whole piece and only then the pitches, and then you're done. Contrasted with MusicXML bullshit, my notation is as de-XMLified as possible.

You may be a Vim guy, but I bet if you opened this in GNU nano and shot off the strategies that the format speaks to, and employed copy/paste, you'd see how I made a symphony into a breeze.

It was a breeze, after all. Can you find a text-format you'd think to be as comfortable, for this?

Input, my PDF, my MIDI.
 
Last edited:
  • Informative
Reactions: Toolbox
Those notes would be A3, G3, E3. The number rolls over at the C note.

Apart from that, that is mostly correct, however I would make the upper []rh into an []RH. This actually makes it a sub-channel, so it can be added or removed between blocks.


[]]rh \...\ Channel A
[=]rh \...\ Channel B
][
[]rh \...\ Channel A
[]RH \...\ Subchannel B1
[]rh \...\ Channel B



Channels themselves are specified early on with []ch type lines, and you can see alignment characters in the beginning of each line. This and []RH subchannels are more visible in the Beethoven file.



These text formats exist, and for some people they're the only way to do music and everything else is point-and-click bullshit.

But there's a reason it's more comfortable than you let off. Because of the vertical separation, the writing of each note-aspect (such as pitch) automatically puts you in position to write the same aspect of the next note. It's unstaggered - you can do the rhythm of the whole piece and only then the pitches, and then you're done. Contrasted with MusicXML bullshit, my notation is as de-XMLified as possible.

You may be a Vim guy, but I bet if you opened this in GNU nano and shot off the strategies that the format speaks to, and employed copy/paste, you'd see how I made a symphony into a breeze.

It was a breeze, after all. Can you find a text-format you'd think to be as comfortable, for this?

Input, my PDF, my MIDI.
Well there's things like Alda or Takt which offer higher level programming abstractions as well as being clean and concise to program and compose in.

I don't use vim and the only time I use nano when working in a console. I usually use Kate for programming.

The thing about text based music systems in general is the extra step between composing the music and the results. That extra step of having to compile or interpret before hearing the results.

What would be pretty cool is an ide built on top of your text system that allows for immediate feedback. But at that point I'm not sure how that would be different than tracker software at that point.
 
Well there's things like Alda or Takt which offer higher level programming abstractions as well as being clean and concise to program and compose in.

I don't use vim and the only time I use nano when working in a console. I usually use Kate for programming.

The thing about text based music systems in general is the extra step between composing the music and the results. That extra step of having to compile or interpret before hearing the results.

What would be pretty cool is an ide built on top of your text system that allows for immediate feedback. But at that point I'm not sure how that would be different than tracker software at that point.

I've heard of these options, and they're both forgettable. I mean, I posted one example of mine and Kosher Dill immediately guessed a (suboptimally correct) way to extend it. And the example was more than a one-line ditty, but those softwares, by not distancing themselves from an XML mentality, make themselves most suited for limericks and one-line ditties.

If I had posted only two lines - []PI and []rh, he might have been still able to guess that a chord is []PI, []PI, []PI, []rh.

But if it'd have been MusicXML, he'd have had to guess <chord><note /><note /><note /></chord>, or whatever, and he wouldn't be able to guess. As a matter of fact, you can't guess chord notation from a single voice of any text notation other than mine. That's because notes are followed by notes - and I realize that - whereas XHTML paragraph elements can be followed by anything, so I have no reason to arrange my information like XML.

It is an arrangement of information, after all. The source code is intended to be as readable as the program output. I'm thinking about IDEs such as you mention. But such efforts would begin with nothing more than an augmented text editor, for autocomplete (and auto-expand), or even just syntax highlighting.

I don't like the word "mobilize", so I strive not to do that. But I'm glad to receive any advice to the above end.

And I'm certainly glad I have my software...
 
I'm thinking about IDEs such as you mention. But such efforts would begin with nothing more than an augmented text editor, for autocomplete (and auto-expand), or even just syntax highlighting.
That and built in compilation allowing immediate feedback of compositions would be a good start.
 
I honestly don't see what this offers over Lilypond.
But I do, and at this time, that's what prides me. My nature repels me from Lilypond GUIs and despite my use of a Lilypond intermediate for scoring output, I find its text syntax so forgettable that I've already forgotten how to reproduce anything here in there.

This software is my passion. I work on it every day, with an obsessive fervour perhaps of the psychiatric sort I'm known for. I will keep working towards my goals and those mentioned here - I will always accept advice.

I thank you Grub, and @Kosher Dill, you deserve to know your valid TFM produces this MIDI (sounds like this MP3), and this PDF. Sadly the PDF is missing a note - it's a task on my list.

And why'd that display as two measures, anyway?
 
  • Like
Reactions: Toolbox
@Kosher Dill, you deserve to know your valid TFM produces this MIDI (sounds like this MP3), and this PDF. Sadly the PDF is missing a note - it's a task on my list.
Why not just attach the files to your post, and similarly with the source code? I don't think anyone wants to download files off of some raw IP address they found on Kiwi Farms, just sayin'.

Anyhow: what's the deal with the rhythm line anyway? Does it serve some other function? Why not just collapse it into a "textual piano roll"? You could use - for silence, like an empty "staff line", and = (or whatever) for extending a note.

Original:
Code:
[]PI  \\----d3d3d3--d3d3e3C3b2a2--b2b2--\\------b2-b2-b2----a2-b2-C3C3C3------------b2-a2-\\\
[]rh  \\----nananaaananananananaaananaaa\\------naanaanaaaaanaanaanananaaaaaaa------naanaa\\\

Collapsed:
Code:
[]PI  \\----d3d3d3==d3d3e3C3b2a2==b2b2==\\------b2=b2=b2====a2=b2=C3C3C3======------b2=a2=\\\
 
But I do, and at this time, that's what prides me. My nature repels me from Lilypond GUIs and despite my use of a Lilypond intermediate for scoring output, I find its text syntax so forgettable that I've already forgotten how to reproduce anything here in there.

Oh yeah, because e.g. <c e g> for a C major chord is so arcane...

To be fair, I didn't immediately realize this was a schizo thread.
 
Wow, I've never seen what schizophrenia in code form looks like before:

Code:
rewind(stdin); tfmlex_init (&SCANNER1);
                                //  FREE pre on loop exit
uint32_t lbm = 1; for (char wid,hold,x,*pre=(char*)calloc(3,1);lbm||(FR pre);) {
    lbm = yylex(0,0,0,SCANNER1); if(TYPEGETPLUS ENL) continue; //if no squarelines here
    typeget(NUMBLK, tx+TYPEGETPLUS[*TYPEGETPLUS]-1); *(int16_t*)pre=0;//2byte b/c 3 done
    ((ALLOC(rhsAt  ) NUMBLK))[NUMBLK-1]=
    ((ALLOC(rhsType) NUMBLK))[NUMBLK-1] NL;
    (ALLOC(argsv) typLns[NUMBLK]);
    for (uint32_t jj=typLns[NUMBLK]; wid=1, jj>((NUMBLK-1)?typLns[NUMBLK-1]:0); --jj) {
        while(tx[begins[jj]+wid]!=']') ++wid;
        while(tx[begins[jj]+wid]==']') ++wid;
        hold= tx[begins[jj]+wid+2]; tx[begins[jj]+wid+2]='\0';
           x=myStrCmp(&tx[begins[jj]+wid],widR,1,pre); if (x)
          *(int16_t*)pre=*(int16_t*)(tx+begins[jj]+wid), //=memcpy 2 bytes
          push(typLns[NUMBLK]-jj,    &rhsAt  [NUMBLK-1]),
          push(x>>1,            &rhsType[NUMBLK-1]);
        tx[begins[jj]+wid+2]=hold;
        hold=tx[begins[jj]+1+wid-2]; tx[begins[jj]+1+wid-2]='\0';
          argsv[jj-1]=strcpy((char*)calloc(--wid,1),&tx[begins[jj]+1]);
        tx[begins[jj]+1+--wid]=hold;
        while (wid--) tx[begins[jj]]='\0', tx[++begins[jj]]='[';
    }
    FREE(TYPEGETPLUS);
}

fclose(stdin); tfmlex_destroy(SCAN); tfmlex_init(&SCANNER1);
 
Wow, I've never seen what schizophrenia in code form looks like before:

Code:
rewind(stdin); tfmlex_init (&SCANNER1);
                                //  FREE pre on loop exit
uint32_t lbm = 1; for (char wid,hold,x,*pre=(char*)calloc(3,1);lbm||(FR pre);) {
    lbm = yylex(0,0,0,SCANNER1); if(TYPEGETPLUS ENL) continue; //if no squarelines here
    typeget(NUMBLK, tx+TYPEGETPLUS[*TYPEGETPLUS]-1); *(int16_t*)pre=0;//2byte b/c 3 done
    ((ALLOC(rhsAt  ) NUMBLK))[NUMBLK-1]=
    ((ALLOC(rhsType) NUMBLK))[NUMBLK-1] NL;
    (ALLOC(argsv) typLns[NUMBLK]);
    for (uint32_t jj=typLns[NUMBLK]; wid=1, jj>((NUMBLK-1)?typLns[NUMBLK-1]:0); --jj) {
        while(tx[begins[jj]+wid]!=']') ++wid;
        while(tx[begins[jj]+wid]==']') ++wid;
        hold= tx[begins[jj]+wid+2]; tx[begins[jj]+wid+2]='\0';
           x=myStrCmp(&tx[begins[jj]+wid],widR,1,pre); if (x)
          *(int16_t*)pre=*(int16_t*)(tx+begins[jj]+wid), //=memcpy 2 bytes
          push(typLns[NUMBLK]-jj,    &rhsAt  [NUMBLK-1]),
          push(x>>1,            &rhsType[NUMBLK-1]);
        tx[begins[jj]+wid+2]=hold;
        hold=tx[begins[jj]+1+wid-2]; tx[begins[jj]+1+wid-2]='\0';
          argsv[jj-1]=strcpy((char*)calloc(--wid,1),&tx[begins[jj]+1]);
        tx[begins[jj]+1+--wid]=hold;
        while (wid--) tx[begins[jj]]='\0', tx[++begins[jj]]='[';
    }
    FREE(TYPEGETPLUS);
}

fclose(stdin); tfmlex_destroy(SCAN); tfmlex_init(&SCANNER1);

I mean, I'm tickled by any comments on my code, but you're looking at it wrongly. That there (since slightly edited I think) is just the uglycode needed to make a beautiful scanner elsewhere.

Check out tfm.l instead. It's full of lines like this:

Code:
^\[\]vy{_}pppp	{
	#// []vy first-layer velocity: common interpretations of "forte" and other sheet-music terms.
	#// This velocity is applied until another line is found that's had a velocity specified.
	#// It is re-applied in subsequent columns.
		Q veloc=doVeloc(8); }
^\[\]vy{_}ppp	Q veloc=doVeloc(20);
^\[\]vy{_}pp	Q veloc=doVeloc(31);
^\[\]vy{_}p	Q veloc=doVeloc(42);
^\[\]vy{_}mp	Q veloc=doVeloc(53);
^\[\]vy{_}mf	Q veloc=doVeloc(64);
^\[\]vy{_}f	Q veloc=doVeloc(80);
^\[\]vy{_}ff	Q veloc=doVeloc(96);
^\[\]vy{_}fff	Q veloc=doVeloc(112);
^\[\]vy{_}ffff	Q veloc=doVeloc(127);

Which gives you half an idea of how to add notations of your own: the rules section of a Flex file consists of little regexes followed by what they do, to be compiled into C code. (The regex wouldn't be so little without the schizocode you commented on.)

To provide update: I've made my timing scheme nearly undefeatable. You can add time signatures, arbitrary n-tuplets, and even multiple of each together in a piece without breaking it for Lilypond or MIDI.

This software is my passion and without it I could never satisfyingly reproduce the tunes and poetries running around my head constantly, such as Aqualung (TFM and MIDI - put it in a player with lyrics).
 
  • Like
Reactions: Wright
Back