private OggPage GetOggPage(ref int offset) { OggPageHeader hdr = new OggPageHeader(); OggPage page = new OggPage(); int pageSize = 0; ByteArrayToStructure(Audio, offset, ref hdr); if (hdr.Header[0] != 'O' || hdr.Header[1] != 'g' || hdr.Header[2] != 'g' || hdr.Header[3] != 'S') { return(null); } page.Header = hdr; pageSize += Marshal.SizeOf(hdr); page.Segments = new byte[0][]; /* where will the segment data start */ int segmentDataPos = pageSize + hdr.PageSegments; /* position in page segment table */ int pageSegTablePos = 0; /* logical number of the segment */ int pageSegNum = 0; while (pageSegTablePos < hdr.PageSegments) { int lenEntry = Audio[offset + pageSize]; int len = lenEntry; pageSize++; pageSegTablePos++; while (lenEntry == 0xFF) { lenEntry = Audio[offset + pageSize]; len += lenEntry; pageSize++; pageSegTablePos++; } Array.Resize(ref page.Segments, pageSegNum + 1); page.Segments[pageSegNum] = new byte[len]; Array.Copy(Audio, offset + segmentDataPos, page.Segments[pageSegNum], 0, len); segmentDataPos += page.Segments[pageSegNum].Length; pageSegNum++; } page.Size = segmentDataPos; offset += segmentDataPos; return(page); }
private void _LoadOggPages(string _path) { FileStream stream = new FileStream(_path, FileMode.Open); long pos = 0; long end = stream.Length; Bunch <OggPage> ps = new Bunch <OggPage>(); while (pos < end) { byte[] header = new byte[27]; stream.Read(header, 0, 27); OggPage p = new OggPage(); p.Granule = Beth.FromEndian(header.SubArray(6, 8)); p.StreamId = (int)Beth.FromEndian(header.SubArray(14, 4)); p.StreamPosition = (int)Beth.FromEndian(header.SubArray(18, 4)); int ss = header[26]; p.Segments = new byte[ss]; pos += 27; int si = 0; while (ss > 0) { ss--; p.Segments[si] = ((byte)stream.ReadByte()); si++; pos++; } p.SegmentsPositionInFile = pos; foreach (byte s in p.Segments) { pos += s; stream.Position += s; } ps.Add(p); } stream.Close(); byte[][] bs1 = ps[0].ReadSegments(_path); byte[][] bs2 = ps[1].ReadSegments(_path); throw new Exception(); }
public void Encode(OggStreamState oggStreamState, VorbisBlock block, Stream stream) { OggPacket packet = new OggPacket(); OggPage page = new OggPage(); /* vorbis does some data preanalysis, then divvies up blocks for * more involved (potentially parallel) processing. Get a single * block for encoding now */ while (BlockOut(block)) { /* analysis, assume we want to use bitrate management */ block.Analyze(); block.BitrateAddBlock(); while (BitrateFlushPacket(packet) != 0) { /* weld the packet into the bitstream */ NativeMethods.ogg_stream_packetin(ref oggStreamState.InternalStruct, ref packet.InternalStruct); /* write out pages (if any) */ bool eos = false; while (!eos) { int result = NativeMethods.ogg_stream_pageout(ref oggStreamState.InternalStruct, ref og); if (result == 0) { break; } page.Write(stream); /* this could be set above, but for illustrative purposes, I do * it here (to show that vorbis does know where the stream ends) */ if (xm.ogg_page_eos(ref page.InternalStruct) != 0) { eos = true; } } } } }
private static bool IsMetaType(OggPage header, string type) { return(Encoding.UTF8.GetString(header.Segments[0], 0, 8) == type); }
public void DumpAudioFiles(string outDirectory, string outFileName, bool singleOgg, string[] tags, string[] titles) { int hdrOffset = 0; OggPage[] metaPages = GetOggHeaders(ref hdrOffset); AddTags(metaPages, tags); if (singleOgg) { string outFile = Path.Combine(outDirectory, outFileName); File.WriteAllBytes(outFile + ".ogg", Audio); //File.WriteAllText(outFile + ".cue", BuildCueSheet(tonie), Encoding.UTF8); } else { for (int chapter = 0; chapter < Header.AudioChapters.Length; chapter++) { string fileName = Path.Combine(outDirectory, outFileName + " - Track #" + (chapter + 1) + ".ogg"); FileStream outFile = File.Open(fileName, FileMode.Create, FileAccess.Write); OggPage[] metaPagesTrack = metaPages.Select(p => new OggPage(p)).ToArray(); if (titles != null && chapter < titles.Length) { string[] trackTags = new[] { "TITLE=" + titles[chapter] }; AddTags(metaPagesTrack, trackTags); } foreach (OggPage page in metaPagesTrack) { page.Write(outFile); } int offset = Math.Max(0, (int)(0x1000 * (Header.AudioChapters[chapter] - 2))); int endOffset = int.MaxValue; if (chapter + 1 < Header.AudioChapters.Length) { endOffset = Math.Max(0, (int)(0x1000 * (Header.AudioChapters[chapter + 1] - 2))); } bool done = false; ulong granuleStart = ulong.MaxValue; uint pageStart = uint.MaxValue; while (!done) { OggPage page = GetOggPage(ref offset); if (page == null) { break; } /* reached the end of this chapter? */ if (offset >= endOffset || offset >= Audio.Length) { /* set EOS flag */ page.Header.Type = 4; done = true; } /* do not write meta headers again. only applies to first chapter */ if (!IsMeta(page)) { /* set granule position relative to chapter start */ if (granuleStart == ulong.MaxValue) { granuleStart = page.Header.GranulePosition; pageStart = page.Header.PageSequenceNumber; } page.Header.GranulePosition -= granuleStart; page.Header.PageSequenceNumber -= pageStart; page.Header.PageSequenceNumber += 2; page.Write(outFile); } } outFile.Close(); } } }
public static void WriteFile(Unity_Studio.EndianStream stream, string file, int offset, int size, Ogg ogg) { // Write to disk using (BinaryWriter writer = new BinaryWriter(File.Open(file, FileMode.Create))) { // Only support header CRC 3605052372 for now OggVorbisHeader head = new OggVorbisHeader(); HeaderPacketBuilder hpb = new HeaderPacketBuilder(); CodecSetup cSetup = new CodecSetup(null); cSetup.BlockSizes[0] = 256; cSetup.BlockSizes[1] = 2048; VorbisInfo info = new VorbisInfo(cSetup, (int)ogg.channels, (int)ogg.frequency, 0); OggPacket headerInfo = hpb.BuildInfoPacket(info); Comments comments = new Comments(); if (ogg.loopStart > 0 && ogg.loopEnd > 0) { comments.AddTag("LOOP_START", ogg.loopStart.ToString()); comments.AddTag("LOOP_END", ogg.loopEnd.ToString()); } OggPacket headerComment = hpb.BuildCommentsPacket(comments); OggPacket headerSetup = new OggPacket(OggVorbisHeader.GetHeader(ogg.crc32), false, 0, 2); OggStream output = new OggStream(1); output.PacketIn(headerInfo); output.PacketIn(headerComment); output.PacketIn(headerSetup); stream.Position = offset; UInt16 packetSize = stream.ReadUInt16(); int prevPacketNo = 2; int prevGranulePos = 0; while (packetSize > 0) { OggPacket packet = new OggPacket(stream.ReadBytes(packetSize), false, 0, prevPacketNo + 1); byte firstByte = packet.PacketData[0]; // OK for stereo int granuleSize = 128; if ((firstByte & 2) != 0) { granuleSize = 1024; } if (ogg.channels == 1) { granuleSize /= 4; } packet.GranulePosition = prevGranulePos + granuleSize; if (stream.Position + 2 < offset + size) { packetSize = stream.ReadUInt16(); } else { packetSize = 0; } packet.EndOfStream = packetSize == 0; prevGranulePos = packet.GranulePosition; prevPacketNo = packet.PacketNumber; output.PacketIn(packet); OggPage page = null; if (output.PageOut(out page, true)) { writer.Write(page.Header); writer.Write(page.Body); } } //float vorbis_quality = ((ogg.quality - 1) + (ogg.quality - 100) * 0.1f) / 99.0f; //VorbisInfo.InitVariableBitRate(ogg.channels, ogg.frequency, ogg.) //writer.Write(); writer.Close(); } }
/// <summary> /// </summary> /// <param name="page">The output data (if any)</param> /// <param name="force">A value indicating whether an unfinished page should be pushed out</param> /// <returns>A value indicating whether data was output</returns> public bool PageOut(out OggPage page, bool force) { const int bufferSize = 4096; var acc = 0; long granulePosition = -1; page = null; var maxValues = _lacingFill > 255 ? 255 : _lacingFill; if (maxValues == 0) { return(false); } int vals; // construct a page - decide how many segments to include // If this is the initial header case, the first page must only include the initial header packet if (!_writesHaveStarted) { // 'initial header page' case granulePosition = 0; for (vals = 0; vals < maxValues; vals++) { if ((_lacingValues[vals] & 0x0ff) < 255) { vals++; break; } } } else { // The extra packets_done, packet_just_done logic here attempts to do two things: // 1) Don't unnecessarily span pages. // 2) Unless necessary, don't flush pages if there are less than four packets on // them; this expands page size to reduce unnecessarily overhead if incoming packets // are large. // These are not necessary behaviors, just 'always better than naive flushing' // without requiring an application to explicitly request a specific optimized // behavior. We'll want an explicit behavior setup pathway eventually as well. var packetsDone = 0; var packetsJustDone = 0; for (vals = 0; vals < maxValues; vals++) { if ((acc > bufferSize) && (packetsJustDone >= 4)) { force = true; break; } acc += _lacingValues[vals] & 0x0ff; if ((_lacingValues[vals] & 0xff) < 255) { granulePosition = _granuleValues[vals]; packetsJustDone = ++packetsDone; } else { packetsJustDone = 0; } } if (vals == 255) { force = true; } } if (!force) { return(false); } var packetHeader = new byte[vals + 27]; // construct the header in temp storage const string headerOggs = "OggS"; for (var i = 0; i < headerOggs.Length; ++i) { packetHeader[i] = (byte)headerOggs[i]; } // stream structure version packetHeader[4] = 0x00; // continued packet flag? packetHeader[5] = 0x00; if ((_lacingValues[0] & 0x100) == 0) { packetHeader[5] |= 0x01; } // first page flag? if (!_writesHaveStarted) { packetHeader[5] |= 0x02; } // last page flag? if (Finished && (_lacingFill == vals)) { packetHeader[5] |= 0x04; } _writesHaveStarted = true; // 64 bits of PCM position for (var i = 6; i < 14; i++) { packetHeader[i] = (byte)(granulePosition & 0xff); granulePosition >>= 8; } // 32 bits of stream serial number var serialno = _serialNumber; for (var i = 14; i < 18; i++) { packetHeader[i] = (byte)(serialno & 0xff); serialno >>= 8; } // 32 bits of page counter (we have both counter and page header // because this val can roll over) if (_pageNumber == -1) { _pageNumber = 0; } var pageno = _pageNumber++; for (var i = 18; i < 22; i++) { packetHeader[i] = (byte)(pageno & 0xff); pageno >>= 8; } // zero for computation; filled in later packetHeader[22] = 0; packetHeader[23] = 0; packetHeader[24] = 0; packetHeader[25] = 0; // segment table var bytes = 0; packetHeader[26] = (byte)(vals & 0xff); for (var i = 0; i < vals; i++) { bytes += packetHeader[i + 27] = (byte)(_lacingValues[i] & 0xff); } var packetBody = new byte[bytes]; Array.Copy(_bodyData, _bodyReturned, packetBody, 0, bytes); uint crcReg = 0; // calculate the checksum foreach (var h in packetHeader) { crcReg = (crcReg << 8) ^ Checksum[(int)(((crcReg >> 24) & 0xff) ^ h)]; } foreach (var b in packetBody) { crcReg = (crcReg << 8) ^ Checksum[(int)(((crcReg >> 24) & 0xff) ^ b)]; } packetHeader[22] = (byte)(crcReg & 0xff); packetHeader[23] = (byte)((crcReg >> 8) & 0xff); packetHeader[24] = (byte)((crcReg >> 16) & 0xff); packetHeader[25] = (byte)((crcReg >> 24) & 0xff); // Create the page page = new OggPage(packetHeader, packetBody); // advance the lacing data and set the body_returned pointer _lacingFill -= vals; Array.Copy(_lacingValues, vals, _lacingValues, 0, _lacingFill); Array.Copy(_granuleValues, vals, _granuleValues, 0, _lacingFill); _bodyReturned += bytes; return(true); }