/* * maestropond.c * * Convert Acorn Maestro files to LilyPond files * * (c) 2011 Thomas White * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include enum note_tone { NOTE_A, NOTE_B, NOTE_C, NOTE_D, NOTE_E, NOTE_F, NOTE_G, NOTE_SILENCE }; enum note_semitone { SEMI_SHARPSHARP, SEMI_SHARP, SEMI_FLAT, SEMI_FLATFLAT }; enum clef { CLEF_TREBLE, CLEF_ALTO, CLEF_TENOR, CLEF_BASS, CLEF_NONE }; enum chord_type { CHORD_UNDEFINED, CHORD_MUSIC, CHORD_CLEF, CHORD_BARLINE, CHORD_TIMESIG }; struct note { enum note_tone nt; enum note_semitone st; int octave; }; struct chord { enum chord_type type; int n_notes; struct note notes[8]; int length; enum clef clef; int timesig_num; int timesig_den; }; struct stave { int n_chords; int max_chords; struct chord *chords; enum clef last_clef; }; struct music { unsigned int n_staves; unsigned int n_perc; struct stave staves[8]; int tempo; /* Maestro music data extracted from file */ unsigned int n_gates; unsigned int lengths[8]; unsigned char *gates; unsigned char *notes[8]; }; static const int bpm[] = { 40, 50, 60, 65, 70, 80, 90, 100, 115, 130, 145, 160, 175, 190, 210 }; static void show_syntax(const char *s) { printf("Syntax: %s [options]\n", s); } static void show_help(const char *s) { show_syntax(s); printf( "\nConvert Acorn Maestro files to LilyPond files.\n" "\n" " -h, --help Display this help message.\n" "\n" ); } static unsigned int get_basic_int(unsigned char *f, size_t *pptr) { unsigned int v; size_t ptr = *pptr; int sig; v = 0; sig = f[ptr++]; if ( sig != 0x40 ) { fprintf(stderr, "Not a BASIC integer (sig %i, val %i)\n", sig, v); goto out; } v += f[ptr++] << 24; v += f[ptr++] << 16; v += f[ptr++] << 8; v += f[ptr++]; out: *pptr = ptr; return v; } static struct chord *add_chord(struct music *mus, int stave) { struct stave *st = &mus->staves[stave]; struct chord *c; if ( st->n_chords >= st->max_chords ) { struct chord *n; st->max_chords += 128; n = realloc(st->chords, st->max_chords*sizeof(struct chord)); if ( n == NULL ) { fprintf(stderr, "Couldn't allocate chord.\n"); abort(); } st->chords = n; } c = &st->chords[st->n_chords++]; c->n_notes = 0; c->type = CHORD_UNDEFINED; return c; } static int maestro_to_beat(int n) { int v = pow(2, n-1); if ( v == 0 ) return 1; return v; } static void music_attribute(unsigned char ma, struct music *mus) { if ( (ma & 0x7f) == 0x40 ) { printf("Warning: reserved gate type.\n"); } else if ( (ma & 0x3f) == 0x20 ) { unsigned int i; for ( i=0; in_staves; i++ ) { struct chord *c = add_chord(mus, i); c->type = CHORD_BARLINE; } printf("\n"); } else if ( (ma & 0x1f) == 0x10 ) { printf("Octave shift\n"); } else if ( (ma & 0xf) == 0x8 ) { int st; st = 1 + ((ma & 0xc0) >> 6); if ( ma & 0x10 ) { printf("Slur on (stave %i)\n", st); } else { printf("Slur off (stave %i)\n", st); } } else if ( (ma & 0x7) == 0x4 ) { int ct, st; struct chord *n; /* This seems to mean the clef was deleted */ if ( !(ma & 0x80) ) { st = (ma & 0x60) >> 5; /* Horrible fudge, seems to be necessary */ if ( st > 0 ) st--; n = add_chord(mus, st); n->type = CHORD_CLEF; ct = (ma & 0x18) >> 3; switch ( ct ) { case 0 : n->clef = CLEF_TREBLE; break; case 1 : n->clef = CLEF_ALTO; break; case 2 : n->clef = CLEF_TENOR; break; case 3 : n->clef = CLEF_BASS; break; } mus->staves[st].last_clef = n->clef; } } else if ( (ma & 0x3) == 0x2 ) { } else if ( (ma & 0x1) == 0x1 ) { int tn, td; unsigned int i; tn = 1 + ((ma & 0x1e) >> 1); td = 1 + ((ma & 0xe0) >> 5); for ( i=0; in_staves; i++ ) { struct chord *c = add_chord(mus, i); c->type = CHORD_TIMESIG; c->timesig_num = tn; c->timesig_den = maestro_to_beat(td-1); } } } static enum note_tone note_letter(int n) { switch ( n ) { case 0 : return NOTE_A; case 1 : return NOTE_B; case 2 : return NOTE_C; case 3 : return NOTE_D; case 4 : return NOTE_E; case 5 : return NOTE_F; case 6 : return NOTE_G; } fprintf(stderr, "Tone number %i not recognised.\n", n); return NOTE_SILENCE; } static struct note pitch_to_note(int pos, int acc, enum clef cl) { struct note n; switch ( cl ) { case CLEF_BASS : n.octave = (pos-6) / 7 - 1; n.nt = note_letter((pos-6) % 7); printf("[%i -> %i:%i]", pos, n.octave, n.nt); break; case CLEF_TREBLE : n.octave = ((pos+4) / 7) - 1; n.nt = note_letter((pos-1) % 7); printf("[%i -> %i:%i]", pos, n.octave, n.nt); break; case CLEF_NONE : fprintf(stderr, "No clef yet!\n"); break; default: abort(); } return n; } static char letter(enum note_tone t) { switch ( t ) { case NOTE_A : return 'a'; case NOTE_B : return 'b'; case NOTE_C : return 'c'; case NOTE_D : return 'd'; case NOTE_E : return 'e'; case NOTE_F : return 'f'; case NOTE_G : return 'g'; case NOTE_SILENCE : return 's'; } return '?'; } static const char *note_to_ly(struct note n, char *t) { int i; t[0] = letter(n.nt); t[1] = '\0'; if ( n.octave > 0 ) { for ( i=0; i> 5; pos = (n1 & 0xf8) >> 3; acc = n2 & 0x07; c->length = maestro_to_beat(len); c->notes[c->n_notes++] = pitch_to_note(pos, acc, st->last_clef); printf("%i %s %p %i\n", maestro_to_beat(len), note_to_ly(pitch_to_note(pos, acc, st->last_clef), t), c, c->n_notes-1); return len; } static int channel_in_stave(int ch, int stave, int n_staves) { assert(n_staves == 2); /* FIXME! */ if ( (ch < 4) && (stave == 0) ) return 1; if ( (ch >= 4) && (stave == 1) ) return 1; return 0; } static void process_gate(struct music *mus, int i, int *nptrs) { int ch; struct stave *os = NULL; struct chord *c; for ( ch=0; ch<8; ch++ ) { int len; unsigned int stv; struct stave *st = NULL; for ( stv=0; stvn_staves; stv++ ) { if ( channel_in_stave(ch, stv, mus->n_staves) ) { st = &mus->staves[stv]; break; } } assert(st != NULL); if ( st != os ) { c = add_chord(mus, stv); c->type = CHORD_MUSIC; os = st; } printf("%i : ", ch); if ( !(mus->gates[i] & (1<notes[ch], &nptrs[ch], c, st); mus->gates[i] = mus->gates[i] & ~(1<n_gates; i++ ) { if ( ma ) { music_attribute(mus->gates[i], mus); ma = 0; continue; } if ( mus->gates[i] == 0 ) { ma = 1; continue; } else { process_gate(mus, i, nptrs); } } } static size_t read_music_data(unsigned char *f, size_t ptr, size_t len, struct music *mus) { unsigned int i; mus->n_gates = get_basic_int(f, &ptr); for ( i=0; i<8; i++ ) { mus->lengths[i] = get_basic_int(f, &ptr); printf("Channel %i, length %i\n", i+1, mus->lengths[i]); } mus->gates = malloc(mus->n_gates); if ( mus->gates == NULL ) { fprintf(stderr, "Failed to allocate gates\n"); } for ( i=0; in_gates; i++ ) { mus->gates[i] = f[ptr++]; } for ( i=0; i<8; i++ ) { unsigned int j; mus->notes[i] = malloc(mus->lengths[i]); if ( mus->notes[i] == NULL ) { fprintf(stderr, "Failed to allocate notes\n"); } for ( j=0; jlengths[i]; j++ ) { mus->notes[i][j] = f[ptr++]; } } return ptr; } static size_t process_stave_data(unsigned char *f, size_t ptr, size_t len, struct music *mus) { mus->n_staves = 1 + f[ptr++]; mus->n_perc = f[ptr++]; printf("%i staves, %i percussion\n", mus->n_staves, mus->n_perc); return ptr; } static size_t process_instrument_data(unsigned char *f, size_t ptr, size_t len, struct music *mus) { int i; for ( i=0; i<8; i++ ) { int ch, v; ch = f[ptr++]; v = f[ptr++]; //printf("Channel %i, voice %i\n", ch+1, v); } return ptr; } static size_t process_volume_data(unsigned char *f, size_t ptr, size_t len, struct music *mus) { int i; for ( i=0; i<8; i++ ) { int v; v = f[ptr++]; //printf("Channel %i, volume %i\n", i+1, v); } return ptr; } static size_t process_stereo_data(unsigned char *f, size_t ptr, size_t len, struct music *mus) { int i; for ( i=0; i<8; i++ ) { int st; st = f[ptr++]; //printf("Channel %i, stereo %i\n", i+1, st); } return ptr; } static size_t process_tempo_data(unsigned char *f, size_t ptr, size_t len, struct music *mus) { mus->tempo = bpm[f[ptr++]]; return ptr; } static const char *clef_to_ly(enum clef c) { switch ( c ) { case CLEF_TREBLE : return "treble"; case CLEF_ALTO : return "alto"; case CLEF_TENOR : return "tenor"; case CLEF_BASS : return "bass"; } return "unknown"; } static void write_stave(FILE *ofh, struct stave *st) { int i; char t[32]; for ( i=0; in_chords; i++ ) { int j; struct chord *c = &st->chords[i]; if ( c->type == CHORD_CLEF ) { fprintf(ofh, "\\clef \"%s\"\n", clef_to_ly(c->clef)); continue; } if ( c->type == CHORD_BARLINE ) { fprintf(ofh, "| "); continue; } if ( c->type == CHORD_TIMESIG ) { fprintf(ofh, "\\time %i/%i\n", c->timesig_num, c->timesig_den); continue; } if ( c->type != CHORD_MUSIC ) { fprintf(stderr, "Unknown chord type.\n"); continue; } if ( c->n_notes == 0 ) { //fprintf(stderr, "I don't understand this chord.\n"); continue; } if ( c->n_notes == 1 ) { fprintf(ofh, "%s%i ", note_to_ly(c->notes[0], t), c->length); continue; } fprintf(ofh, "<"); for ( j=0; jn_notes; j++ ) { fprintf(ofh, "%s ", note_to_ly(c->notes[j], t)); } fprintf(ofh, ">%i ", c->length); } } static void write_lilypond(struct music *mus, FILE *ofh) { unsigned int i; fprintf(ofh, "\\score {\n"); fprintf(ofh, " <<\n"); for ( i=0; in_staves; i++ ) { fprintf(ofh, "\\new Staff {\n"); fprintf(ofh, "\\tempo 4 = %i\n", mus->tempo); write_stave(ofh, &mus->staves[i]); fprintf(ofh, "}\n"); } fprintf(ofh, ">>\n"); fprintf(ofh, "\\layout { }\n"); fprintf(ofh, "\\midi { }\n"); fprintf(ofh, "}\n"); } static void convert_file(const char *filename) { struct stat statbuf; FILE *fh; FILE *ofh; unsigned char *f; size_t r, ptr; unsigned int i; struct music mus; if ( stat(filename, &statbuf) == -1 ) { fprintf(stderr, "Couldn't file file '%s'\n", filename); return; } f = malloc(statbuf.st_size); if ( f == NULL ) { fprintf(stderr, "Couldn't allocate memory.\n"); return; } /* Load data */ fh = fopen(filename, "rb"); if ( fh == NULL ) { fprintf(stderr, "Failed to open file '%s'\n", filename); return; } r = fread(f, 1, statbuf.st_size, fh); if ( r != (size_t)statbuf.st_size ) { fprintf(stderr, "Failed to read file" " (got %lli out of %lli bytes).\n", (long long int)r, (long long int)statbuf.st_size); fclose(fh); free(f); return; } fclose(fh); ofh = fopen("maestropond.ly", "w"); if ( ofh == NULL ) { fprintf(stderr, "Failed to open output file.\n"); return; } fprintf(ofh, "\\version \"2.14.2\"\n"); fprintf(ofh, "\\header {\n"); fprintf(ofh, " title = \"%s\"\n", filename); fprintf(ofh, " composer = \"Unknown\"\n"); fprintf(ofh, "}\n"); if ( memcmp(f, "Maestro\n", 8) != 0 ) { fprintf(stderr, "Not a Maestro file.\n"); free(f); return; } if ( f[8] != 2 ) { fprintf(stderr, "Unrecognised Maestro file type (%i)\n", f[8]); free(f); return; } ptr = 9; while ( ptr < r ) { switch ( f[ptr++] ) { case 1 : ptr = read_music_data(f, ptr, r, &mus); break; case 2 : ptr = process_stave_data(f, ptr, r, &mus); break; case 3 : ptr = process_instrument_data(f, ptr, r, &mus); break; case 4 : ptr = process_volume_data(f, ptr, r, &mus); break; case 5 : ptr = process_stereo_data(f, ptr, r, &mus); break; case 6 : ptr = process_tempo_data(f, ptr, r, &mus); break; } } for ( i=0; i= argc ) { show_syntax(argv[0]); return 1; } infile = argv[optind++]; printf("Input: '%s'\n", infile); convert_file(infile); return 0; }