Close

Always Play Bach

A project log for Rubidium 2.0

This is an all in one spectrum and logic analyzer, robotics control platform, and modular synthesizer with audio in and sheet music out!

glgormanglgorman 10/26/2021 at 05:320 Comments

 If you have never seen the videos on YouTube about "Pachelbel Rant" and how almost every pop song ever written just might be based on "Pachelbel's Cannon in D" - please search for it and check it out NOW, and while you are at it - check out another group that claims that if you can "learn to play just four chords" you can crank out every pop hit from the last 40 years and that one is by a group that calls themselves "Axis of Awesome" and you can find their variations of the "Axis of Awesome Four Chord Song" also on YouTube.

 I have my own theory of course, that nearly every rock song ever written can be improvised from J.S. Bach's "Fugue in G"., which is a truly wonderful piece, and eventually, I will be uploading "something" or another to YouTube demonstrating some live "audio in and sheet music out" capabilities either running entirely on the parallax propeller P2 chip, including VGA/HDMI generated on-chip; or else (and initially) by just using the propeller in oscilloscope mode, while running the spectrum analyzer software on a PC connected via USB., but of course, I had to throw together "some kind of sheet music software" that doesn't need 250 Megabytes.  Right now this one is using 177 LINES of C++, and even though we don't have C++ yet on the Propeller P2, as long as I avoid polymorphism, multiple inheritances, C++ exceptions, etc., and try to stick to things that work well with structs, namespaces, and MoveTo and LineTo style drawing - this is very doable. 

 Also, I am adding rudimentary support for actually PRINTING sheet music to the C++ version of Propeller Debug Terminal and will be adding some source code to the repository in GitHub very shortly.  In the meantime, you might want to check out the "music_notation_test.pdf" upload that I am putting in with the files on this site, i.e...., as a part of the proof of concept.  Object Pascal for the Propeller would also be a good way to pull all of this together; whether based on UCSD Pascal, or whether based on Lazarus, either would serve as a very nice way to get a  GUI up and running with some real workhorse software - so that is how all of this is going to come together. 

In any case, perhaps this would be a good time to dig into some of the internals of how the data stream decoding/parsing and command processing is going to work, in the various modes that are going to be offered on the PC side of things.  Here is a debugging session where we can see how a message from the serial/USB communications object is being generated into the application's "mainFrame" which in turn is in the process of being tokenized. 

This particular function takes strings of characters as they are received and then splits them whenever a line break occurs.  If you look carefully, the data stream from the Propeller has a few places where there are some embedded commands like "SCOPE s POS 710 1000 size", followed on the next line (aaargh!) by the parameters "512 276 samples 512 rates 512 longs_8bit ".  This is of course what needs to be further parsed out, that is to say, by further tokenizing each string, in order to recognize keywords like "SOCPE" or "ELLIPSE", on the one hand; which I why I have had to implement my own TREESEARCH and symbol table management functions.  So that means now that it is a simple matter to fully tokenize each string, and then either allow the original code to process the data by feeding it whatever it needs each time that it makes a call to "NextElement(ele_num)", so that this thing will finally work just like the original Delphi.  Pretty simple actually.  Still what I want to do, as I have mentioned before, for example, while looking at how the UCSD Pascal compiler works - is to try to express the grammar of the debugging protocol, not with long chains of case statements, which go on for 100's, but not quite 1000's of lines, but rather I want to be able to express Backus-Naur form of the implied grammar with tables of keywords, enums, and function pointers, sort of like how I have figured out how to do with the port of the Pascal compiler.  Thus even though the function "IDSEARCH" turned out to be "missing" from the UCSD distribution, I came up with this C++ object that helps move things along nicely, i.e., as far as parsing Pascal programs goes.

struct key_info
{
    ALPHA        ID;
    SYMBOL        SY;
    OPERATOR    OP;
    key_info() { };
    key_info(char *STR, SYMBOL _SY, OPERATOR _OP)
    {
        strcpy_s(ID,16,STR);
        SY = _SY;
        OP = _OP;
    }
};

key_info key_map[] =
{
    key_info("DO",DOSY,NOOP),
    key_info("WITH",WITHSY,NOOP),
    key_info("IN",SETSY,INOP),
    key_info("TO",TOSY,NOOP),
    key_info("SET",SETSY,NOOP),
    key_info("DOWNTO",DOWNTOSY,NOOP),
    key_info("LABEL",LABELSY,NOOP),
    key_info("PACKED",PACKEDSY,NOOP),
    key_info("END",ENDSY,NOOP),
    key_info("CONST",CONSTSY,NOOP),
    key_info("ARRAY",ARRAYSY,NOOP),
    key_info("UNTIL",UNTILSY,NOOP),
    key_info("TYPE",TYPESY,NOOP),
    key_info("RECORD",RECORDSY,NOOP),
    key_info("OF",OFSY,NOOP),
    key_info("VAR",VARSY,NOOP),
    key_info("FILE",FILESY,NOOP),
    key_info("THEN",THENSY,NOOP),
    key_info("PROCSY",PROCSY,NOOP),
    key_info("PROCEDURE",PROCSY,NOOP),
    key_info("USES",USESSY,NOOP),
    key_info("ELSE",ELSESY,NOOP),
    key_info("FUNCTION",FUNCSY,NOOP),
    key_info("UNIT",UNITSY,NOOP),
    key_info("BEGIN",BEGINSY,NOOP),
    key_info("PROGRAM",PROGSY,NOOP),
    key_info("INTERFACE",INTERSY,NOOP),
    key_info("IF",IFSY,NOOP),
    key_info("SEGMENT",SEPARATSY,NOOP),
    key_info("IMPLEMENTATION",IMPLESY,NOOP),
    key_info("CASE",CASESY,NOOP),
    key_info("FORWARD",FORWARDSY,NOOP),
    key_info("EXTERNAL",EXTERNLSY,NOOP),
    key_info("REPEAT",REPEATSY,NOOP),
    key_info("NOT",NOTSY,NOOP),
    key_info("OTHERWISE",OTHERSY,NOOP),
    key_info("WHILE",WHILESY,NOOP),
    key_info("AND",RELOP,ANDOP),
    key_info("DIV",MULOP,IDIV),
    key_info("MOD",MULOP,IMOD),
    key_info("FOR",FORSY,NOOP),
    key_info("OR",RELOP,OROP),
};

Now of course, as stated, in the original UCSD Pascal distribution, they apparently implemented something like this data structure along with the function that uses it in 6502 assembly, which the documentation refers to as MAGIC.   Yeah, MAGIC!  Great?!!  Well, now we know enough to be able to implement an even more generic, and extensible grammar, that can be made to additional things, like maybe translating human-readable OpenGL commands generated using simple print statements into the appropriate function calls, or else defining other types of grammar, that is to say, without having to deeply nested, impossible to debug legacy code, and so on.    

Discussions