/* * 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 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 }; struct note { enum note_tone nt; enum note_semitone st; int octave; }; struct chord { int n_notes; struct note notes[8]; int length; }; struct stave { int n_chords; int max_chords; struct chord *chords; }; struct music { unsigned int n_staves; unsigned int n_perc; struct stave staves[8]; /* 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 void music_attribute(unsigned char ma, struct music *mus) { if ( (ma & 0x7f) == 0x40 ) { printf("Warning: reserved gate type.\n"); } else if ( (ma & 0x3f) == 0x20 ) { //fprintf(ofh, "|\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; ct = (ma & 0x18) >> 3; switch ( ct ) { case 0 : //fprintf(ofh, "\\clef \"treble\"\n"); break; case 1 : //fprintf(ofh, "\\clef \"treble\"\n"); break; case 2 : //fprintf(ofh, "\\clef \"treble\"\n"); break; case 3 : //fprintf(ofh, "\\clef \"treble\"\n"); break; } st = (ma & 0x60) >> 5; printf(" stave %i\n", st); } else if ( (ma & 0x3) == 0x2 ) { } else if ( (ma & 0x1) == 0x1 ) { int tn, td; tn = 1 + ((ma & 0x1e) >> 1); td = 1 + ((ma & 0xe0) >> 5); //fprintf(ofh, "\\time %i/%i\n", tn, td); } } static struct note get_note(unsigned char *notes, int *nptr) { unsigned char n1, n2; int rest = 0; struct note n; n1 = notes[*nptr++]; if ( n1 & 0xf8 ) { n2 = notes[*nptr++]; } else { rest = 1; } if ( rest ) { printf(" %i", n1); } else { int pos, acc; pos = (n1 & 0xf8) >> 3; acc = n2 & 0x07; n.nt = NOTE_A; /* FIXME: Convert and store */ } return n; } static int find_max_length(unsigned char g, unsigned char **notes, int *nptrs) { int ch; int lmax = 100; for ( ch=0; ch<8; ch++ ) { int len; if ( !(g & 1<> 5; if ( len < lmax ) lmax = len; } if ( lmax == 100 ) { fprintf(stderr, "No note length in gate!\n"); } return lmax; } static void find_note_with_length(unsigned char *notes, int *nptr, struct chord *c, int ls) { int len; len = (notes[*nptr] & 0xe0) >> 5; if ( len != ls ) return; c->notes[c->n_notes++] = get_note(notes, nptr); } static void interpret_gates(struct music *mus) { unsigned int i; int ma = 0; int nptrs[8]; for ( i=0; i<8; i++ ) nptrs[i] = 0; for ( i=0; in_gates; i++ ) { if ( ma ) { music_attribute(mus->gates[i], mus); ma = 0; continue; } if ( mus->gates[i] == 0 ) { ma = 1; continue; } else { int l, j; struct chord c; l = find_max_length(mus->gates[i], mus->notes, nptrs); /* Find the notes of this length on this stave */ c.n_notes = 0; c.length = l; for ( j=0; j<4; j++ ) { if ( !(mus->gates[i] & (1<notes[j], &nptrs[j], &c, l); } } } } 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) { int tempo; tempo = f[ptr++]; printf("Tempo = %i bpm\n", bpm[tempo]); return ptr; } 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; }