public static Timestamp ConvertTimeToStreamPos(Timestamp where, int rate, bool isStereo) { var result = new Timestamp(where.ConvertToFramerate(rate * (isStereo ? 2 : 1))); // When the Stream is a stereo stream, we have to assure // that the sample position is an even number. if (isStereo && (result.TotalNumberOfFrames & 1) != 0) { result = result.AddFrames(-1); // We cut off one sample here. } // Since Timestamp allows sub-frame-precision it might lead to odd behaviors // when we would just return result. // // An example is when converting the timestamp 500ms to a 11025 Hz based // stream. It would have an internal frame counter of 5512.5. Now when // doing calculations at frame precision, this might lead to unexpected // results: The frame difference between a timestamp 1000ms and the above // mentioned timestamp (both with 11025 as framerate) would be 5512, // instead of 5513, which is what a frame-precision based code would expect. // // By creating a new Timestamp with the given parameters, we create a // Timestamp with frame-precision, which just drops a sub-frame-precision // information (i.e. rounds down). return(new Timestamp(result.Seconds, result.NumberOfFrames, result.Framerate)); }
public void DecodeFrame(Stream frame, int sectorCount) { // A frame is essentially an MPEG-1 intra frame var bits = new BitStream(frame, BitStreamType.SixteenBits, true, true); bits.Skip(16); // unknown bits.Skip(16); // 0x3800 ushort scale = (ushort)bits.GetBits(16); ushort version = (ushort)bits.GetBits(16); if (version != 2 && version != 3) { throw new InvalidOperationException("Unknown PSX stream frame version"); } // Initalize default v3 DC here _lastDC[0] = _lastDC[1] = _lastDC[2] = 0; for (int mbX = 0; mbX < _macroBlocksW; mbX++) { for (int mbY = 0; mbY < _macroBlocksH; mbY++) { DecodeMacroBlock(bits, mbX, mbY, scale, version); } } // Output data onto the frame YUVToRGBManager.Convert420(_surface, LuminanceScale.ScaleFull, _yBuffer, _cbBuffer, _crBuffer, _surface.Width, _surface.Height, _macroBlocksW * 16, _macroBlocksW * 8); _curFrame++; // Increase the time by the amount of sectors we read // One may notice that this is still not the most precise // method since a frame takes up the time its sectors took // up instead of the amount of time it takes the next frame // to be read from the sectors. The actual frame rate should // be constant instead of variable, so the slight difference // in a frame's showing time is negligible (1/150 of a second). _nextFrameStartTime = _nextFrameStartTime.AddFrames(sectorCount); }
public static Timestamp ConvertTimeToStreamPos(Timestamp where, int rate, bool isStereo) { var result = new Timestamp(where.ConvertToFramerate(rate * (isStereo ? 2 : 1))); // When the Stream is a stereo stream, we have to assure // that the sample position is an even number. if (isStereo && (result.TotalNumberOfFrames & 1) != 0) result = result.AddFrames(-1); // We cut off one sample here. // Since Timestamp allows sub-frame-precision it might lead to odd behaviors // when we would just return result. // // An example is when converting the timestamp 500ms to a 11025 Hz based // stream. It would have an internal frame counter of 5512.5. Now when // doing calculations at frame precision, this might lead to unexpected // results: The frame difference between a timestamp 1000ms and the above // mentioned timestamp (both with 11025 as framerate) would be 5512, // instead of 5513, which is what a frame-precision based code would expect. // // By creating a new Timestamp with the given parameters, we create a // Timestamp with frame-precision, which just drops a sub-frame-precision // information (i.e. rounds down). return new Timestamp(result.Seconds, result.NumberOfFrames, result.Framerate); }
void PreProcess() { var block = new Block(); // Scan through the file and collect all blocks while (true) { block.Code = _br.ReadByte(); block.Length = 0; // If we hit EOS here we found the end of the VOC file. // According to http://wiki.multimedia.cx/index.php?title=Creative_Voice // there is no need for an "Terminator" block to be present. // In case we hit a "Terminator" block we also break here. if (IsAtEndOfStream || block.Code == 0) { break; } // We will allow invalid block numbers as terminators. This is needed, // since some games ship broken VOC files. The following occasions are // known: // - 128 is used as terminator in Simon 1 Amiga CD32 // - Full Throttle contains a VOC file with an incorrect block length // resulting in a sample (127) to be read as block code. if (block.Code > 9) { throw new NotSupportedException(string.Format("VocStream.PreProcess: Caught {0} as terminator", block.Code)); } block.Length = _stream.ReadByte(); block.Length |= _stream.ReadByte() << 8; block.Length |= _stream.ReadByte() << 16; // Premature end of stream => error! if (IsAtEndOfStream) { throw new InvalidOperationException("VocStream.PreProcess: Reading failed"); } int skip = 0; switch (block.Code) { // Sound data case 1: // Sound data (New format) case 9: if (block.Code == 1) { if (block.Length < 2) { throw new InvalidOperationException(string.Format("Invalid sound data block length {0} in VOC file", block.Length)); } // Read header data int freqDiv = _stream.ReadByte(); // Prevent division through 0 if (freqDiv == 256) { throw new InvalidOperationException("Invalid frequency divisor 256 in VOC file"); } block.Rate = GetSampleRateFromVOCRate(freqDiv); int codec = _stream.ReadByte(); // We only support 8bit PCM if (codec != 0) { throw new NotSupportedException(string.Format("Unhandled codec {0} in VOC file", codec)); } block.Samples = skip = block.Length - 2; block.Offset = (int)_stream.Position; // Check the last block if there is any if (_blocks.Count > 0) { int lastBlock = _blocks.Count; --lastBlock; // When we have found a block 8 as predecessor // we need to use its settings if (_blocks[lastBlock].Code == 8) { block.Rate = _blocks[lastBlock].Rate; // Remove the block since we don't need it anymore _blocks.RemoveAt(lastBlock); } } } else { if (block.Length < 12) { throw new InvalidOperationException(string.Format("Invalid sound data (wew format) block length {0} in VOC file", block.Length)); } block.Rate = _br.ReadInt32(); int bitsPerSample = _stream.ReadByte(); // We only support 8bit PCM if (bitsPerSample != 8) { throw new NotSupportedException(string.Format("Unhandled bits per sample {0} in VOC file", bitsPerSample)); } int channels = _stream.ReadByte(); // We only support mono if (channels != 1) { throw new NotSupportedException(string.Format("Unhandled channel count {0} in VOC file", channels)); } int codec = _br.ReadInt16(); // We only support 8bit PCM if (codec != 0) { throw new NotSupportedException(string.Format("Unhandled codec {0} in VOC file", codec)); } /*uint32 reserved = */ _br.ReadInt32(); block.Offset = (int)_stream.Position; block.Samples = skip = block.Length - 12; } // Check whether we found a new highest rate if (_rate < block.Rate) { _rate = block.Rate; } break; // Silence case 3: { if (block.Length != 3) { throw new InvalidOperationException(string.Format("Invalid silence block length {0} in VOC file", block.Length)); } block.Offset = 0; block.Samples = _br.ReadInt16() + 1; int freqDiv = _stream.ReadByte(); // Prevent division through 0 if (freqDiv == 256) { throw new InvalidOperationException("Invalid frequency divisor 256 in VOC file"); } block.Rate = GetSampleRateFromVOCRate(freqDiv); } break; // Repeat start case 6: if (block.Length != 2) { throw new InvalidOperationException(string.Format("Invalid repeat start block length {0} in VOC file", block.Length)); } block.Count = _br.ReadInt16() + 1; break; // Repeat end case 7: break; // Extra info case 8: { if (block.Length != 4) { return; } int freqDiv = _br.ReadInt16(); // Prevent division through 0 if (freqDiv == 65536) { throw new InvalidOperationException("Invalid frequency divisor 65536 in VOC file"); } int codec = _stream.ReadByte(); // We only support RAW 8bit PCM. if (codec != 0) { throw new NotSupportedException(string.Format("Unhandled codec {0} in VOC file", codec)); } int channels = _stream.ReadByte() + 1; // We only support mono sound right now if (channels != 1) { throw new NotSupportedException(string.Format("Unhandled channel count {0} in VOC file", channels)); } block.Offset = 0; block.Samples = 0; block.Rate = (int)(256000000L / (65536L - freqDiv)); } break; default: // Console.Error.WriteLine("Unhandled code {0} in VOC file (len {1})", block.Code, block.Length); // Skip the whole block and try to use the next one. skip = block.Length; break; } // Premature end of stream => error! if (IsAtEndOfStream) { throw new InvalidOperationException("VocStream::preProcess: Reading failed"); } // Skip the rest of the block if (skip != 0) { _stream.Seek(skip, SeekOrigin.Current); } _blocks.Add(block); } // Since we determined the sample rate we need for playback now, we will // initialize the play length. _length = new Timestamp(0, _rate); // Calculate the total play time and do some more sanity checks foreach (var i in _blocks) { // Check whether we found a block 8 which survived, this is not // allowed to happen! if (i.Code == 8) { throw new InvalidOperationException("VOC file contains unused block 8"); } // For now only use blocks with actual samples if (i.Code != 1 && i.Code != 9) { continue; } // Check the sample rate if (i.Rate != _rate) { throw new InvalidOperationException(string.Format("VOC file contains chunks with different sample rates ({0} != {1})", _rate, i.Rate)); } _length = _length.AddFrames(i.Samples); } // Set the current block to the first block in the stream Rewind(); }
void PreProcess() { var block = new Block(); // Scan through the file and collect all blocks while (true) { block.Code = _br.ReadByte(); block.Length = 0; // If we hit EOS here we found the end of the VOC file. // According to http://wiki.multimedia.cx/index.php?title=Creative_Voice // there is no need for an "Terminator" block to be present. // In case we hit a "Terminator" block we also break here. if (IsAtEndOfStream || block.Code == 0) break; // We will allow invalid block numbers as terminators. This is needed, // since some games ship broken VOC files. The following occasions are // known: // - 128 is used as terminator in Simon 1 Amiga CD32 // - Full Throttle contains a VOC file with an incorrect block length // resulting in a sample (127) to be read as block code. if (block.Code > 9) { throw new NotSupportedException(string.Format("VocStream.PreProcess: Caught {0} as terminator", block.Code)); } block.Length = _stream.ReadByte(); block.Length |= _stream.ReadByte() << 8; block.Length |= _stream.ReadByte() << 16; // Premature end of stream => error! if (IsAtEndOfStream) { throw new InvalidOperationException("VocStream.PreProcess: Reading failed"); } int skip = 0; switch (block.Code) { // Sound data case 1: // Sound data (New format) case 9: if (block.Code == 1) { if (block.Length < 2) { throw new InvalidOperationException(string.Format("Invalid sound data block length {0} in VOC file", block.Length)); } // Read header data int freqDiv = _stream.ReadByte(); // Prevent division through 0 if (freqDiv == 256) { throw new InvalidOperationException("Invalid frequency divisor 256 in VOC file"); } block.Rate = GetSampleRateFromVOCRate(freqDiv); int codec = _stream.ReadByte(); // We only support 8bit PCM if (codec != 0) { throw new NotSupportedException(string.Format("Unhandled codec {0} in VOC file", codec)); } block.Samples = skip = block.Length - 2; block.Offset = (int)_stream.Position; // Check the last block if there is any if (_blocks.Count > 0) { int lastBlock = _blocks.Count; --lastBlock; // When we have found a block 8 as predecessor // we need to use its settings if (_blocks[lastBlock].Code == 8) { block.Rate = _blocks[lastBlock].Rate; // Remove the block since we don't need it anymore _blocks.RemoveAt(lastBlock); } } } else { if (block.Length < 12) { throw new InvalidOperationException(string.Format("Invalid sound data (wew format) block length {0} in VOC file", block.Length)); } block.Rate = _br.ReadInt32(); int bitsPerSample = _stream.ReadByte(); // We only support 8bit PCM if (bitsPerSample != 8) { throw new NotSupportedException(string.Format("Unhandled bits per sample {0} in VOC file", bitsPerSample)); } int channels = _stream.ReadByte(); // We only support mono if (channels != 1) { throw new NotSupportedException(string.Format("Unhandled channel count {0} in VOC file", channels)); } int codec = _br.ReadInt16(); // We only support 8bit PCM if (codec != 0) { throw new NotSupportedException(string.Format("Unhandled codec {0} in VOC file", codec)); } /*uint32 reserved = */ _br.ReadInt32(); block.Offset = (int)_stream.Position; block.Samples = skip = block.Length - 12; } // Check whether we found a new highest rate if (_rate < block.Rate) _rate = block.Rate; break; // Silence case 3: { if (block.Length != 3) { throw new InvalidOperationException(string.Format("Invalid silence block length {0} in VOC file", block.Length)); } block.Offset = 0; block.Samples = _br.ReadInt16() + 1; int freqDiv = _stream.ReadByte(); // Prevent division through 0 if (freqDiv == 256) { throw new InvalidOperationException("Invalid frequency divisor 256 in VOC file"); } block.Rate = GetSampleRateFromVOCRate(freqDiv); } break; // Repeat start case 6: if (block.Length != 2) { throw new InvalidOperationException(string.Format("Invalid repeat start block length {0} in VOC file", block.Length)); } block.Count = _br.ReadInt16() + 1; break; // Repeat end case 7: break; // Extra info case 8: { if (block.Length != 4) return; int freqDiv = _br.ReadInt16(); // Prevent division through 0 if (freqDiv == 65536) { throw new InvalidOperationException("Invalid frequency divisor 65536 in VOC file"); } int codec = _stream.ReadByte(); // We only support RAW 8bit PCM. if (codec != 0) { throw new NotSupportedException(string.Format("Unhandled codec {0} in VOC file", codec)); } int channels = _stream.ReadByte() + 1; // We only support mono sound right now if (channels != 1) { throw new NotSupportedException(string.Format("Unhandled channel count {0} in VOC file", channels)); } block.Offset = 0; block.Samples = 0; block.Rate = (int)(256000000L / (65536L - freqDiv)); } break; default: // Console.Error.WriteLine("Unhandled code {0} in VOC file (len {1})", block.Code, block.Length); // Skip the whole block and try to use the next one. skip = block.Length; break; } // Premature end of stream => error! if (IsAtEndOfStream) { throw new InvalidOperationException("VocStream::preProcess: Reading failed"); } // Skip the rest of the block if (skip != 0) _stream.Seek(skip, SeekOrigin.Current); _blocks.Add(block); } // Since we determined the sample rate we need for playback now, we will // initialize the play length. _length = new Timestamp(0, _rate); // Calculate the total play time and do some more sanity checks foreach (var i in _blocks) { // Check whether we found a block 8 which survived, this is not // allowed to happen! if (i.Code == 8) { throw new InvalidOperationException("VOC file contains unused block 8"); } // For now only use blocks with actual samples if (i.Code != 1 && i.Code != 9) continue; // Check the sample rate if (i.Rate != _rate) { throw new InvalidOperationException(string.Format("VOC file contains chunks with different sample rates ({0} != {1})", _rate, i.Rate)); } _length = _length.AddFrames(i.Samples); } // Set the current block to the first block in the stream Rewind(); }