static int SeekSet0 (demux_t *demux) { stream_t *stream = demux->s; demux_sys_t *sys = demux->p_sys; /* Default SMF tempo is 120BPM, i.e. half a second per quarter note */ date_Init (&sys->pts, sys->ppqn * 2, 1); date_Set (&sys->pts, VLC_TS_0); sys->pulse = 0; sys->tick = VLC_TS_0; for (unsigned i = 0; i < sys->trackc; i++) { mtrk_t *tr = sys->trackv + i; tr->offset = 0; tr->next = 0; /* Why 0xF6 (Tuning Calibration)? * Because it has zero bytes of data, so the parser will detect the * error if the first event uses running status. */ tr->running_event = 0xF6; if (stream_Seek (stream, tr->start) || ReadDeltaTime (stream, tr)) { msg_Err (demux, "fatal parsing error"); return -1; } } return 0; }
/***************************************************************************** * Demux: read chunks and send them to the synthesizer ***************************************************************************** * Returns -1 in case of error, 0 in case of EOF, 1 otherwise *****************************************************************************/ static int Demux (demux_t *p_demux) { stream_t *s = p_demux->s; demux_sys_t *p_sys = p_demux->p_sys; uint64_t pulse = p_sys->pulse, next_pulse = UINT64_MAX; if (pulse == UINT64_MAX) return 0; /* all tracks are done */ es_out_Control (p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + date_Get (&p_sys->pts)); for (unsigned i = 0; i < p_sys->trackc; i++) { mtrk_t *track = p_sys->trackv + i; while (track->next == pulse) { if (HandleMessage (p_demux, track) || ReadDeltaTime (s, track)) { msg_Err (p_demux, "fatal parsing error"); return VLC_EGENERIC; } } if (track->next < next_pulse) next_pulse = track->next; } mtime_t cur_tick = (date_Get (&p_sys->pts) + 9999) / 10000, last_tick; if (next_pulse != UINT64_MAX) last_tick = date_Increment (&p_sys->pts, next_pulse - pulse) / 10000; else last_tick = cur_tick + 1; /* MIDI Tick emulation (ping the decoder every 10ms) */ while (cur_tick < last_tick) { block_t *tick = block_New (p_demux, 1); if (tick == NULL) break; tick->p_buffer[0] = 0xF9; tick->i_dts = tick->i_pts = VLC_TS_0 + cur_tick++ * 10000; es_out_Send (p_demux->out, p_sys->es, tick); } p_sys->pulse = next_pulse; return 1; }
/***************************************************************************** * Open: check file and initializes structures *****************************************************************************/ static int Open (vlc_object_t * p_this) { demux_t *p_demux = (demux_t *)p_this; stream_t *stream = p_demux->s; demux_sys_t *p_sys; const uint8_t *peek; unsigned tracks, ppqn; bool multitrack; /* (Try to) parse the SMF header */ /* Header chunk always has 6 bytes payload */ if (stream_Peek (stream, &peek, 14) < 14) return VLC_EGENERIC; /* Skip RIFF MIDI header if present */ if (!memcmp (peek, "RIFF", 4) && !memcmp (peek + 8, "RMID", 4)) { uint32_t riff_len = GetDWLE (peek + 4); msg_Dbg (p_this, "detected RIFF MIDI file (%u bytes)", (unsigned)riff_len); if ((stream_Read (stream, NULL, 12) < 12)) return VLC_EGENERIC; /* Look for the RIFF data chunk */ for (;;) { char chnk_hdr[8]; uint32_t chnk_len; if ((riff_len < 8) || (stream_Read (stream, chnk_hdr, 8) < 8)) return VLC_EGENERIC; riff_len -= 8; chnk_len = GetDWLE (chnk_hdr + 4); if (riff_len < chnk_len) return VLC_EGENERIC; riff_len -= chnk_len; if (!memcmp (chnk_hdr, "data", 4)) break; /* found! */ if (stream_Read (stream, NULL, chnk_len) < (ssize_t)chnk_len) return VLC_EGENERIC; } /* Read real SMF header. Assume RIFF data chunk length is proper. */ if (stream_Peek (stream, &peek, 14) < 14) return VLC_EGENERIC; } if (memcmp (peek, "MThd\x00\x00\x00\x06", 8)) return VLC_EGENERIC; peek += 8; /* First word: SMF type */ switch (GetWBE (peek)) { case 0: multitrack = false; break; case 1: multitrack = true; break; default: /* We don't implement SMF2 (as do many) */ msg_Err (p_this, "unsupported SMF file type %u", GetWBE (peek)); return VLC_EGENERIC; } peek += 2; /* Second word: number of tracks */ tracks = GetWBE (peek); peek += 2; if (!multitrack && (tracks != 1)) { msg_Err (p_this, "invalid SMF type 0 file"); return VLC_EGENERIC; } msg_Dbg (p_this, "detected Standard MIDI File (type %u) with %u track(s)", multitrack, tracks); /* Third/last word: timing */ ppqn = GetWBE (peek); if (ppqn & 0x8000) { /* FIXME */ msg_Err (p_this, "SMPTE timestamps not implemented"); return VLC_EGENERIC; } else { msg_Dbg (p_this, " %u pulses per quarter note", ppqn); } p_sys = malloc (sizeof (*p_sys) + (sizeof (mtrk_t) * tracks)); if (p_sys == NULL) return VLC_ENOMEM; /* We've had a valid SMF header - now skip it*/ if (stream_Read (stream, NULL, 14) < 14) goto error; p_demux->pf_demux = Demux; p_demux->pf_control = Control; p_demux->p_sys = p_sys; /* Default SMF tempo is 120BPM, i.e. half a second per quarter note */ date_Init (&p_sys->pts, ppqn * 2, 1); date_Set (&p_sys->pts, 0); p_sys->pulse = 0; p_sys->ppqn = ppqn; p_sys->trackc = tracks; /* Prefetch track offsets */ for (unsigned i = 0; i < tracks; i++) { uint8_t head[8]; if (i > 0) { /* Seeking screws streaming up, but there is no way around this, * as SMF1 tracks are performed simultaneously. * Not a big deal as SMF1 are usually only a few kbytes anyway. */ if (stream_Seek (stream, p_sys->trackv[i-1].end)) { msg_Err (p_this, "cannot build SMF index (corrupted file?)"); goto error; } } for (;;) { if (stream_Read (stream, head, 8) < 8) { /* FIXME: don't give up if we have at least one valid track */ msg_Err (p_this, "incomplete SMF chunk, file is corrupted"); goto error; } if (memcmp (head, "MTrk", 4) == 0) break; msg_Dbg (p_this, "skipping unknown SMF chunk"); stream_Read (stream, NULL, GetDWBE (head + 4)); } p_sys->trackv[i].offset = stream_Tell (stream); p_sys->trackv[i].end = p_sys->trackv[i].offset + GetDWBE (head + 4); p_sys->trackv[i].next = 0; ReadDeltaTime (stream, p_sys->trackv + i); p_sys->trackv[i].running_event = 0xF6; /* Why 0xF6 (Tuning Calibration)? * Because it has zero bytes of data, so the parser will detect the * error if the first event uses running status. */ } es_format_t fmt; es_format_Init (&fmt, AUDIO_ES, VLC_CODEC_MIDI); fmt.audio.i_channels = 2; fmt.audio.i_rate = 44100; /* dummy value */ p_sys->es = es_out_Add (p_demux->out, &fmt); return VLC_SUCCESS; error: free (p_sys); return VLC_EGENERIC; }
//============================================================== // Process of 1 Event //-------------------------------------------------------------- // Input // Nothing // Output // Nothing // Memo // SeqTrack::LoadTrack() から call される。 //============================================================== bool HOSATrack::ReadEvent(void) { //================================== // [ Local 変数 ] //---------------------------------- const ULONG beginOffset = curOffset; //start offset point const BYTE cCommand = GetByte(curOffset++); //command (op-code) const BYTE cCom_bit0 = (cCommand & 0x1F); //length / contents const BYTE cCom_bit5 = (cCommand & 0x60) >> 5; //Delta times const BYTE cCom_bit7 = (cCommand & 0x80) >> 7; //0=Notes / 1=Controls // unsigned int iMinLengthCounter; //デルタタイム最小値 // int i; //general // vector<char>::iterator it_note; // vector<int unsigned >::iterator it_Length; //================================== // [ Process of "Delta time" and "Note off Event" ] //---------------------------------- //Command用の(DeltaCounter==0)まで繰り返し。 //while(iDeltaTimeCounter!=0){ // //Search the minimum length time [ticks] // iMinLengthCounter = iDeltaTimeCounter; // for(i=0;i<listLength.size();i++){ // if(iMinLengthCounter > listLength[i]){ // iMinLengthCounter = listLength[i]; // } // } // //Writing delta time. // AddDelta(iMinLengthCounter); // // //Subtract the minimum length time from "Delta time" and "Length times" // iDeltaTimeCounter -= iMinLengthCounter; // for(i=0;i<listNote.size();i++){ // listLength[i] -= iMinLengthCounter; // } // //Search the ("Length times" == 0) // it_note = listNote.begin(); // it_Length = listLength.begin(); // while(it_Length != listLength.end()){ // if(*it_Length==0){ // //Write "Note-off Event" // AddNoteOffNoItem(*it_note); // //Erase note-information from vector. // it_note = listNote.erase(it_note); // it_Length = listLength.erase(it_Length); // } else { // it_note++; // it_Length++; // } // } //} //================================== // [ Process of command (note / control) ] //---------------------------------- //---------------------------------- // Command: Notes (cCommand == 0x00 - 0x7F) if(cCom_bit7==0){ //-------- //[2]Update the default Note Number cNoteNum = GetByte(curOffset++); //-------- //[3]Update the default Delta time ReadDeltaTime(cCom_bit5, &iDeltaTimeCom); //-------- //[4]Update the default Length iLengthTimeNote = GetShort(parentSeq->dwOffset + 0x10 + cCom_bit0*2); if(iLengthTimeNote==0){ // iLengthTimeNote = ReadVarLen(curOffset); //No count curOffset iLengthTimeNote = DecodeVariable(); }; //-------- //[5]Update the default Velocity if(cNoteNum & 0x80){ cNoteNum &= 0x7F; cVelocity = GetByte(curOffset++); }; //-------- //[3]Update the default Delta time (continuation) if(cCom_bit5==1){ iDeltaTimeCom = iLengthTimeNote; //Delta time is the same as note length. }; iDeltaTimeNote = iDeltaTimeCom; //Default delta time for Note. //-------- //Write Note-On Event AddNoteByDur(beginOffset, curOffset-beginOffset, cNoteNum, cVelocity, iLengthTimeNote); //---------------------------------- // Command: Controls (cCommand == 0x80 - 0x9F, 0xC0 - 0xFF) } else { if(cCom_bit5!=1){ switch(cCom_bit0){ //------------ //End of Track case(0x00): AddEndOfTrack(beginOffset, curOffset-beginOffset); return false; break; //------------ //Tempo case(0x01): cTempo = GetByte(curOffset++); AddTempoBPM(beginOffset, curOffset-beginOffset, cTempo); break; //------------ //Reverb case(0x02): curOffset++; AddGenericEvent(beginOffset, curOffset-beginOffset, L"Reverb Depth", NULL, CLR_REVERB); break; //------------ //Instrument case(0x03): cInstrument = GetByte(curOffset++); AddProgramChange(beginOffset, curOffset-beginOffset, cInstrument); break; //------------ //Volume case(0x04): cVolume = GetByte(curOffset++); AddVol(beginOffset, curOffset-beginOffset, cVolume); break; //------------ //Panpot case(0x05): cPanpot = GetByte(curOffset++); AddPan(beginOffset, curOffset-beginOffset, cPanpot); break; //------------ //Expression case(0x06): cExpression = GetByte(curOffset++); AddExpression(beginOffset, curOffset-beginOffset, cExpression); break; //------------ //Unknown case(0x07): curOffset++; curOffset++; AddUnknown(beginOffset, curOffset-beginOffset); break; //------------ //Dal Segno. (Loop) case(0x09): curOffset++; AddGenericEvent(beginOffset, curOffset-beginOffset, L"Dal Segno.(Loop)", NULL, CLR_LOOP); break; //------------ //Unknown case(0x0F): AddUnknown(beginOffset, curOffset-beginOffset); break; //------------ //Unknowns default: curOffset++; AddUnknown(beginOffset, curOffset-beginOffset); break; } //-------- //[3]Delta time ULONG beginOffset2 = curOffset; ReadDeltaTime(cCom_bit5, &iDeltaTimeCom); if(curOffset != beginOffset2){ AddGenericEvent(beginOffset2,curOffset-beginOffset2, L"Delta time", NULL, CLR_CHANGESTATE); }; //---------------------------------- // Command: Notes (cCommand == 0xA0 - 0xBF) } else { if(cCom_bit0 & 0x10){ //Add (Command = 0xB0 - 0xBF) cNoteNum+=(cCom_bit0 & 0x0F); }else{ //Sub (Command = 0xA0 - 0xAF) cNoteNum-=(cCom_bit0 & 0x0F); }; //-------- //Write Note-On Event AddNoteByDur(beginOffset, curOffset-beginOffset, cNoteNum, cVelocity, iLengthTimeNote); iDeltaTimeCom = iDeltaTimeNote; } } //================================== // [ Process of "Note" with note length ] //---------------------------------- //if(fNoteOutput!=0){ // //Write "Note-on Event" // AddNoteOn(beginOffset, curOffset-beginOffset, fNoteOutput, cVelocity); // //Add note-information to vector. // listNote.push_back(fNoteOutput); //Note Number // listLength.push_back(iLengthTimeNote); //Length // //Next delta time // iDeltaTimeCom = iDeltaTimeNote; //} //================================== // [ Process of "Setting Delta time" ] //---------------------------------- //iDeltaTimeCounter = iDeltaTimeCom; AddTime(iDeltaTimeCom); return true; }