# /* * M O Z A R T . C * * This is an implementation of the "Melody Dicer," attributed to Mozart * that generates an "infinite" number of short minuettes. The note * tables have not been included as I have not been able to find a * public-domain source. If you are interested, you should try to find * a copy of the "Melody Dicer" game, available at music stores everywhere. * * As implemented, this program generated music tables that were used to * drive a home-made synthesizer board attached to an RT-11 system. * Converting to other synthesizers should not be too great a chore. * * The documentation file, mozart.rno, describes a similar algorithm by * Kirnberger. Unfortunately, we haven't gotten around to typing in * the notes yet. * * [And you thought programmers weren't sadistic.] * */ #define NMEASURE 200 /* Measures */ #define NVOICES 1800 /* Voice information (n_voice * m_meas) */ #define NNOTES 4000 /* Notes in compiled music */ #define NPNOTES 256 /* Play notes in each voice */ #define NPBYTES 768 /* Bytes in each voice (NPNOTES * 3) */ #define NPVOICE 6 /* Voices for each measure */ #define MULTIPLIER 12 /* Multiply duration to get play dur. */ /* * Special "notes" */ #define REST 192 /* "Rest" note */ #define GAP 193 /* Descriptor for voice header */ #define XPOSE 194 /* "" */ #define ATACK 195 /* "" */ #define DECAY 196 /* "" */ #define VOL 197 /* "" */ #define SUSTN 198 /* "" */ #define RELSE 199 /* "" */ #define STOP 203 /* End of other voice tables */ #define END 205 /* End of first voice table */ /* * RT11 definitions */ #define JSW 044 /* Job status word */ #define SPECMODE 010100 /* Bit 12, console special mode */ struct { int mem_word; } /* * Structure definitions */ struct measure { struct voice *m_voice[2]; /* Points to NPVOICE voices */ int m_vcount[2]; /* Number of voices for measure */ }; struct voice { struct note *v_note; /* Notes for this voice */ int v_count; /* Number of notes for voice */ }; struct note { char n_note; char n_dur; }; /* * Tables for compilation */ char *special[] { "REST ", "GAP ", "XPOSE", "ATACK", "DECAY", "VOL ", "SUSTN", "RELSE", "?200 ", "?201 ", "?202 ", "STOP ", "?204 ", "END ", }; /* * ttab translates notes (A-G) to numbers. */ int ttab[] { 0, 1, /* A A# */ 2, -1, /* B */ 3, 4, /* C C# */ 5, 6, /* D D# */ 7, -1, /* E */ 8, 9, /* F F# */ 10, 11, /* G G# */ }; /* * dtab translates durations to length codes */ struct dtab { int d_val; int d_dur; } dtab[] { { 64, 1 }, { 32, 3 }, { 16, 6 }, { 8,12 }, { 4,24 }, { 2,48 }, { 1,96 }, { 0, 0 }, }; #define RESTDUR (3 * 12) /* A missing voice = 3 1/8 note rest */ /* 1/8 notes have 12 duration in dtab[] */ int restword = (RESTDUR)*MULTIPLIER; char *restbyte = &restword; /* Pointer to restword as two bytes */ char *ntext[] { "AN", "AS", "BN", "CN", "CS", "DN", "DS", "EN", "FN", "FS", "GN", "GS" }; /* * Mtab has measure numbers (from the text). It is stored as printed. * I.e, first all numbers for dice 2, then for dice 3, etc. */ int mtab[11][16] { 96, 22,141, 41,105,122, 11, 30, 70,121, 26, 9,112, 49,109, 14, 32, 6,128, 63,146, 46,134, 81, 117, 39,126, 56,174, 18,116, 83, 69, 95,158, 13,153, 55,110, 24, 66,139, 15,132, 73, 58,145, 79, 40, 17,113, 85,161, 2,159,100, 90,176, 7, 34, 67,160, 52,170, 148, 74,163, 45, 80, 97, 36,107, 25,143, 64,125, 76,136, 1, 93, 104,157, 27,167,154, 68,118, 91, 138, 71,150, 29,101,162, 23,151, 152, 60,171, 53, 99,133, 21,127, 16,155, 57,175, 43,168, 89,172, 119, 84,114, 50,140, 86,169, 94, 120, 88, 48,166, 51,115, 72,111, 98,142, 42,156, 75,129, 62,123, 65, 77, 19, 82,137, 38,149, 8, 3, 87,165, 61,135, 47,147, 33, 102, 4, 31,164,144, 59,173, 78, 54,130, 10,103, 28, 37,106, 5, 35, 20,108, 92, 12,124, 44,131 }; struct voice voice[NVOICES]; struct voice *p_voice = voice; /* Free pointer into voice */ struct measure measure[NMEASURE]; /* Voices for this measure */ struct note note[NNOTES]; struct note *p_note = note; /* Free pointer into note[] */ char voice0[NPBYTES]; /* Voices are stored here */ char voice1[NPBYTES]; char voice2[NPBYTES]; char voice3[NPBYTES]; char voice4[NPBYTES]; char voice5[NPBYTES]; char **voices[NPVOICE] { &voice0, &voice1, &voice2, &voice3, &voice4, &voice5 }; char **pvoices[NPVOICE]; /* -> voice's current note */ char **voicetop[NPVOICE] { &voice0[NPBYTES], &voice1[NPBYTES], &voice2[NPBYTES], &voice3[NPBYTES], &voice4[NPBYTES], &voice5[NPBYTES] }; struct psetup { int ps_note; /* Funny note to do */ int ps_value; /* Default value */ int ps_index; /* index into psetval[] */ char *ps_text; /* Name of this parameter */ } psetup[] { { GAP, 65535, 0, "Gap" }, { XPOSE, 65024, 1, "Transpose" }, { ATACK, 8192, 2, "Attack" }, { DECAY, 50, 3, "Decay" }, { VOL, 55000, 4, "Volume" }, { SUSTN, 20000, 5, "Sustain" }, { RELSE, 50, 6, "Release" }, { 0, 0, 0, "" }, }; int psetval[7][NPVOICE]; /* First index is value, 2nd voice */ /* * Handy variables for byte <-> word conversion */ int intword; char *byteword = &intword; int iobuf[260]; /* 520 byte i/o buffer */ char line[133]; /* Input buffer */ int end_file = 0; /* End file marker */ int line_count = 0; int curr_meas = 0; int curr_voice = 0; int echoflag = 0; main() { extern int play(); int dmusic; int pmusic; int doctal; printf("Mozart Dice Dance composer, compiling grammar\n"); if (fopen("mozart.txt", iobuf) == -1) { printf("Can't open mozart.txt\n"); exit(); } echoflag = getyesno("Echo input lines", "No"); compile(); /* Compile the music */ if (getyesno("Dump measure tables", "No")) d_meas(); /* Dump the buffers */ setpreset(); /* Initialize preset buffer */ for (;;) { dmusic = getyesno("Dump music", "No"); if (dmusic) doctal = getyesno("Dump in octal", "No"); pmusic = getyesno("Play music", "Yes"); getsetup(); printf("Type any key to stop playing\n"); for (;;) { JSW->mem_word |= SPECMODE; if (ittinr() >= 0) break; getplay(); if (dmusic) dump(doctal); /* Dump the music */ if (pmusic) { /* * play runs the synthesizer. */ call(&play, 6, &voice0, &voice1, &voice2, &voice3, &voice4, &voice5); } } JSW->mem_word &= (~SPECMODE); } } setpreset() /* * Called once to initialize the voicing parameters */ { register struct psetup *ps; register int vindex; for (ps = psetup; ps->ps_note; ps++) for (vindex = 0; vindex < NPVOICE; vindex++) psetval[ps->ps_index][vindex] = ps->ps_value; } getsetup() /* * Modify the playing parameters */ { register struct psetup *ps; register int vindex; for (;;) { printf("Enter voice number (0 .. %d), exits: ", NPVOICE - 1); if (getcommand()) exit(); if (line[0] == '\0') return; vindex = atoi(line); if (vindex < 0 || vindex >= NPVOICE) { printf("Illegal voice %d\n", vindex); continue; } printf("Current control values, enter to retain\n"); for (ps = psetup; ps->ps_note; ps++) { printf("%s = %l ? ", ps->ps_text, psetval[ps->ps_index][vindex]); if (getcommand()) exit(); if (line[0] != '\0') { psetval[ps->ps_index][vindex] = atoi(line); } } } } getplay() /* * Compile music into the playing buffers */ { register int meas; /* Which measure */ register int vindex; /* Voice index for finish */ int saveroll[17]; /* Saves dice rolls */ int mindex[17]; /* Measure index */ getinit(); /* Initialize voices */ printf("\nMeasure: "); for (meas = 1; meas <= 16; meas++) { vindex = roll(); saveroll[meas] = vindex; mindex[meas] = mtab[vindex-2][meas-1]; printf("%4d", meas); } printf("\nDice: "); for (meas = 1; meas <= 16; meas++) printf("%4d", saveroll[meas]); printf("\nTable measure:"); for (meas = 1; meas <= 16; meas++) printf("%4d", mindex[meas]); printf("\n"); for (meas = 1; meas <= 8; meas++) p_meas(mindex[meas], meas, 0, saveroll[meas]); for (meas = 1; meas <= 7; meas++) p_meas(mindex[meas], meas, 0, saveroll[meas]); p_meas(mindex[8], 8, 1, saveroll[8]); /* Get alternate ending */ for (meas = 9; meas <= 16; meas++) p_meas(mindex[meas], meas, 0, saveroll[meas]); pstuff(0, REST, restbyte[1], restbyte[0], 17); pstuff(0, END, 0, 0, 17); /* Stuff the end signals */ for (vindex = 1; vindex < NPVOICE; vindex++) { pstuff(vindex, REST, restbyte[1], restbyte[0], 17); pstuff(vindex, STOP, 0, 0, 17); } } getinit() /* * Initialize the playing field */ { register int vindex; register struct psetup *ps; for (vindex = 0; vindex < NPVOICE; vindex++) { pvoices[vindex] = voices[vindex]; for (ps = psetup; ps->ps_note; ps++) { intword = psetval[ps->ps_index][vindex]; pstuff(vindex, ps->ps_note, byteword[1], byteword[0], 0); } } } p_meas(mindex, meas, altvoice) int mindex; /* Which measure table to output */ int meas; /* Which measure we're doing now */ int altvoice; /* Alternate voice index (0 or 1) */ /* * Output all voices for this measure */ { register struct voice *vp; /* Voice table pointer */ register struct measure *mp; /* Minuette measure to do */ register int vcount; /* Voices for this measure */ int mcount; /* Voices in minuette measure */ mp = &measure[mindex]; /* Here's the one to do */ vp = mp->m_voice[altvoice]; mcount = mp->m_vcount[altvoice]; if (mcount != NPVOICE) { printf("Wrong number of voices (%d) for table measure, ", mcount, mindex); printf("alternate %d\n", altvoice+1); if (mcount > NPVOICE) mcount = NPVOICE; } for (vcount = 0; vcount < mcount; vcount++) { getvoice(vp, vcount, altvoice, meas); vp++; } for (; vcount < NPVOICE; vcount++) pstuff(vcount, REST, restbyte[1], restbyte[0], meas); } getvoice(vptr, vcount, altvoice, meas) struct voice *vptr; /* Voice to generate */ int vcount; /* Which one to stuff it into */ int altvoice; /* Selector */ int meas; /* Measure number (for debugging) */ /* * Output notes for this measure */ { register struct note *np; register int ncount; register int count; np = vptr->v_note; ncount = vptr->v_count; for (count = 0; count < ncount; count++) { intword = (np->n_dur & 255); intword = intword * MULTIPLIER; pstuff(vcount, np->n_note, byteword[1], byteword[0], meas); np++; } } pstuff(vindex, notebyte, highbyte, lowbyte, meas) int vindex; /* Voice number (0 - 5) */ char notebyte; /* Note byte */ char highbyte; /* High byte of duration */ char lowbyte; /* Low byte of duration */ /* * Enter the next note for this voice. This is the only routine to enter * data into pvoices[]. */ { register char *pp; pp = pvoices[vindex]; if (pp >= voicetop[vindex]) { printf("Too many notes for voice %d, measure %d\n", vindex, meas); fatal("Voice", voicetop[vindex] - voices[vindex]); } *pp++ = notebyte; *pp++ = highbyte; *pp++ = lowbyte; pvoices[vindex] = pp; } dump(doctal) int doctal; /* Dump in octal if set */ /* * Dumb routine to dump some music */ { register int vindex; int anything; /* Set if a note prints */ register int temp; register char *pp; char **sptr; for (vindex = 0; vindex < NPVOICE; vindex++) pvoices[vindex] = voices[vindex]; /* Init ptrs */ for (anything = 1; anything;) { anything = 0; /* Nothing yet */ for (vindex = 0; vindex < NPVOICE; vindex++) { pp = pvoices[vindex]; /* Do this one */ temp = *pp++ & 0377; /* note */ byteword[1] = *pp++ & 0377; /* hidur */ byteword[0] = *pp++ & 0377; /* lodur */ if (doctal) { printf(":%03o %03o %03o", temp, byteword[1] & 0377, byteword[0] & 0377); } else { if (temp > REST) printf("%s %6l", special[temp-REST], intword); else if (temp == REST) printf("R %9d", intword / MULTIPLIER); else { temp /= 2; printf("%s%d %8d", ntext[temp % 12], temp / 12, intword / MULTIPLIER); } } printf((vindex == NPVOICE-1) ? "\n" : " "); if (temp == STOP || temp == END) continue; anything = 1; pvoices[vindex] = pp; } } } d_meas() /* * Dump the measure table. */ { register struct measure *mp; register struct voice *vp; if (getyesno("Dump selective measures", "No")) { for (;;) { printf("Measure, 0 ends: "); if (getcommand()) return; curr_meas = atoi(line); if (curr_meas <= 0) return; if (curr_meas >= NMEASURE) { printf("Bad measure number %d\n", curr_meas); continue; } mp = &measure[curr_meas]; printf(".%d\n", curr_meas); if (mp->m_voice[0] == mp->m_voice[1]) d_voice(mp->m_voice[0], mp->m_vcount[0], 0); else { d_voice(mp->m_voice[0], mp->m_vcount[0], 1); d_voice(mp->m_voice[1], mp->m_vcount[1], 2); } } } /* * Dump all measures */ curr_meas = -1; for (mp = measure; mp < &measure[NMEASURE]; mp++) { curr_meas++; if (mp->m_vcount[0] == 0) continue; /* No entry for this measure */ printf(".%d\n", curr_meas); vp = mp->m_voice[0]; if (vp != mp->m_voice[1]) { d_voice(vp, mp->m_vcount[0], 1); d_voice(mp->m_voice[1], mp->m_vcount[1], 2); } else d_voice(vp, mp->m_vcount[0], 0); } } d_voice(vptr, mcount, altflag) struct voice *vptr; /* Voice pointer */ int mcount; /* Number of voices */ int altflag; /* Alternate signal */ /* * Dump all voices for the current measure. altflag = * 0 No alternation * 1 Dump alternate 1 * 2 Dump alternate 2 */ { register struct note *np; register int nvoice; register int ncount; if (altflag) printf("]%d\n", altflag); printf("* measure %d, [%d] %d voices\n", curr_meas, altflag, mcount); for (nvoice = 0; nvoice < mcount; nvoice++) { np = vptr->v_note; printf("* voice %d has %d notes\n", nvoice+1, vptr->v_count); for (ncount = 0; ncount < vptr->v_count; ncount++) d_note(np++); vptr++; } printf("\n"); } d_note(nptr) struct note *nptr; /* * Dump a note */ { register int nval; register struct dtab *dp; nval = nptr->n_note & 255; if (nval == REST) printf("R ,"); else { nval /= 2; printf("N %s%d,", ntext[nval % 12], nval / 12); } for (dp = dtab; dp->d_dur != 0 && dp->d_dur != nptr->n_dur; dp++); printf("%d\n", dp->d_val); } compile() /* * Compile the music */ { register struct measure *mp; /* Measure pointer */ register int mcount; register int vcount; char *skipbl(); mcount = 0; skipline(1); /* Find first line */ while (!end_file) { if (line[0] != '.') { bug("Expecting a new measure"); skipline(1); continue; } curr_meas = atoi(skipbl(&line[1])); mcount++; if (curr_meas <= 0 || curr_meas >= NMEASURE) { bug1("Illegal (or missing) measure [%d]\n", curr_meas); skipline(1); continue; } mp = &measure[curr_meas]; if (mp->m_vcount[0] != 0) { bug1("You've entered measure %d already\n", curr_meas); } if (skipline(1)) { bug("Eof after measure number"); continue; } if (line[0] != ']') { /* No alternate */ mp->m_voice[0] = p_voice; mp->m_voice[1] = p_voice; vcount = in_voice(0); mp->m_vcount[0] = vcount; mp->m_vcount[1] = vcount; if (vcount != NPVOICE) bug1("Wrong number of voices (%d) in measure", vcount); } else { /* Alternate 1 */ if (line[1] != '1') { bug1("Expecting ]1, got ]%c", line[1]); continue; } skipline(1); mp->m_voice[0] = p_voice; vcount = in_voice(); mp->m_vcount[0] = vcount; if (vcount != NPVOICE) bug1("Wrong number of voices (%d) in alt 1", vcount); if (skipline(0)) { bug("Eof after alternate 1"); continue; } if (line[0] != ']' || line[1] != '2') { bug("Illegal alternate, expecting ]2"); continue; } skipline(1); mp->m_voice[1] = p_voice; vcount = in_voice(); mp->m_vcount[1] = vcount; if (vcount != NPVOICE) bug1("Wrong number of voices (%d) in alt 2", vcount); } } printf("* %d measures entered\n", mcount); } in_voice() /* * Enter the next voices for this measure/alternate. Return * the number of voices entered. * * On entrance, line -> first voice line * Return on blank line or . or ] seen. * * Warning: the input file must have one of the following formats: * notes (both normal and alternate) * * or * notes * ]alternate */ { register struct voice *vp; register int nnotes; char *skipbl(); struct voice *nextvoice(); curr_voice = 0; while (!skipline(0)) { if (line[0] == ']' || line[0] == '.') break; curr_voice++; vp = nextvoice(); /* Vp -> voice box */ vp->v_note = p_note; while (!end_file && line[0] != 0 && line[0] != ']' && line[0] != '.') { vp->v_count = in_note(); } } return(curr_voice); } in_note() /* * Read the notes, return the number read. */ { register char *lp; register struct dtab *dp; register int c; struct note *np; int ncount; struct note *nextnote(); char *skipbl(); ncount = 0; /* * Note line format: * N AN0,len * or * R ,len * * Where N is note, R is rest. len is the length (must be in the table) * Each note is given as [A-G] followed by N (natural) or S (sharp), * followed by the octave number [0-9]. AN0 (the lowest note) is 0. */ while (!end_file && line[0] != 0 && line[0] != ']' && line[0] != '.') { np = nextnote(); ncount++; lp = skipbl(line); if ((c = *lp++) == 'R') { np->n_note = REST; } else if (c == 'N') { lp = skipbl(lp); c = *lp++ - 'A'; if (c < 0 || c > ('G' - 'A')) { bug1("Illegal character '%c'\n", c + 'A'); continue; } c += c; /* Times two for index */ if (*lp == 'S') { c++; /* Fix index for sharps */ lp++; /* Eat the character */ } else if (*lp == 'N') lp++; /* Eat natural chars. */ /* * Assuming 7 octaves?? */ if (*lp < '0' || *lp > '7') { bug1("Illegal octave '%c'\n", *lp); continue; } np->n_note = (ttab[c] + (12 * (*lp++ - '0'))) * 2; } else { bug1("Expecting note/rest, got '%c'\n", c); np->n_note = REST; /* stuff something */ } /* * Got the note, now for the duration. */ lp = skipbl(lp); if (*lp++ != ',') bug1("Expecting comma before duration, got '%c'\n", *--lp); c = atoi(lp); /* Compile duration */ for (dp = dtab; dp->d_val && dp->d_val != c; dp++); if (dp->d_val == 0) bug1("Unknown duration %d\n", c); np->n_dur = dp->d_dur; getline(); } return(ncount); } struct voice *nextvoice() /* * Update voice pointer */ { if (p_voice >= &voice[NVOICES]) fatal("voice", NVOICES); return(p_voice++); } struct note *nextnote() /* * Update notes pointer */ { if (p_note >= ¬e[NNOTES]) fatal("note", NNOTES); return(p_note++); } char *skipbl(lineptr) char *lineptr; /* * Skip over blanks, return updated line pointer */ { register char *lp; for (lp = lineptr; *lp && *lp <= ' '; lp++); return(lp); } skipline(flag) int flag; /* 1 to read initially */ /* * Skip blank lines, flag is non-zero to read a line before checking * for blanks. Return 1 on end of file. */ { if (flag) line[0] = 0; while (!end_file && line[0] == 0) getline(); /* Skip blank lines */ return(end_file); } getline() /* * Read text (from iobuf) to global line[]. * Return 1 on end of file, zero on ok. */ { register char *t; register char c; reread: t = line; line_count++; while ((c = getc(iobuf)) >= 0) { if (c == 0 || c == '\r') /* Skip CR or NULL */ continue; /* Rt11 last block */ if (c == '\n') { /* * Squeeze out trailing blanks */ while (t > line && t[-1] == ' ') t--; *t = 0; if (line[0] == '*') /* Allow * comments */ goto reread; if (echoflag) printf(">> \"%s\"\n", line); return(0); } *t++ = c; } if (echoflag) printf("End of file\n"); line[0] = 0; end_file = 1; return(1); } getyesno(prompt, normal) char *prompt; /* Prompt string */ char *normal; /* Default answer "Yes" or "No" */ { printf("%s? (Yes/No) <%s>: ", prompt, normal); if (getcommand()) return(0); /* End of file is very false */ if (line[0] == 0) line[0] = normal[0]; return((line[0] | 040) == 'y'); } getcommand() /* * Read text from keyboard to global line[]. * Return 1 on end of file, zero on ok. Note: rt11 probably trashes the * job so you'll never see the end of file. */ { register char *t; register char c; t = line; line_count++; while ((c = getchar()) >= 0) { if (c == 0 || c == '\r') /* Skip CR or NULL */ continue; /* Rt11 last block */ if (c == '\n') { /* * Squeeze out trailing blanks */ while (t > line && t[-1] == ' ') t--; *t = 0; return(0); } *t++ = c; } line[0] = 0; end_file = 1; return(1); } bug(s) char *s; /* * Bug printout */ { printf("? %s\n", s); printf("? Input line %d \"%s\"\n", line_count, line); printf("? Current measure is %d, current voice is %d\n\n", curr_meas, curr_voice); } bug1(s, i) char *s; int i; /* * Bug printout */ { printf(s, i); printf("? Input line %d \"%s\"\n", line_count, line); printf("? Current measure is %d, current voice is %d\n\n", curr_meas, curr_voice); } fatal(s, i) char *s; int i; /* * Table overflow */ { printf("%s table overflow, max. is %d\n", s, i); printf("Input line is \"%s\"\n", line); printf("Measure %d\n", curr_meas); exit(1); } roll() /* * Roll the dice twice, return their sum. Note: a number between 2 and * 12 is always returned. */ { return(irand(6) + irand(6) + 2); }