sboolean MP3Stream_SeekTo( channel_t *ch, float fTimeToSeekTo ) { const float fEpsilon = 0.05f; // accurate to 1/50 of a second, but plus or minus this gives 1/10 of second MP3Stream_Rewind( ch ); // // sanity... :-) // const float fTrackLengthInSeconds = MP3Stream_GetPlayingTimeInSeconds( &ch->MP3StreamHeader ); if (fTimeToSeekTo > fTrackLengthInSeconds) { fTimeToSeekTo = fTrackLengthInSeconds; } // now do the seek... // while (1) { float fPlayingTimeElapsed = MP3Stream_GetPlayingTimeInSeconds( &ch->MP3StreamHeader ) - MP3Stream_GetRemainingTimeInSeconds( &ch->MP3StreamHeader ); float fAbsTimeDiff = fabs(fTimeToSeekTo - fPlayingTimeElapsed); if ( fAbsTimeDiff <= fEpsilon) return qtrue; // when decoding, use fast-forward until within 3 seconds, then slow-decode (which should init stuff properly?)... // int iBytesDecodedThisPacket = C_MP3Stream_Decode( &ch->MP3StreamHeader, (fAbsTimeDiff > 3.0f) ); // bFastForwarding if (iBytesDecodedThisPacket == 0) break; // EOS } return qfalse; }
// decode one packet of MP3 data only (typical output size is 2304, or 2304*2 for stereo, so input size is less // // return is decoded byte count, else 0 for finished // int MP3Stream_Decode( LP_MP3STREAM lpMP3Stream, qboolean bDoingMusic ) { lpMP3Stream->iCopyOffset = 0; if (0)//!bDoingMusic) { // SOF2: need to make a local buffer up so we can decode the piece we want from a contiguous bitstream rather than // this linklist junk... // // since MP3 packets are generally 416 or 417 bytes in length it seems reasonable to just find which linked-chunk // the current read offset lies within then grab the next one as well (since they're 2048 bytes) and make one // buffer with just the two concat'd together. Shouldn't be much of a processor hit. // sndBuffer *pChunk = (sndBuffer *) lpMP3Stream->pbSourceData; // // may as well make this static to avoid cut down on stack-validation run-time... // static byte byRawBuffer[SND_CHUNK_SIZE_BYTE*2]; // *2 for byte->short // easily enough to decode one frame of MP3 data, most are 416 or 417 bytes // fast-forward to the correct chunk... // int iBytesToSkipPast = lpMP3Stream->iSourceReadIndex; while (iBytesToSkipPast >= SND_CHUNK_SIZE_BYTE) { pChunk = pChunk->next; if (!pChunk) { // err.... reading off the end of the data stream guys... // // pChunk = (sndBuffer *) lpMP3Stream->pbSourceData; // restart return 0; // ... 0 bytes decoded, so will just stop caller-decoder all nice and legal as EOS } iBytesToSkipPast -= SND_CHUNK_SIZE_BYTE; } // ok, pChunk is now the 2k or so chunk we're in the middle of... // int iChunk1BytesToCopy = SND_CHUNK_SIZE_BYTE - iBytesToSkipPast; memcpy(byRawBuffer,((byte *)pChunk->sndChunk) + iBytesToSkipPast, iChunk1BytesToCopy); // // concat next chunk on to this as well... // pChunk = pChunk->next; if (pChunk) { memcpy(byRawBuffer + iChunk1BytesToCopy, pChunk->sndChunk, SND_CHUNK_SIZE_BYTE); } else { memset(byRawBuffer + iChunk1BytesToCopy, 0, SND_CHUNK_SIZE_BYTE); } // now we need to backup some struct fields, fake 'em, do the lo-level call, then restore 'em... // byte *pbSourceData_Old = lpMP3Stream->pbSourceData; int iSourceReadIndex_Old= lpMP3Stream->iSourceReadIndex; lpMP3Stream->pbSourceData = &byRawBuffer[0]; lpMP3Stream->iSourceReadIndex= 0; // since this is zero, not the buffer offset within a chunk, we can play tricks further down when restoring unsigned int uiBytesDecoded = C_MP3Stream_Decode( lpMP3Stream ); lpMP3Stream->iSourceReadIndex += iSourceReadIndex_Old; // note '+=' rather than '=', to take account of movement. lpMP3Stream->pbSourceData = pbSourceData_Old; return uiBytesDecoded; } else { // SOF2 music, or EF1 anything... // return C_MP3Stream_Decode( lpMP3Stream ); } }