/** * Parses the given track header * * @since 1.0 * @uses TrackHeader::LENGTH * @uses Util::unpack() * * @param binary $header * @throws InvalidArgumentException * @throws {@link ParseException} if the header is invalid * @return TrackHeader */ public function parseTrackHeader($header) { if (strlen($header) !== TrackHeader::LENGTH) { throw new InvalidArgumentException('Track header must be ' . TrackHeader::LENGTH . ' bytes'); } $id = Util::unpack(substr($header, 0, 4)); $size = array_reverse(Util::unpack(substr($header, 4))); if ($id !== array(0x4d, 0x54, 0x72, 0x6b)) { throw new ParseException('Invalid track header, expected [4D 54 72 6B]'); } $shift = 0; $trackSize = 0; foreach ($size as $byte) { $trackSize |= $byte << $shift; $shift += 8; } return new TrackHeader($trackSize); }
/** * Parses the given file header * * @since 1.0 * @uses FileHeader::LENGTH * @uses Util::unpack() * * @param binary $header * @throws InvalidArgumentException * @throws {@link ParseException} if the file header is not a valid MIDI file header * @return FileHeader */ public function parseFileHeader($header) { if (strlen($header) !== FileHeader::LENGTH) { throw new \InvalidArgumentException('MIDI file header must be ' . FileHeader::LENGTH . ' bytes'); } $id = Util::unpack(substr($header, 0, 4)); $chunkSize = Util::unpack(substr($header, 4, 4)); $format = Util::unpack(substr($header, 8, 2)); $tracks = Util::unpack(substr($header, 10, 2)); $timeDivision = Util::unpack(substr($header, 12, 2)); if ($id !== array(0x4d, 0x54, 0x68, 0x64)) { throw new ParseException('Invalid file header, expected byte sequence [4D 54 68 64]'); } if ($chunkSize !== array(0x0, 0x0, 0x0, 0x6)) { throw new ParseException('File header chunk size must be [00 00 00 06]'); } $format = $format[0] << 8 | $format[1]; $timeDivision = $timeDivision[0] << 8 | $timeDivision[1]; $tracks = $tracks[0] << 8 | $tracks[1]; if ($format !== 0 && $format !== 1 && $format !== 2) { throw new ParseException('MIDI file format must be 0, 1 or 2 (got ' . $format . ')'); } return new FileHeader($format, $tracks, $timeDivision); }
/** * Creates a meta event, or returns an {@link UnknownMetaEvent} if the specified * event type is not supported * * @since 1.1 * @uses Util::unpack() * * @param int $eventType See {@link MetaEventType} * @param binary $data Binary data associated with the event * @return MetaEvent */ public function createMetaEvent($eventType, $data) { switch ($eventType) { case MetaEventType::SEQUENCE_NUMBER: $data = Util::unpack($data); return new SequenceNumberEvent($data[0], $data[1]); case MetaEventType::TEXT_EVENT: return new TextEvent($data); case MetaEventType::COPYRIGHT_NOTICE: return new CopyrightNoticeEvent($data); case MetaEventType::TRACK_NAME: return new TrackNameEvent($data); case MetaEventType::INSTRUMENT_NAME: return new InstrumentNameEvent($data); case MetaEventType::LYRICS: return new LyricsEvent($data); case MetaEventType::MARKER: return new MarkerEvent($data); case MetaEventType::CUE_POINT: return new CuePointEvent($data); case MetaEventType::END_OF_TRACK: return new EndOfTrackEvent(); case MetaEventType::CHANNEL_PREFIX: $data = Util::unpack($data); return new ChannelPrefixEvent($data[0]); case MetaEventType::SET_TEMPO: $data = Util::unpack($data); $mpqn = $data[0] << 16 | $data[1] << 8 | $data[2]; return new SetTempoEvent($mpqn); case MetaEventType::SMPTE_OFFSET: $data = Util::unpack($data); $frameRate = $data[0] >> 5 & 0xff; $hour = $data[0] & 0x1f; list(, $minute, $second, $frame, $subFrame) = $data; return new SmpteOffsetEvent($frameRate, $hour, $minute, $second, $frame, $subFrame); case MetaEventType::TIME_SIGNATURE: $data = Util::unpack($data); return new TimeSignatureEvent($data[0], pow(2, $data[1]), $data[2], $data[3]); case MetaEventType::KEY_SIGNATURE: $data = Util::unpack($data); return new KeySignatureEvent($data[0], $data[1]); case MetaEventType::SEQUENCER_SPECIFIC: return new SequencerSpecificEvent($data); default: return new UnknownMetaEvent($data); } }
/** * Parses the buffer stream for a meta event * * @since 1.0 * @uses read() * @uses Util::unpack() * @uses getDelta() * @uses EventFactory::createMetaEvent() * * @return MetaEvent */ protected function parseMetaEvent() { $metaEventType = Util::unpack($this->read(1, true)); $metaEventType = $metaEventType[0]; $length = $this->getDelta(); $data = $this->read($length, true); return $this->eventFactory->createMetaEvent($metaEventType, $data); }
/** * Reads a delta time from the buffer stream * * @since 1.0 * @uses read() * @uses Util::unpack() * @uses Util::getTicksFromDeltaByteSequence() * * @return int The number of clock ticks in the delta time */ protected function getDelta() { $byte = $this->read(1, true); $value = Util::unpack($byte); $delta = ''; while ($this->file->valid() && $value[0] > 0x7f) { $delta .= $byte; $byte = $this->read(1); $value = Util::unpack($byte); } if ($byte !== null) { $delta .= $byte; } return Util::getTicksFromDeltaByteSequence($delta); }