enum plugin_status plugin_start(const void* parameter) { int retval; if (parameter == NULL) { rb->splash(HZ*2, " Play .MID file "); return PLUGIN_OK; } rb->lcd_setfont(FONT_SYSFIXED); midi_debug("%s", parameter); /* rb->splash(HZ, true, parameter); */ #ifdef RB_PROFILE rb->profile_thread(); #endif retval = midimain(parameter); #ifdef RB_PROFILE rb->profstop(); #endif rb->pcm_play_stop(); rb->pcm_set_frequency(HW_SAMPR_DEFAULT); rb->splash(HZ, "FINISHED PLAYING"); if (retval == -1) return PLUGIN_ERROR; return PLUGIN_OK; }
void get_more(unsigned char** start, size_t* size) { #ifndef SYNC if(lastswap != swap) { midi_debug("Buffer miss!"); /* Comment out the midi_debug to make missses less noticable. */ } #else synthbuf(); /* For some reason midiplayer crashes when an update is forced */ #endif *size = samples_in_buf*sizeof(int32_t); #ifndef SYNC *start = (unsigned char*)((swap ? gmbuf : gmbuf + BUF_SIZE)); swap = !swap; #else *start = (unsigned char*)(gmbuf); #endif }
int readID(int file) { char id[5]; id[4]=0; BYTE a; for(a=0; a<4; a++) id[a]=readChar(file); if(eof(file)) { midi_debug("End of file reached."); return ID_EOF; } if(rb->strcmp(id, "MThd")==0) return ID_MTHD; if(rb->strcmp(id, "MTrk")==0) return ID_MTRK; if(rb->strcmp(id, "RIFF")==0) return ID_RIFF; return ID_UNKNOWN; }
enum plugin_status plugin_start(const void* parameter) { int retval; if (parameter == NULL) { rb->splash(HZ*2, " Play .MID file "); return PLUGIN_OK; } rb->lcd_setfont(0); #if defined(HAVE_ADJUSTABLE_CPU_FREQ) rb->cpu_boost(true); #endif midi_debug("%s", parameter); /* rb->splash(HZ, true, parameter); */ #ifdef RB_PROFILE rb->profile_thread(); #endif retval = midimain(parameter); #ifdef RB_PROFILE rb->profstop(); #endif rb->pcm_play_stop(); rb->pcm_set_frequency(HW_SAMPR_DEFAULT); #if defined(HAVE_ADJUSTABLE_CPU_FREQ) rb->cpu_boost(false); #endif rb->splash(HZ, "FINISHED PLAYING"); if (retval == -1) return PLUGIN_ERROR; return PLUGIN_OK; }
struct Track * readTrack(int file) { struct Track * trk = &tracks[curr_track++]; rb->memset(trk, 0, sizeof(struct Track)); trk->size = readFourBytes(file); trk->pos = 0; trk->delta = 0; int numEvents=0; int pos = rb->lseek(file, 0, SEEK_CUR); while(readEvent(file, NULL)) /* Memory saving technique */ numEvents++; /* Attempt to read in events, count how many */ /* THEN allocate memory and read them in */ rb->lseek(file, pos, SEEK_SET); int trackSize = (numEvents+1) * sizeof(struct Event); void * dataPtr = malloc(trackSize); trk->dataBlock = dataPtr; numEvents=0; while(readEvent(file, dataPtr)) { if(trackSize < dataPtr-trk->dataBlock) { midi_debug("Track parser memory out of bounds"); exit(1); } dataPtr+=sizeof(struct Event); numEvents++; } trk->numEvents = numEvents; return trk; }
static void get_more(const void** start, size_t* size) { #ifndef SYNC if(lastswap != swap) { midi_debug("Buffer miss!"); /* Comment out the midi_debug to make missses less noticable. */ } #else synthbuf(); /* For some reason midiplayer crashes when an update is forced */ #endif *size = samples_in_buf*sizeof(int32_t); #ifndef SYNC *start = swap ? gmbuf : gmbuf + BUF_SIZE; swap = !swap; #else *start = gmbuf; #endif if (samples_in_buf==0) { *start = NULL; quit = true; /* this was the last buffer to play */ } }
static int midimain(const void * filename) { int a, notes_used, vol; bool is_playing = true; /* false = paused */ #if defined(HAVE_ADJUSTABLE_CPU_FREQ) rb->cpu_boost(true); #endif midi_debug("Loading file"); mf = loadFile(filename); if (mf == NULL) { midi_debug("Error loading file."); #if defined(HAVE_ADJUSTABLE_CPU_FREQ) rb->cpu_boost(false); #endif return -1; } if (initSynth(mf, ROCKBOX_DIR "/patchset/patchset.cfg", ROCKBOX_DIR "/patchset/drums.cfg") == -1) { #if defined(HAVE_ADJUSTABLE_CPU_FREQ) rb->cpu_boost(false); #endif return -1; } rb->pcm_play_stop(); #if INPUT_SRC_CAPS != 0 /* Select playback */ rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); #endif rb->pcm_set_frequency(SAMPLE_RATE); /* 44100 22050 11025 */ /* * tick() will do one MIDI clock tick. Then, there's a loop here that * will generate the right number of samples per MIDI tick. The whole * MIDI playback is timed in terms of this value.. there are no forced * delays or anything. It just produces enough samples for each tick, and * the playback of these samples is what makes the timings right. * * This seems to work quite well. On a laptop, anyway. */ midi_debug("Okay, starting sequencing"); bpm = mf->div*1000000/tempo; number_of_samples = SAMPLE_RATE/bpm; /* Skip over any junk in the beginning of the file, so start playing */ /* after the first note event */ do { notes_used = 0; for (a = 0; a < MAX_VOICES; a++) if (voices[a].isUsed) notes_used++; tick(); } while (notes_used == 0); #if defined(HAVE_ADJUSTABLE_CPU_FREQ) rb->cpu_boost(false); #endif playing_time = 0; samples_this_second = 0; synthbuf(); rb->pcm_play_data(&get_more, NULL, NULL, 0); while (!quit) { #ifndef SYNC synthbuf(); #endif rb->yield(); /* Prevent idle poweroff */ rb->reset_poweroff_timer(); /* Code taken from Oscilloscope plugin */ switch (rb->button_get(false)) { case MIDI_VOL_UP: case MIDI_VOL_UP | BUTTON_REPEAT: { vol = rb->global_settings->volume; if (vol < rb->sound_max(SOUND_VOLUME)) { vol++; rb->sound_set(SOUND_VOLUME, vol); rb->global_settings->volume = vol; } break; } case MIDI_VOL_DOWN: case MIDI_VOL_DOWN | BUTTON_REPEAT: { vol = rb->global_settings->volume; if (vol > rb->sound_min(SOUND_VOLUME)) { vol--; rb->sound_set(SOUND_VOLUME, vol); rb->global_settings->volume = vol; } break; } case MIDI_REWIND: { /* Rewinding is tricky. Basically start the file over */ /* but run through the tracks without the synth running */ rb->pcm_play_stop(); #if defined(HAVE_ADJUSTABLE_CPU_FREQ) rb->cpu_boost(true); #endif seekBackward(5); #if defined(HAVE_ADJUSTABLE_CPU_FREQ) rb->cpu_boost(false); #endif lastswap = !swap; synthbuf(); midi_debug("Rewind to %d:%02d\n", playing_time/60, playing_time%60); if (is_playing) rb->pcm_play_data(&get_more, NULL, NULL, 0); break; } case MIDI_FFWD: { rb->pcm_play_stop(); seekForward(5); lastswap = !swap; synthbuf(); midi_debug("Skip to %d:%02d\n", playing_time/60, playing_time%60); if (is_playing) rb->pcm_play_data(&get_more, NULL, NULL, 0); break; } case MIDI_PLAYPAUSE: { if (is_playing) { midi_debug("Paused at %d:%02d\n", playing_time/60, playing_time%60); is_playing = false; rb->pcm_play_stop(); } else { midi_debug("Playing from %d:%02d\n", playing_time/60, playing_time%60); is_playing = true; rb->pcm_play_data(&get_more, NULL, NULL, 0); } break; } #ifdef MIDI_RC_QUIT case MIDI_RC_QUIT: #endif case MIDI_QUIT: quit = true; } } return 0; }
struct MIDIfile * loadFile(const char * filename) { struct MIDIfile * mfload; int file = rb->open (filename, O_RDONLY); if(file < 0) { midi_debug("Could not open file"); return NULL; } mfload = &midi_file; rb->memset(mfload, 0, sizeof(struct MIDIfile)); int fileID = readID(file); if(fileID != ID_MTHD) { if(fileID == ID_RIFF) { midi_debug("Detected RMID file"); midi_debug("Looking for MThd header"); char dummy[17]; rb->read(file, &dummy, 16); if(readID(file) != ID_MTHD) { rb->close(file); midi_debug("Invalid MIDI header within RIFF."); return NULL; } } else { rb->close(file); midi_debug("Invalid file header chunk."); return NULL; } } if(readFourBytes(file)!=6) { rb->close(file); midi_debug("Header chunk size invalid."); return NULL; } if(readTwoBytes(file)==2) { rb->close(file); midi_debug("MIDI file type 2 not supported"); return NULL; } mfload->numTracks = readTwoBytes(file); mfload->div = readTwoBytes(file); int track=0; midi_debug("File has %d tracks.", mfload->numTracks); while(! eof(file) && track < mfload->numTracks) { unsigned char id = readID(file); if(id == ID_EOF) { if(mfload->numTracks != track) { midi_debug("Warning: file claims to have %d tracks. I only see %d here.", mfload->numTracks, track); mfload->numTracks = track; } rb->close(file); return mfload; } if(id == ID_MTRK) { mfload->tracks[track] = readTrack(file); track++; } else { midi_debug("SKIPPING TRACK"); int len = readFourBytes(file); while(--len) readChar(file); } } rb->close(file); return mfload; }
/* Returns 0 if done, 1 if keep going */ static int readEvent(int file, void * dest) { struct Event dummy; struct Event * ev = (struct Event *) dest; if(ev == NULL) ev = &dummy; /* If we are just counting events instead of loading them */ ev->delta = readVarData(file); int t=readChar(file); if((t&0x80) == 0x80) /* if not a running status event */ { ev->status = t; if(t == 0xFF) { ev->d1 = readChar(file); ev->len = readVarData(file); /* Allocate and read in the data block */ if(dest != NULL) { /* Null-terminate for text events */ ev->evData = malloc(ev->len+1); /* Extra byte for the null termination */ rb->read(file, ev->evData, ev->len); ev->evData[ev->len] = 0; switch(ev->d1) { case 0x01: /* Generic text */ { midi_debug("Text: %s", ev->evData); break; } case 0x02: /* A copyright string within the file */ { midi_debug("Copyright: %s", ev->evData); break; } case 0x03: /* Sequence of track name */ { midi_debug("Name: %s", ev->evData); break; } case 0x04: /* Instrument (synth) name */ { midi_debug("Instrument: %s", ev->evData); break; } case 0x05: /* Lyrics. These appear on the tracks at the right times */ { /* Usually only a small 'piece' of the lyrics. */ /* Maybe the sequencer should print these at play time? */ midi_debug("Lyric: %s", ev->evData); break; } case 0x06: /* Text marker */ { midi_debug("Marker: %s", ev->evData); break; } case 0x07: /* Cue point */ { midi_debug("Cue point: %s", ev->evData); break; } case 0x08: /* Program name */ { midi_debug("Patch: %s", ev->evData); break; } case 0x09: /* Device name. Very much irrelevant here, though. */ { midi_debug("Port: %s", ev->evData); break; } } } else { /* * Don't allocate anything, just see how much it would take * To make memory usage efficient */ unsigned int a=0; for(a=0; a<ev->len; a++) readChar(file); //Skip skip } if(ev->d1 == 0x2F) { return 0; /* Termination meta-event */ } } else /* If part of a running status event */ { rStatus = t; ev->status = t; ev->d1 = readChar(file); if ( ((t & 0xF0) != 0xD0) && ((t & 0xF0) != 0xC0) && ((t & 0xF0) > 0x40) ) { ev->d2 = readChar(file); } else ev->d2 = 127; } } else /* Running Status */ { ev->status = rStatus; ev->d1 = t; if ( ((rStatus & 0xF0) != 0xD0) && ((rStatus & 0xF0) != 0xC0) && ((rStatus & 0xF0) > 0x40) ) { ev->d2 = readChar(file); } else ev->d2 = 127; } return 1; }