/* * parser.c * Handles parsing of songs and playing of the parsed notes * * Created on: Nov 25, 2025 * Author: sowgro */ #include #include "endian_converters.h" #include #include "endian_converters.h" #include "hw8.h" #include "activity4.h" #include "systick.h" #include "tone.h" static track_t tracks[4]; static int nTracks; static int startTime; static song_info_t song_info; /** * Prepare the parser to play the notes * Expected that parse_song() was called first */ void parser_play_init() { startTime = systick_get_count(); } /** * Play any notes that are ready to be played * Should be called continuously in the play state * Expected that parse_song() was called first */ void parser_play_loop() { int curAbsTime = systick_get_count() - startTime; for (int i = 0; i < nTracks; i++) { note_event_t curEvent = tracks[i].events[tracks[i].curEventIndex]; if (curEvent.abs_time >= curAbsTime) { switch (curEvent.ev_type) { case NOTE_OFF_EVENT: remove_tone(curEvent.key_number); break; case NOTE_ON_EVENT: add_tone(curEvent.key_number, curEvent.value); break; } tracks[i].curEventIndex++; } } play_tones(); } /** * Parse a song and populate the static fields with its information * @param p_song a pointer to the current position in the song */ void parse_song(uint8_t *p_song) { header_t header; header = get_header(p_song); p_song += sizeof(header_t) - 2; // move pointer past header parse_song_info(p_song, &song_info); nTracks = header.ntrcks; for(int i = 0; i < nTracks; i++){ p_song = parse_track(p_song, &tracks[i]); } } /** * Parse the track and populate the track parameter with its information * @param p_song a pointer to the current position in the song * @param track a pointer to a track to output to * @returns a pointer to the song after the track */ uint8_t *parse_track(uint8_t *p_song, track_t *track) { p_song += 4; // skip MTrk uint32_t MTrk_len = convert_to_uint32(p_song); p_song += 4; int curEvent = 0; uint32_t prev_abs_time = 0; for (uint8_t *p_end = p_song + MTrk_len; p_song != p_end; p_song++) { note_event_t note; parseDelay_result_t delay_result = parseDelay(p_song); uint32_t abs_time = prev_abs_time + delay_result.value; note.abs_time = abs_time; prev_abs_time = abs_time; p_song += delay_result.bytes_used; if (*p_song >> 4 == 0b1000) { note.ev_type = NOTE_OFF_EVENT; } else if (*p_song >> 4 == 0b1001) { note.ev_type = NOTE_ON_EVENT; } else if (*p_song >> 4 == 0b1010) { note.ev_type = KEY_PRESSURE; } else { continue; } // size_t channelNumber = *p_song && 0b00001111; p_song++; note.key_number = *p_song; p_song++; note.value = *p_song; p_song++; track->events[curEvent++] = note; } track->nEvents = curEvent; track->curEventIndex = 0; return p_song; } /** * Parses the track information * @param p_song a pointer to the current position in the song * @param ret the output parameter * @returns a pointer to the song after the track info */ uint8_t *parse_song_info(uint8_t *p_song, song_info_t *ret) { ret->copyright = 0; ret->tempo = 0; ret->title = 0; p_song += 4; // move past MTrk label uint32_t MTrk_len = convert_to_uint32(p_song); // read in size of MTrk p_song += sizeof(MTrk_len); for (uint8_t *p_end = p_song + MTrk_len; p_song != p_end; p_song++) { // FF 02 - copyright if (convert_to_uint16(p_song) == 0xFF02) { p_song += sizeof(uint16_t); uint8_t ev_len = *(uint8_t *) p_song; p_song += sizeof(ev_len); ret->copyright = (char *) p_song; ret->copyright[ev_len] = 0; } // FF 03 - title if (convert_to_uint16(p_song) == 0xFF03) { p_song += sizeof(uint16_t); uint8_t ev_len = *(uint8_t *) p_song; p_song += sizeof(ev_len); ret->title = (char *) p_song; ret->title[ev_len] = 0; } // FF 51 - tempo if (convert_to_uint16(p_song) == 0xFF51) { p_song += sizeof(uint16_t); p_song += sizeof(uint8_t); // skip length, always 03 ret->tempo = convert_to_uint24(p_song); } } return p_song; } /** * An accessor for other files to get the song information */ song_info_t parser_get_song_info() { return song_info; }