/*importmidi.cpp * midi file import functions * for Denemo, a gtk+ frontend to GNU Lilypond * (c) 2003-2005 AJAnderson * */ #include #include "importmidi.h" #include "utils.h" #include "commandfuncs.h" #include "objops.h" #include "measureops.h" #include "chordops.h" #include "scoreops.h" #include "staffops.h" #include "contexts.h" #include "dialogs.h" #include "processstaffname.h" #include "moveviewport.h" #include "file.h" #include #include #include int PPQN; //DenemoScore *si2; //GList *notestack = NULL; struct nstack notestack; int bartime; int barlength; int lastoff = 0; int trackplus = 0; int breaker = 0; int key = 0; int tempo = 120; /** * Import midi file * TODO Finish importer */ //void add_newtrack(DenemoScore *si); gint importMidi (gchar * filename, DenemoGUI * gui) { gint ret = 0; // (-1 on failure) int data = 0; FILE *fp = 0; int tracks = 0; bartime = 0; lastoff = 0; trackplus = 0; breaker = 0; DenemoScore *si; deletescore (NULL, gui); si = gui->si; if (fopen (filename, "r") != 0) fp = fopen (filename, "r"); /*scan to first track */ while (1) { data = readBytes (fp, 1); if (data == 0x4d) { data = readBytes (fp, 3); if (data == 0x546864) { tracks = readheader (fp); printf ("\ntracks = %i\n", tracks); }; if (data == 0x54726b) { printf ("\nReading Track\n"); printf ("\nbreaker = %i\n", breaker); if (breaker == 0) { readtrack (fp, si); } tracks--; }; }; if (tracks == 0) break; }; fclose (fp); /* gtk_widget_draw (gui->scorearea, NULL); */ ret = 1; return ret; } /** * Read variable length valuue * */ int readVariable (FILE * fp) { /*so all are 7bit numbers, and all have bit 8 high except the last */ int total = 0; int last = 0; do { last = fgetc (fp); total = (total << 7) + (last & 0x7f); } while ((last >> 7) != 0); return total; } /** * Read the number of bytes specified * * @param fp pointer to the file descriptor * @param numb number of bytes to read */ int readBytes (FILE * fp, int numb) { int read = 0; while (numb > 0) { read = (read << 8) + (int) fgetc (fp); numb--; } return read; } /** * Read Midi file header * */ int readheader (FILE * fp) { int discard; printf ("\nHeader length confirmed as %d Bytes", readBytes (fp, 4)); printf ("\nMidi format is %d", readBytes (fp, 2)); discard = readBytes (fp, 2); printf ("\nNo. of Tracks is %d", discard); PPQN = readBytes (fp, 2); printf ("\nPPQN is %d", PPQN); return discard; } /** * Read track from midi file * */ void readtrack (FILE * fp, DenemoScore * si) { int stat; int data1; int data2; int tlength; int time = 0; /*track time */ int dtime; /*delta time, distance to next event */ int event = 0; tlength = readBytes (fp, 4); if (trackplus != 0) { si->currentstaffnum++; newstaff (si, ADDFROMLOAD, DENEMO_NONE); si->currentstaff = g_list_last (si->thescore); lastoff = 0; si->cursor_x = 0; bartime = 0; si->currentmeasurenum = 1; } while (tlength != 0) { //printf("\ntlength = %i\n",tlength); dtime = readVariable (fp); time = time + dtime; tlength--; if (dtime > 127) tlength--; /* 2 digit variable */ if (dtime > 16383) tlength--; /* 3 digit variable */ if (dtime > 2097151) tlength--; /* 3 digit variable */ /* leap of faith, all channels in track ar same */ stat = readBytes (fp, 1); tlength--; if (stat == 0xff) { /*next Byte is command */ data1 = readBytes (fp, 1); /*next Byte is length */ data2 = readBytes (fp, 1); /*pretend to read it */ switch (data1) { case 88: { /*time signature */ dotimesig (fp, si); tlength = tlength - 6; break; } case 89: { /*key signature */ dokeysig (fp, si); tlength = tlength - 4; break; } case 81: { /* set tempo */ if (data2 == 3) { dotempo (fp, si); } tlength = tlength - 5; break; } case 3: { dotrackname (fp, si, data2); tlength = tlength - (data2 + 2); break; } case 4: { doinstrname (fp, si, data2); tlength = tlength - (data2 + 2); break; } default: { /*read off surplus */ data1 = readBytes (fp, data2); tlength = tlength - (data2 + 2); break; } } } else { if (stat < 128) { /*running status event is same as before */ data1 = stat; } else { event = stat; data1 = readBytes (fp, 1); tlength--; }; switch (event >> 4) { case 8: /*note off */ { data2 = readBytes (fp, 1); tlength--; donoteoff (si, data1, time); break; } case 9: /*note on */ { //breaker = 1; data2 = readBytes (fp, 1); tlength--; donoteon (si, data1, data2, time); break; } default: { data2 = readBytes (fp, 1); tlength--; break; } case 11: /*midi controller eg. volume, pan */ { data2 = readBytes (fp, 1); //printf("\nMIDI Controller %d, value %d",data1,data2); tlength--; break; } case 12: /*change instrument, ignore */ { //printf("\nChange to Instrument% d",data1); break; } }; } } trackplus++; printf ("\ntrackplus = %i\n", trackplus); } /** * Insert time signature into current staff * */ void dotimesig (FILE * fp, DenemoScore * si) { /*only does initial TS */ DenemoStaff *curstaffstruct = (DenemoStaff *) si->currentstaff->data; curstaffstruct->stime1 = readBytes (fp, 1); curstaffstruct->stime2 = (gint) pow (2, (readBytes (fp, 1))); si->haschanged = TRUE; barlength = PPQN * 4 * curstaffstruct->stime1 / curstaffstruct->stime2; readBytes (fp, 2); /*skip last two characters */ } /** * Insert key signature into the current staff * */ void dokeysig (FILE * fp, DenemoScore * si) { /*assume major */ int isminor = 0; key = readBytes (fp, 1); /*read in sharps */ isminor = readBytes (fp, 1); if (key > 7) key = key - 256; /*get flat key num, see keysigdialog.cpp */ DenemoStaff *curstaffstruct = (DenemoStaff *) si->currentstaff->data; curstaffstruct->skey = key; curstaffstruct->skey_isminor = isminor; initkeyaccs (curstaffstruct->skeyaccs, key); si->haschanged = TRUE; } void dotempo (FILE * fp, DenemoScore * si) { int n, x; x = n = 0; while (n++ < 3) x = ((x & 0x007FFFFF) << 8) + (int) readBytes (fp, 1); tempo = (int) (6.0e7 / (double) x); //printf("\ntempo = %i\n",tempo); si->tempo = tempo; } void dotrackname (FILE * fp, DenemoScore * si, int x) { DenemoStaff *curstaffstruct = (DenemoStaff *) si->currentstaff->data; int n = 0; char z[16]; while (n < x) z[n++] = readBytes (fp, 1); z[x] = 0; strcpy (curstaffstruct->denemo_name->str, z); set_lily_name (curstaffstruct->denemo_name, curstaffstruct->lily_name); } void doinstrname (FILE * fp, DenemoScore * si, int x) { DenemoStaff *curstaffstruct = (DenemoStaff *) si->currentstaff->data; int n = 0; char z[16]; while (n < x) z[n++] = readBytes (fp, 1); z[x] = 0; strcpy (curstaffstruct->midi_instrument->str, z); } static nstack stack (int pitch, int time) { nstack mystack; mystack.pitch = pitch; mystack.time = time; return mystack; } /** * Process note on command */ void donoteon (DenemoScore * si, int pitchon, int attack, int timeon) { if (attack == 0) { donoteoff (si, pitchon, timeon); } else { /* add a note to the stack */ notestack = stack (pitchon, timeon); printf ("\npitchon = %i timeon = %i\n", pitchon, timeon); } } /** * Process note off command */ void donoteoff (DenemoScore * si, int pitchoff, int timeoff) { int duration = 0; int starttime = 0; int rest = 0; //printf("\npitchoff = %i\n",pitchoff); if (comparenote (notestack.pitch, pitchoff)) starttime = notestack.time; printf ("\nstarttime = %i lastoff = %i\n", starttime, lastoff); if (starttime != lastoff) { /*add a rest before note */ rest = starttime - lastoff; /* Until I find a better way to pad the end of a measure */ if (rest > PPQN) { if (rest % PPQN) addnote (si, 200, (rest % PPQN), 0); rest = rest - (rest % PPQN); while (rest > 0) { addnote (si, 200, PPQN, 0); rest = rest - PPQN; } } if ((rest < PPQN) && (rest != 0)) addnote (si, 200, rest, 0); lastoff = starttime; } duration = timeoff - starttime; addnote (si, pitchoff, duration, 0); /*delete pointer */ //notestack = g_list_remove (notestack, pulloff); //delete ((nstack *) pulloff->data); lastoff = timeoff; } /** * Compare note a to note b and return the result * */ gint comparenote (int a, int b) { if (a == b) { return 1; } /* Identical */ else { return 0; } /* Not identical */ } /** * Add note to the current staff */ void addnote (DenemoScore * si, int pitchadd, int endnote, int notie) { DenemoObject *mudela_obj_new; int leftover = 0; int dotted = 0; int tied = 0; int dsq = 0; int notetype = 2; //enharmonic enote = enharmonic (0); struct harmonic enote = enharmonic (0); if (notie == 0) { if (bartime >= barlength) /*(bartime == barlength) not entirely functional */ { /* bartime >= barlenth will be true if there are rests or notes going over end of measures. */ if (!si->currentmeasure->next) /* Add a measure and make it currentmeasure */ si->currentmeasure = addmeasures (si, si->currentmeasurenum, 1, 1); else si->currentmeasure = si->currentmeasure->next; /* Now the stuff that needs to be done for each case */ si->currentmeasurenum++; si->currentobject = NULL; si->cursor_x = 0; memcpy (si->cursoraccs, si->nextmeasureaccs, SEVENGINTS); memcpy (si->curmeasureaccs, si->nextmeasureaccs, SEVENGINTS); si->curmeasureclef = si->cursorclef; bartime = 0; } bartime = bartime + endnote; if (bartime > barlength) { leftover = bartime - barlength; /*tied over bar line */ endnote = endnote - leftover; } } /*convert length to 2 = crotchet, 3 = quaver etc...... */ dsq = (8 * endnote) / PPQN; switch (dsq) { case 1: /*demi-semi-quaver */ { notetype = 5; break; } case 2: /*semi-quaver */ { notetype = 4; break; } case 3: /*dotted semi-quaver */ { notetype = 4; tied = PPQN / 8; break; } case 4: /*quaver */ { notetype = 3; break; } case 5: /*quaver tied demi */ { notetype = 3; tied = PPQN / 8; break; } case 6: /*quaver */ { notetype = 3; tied = PPQN / 4; break; } case 7: /*quaver */ { notetype = 3; tied = 3 * PPQN / 8; break; } case 8: /*crotchet */ { notetype = 2; break; } case 9: /*crotchet */ { notetype = 2; tied = PPQN / 8; break; } case 10: /*crotchet */ { notetype = 2; tied = PPQN / 4; break; } case 11: /*crotchet */ { notetype = 2; tied = PPQN * 3 / 8; break; } case 12: /*crotchet */ { notetype = 2; tied = PPQN / 2; break; } case 13: /*crotchet */ { notetype = 2; tied = 5 * PPQN / 8; break; } case 14: /*crotchet */ { notetype = 2; tied = 3 * PPQN / 4; break; } case 15: /*crotchet */ { notetype = 2; tied = 7 * PPQN / 8; break; } case 16: /*minim */ { notetype = 1; break; } case 30: { notetype = 1; tied = 7 * PPQN / 4; break; } case 32: /*semi-breve */ { notetype = 0; break; } /*default: { notetype = 2; tied = endnote - PPQN; break; } */ } /* Now actually create the chord */ mudela_obj_new = newchord (notetype, 0, (tied || leftover)); if (pitchadd != 200) { enote = enharmonic (pitchadd); addtone (mudela_obj_new, enote.pitch, enote.enshift, si->cursorclef); //si2->mode = INPUTNORMAL; } /* else { si2->mode = INPUTREST; Needs to be resolved } */ object_insert (si, mudela_obj_new); if (dotted != 0) { /*add dots */ } if (tied != 0) { addnote (si, pitchadd, tied, 1); } if (leftover != 0) /*notes tied over bar-line */ { addnote (si, pitchadd, leftover, 0); }; } harmonic enharmonic (int input) { harmonic local; local.pitch = (input / 12) - 5; local.enshift = input % 12; //printf("\nkey = %i local.pitch = %i enchift = %i input = %i\n",key, local.pitch, local.enshift, input); switch (local.enshift) { case 0: //c { local.pitch = (key > 6) ? (-1 + local.pitch * 7) : (local.pitch * 7); local.enshift = (key > 6) ? (1) : (0); break; } case 1: //c# { local.pitch = (key < -3) ? (1 + local.pitch * 7) : (local.pitch * 7); local.enshift = (key < -3) ? (-1) : (1); break; } case 2: //D { local.pitch = 1 + local.pitch * 7; local.enshift = 0; break; } case 3: //D# { local.pitch = (key < -1) ? (2 + local.pitch * 7) : (1 + local.pitch * 7); local.enshift = (key < -1) ? (-1) : (1); break; } case 4: //E { local.pitch = (key < -6) ? (3 + local.pitch * 7) : (2 + local.pitch * 7); local.enshift = (key < -6) ? (-1) : (0); break; } case 5: //F { local.pitch = (key > 5) ? (2 + local.pitch * 7) : (3 + local.pitch * 7); local.enshift = (key > 5) ? (1) : (0); break; } case 6: //F# { local.pitch = (key < -4) ? (4 + local.pitch * 7) : (3 + local.pitch * 7); local.enshift = (key < -4) ? (-1) : (1); break; } case 7: //G { local.pitch = 4 + local.pitch * 7; local.enshift = 0; break; } case 8: //G# { local.pitch = (key < -2) ? (5 + local.pitch * 7) : (4 + local.pitch * 7); local.enshift = (key < -2) ? (-1) : (1); break; } case 9: //A { local.pitch = 5 + local.pitch * 7; local.enshift = 0; break; } case 10: //A# { local.pitch = (key < 0) ? (6 + local.pitch * 7) : (5 + local.pitch * 7); local.enshift = (key < 0) ? (-1) : (1); break; } case 11: //B { local.pitch = (key < -5) ? (7 + local.pitch * 7) : (6 + local.pitch * 7); local.enshift = (key < -5) ? (-1) : (0); break; } }; return local; }