Пример #1
0
        public Ps2SqTimingStruct getTimeInSecondsForSequenceNumber(Stream pStream, int pSequenceNumber)
        {
            Ps2SqTimingStruct ret = new Ps2SqTimingStruct();

            if (pSequenceNumber <= this.midiChunk.maxSeqCount &&
                this.midiChunk.subSeqOffsetAddr[pSequenceNumber] != EMPTY_MIDI_OFFSET)
            {
                uint  tempo           = this.getTempoForSequenceNumber(pStream, pSequenceNumber);
                long  eofOffset       = this.getEndOfTrackForSequenceNumber(pStream, pSequenceNumber);
                Int32 midiBlockOffset = MIDI_CHUNK_OFFSET + this.midiChunk.subSeqOffsetAddr[pSequenceNumber];

                MidiDataChunkStruct dataChunkHeader = new MidiDataChunkStruct();
                dataChunkHeader.sequenceOffsetRelativeToChunk = BitConverter.ToUInt32(ParseFile.ParseSimpleOffset(pStream, (long)midiBlockOffset, 4), 0);
                dataChunkHeader.resolution = BitConverter.ToUInt16(ParseFile.ParseSimpleOffset(pStream, (long)(midiBlockOffset + 4), 2), 0);

                if (dataChunkHeader.sequenceOffsetRelativeToChunk == 6) // uncompressed
                {
                    ret = getTimeInSecondsForChunk(pStream, (long)(midiBlockOffset + dataChunkHeader.sequenceOffsetRelativeToChunk),
                                                   eofOffset, tempo, dataChunkHeader.resolution);
                }
                else
                {
                    throw new Exception("Compressed Sequences Not Supported.");
                }
            }

            return(ret);
        }
Пример #2
0
        private Ps2SqTimingStruct getTimeInSecondsForChunk(Stream pStream, long pStartOffset, long pEndOffset, uint pTempo,
                                                           ushort pResolution)
        {
            long incomingStreamPosition = pStream.Position;

            int  currentByte;
            long currentOffset   = 0;
            long iterationOffset = 0;
            long commandOffset   = 0;
            int  dataByte1;
            int  dataByte2;

            int  runningCommand = 0;
            int  dataByteCount;
            uint tempo = 0;

            int metaCommandByte;
            int metaCommandLengthByte;

            bool running          = false;
            bool emptyTimeFollows = false;

            bool   loopFound    = false;
            bool   loopEndFound = false;
            double loopTime;

            int            loopTimeMultiplier;
            Stack <double> loopTimeStack = new Stack <double>();
            ulong          loopTicks;
            Stack <ulong>  loopTickStack = new Stack <ulong>();

            int loopsOpened = 0;
            int loopsClosed = 0;

            UInt64 currentTicks;
            UInt64 totalTicks = 0;

            double currentTime;
            double totalTime            = 0;
            double timeSinceLastLoopEnd = 0; // used for extra Loop End tag hack

            byte[]            tempoValBytes;
            Ps2SqTimingStruct ret = new Ps2SqTimingStruct();

            ret.Warnings = String.Empty;

            pStream.Position = pStartOffset;

            while (pStream.Position < pEndOffset)
            {
                iterationOffset = pStream.Position;

                // get time
                if (!emptyTimeFollows)
                {
                    currentByte   = pStream.ReadByte();
                    currentOffset = pStream.Position - 1;

                    // build 7-bit num from bytes (variable length string)
                    if ((currentByte & 0x80) != 0)
                    {
                        currentTicks = (ulong)(currentByte & 0x7F);

                        do
                        {
                            currentByte   = pStream.ReadByte();
                            currentOffset = pStream.Position - 1;

                            currentTicks = (currentTicks << 7) + (ulong)(currentByte & 0x7F);
                        } while ((currentByte & 0x80) != 0);
                    }
                    else // only one byte, no need for conversion
                    {
                        currentTicks = (ulong)currentByte;
                    }

                    if ((tempo == 0) && (currentTicks != 0))
                    {
                        throw new Exception("Tempo not set and current ticks not equal zero");
                    }

                    currentTime = currentTicks != 0? (double)((currentTicks * (ulong)tempo) / (ulong)pResolution) : 0;

                    //if (currentTime > 10000000) // debug code used to find strange values
                    //{
                    //    int x2 = 1;
                    //}

                    if (loopTimeStack.Count > 0)
                    {
                        loopTime  = loopTimeStack.Pop();
                        loopTime += currentTime;
                        loopTimeStack.Push(loopTime);

                        loopTicks  = loopTickStack.Pop();
                        loopTicks += currentTicks;
                        loopTickStack.Push(loopTicks);
                    }
                    else
                    {
                        if (pStream.Position != pEndOffset) // time at the end of the file is ignored
                        {
                            totalTime  += currentTime;
                            totalTicks += (ulong)currentTicks;

                            timeSinceLastLoopEnd += currentTime;
                        }
                        else
                        {
                            goto DONE;
                        }
                    }
                }

                // get command
                currentByte   = pStream.ReadByte();
                currentOffset = pStream.Position - 1;

                //if (currentOffset > 0x19B5) // code to quickly get to a position for debugging
                //{
                //    int x = 1;
                //}


                //if (currentByte != 0xFF) // process normal commands
                //{
                if ((currentByte & 0x80) == 0)     // data byte, we should be running
                {
                    if (runningCommand == 0)
                    {
                        throw new Exception(String.Format("Empty running command at 0x{0}", currentOffset.ToString("X2")));
                    }
                    else
                    {
                        running = true;
                    }
                }
                else     // new command
                {
                    runningCommand = currentByte;
                    running        = false;

                    commandOffset = currentOffset;
                }

                dataByteCount = this.dataBytesPerCommand[runningCommand & 0xF0];

                if (dataByteCount == 0)     // 0xFF
                {
                    // get meta command bytes
                    if (!running)
                    {
                        metaCommandByte = pStream.ReadByte();
                        currentOffset   = pStream.Position - 1;
                    }
                    else
                    {
                        metaCommandByte = currentByte;
                    }

                    // get length bytes
                    metaCommandLengthByte = pStream.ReadByte();

                    // check for tempo switch here
                    if (metaCommandByte == 0x51)
                    {
                        // tempo switch
                        tempoValBytes = new byte[4];
                        Array.Copy(ParseFile.ParseSimpleOffset(pStream, pStream.Position, metaCommandLengthByte), 0, tempoValBytes, 1, 3);

                        Array.Reverse(tempoValBytes);     // flip order to LE for use with BitConverter
                        tempo = BitConverter.ToUInt32(tempoValBytes, 0);
                    }

                    // skip data bytes, they have already been grabbed above if needed
                    pStream.Position += (long)metaCommandLengthByte;
                    currentOffset     = pStream.Position;

                    // time should follow, since these data bytes can be over 0x80 anyhow
                    emptyTimeFollows = false;
                }
                else
                {
                    if (running)
                    {
                        dataByte1 = currentByte;
                    }
                    else
                    {
                        dataByte1     = pStream.ReadByte();
                        currentOffset = pStream.Position - 1;
                    }

                    if (dataByteCount == 2)
                    {
                        dataByte2     = pStream.ReadByte();
                        currentOffset = pStream.Position - 1;

                        // if high bit of last data byte is 1, next delta tick is zero and byte will be skipped
                        if ((dataByte2 & 0x80) != 0)
                        {
                            emptyTimeFollows = true;
                        }
                        else
                        {
                            emptyTimeFollows = false;
                        }

                        // check for loop start
                        if ((((currentByte & 0xF0) == 0xB0) || ((runningCommand & 0xF0) == 0xB0)) &&
                            dataByte1 == 0x63 &&
                            (dataByte2 == 0x00 || dataByte2 == 0x80))
                        {
                            loopTimeStack.Push(0);
                            loopTickStack.Push(0);
                            loopsOpened++;
                        }

                        // check for loop end
                        if ((((currentByte & 0xF0) == 0xB0) || ((runningCommand & 0xF0) == 0xB0)) &&
                            dataByte1 == 0x63 &&
                            (dataByte2 == 0x01 || dataByte2 == 0x81))
                        {
                            loopEndFound = true;
                            loopsClosed++;
                        }

                        // check for loop count
                        if (loopEndFound &&
                            (((currentByte & 0xF0) == 0xB0) || ((runningCommand & 0xF0) == 0xB0)) &&
                            dataByte1 == 0x26)
                        {
                            if (loopTimeStack.Count > 0)     // check for unmatched close tag
                            {
                                loopTimeMultiplier = dataByte2;

                                // filter out high bytes, they are just indicator if next delta tick is zero
                                loopTimeMultiplier = loopTimeMultiplier & 0x0F;

                                if (loopTimeMultiplier == 0)
                                {
                                    loopTimeMultiplier = 2;
                                    loopFound          = true;
                                }

                                // add loop time
                                loopTime   = loopTimeStack.Pop();
                                loopTime   = (loopTime * loopTimeMultiplier);
                                totalTime += loopTime;

                                loopTicks   = loopTickStack.Pop();
                                loopTicks   = (loopTicks * (ulong)loopTimeMultiplier);
                                totalTicks += loopTicks;

                                timeSinceLastLoopEnd = 0;
                            }
                            else
                            {
                                ret.Warnings += "Unmatched Loop End tag(s) found." + Environment.NewLine;
                            }

                            loopEndFound = false;
                        }
                    }
                    else
                    {
                        // if high bit of last data byte is 1, next delta tick is zero and byte will be skipped
                        if ((dataByte1 & 0x80) != 0)
                        {
                            emptyTimeFollows = true;
                        }
                        else
                        {
                            emptyTimeFollows = false;
                        }
                    }

                    currentOffset = pStream.Position - 1;
                }
                //}
                //else // meta event (only tempo is relevant for timing)
                //{
                //    // get meta command bytes
                //    metaCommandByte = pStream.ReadByte();
                //    currentOffset = pStream.Position - 1;

                //    // get length bytes
                //    metaCommandLengthByte = pStream.ReadByte();

                //    // check for tempo switch here
                //    if (metaCommandByte == 0x51)
                //    {
                //        // tempo switch
                //        tempoValBytes = new byte[4];
                //        Array.Copy(ParseFile.parseSimpleOffset(pStream, pStream.Position, metaCommandLengthByte), 0, tempoValBytes, 1, 3);

                //        Array.Reverse(tempoValBytes); // flip order to LE for use with BitConverter
                //        tempo = BitConverter.ToUInt32(tempoValBytes, 0);
                //    }

                //    // skip data bytes, they have already been grabbed above if needed
                //    pStream.Position += (long)metaCommandLengthByte;
                //    currentOffset = pStream.Position;

                //    // time should follow, since these data bytes can be over 0x80 anyhow
                //    emptyTimeFollows = false;
                //}
            } // while (pStream.Position < pEndOffset)


DONE:       // Marker used for skipping delta ticks at the end of a file.

            // Not sure how to handle, but for now count each unclosed loop twice,
            //   since it should be the outermost loop.
            if (loopTimeStack.Count > 0)
            {
                ret.Warnings += "Unmatched Loop Start tag(s) found." + Environment.NewLine;

                while (loopTimeStack.Count > 0)
                {
                    totalTime += loopTimeStack.Pop() * 2d;
                    loopFound  = true;
                }
            }

            if (loopsClosed > loopsOpened)
            {
                totalTime -= timeSinceLastLoopEnd;
            }

            ret.TimeInSeconds = ((totalTime) * Math.Pow(10, -6));

            if (loopFound)
            {
                ret.FadeInSeconds = 10; // looping
            }
            else
            {
                ret.FadeInSeconds = 1;  // non-looping
            }

            // return stream to incoming position
            pStream.Position = incomingStreamPosition;
            return(ret);
        }