/// <summary> /// VorbisFileWriter that actually writes to a stream /// </summary> /// <param name="outStream">Stream to be written to</param> /// <param name="sampleRate">The sample rate to use</param> /// <param name="channels">The number of channels to use</param> /// <param name="quality">The base quality for Vorbis encoding</param> public VorbisFileWriter(Stream outStream, int sampleRate, int channels, float quality = 0.5f) { this.outStream = outStream; SampleRate = sampleRate; Channels = channels; if (!startBuffers.ContainsKey(sampleRate)) { throw new InvalidOperationException($"Vorbis writer does not support {sampleRate} sample rate."); } // Stores all the static vorbis bitstream settings Console.WriteLine($"Initiating variable bit rate: {channels} channels, {sampleRate} sample rate, {quality} quality"); var info = VorbisInfo.InitVariableBitRate(channels, sampleRate, quality); // set up our packet->stream encoder var serial = MainWindow.MainRandom.Next(); oggStream = new OggStream(serial); // ========================================================= // HEADER // ========================================================= // Vorbis streams begin with three headers; the initial header (with // most of the codec setup parameters) which is mandated by the Ogg // bitstream spec. The second header holds any comment fields. The // third header holds the bitstream codebook. var headerBuilder = new HeaderPacketBuilder(); var comments = new Comments(); var infoPacket = headerBuilder.BuildInfoPacket(info); var commentsPacket = headerBuilder.BuildCommentsPacket(comments); var booksPacket = headerBuilder.BuildBooksPacket(info); oggStream.PacketIn(infoPacket); oggStream.PacketIn(commentsPacket); oggStream.PacketIn(booksPacket); // Flush to force audio data onto its own page per the spec FlushPages(oggStream, outStream, true); // ========================================================= // BODY (Audio Data) // ========================================================= processingState = ProcessingState.Create(info); // Append some zeros at the start so the result has the same length as the input int bufferSize = startBuffers[sampleRate]; float[][] outSamples = new float[channels][]; for (int ch = 0; ch < channels; ch++) { outSamples[ch] = new float[bufferSize]; } processingState.WriteData(outSamples, bufferSize); }
private static void ProcessChunk(float[][] floatSamples, ProcessingState processingState, OggStream oggStream, int writeBufferSize) { processingState.WriteData(floatSamples, writeBufferSize, 0); while (!oggStream.Finished && processingState.PacketOut(out OggPacket packet)) { oggStream.PacketIn(packet); } }
/// <summary> /// Encodes and writes a number of float samples /// </summary> /// <param name="floatSamples">The samples. The array shape is [channel][sample]</param> /// <param name="count">The number of samples to write.</param> public void WriteFloatSamples(float[][] floatSamples, int count) { //Console.WriteLine($"Writing {count} samples!"); processingState.WriteData(floatSamples, count); while (!oggStream.Finished && processingState.PacketOut(out var packet)) { //Console.WriteLine($"Got packet with {packet.PacketData.Length} bytes of data"); oggStream.PacketIn(packet); FlushPages(oggStream, outStream, false); } }