/** * 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); }
/** * 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); }
/** * Gets the binary representation of this meta event * * @since 1.0 * @uses Util::pack() * @uses Util::getDeltaByteSequence() * @uses getSubtype() * * @return binary */ public function toBinary() { if (is_array($this->data)) { $data = ''; foreach ($this->data as $datum) { $data .= Util::pack($datum); } } else { $data = $this->data; } return Util::pack($this->getType(), $this->getSubtype()) . Util::getDeltaByteSequence($this->length) . $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); }
/** * Gets whether this is a normal system exclusive event * * Normal sysex events are signified by the last byte of data * being 0xF7. * * @since 1.0 * @uses Util::pack() * @see isDivided() * * @return bool */ public function isNormal() { $byte = end($this->data); reset($this->data); return $byte === Util::pack(0xf7); }
/** * Gets a binary representation of this event * * @since 1.0 * @uses Util::pack() * * @return binary */ public function toBinary() { $binary = $this->isContinuation() ? '' : Util::pack($this->getType() | $this->channel); $binary .= Util::pack($this->param1, $this->param2); return $binary; }
/** * @since 1.0 * @uses getChunkClass() * @uses formatOffset() * @uses Delta::toBinary() * @uses Util::binaryToHex() * @uses Event::toBinary() * @uses Event::getLength() * @uses Delta::getLength() * * @param Event $event * @return string */ public function beforeEvent(Event $event) { if ($this->delta === null) { return ''; } $class = $this->getChunkClass($event); $text = "<tr class=\"{$class}\">" . $this->formatOffset($this->offset); // -- all of this goofy looking math just formats everything prettily -- // $deltaHex = Util::binaryToHex($this->delta->toBinary()); $delta = '<span class="delta">' . wordwrap(implode(' ', $deltaHex), 23, '<br />') . '</span>'; $eventHex = Util::binaryToHex($event->toBinary()); $lineLength = 23 - strlen(implode(' ', $deltaHex)) % 23; $eventSegment = wordwrap(implode(' ', $eventHex), $lineLength, '|'); $bar = strpos($eventSegment, '|'); if ($bar !== false) { $eventSegment = substr($eventSegment, 0, $bar) . '<br />' . wordwrap(substr($eventSegment, $bar + 1), 23, '<br />'); } // -- end goofiness -- // $text .= '<td><tt>' . $delta . ' ' . $eventSegment . '</tt></td>'; $this->offset += $event->getLength() + $this->delta->getLength(); return $text; }
/** * 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); } }
public function testPackWithNullValues() { $this->assertSame(pack('C2', 0x30, 0x20), Util::pack(0x30, 0x20, null)); }
/** * 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); }
/** * @since 1.0 * @uses Util::getDeltaByteSequence() * * @return binary */ public function toBinary() { return Util::getDeltaByteSequence($this->ticks); }
/** * @since 1.0 * @uses Util::pack() * * @return binary */ public function toBinary() { return Util::pack(0x4d, 0x54, 0x68, 0x64) . Util::pack(0x0, 0x0, 0x0, 0x6) . Util::pack(0x0, $this->midiFormat) . Util::pack($this->numTracks >> 8, $this->numTracks & 0xff) . Util::pack($this->timeDivision >> 8, $this->timeDivision & 0xff); }
public function testToBinary() { $binary = Util::pack(EventType::SYSTEM_EXCLUSIVE) . Util::getDeltaByteSequence(255) . str_repeat('x', 255); $this->assertEquals($binary, $this->obj->toBinary()); }