#include "parser.h" #include "mix.h" #include #include #include #include /* NONE_TYPE is for empty notes. */ /* Since different channels have different time specifications, an empty line can only be parsed as an empty note. So we have to explicitly mark a silent note. */ /* A note of type BEAT_DURATION_TYPE stores the new bpm in the secs field. */ /* A note of type VOLUME_TYPE stores the new volume in the secs field. */ typedef enum { NONE_TYPE, NOTE_TYPE, SILENT_TYPE, INSTRUMENT_TYPE, BEAT_DURATION_TYPE, VOLUME_TYPE } PType; struct PNote_s { Semitones *tones; Beats secs; LENT len; PType type; /* The instrument type, if it is an instrument changing note. */ InstrumentTypes *name; }; struct PUnit_s { PNote *notes; /* the notes of channels */ LENT len; /* the length of channels */ }; /* Helper macro */ #define EMPTY_NOTE ((PNote) { NULL, 0.0, 0, NONE_TYPE, NULL }) #define EMPTY_UNIT ((PUnit) { NULL, 0 }) /* Helper function */ H_ATTR unsigned char is_empty_note(PNote *n) { return n->type == NONE_TYPE; } /* Destructor */ U_ATTR void destroy_sheet(PSheet sh) { for (LENT i = 0; i < sh.len; i++) { for (LENT j = 0; j < (sh.data+i)->len; j++) { if (((sh.data+i)->notes+j)->tones) free(((sh.data+i)->notes+j)->tones); if (((sh.data+i)->notes+j)->name) free(((sh.data+i)->notes+j)->name); } free((sh.data+i)->notes); } free(sh.data); } /* For debugging */ U_ATTR static void print_note(const PNote note) { switch (note.type) { case NONE_TYPE: fprintf(stderr, "an empty note\n"); break; case SILENT_TYPE: fprintf(stderr, "a silent note with %.3f beats\n", note.secs); break; case NOTE_TYPE: fprintf(stderr, "a note with %.3f beats and %lu semitone%s\n", note.secs, note.len, (note.len < 2) ? "" : "s"); fprintf(stderr, "The semitone%s: ", (note.len<2) ? " is" : "s are"); for (LENT i = 0; i < note.len; i++) fprintf(stderr, "%.2f%s", *(note.tones+i), (i==note.len-1) ? "" : ", "); fprintf(stderr, "\n"); break; case INSTRUMENT_TYPE: fprintf(stderr, "an instrument changing note\n"); break; case BEAT_DURATION_TYPE: fprintf(stderr, "a note of the type that changes the bpm to " "%.3f beats per minute.\n", note.secs); break; case VOLUME_TYPE: fprintf(stderr, "a note of the type that changes the volume to " "%.3f times.\n", note.secs); break; } } U_ATTR static void print_unit(const PUnit u) { fprintf(stderr, "Printing a unit with %lu channel%s\n", u.len, (u.len>1) ? "s" : ""); for (LENT i = 0; i < u.len; i++) { #ifdef DEBUG fprintf(stderr, "The %lu-th note: ", i); fprintf(stderr, "%p\n", (u.notes+i)); #endif print_note(*(u.notes+i)); fprintf(stderr, "\n"); } } U_ATTR void print_sheet(const PSheet sh) { for (LENT i = 0; i < sh.len;) print_unit(*(sh.data+i++)); } /* The main read function. Warning: A valid sheet file should have a "channel specification" in the first line. A "channel specification" line is a line that has no numbers, but just semi-colons. If there are N channels, then there should be N-1 semi-colons in the specification line. What if there are no channels? Why read a sheet without channels? If there are no specification lines in the beginning, then one channel format will be assumed. But this is prone to errors, and not recommended. */ U_ATTR PSheet read_sheet(const char *str, LENT len) { PSheet result = { NULL, 0 }; Node_t stack = EMPTY_NODE, stack_next = EMPTY_NODE; Node_t *stack_pointer = NULL; LENT counter = 0, increment = 0, line_count = 0; LENT stack_len = 0; char current = 0; NUMT local = 0.0; int channel_num = 0, num_chars_read = 0; int num_channels = 0; unsigned char first_line = 1, note_used = 0; PUnit *unit_pointer = NULL; PUnit current_unit = EMPTY_UNIT; PUnit *unit_stack = NULL; int unit_stack_len = 0; /* A note cannot consist of more than 5 semitones. One more semitone will be allocated so that it can store the time information. */ Semitones *st_stack = NULL; int st_stack_len = 0; InstrumentTypes *ins_pointer = NULL; PNote temp_note = EMPTY_NOTE, *notes_pointer = NULL; Semitones *st_pointer = NULL; /* A note without time information will use the previous beats information. A first note without time information will use 1.0 as the beats. */ Beats *beats_channel_array = NULL; for (;counter < len;) { current = *(str+counter); increment = 1; switch (current) { /* 13 is also a newline character, it seems. */ case 13: case '\n': if (!note_used) free(current_unit.notes); if (first_line) { num_channels = channel_num+1; current_unit.len = num_channels; beats_channel_array = MYALLOC(Beats, num_channels); for (int i = 0; i < num_channels; i++) { *(beats_channel_array+i) = 1.0; } } else if (note_used) { unit_pointer = MYALLOC(PUnit, 1); *unit_pointer = current_unit; PUSH_NODE(stack_len, stack_pointer, stack, unit_pointer, v); /* Since it is used, we set its pointer to NULL to prevent accidental frees. */ current_unit.notes = NULL; } notes_pointer = MYALLOC(PNote, num_channels); for (int i = 0; i < num_channels;) *(notes_pointer+i++) = EMPTY_NOTE; current_unit.notes = notes_pointer; channel_num = 0; first_line = 0; line_count++; note_used = 0; break; case '#': /* Comments */ for (;*(str+counter) != '\n' && *(str+counter) != 13;) counter++; increment = 2; counter -= 2; break; case ',': note_used = 1; channel_num++; break; default: /* Limited support for a sheet without channel specifications. */ if (first_line) { num_channels = (channel_num=0)+1; current_unit.len = num_channels; notes_pointer = MYALLOC(PNote, num_channels); beats_channel_array = MYALLOC(Beats, num_channels); for (int i = 0; i < num_channels; i++) { *(notes_pointer+i) = EMPTY_NOTE; *(beats_channel_array+i) = 1.0; } current_unit.notes = notes_pointer; first_line = 0; } note_used = 1; /* fprintf(stderr, "here the char is %c\n", *(str+counter)); */ if (*(str+counter) == ' ') { /* Just skip spaces so that they don't block us reading anchor chars. */ } else if (*(str+counter) == 'v') { /* changing volume note */ counter++; for (;*(str+counter) == ' ';) counter++; if (*(str+counter) != '\n' && *(str+counter) != 13 && sscanf(str+counter, "%lf%n", &local, &num_chars_read) == 1) { temp_note = (PNote) { NULL, local, 0, VOLUME_TYPE, NULL }; /* fprintf(stderr, "print a note\n"); * print_note(temp_note); */ counter += num_chars_read-1; } *(current_unit.notes+channel_num) = temp_note; } else if (*(str+counter) == 'b') { /* changing bpm note */ counter++; for (;*(str+counter) == ' ';) counter++; if (*(str+counter) != '\n' && *(str+counter) != 13 && sscanf(str+counter, "%lf%n", &local, &num_chars_read) == 1) { temp_note = (PNote) { NULL, local, 0, BEAT_DURATION_TYPE, NULL }; /* fprintf(stderr, "print a note\n"); * print_note(temp_note); */ counter += num_chars_read; } *(current_unit.notes+channel_num) = temp_note; } else if (*(str+counter) == 's') { /* a silent note */ counter++; /* skip all spaces that follow */ for (;*(str+counter) == ' ';) counter++; if (*(str+counter) != '\n' && *(str+counter) != 13 && sscanf(str+counter, "%lf%n", &local, &num_chars_read) == 1) { *(beats_channel_array+channel_num) = local; temp_note = (PNote) { NULL, local, 1, SILENT_TYPE, NULL }; increment = num_chars_read+1; } else { /* a note without time; use the previous beat instead. */ temp_note = (PNote) { NULL, *(beats_channel_array+channel_num), 1, SILENT_TYPE, NULL }; } #ifdef DEBUG fprintf(stderr, "print a note while reading\n"); print_note(temp_note); fprintf(stderr, "\n\n\n\n"); #endif *(current_unit.notes+channel_num) = temp_note; temp_note = EMPTY_NOTE; /* subtract one from the increment so that we don't unknowingly skip one character. */ increment--; } else if (sscanf(str+counter, "%lf%n", &local, &num_chars_read) == 1) { /* a note consisting of one single semitone */ st_pointer = MYALLOC(Semitones, 1); *st_pointer = local; counter += num_chars_read; /* skip all spaces that follow */ for (;*(str+counter) == ' ';) counter++; if (*(str+counter) != '\n' && *(str+counter) != 13 && sscanf(str+counter, "%lf%n", &local, &num_chars_read) == 1) { *(beats_channel_array+channel_num) = local; temp_note = (PNote) { st_pointer, local, 1, NOTE_TYPE, NULL }; increment = num_chars_read+1; } else { /* a note without time; use the previous beat instead. */ temp_note = (PNote) { st_pointer, *(beats_channel_array+channel_num), 1, NOTE_TYPE, NULL }; } #ifdef DEBUG fprintf(stderr, "print a note while reading\n"); print_note(temp_note); fprintf(stderr, "\n\n\n\n"); #endif *(current_unit.notes+channel_num) = temp_note; temp_note = EMPTY_NOTE; /* subtract one from the increment so that we don't unknowingly skip one character. */ increment--; } else if (*(str+counter) == '(') { /* a note consisting of multiple semitones */ st_stack = MYALLOC(Semitones, 10); st_stack_len = 0; counter++; for (;sscanf(str+counter, "%lf %n", &local, &num_chars_read) == 1; counter += num_chars_read) { *(st_stack+st_stack_len++) = local; } /* Now skip to the closing parenthesis */ for (;counter < len && *(str+counter) != ')';) counter++; if (st_stack_len>1) { st_pointer = MYALLOC(Semitones, st_stack_len-1); for (int i = 0; i < st_stack_len-1; i++) *(st_pointer+i) = *(st_stack+i); *(beats_channel_array+channel_num) = *(st_stack+st_stack_len-1); temp_note = (PNote) { st_pointer, *(st_stack+st_stack_len-1), st_stack_len-1, NOTE_TYPE, NULL }; } else if (!st_stack_len) { /* empty note */ temp_note = EMPTY_NOTE; } else { /* one semitone note */ st_pointer = MYALLOC(Semitones, 1); *st_pointer = *st_stack; temp_note = (PNote) { st_pointer, *(beats_channel_array+channel_num), 1, NOTE_TYPE, NULL }; } free(st_stack); *(current_unit.notes+channel_num) = temp_note; temp_note = EMPTY_NOTE; } else if (*(str+counter) == 'i') { /* an instrument changing unit */ /* Just piano for now */ ins_pointer = MYALLOC(InstrumentTypes, 1); *ins_pointer = PIANO; *(current_unit.notes+channel_num) = (PNote) { NULL, 0, 0, INSTRUMENT_TYPE, ins_pointer }; } else { /* invalid input */ fprintf(stderr, "Invalid input in the sheet.\n" "channel = %d, line = %lu\n", channel_num, line_count); } break; } counter += increment; } free(current_unit.notes); free(beats_channel_array); /* Then fill the variables up with the data we have read. */ LENT total_len = stack_len; unit_stack = MYALLOC(PUnit, total_len); for (LENT i = 0; i < total_len; i++) { POP_NODE(stack_len, unit_pointer, stack, stack_next, v); *(unit_stack+total_len-1-(unit_stack_len++)) = *unit_pointer; free(unit_pointer); } result.data = unit_stack; result.len = total_len; return result; } /* Get the number of note_type notes in a sheet. Also calculate the number of notes per channel and store in nums_per_channel, which is assumed to be already allocated. */ U_ATTR static LENT sheet_note_num(const PSheet sh, LENT *nums_per_channel) { LENT note_num = 0; for (LENT i = 0; i < (sh.data)->len;) *(nums_per_channel+i++) = 0; for (LENT i = 0; i < sh.len; i++) { PUnit pu = *(sh.data+i); for (LENT j = 0; j < pu.len; j++) if ((pu.notes+j)->type == NOTE_TYPE || (pu.notes+j)->type == SILENT_TYPE) { note_num++; *(nums_per_channel+j) += 1; } } return note_num; } H_ATTR static WaveFrag silent_note(Beats b, NUMT dur) { WaveFrag w; LENT sample_num = (LENT) floor(SAMPLE_RATE * b * dur); w.w = MYALLOC(Pulse, sample_num); w.n = sample_num; for (LENT i = 0; i < sample_num; i++) *(w.w+i) = 0.0; return w; } U_ATTR WaveFrag play_sheet(const PSheet sh, const Volume v) { WaveFrag wf = (WaveFrag) { NULL, 0 }; /* If there is not unit or if the channel number is zero then we return an empty wave fragment. */ if (!(sh.len) || !(sh.data->len)) return wf; Instrument *piano = make_piano(); Instrument *violin = make_violin(); Instrument *inst = piano; /* For mixing sounds in notes */ WaveFrag *war = NULL; WaveFrag **temp = NULL; LENT *channel_num_array = NULL; /* For mixing semitones */ WaveFrag *st_temp = MYALLOC(WaveFrag, 10); LENT total_len = 0, channel_num = sh.data->len; LENT *num_notes_per_channel = MYALLOC(LENT, channel_num); Beats dur = BEAT_DUR; /* Each channel can specify its sound. */ Volume *volume_per_channel = MYALLOC(Volume, channel_num);; #ifdef DEBUG fprintf(stderr, "before finding num\n"); #endif total_len = sheet_note_num(sh, num_notes_per_channel); /* for (LENT i = 0; i < sh.len;) * total_len += (sh.data+i++)->len; */ /* Allocate spaces */ temp = MYALLOC(WaveFrag*, channel_num); channel_num_array = MYALLOC(LENT, channel_num); for (LENT i = 0; i < channel_num; i++) { #ifdef DEBUG fprintf(stderr, "num_notes_per_channel + %lu = %lu\n", i, *(num_notes_per_channel+i)); #endif *(temp+i) = MYALLOC(WaveFrag, *(num_notes_per_channel+i)); *(channel_num_array+i) = 0; *(volume_per_channel+i) = 1.0; } war = MYALLOC(WaveFrag, channel_num); #ifdef DEBUG fprintf(stderr, "before for loop\n"); #endif for (LENT i = 0; i < sh.len; i++) { PUnit pu = *(sh.data+i); if (pu.len != channel_num) { fprintf(stderr, "Every unit should have the same number of channels.\n" "But unit %lu has %lu channels\n", i, pu.len); exit(1); } for (LENT j = 0; j < channel_num; j++) { PNote pn = *(pu.notes+j); if (pn.type != NOTE_TYPE && pn.type != SILENT_TYPE && pn.type != BEAT_DURATION_TYPE && pn.type != VOLUME_TYPE) continue; if (pn.len >= 10) { fprintf(stderr, "A note cannot have more than 10 semitones together.\n"); fprintf(stderr, "node %lu and number = %lu\n", j, pn.len); exit(1); } #ifdef DEBUG fprintf(stderr, "i = %lu, j = %lu\n", i, j); #endif switch (pn.type) { case NOTE_TYPE: for (LENT k = 0; k < pn.len; k++) { *(st_temp+k) = make_sound(inst, v * *(volume_per_channel+j), stoh(*(pn.tones+k)), pn.secs * dur); } break; case BEAT_DURATION_TYPE: dur = 60.0 / pn.secs; break; case VOLUME_TYPE: *(volume_per_channel+j) = pn.secs; break; case SILENT_TYPE: *(st_temp) = silent_note(pn.secs, dur); break; default: /* Not goint to happen */ fprintf(stderr, "something weird happens in play_sheet.\n"); exit(1); break; } #ifdef DEBUG fprintf(stderr, "after making sound\n"); if (j==1) { print_note(pn); fprintf(stderr, "channel num + 1 = %lu\n", *(channel_num_array+1)); } #endif *(*(temp+j)+*(channel_num_array+j)) = mix_waves(st_temp, pn.len); #ifdef DEBUG fprintf(stderr, "after mixing\n"); #endif *(channel_num_array+j) = *(channel_num_array+j) + 1; /* *(temp + i*channel_num + j) = mix_waves(st_temp, pn.len); */ for (int k = 0; k < pn.len; k++) free((st_temp+k)->w); } } #ifdef DEBUG fprintf(stderr, "after for loop\n"); #endif free(st_temp); /* Now all the waves are produced. We just need to merge and mix them. */ /* Note: merge_waves free the waves in the input. */ for (LENT j = 0; j < channel_num; j++) *(war+j) = merge_waves(inst, *(temp+j), *(num_notes_per_channel+j), 1); free(temp); free(num_notes_per_channel); free(channel_num_array); free(volume_per_channel); #ifdef DEBUG fprintf(stderr, "after merging\n"); #endif wf = mix_waves(war, channel_num); for (LENT i = 0; i < channel_num;) free((war+i++)->w); free(war); destroy_piano(piano); destroy_violin(violin); #ifdef DEBUG fprintf(stderr, "after mixing\n"); #endif return wf; } /* An embedded test function */ #ifdef TEST int main(int argc, char **argv) { char *file_content = MYALLOC(char, 512); LENT len = read_entire_file("test.txt", &file_content); PSheet sh = read_sheet(file_content, len); print_sheet(sh); WaveFrag wf = play_sheet(sh, 0.3); destroy_sheet(sh); printf("w.n = %lu\n", wf.n); printf("some sound is %lf\n", *(wf.w+10)); free(wf.w); free(file_content); return 0; } #endif