/// <summary> /// Saving the decoded Packets to our active Buffer, if the Buffer is full queue it into the OutputQueue /// and wait until another buffer gets freed up /// </summary> private void AudioPacketDecoded(object sender, PacketReceivedEventArgs args) { foreach (var p in args.PacketDescriptions) { currentByteCount += p.DataByteSize; AudioStreamPacketDescription pd = p; int left = bufferSize - currentBuffer.CurrentOffset; if (left < pd.DataByteSize) { EnqueueBuffer(); WaitForBuffer(); } AudioQueue.FillAudioData(currentBuffer.Buffer, currentBuffer.CurrentOffset, args.InputData, (int)pd.StartOffset, pd.DataByteSize); // Set new offset for this packet pd.StartOffset = currentBuffer.CurrentOffset; // Add the packet to our Buffer currentBuffer.PacketDescriptions.Add(pd); // Add the Size so that we know how much is in the buffer currentBuffer.CurrentOffset += pd.DataByteSize; } if ((fileStream != null && currentByteCount == fileStream.DataByteCount) || lastPacket) { EnqueueBuffer(); } }
/// <summary> /// Saving the decoded Packets to our active Buffer, if the Buffer is full queue it into the OutputQueue /// and wait until another buffer gets freed up /// </summary> private void AudioPacketDecoded(object sender, PacketReceivedEventArgs args) { if (BitRate == 0) { BitRate = ((AudioFileStream)sender).BitRate; } foreach (var p in args.PacketDescriptions) { _currentByteCount += p.DataByteSize; AudioStreamPacketDescription packetDescription = p; int left = BufferSize - _currentBuffer.CurrentOffset; if (left < packetDescription.DataByteSize) { EnqueueBuffer(); WaitForBuffer(); } AudioQueue.FillAudioData(_currentBuffer.Buffer, _currentBuffer.CurrentOffset, args.InputData, (int)packetDescription.StartOffset, packetDescription.DataByteSize); // Set new offset for this packet packetDescription.StartOffset = _currentBuffer.CurrentOffset; // Add the packet to our Buffer _currentBuffer.PacketDescriptions.Add(packetDescription); // Add the Size so that we know how much is in the buffer _currentBuffer.CurrentOffset += packetDescription.DataByteSize; } if ((_audioFileStream != null && _currentByteCount == _audioFileStream.DataByteCount) || _lastPacket) { _lastPacket = false; Console.WriteLine($"{nameof(AudioPacketDecoded)} This is the lastpacket"); EnqueueBuffer(); } }
public void FillBuffer(AudioBufferList data, uint numberFrames, AudioStreamPacketDescription[] packetDescs) { uint numPackets = numberFrames; int err =AudioConverterFillComplexBuffer( _audioConverter, complexInputDataProc, GCHandle.ToIntPtr(_handle), ref numPackets, data, packetDescs); if(err != 0 || numPackets == 0) { throw new InvalidOperationException(String.Format("Error code:{0}", err)); } }
/// <summary> /// Saving the decoded Packets to our active Buffer, if the Buffer is full queue it into the OutputQueue /// and wait until another buffer gets freed up /// </summary> void AudioPacketDecoded(object sender, PacketReceivedEventArgs args) { foreach (var p in args.PacketDescriptions) { currentByteCount += p.DataByteSize; AudioStreamPacketDescription pd = p; int left = bufferSize - currentBuffer.CurrentOffset; if (left < pd.DataByteSize) { EnqueueBuffer(); WaitForBuffer(); } AudioQueue.FillAudioData(currentBuffer.Buffer, currentBuffer.CurrentOffset, args.InputData, (int)pd.StartOffset, pd.DataByteSize); #if true // Set new offset for this packet pd.StartOffset = currentBuffer.CurrentOffset; // Add the packet to our Buffer currentBuffer.PacketDescriptions.Add(pd); // Add the Size so that we know how much is in the buffer currentBuffer.CurrentOffset += pd.DataByteSize; #else // Fill out the packet description pdesc [packetsFilled] = pd; pdesc [packetsFilled].StartOffset = bytesFilled; bytesFilled += packetSize; packetsFilled++; var t = OutputQueue.CurrentTime; Console.WriteLine("Time: {0}", t); // If we filled out all of our packet descriptions, enqueue the buffer if (pdesc.Length == packetsFilled) { EnqueueBuffer(); WaitForBuffer(); } #endif } if ((fileStream != null && currentByteCount == fileStream.DataByteCount) || lastPacket) { EnqueueBuffer(); } }
static int complexInputDataProc( IntPtr inAudioConverrter, ref uint ioNumberDataPackets, AudioBufferList ioData, ref AudioStreamPacketDescription[] outDataPacketDescription, //AudioStreamPacketDescription** IntPtr inUserData ) { // getting audiounit instance var handler = GCHandle.FromIntPtr(inUserData); var inst = (_AudioConverter)handler.Target; // evoke event handler with an argument if (inst.EncoderCallback != null) { var args = new _AudioConverterEventArgs( ioNumberDataPackets, ioData, outDataPacketDescription); inst.EncoderCallback(inst, args); } return 0; // noerror }
public static bool Convert(string input, string output, AudioFormatType targetFormat, AudioFileType containerType, Microsoft.Xna.Framework.Content.Pipeline.Audio.ConversionQuality quality) { CFUrl source = CFUrl.FromFile(input); CFUrl dest = CFUrl.FromFile(output); var dstFormat = new AudioStreamBasicDescription(); var sourceFile = AudioFile.Open(source, AudioFilePermission.Read); AudioFormatType outputFormat = targetFormat; // get the source data format var srcFormat = (AudioStreamBasicDescription)sourceFile.DataFormat; var outputSampleRate = 0; switch (quality) { case Microsoft.Xna.Framework.Content.Pipeline.Audio.ConversionQuality.Low: outputSampleRate = (int)Math.Max(8000, srcFormat.SampleRate / 2); break; default: outputSampleRate = (int)Math.Max(8000, srcFormat.SampleRate); break; } dstFormat.SampleRate = (outputSampleRate == 0 ? srcFormat.SampleRate : outputSampleRate); // set sample rate if (outputFormat == AudioFormatType.LinearPCM) { // if the output format is PC create a 16-bit int PCM file format description as an example dstFormat.Format = outputFormat; dstFormat.ChannelsPerFrame = srcFormat.ChannelsPerFrame; dstFormat.BitsPerChannel = 16; dstFormat.BytesPerPacket = dstFormat.BytesPerFrame = 2 * dstFormat.ChannelsPerFrame; dstFormat.FramesPerPacket = 1; dstFormat.FormatFlags = AudioFormatFlags.LinearPCMIsPacked | AudioFormatFlags.LinearPCMIsSignedInteger; } else { // compressed format - need to set at least format, sample rate and channel fields for kAudioFormatProperty_FormatInfo dstFormat.Format = outputFormat; dstFormat.ChannelsPerFrame = (outputFormat == AudioFormatType.iLBC ? 1 : srcFormat.ChannelsPerFrame); // for iLBC num channels must be 1 // use AudioFormat API to fill out the rest of the description var fie = AudioStreamBasicDescription.GetFormatInfo(ref dstFormat); if (fie != AudioFormatError.None) { return(false); } } var converter = AudioConverter.Create(srcFormat, dstFormat); converter.InputData += HandleInputData; // if the source has a cookie, get it and set it on the Audio Converter ReadCookie(sourceFile, converter); // get the actual formats back from the Audio Converter srcFormat = converter.CurrentInputStreamDescription; dstFormat = converter.CurrentOutputStreamDescription; // if encoding to AAC set the bitrate to 192k which is a nice value for this demo // kAudioConverterEncodeBitRate is a UInt32 value containing the number of bits per second to aim for when encoding data if (dstFormat.Format == AudioFormatType.MPEG4AAC) { uint outputBitRate = 192000; // 192k // ignore errors as setting may be invalid depending on format specifics such as samplerate try { converter.EncodeBitRate = outputBitRate; } catch { } // get it back and print it out outputBitRate = converter.EncodeBitRate; } // create the destination file var destinationFile = AudioFile.Create(dest, containerType, dstFormat, AudioFileFlags.EraseFlags); // set up source buffers and data proc info struct afio = new AudioFileIO(32768); afio.SourceFile = sourceFile; afio.SrcFormat = srcFormat; if (srcFormat.BytesPerPacket == 0) { // if the source format is VBR, we need to get the maximum packet size // use kAudioFilePropertyPacketSizeUpperBound which returns the theoretical maximum packet size // in the file (without actually scanning the whole file to find the largest packet, // as may happen with kAudioFilePropertyMaximumPacketSize) afio.SrcSizePerPacket = sourceFile.PacketSizeUpperBound; // how many packets can we read for our buffer size? afio.NumPacketsPerRead = afio.SrcBufferSize / afio.SrcSizePerPacket; // allocate memory for the PacketDescription structures describing the layout of each packet afio.PacketDescriptions = new AudioStreamPacketDescription [afio.NumPacketsPerRead]; } else { // CBR source format afio.SrcSizePerPacket = srcFormat.BytesPerPacket; afio.NumPacketsPerRead = afio.SrcBufferSize / afio.SrcSizePerPacket; // allocate memory for the PacketDescription structures describing the layout of each packet afio.PacketDescriptions = new AudioStreamPacketDescription [afio.NumPacketsPerRead]; } // set up output buffers int outputSizePerPacket = dstFormat.BytesPerPacket; // this will be non-zero if the format is CBR const int theOutputBufSize = 32768; var outputBuffer = Marshal.AllocHGlobal(theOutputBufSize); AudioStreamPacketDescription[] outputPacketDescriptions = null; if (outputSizePerPacket == 0) { // if the destination format is VBR, we need to get max size per packet from the converter outputSizePerPacket = (int)converter.MaximumOutputPacketSize; } // allocate memory for the PacketDescription structures describing the layout of each packet outputPacketDescriptions = new AudioStreamPacketDescription [theOutputBufSize / outputSizePerPacket]; int numOutputPackets = theOutputBufSize / outputSizePerPacket; // if the destination format has a cookie, get it and set it on the output file WriteCookie(converter, destinationFile); // write destination channel layout if (srcFormat.ChannelsPerFrame > 2) { WriteDestinationChannelLayout(converter, sourceFile, destinationFile); } long totalOutputFrames = 0; // used for debugging long outputFilePos = 0; AudioBuffers fillBufList = new AudioBuffers(1); bool error = false; // loop to convert data while (true) { // set up output buffer list fillBufList [0] = new AudioBuffer() { NumberChannels = dstFormat.ChannelsPerFrame, DataByteSize = theOutputBufSize, Data = outputBuffer }; // convert data int ioOutputDataPackets = numOutputPackets; var fe = converter.FillComplexBuffer(ref ioOutputDataPackets, fillBufList, outputPacketDescriptions); // if interrupted in the process of the conversion call, we must handle the error appropriately if (fe != AudioConverterError.None) { error = true; break; } if (ioOutputDataPackets == 0) { // this is the EOF conditon break; } // write to output file var inNumBytes = fillBufList [0].DataByteSize; var we = destinationFile.WritePackets(false, inNumBytes, outputPacketDescriptions, outputFilePos, ref ioOutputDataPackets, outputBuffer); if (we != 0) { error = true; break; } // advance output file packet position outputFilePos += ioOutputDataPackets; if (dstFormat.FramesPerPacket != 0) { // the format has constant frames per packet totalOutputFrames += (ioOutputDataPackets * dstFormat.FramesPerPacket); } else { // variable frames per packet require doing this for each packet (adding up the number of sample frames of data in each packet) for (var i = 0; i < ioOutputDataPackets; ++i) { totalOutputFrames += outputPacketDescriptions [i].VariableFramesInPacket; } } } Marshal.FreeHGlobal(outputBuffer); if (!error) { // write out any of the leading and trailing frames for compressed formats only if (dstFormat.BitsPerChannel == 0) { // our output frame count should jive with WritePacketTableInfo(converter, destinationFile); } // write the cookie again - sometimes codecs will update cookies at the end of a conversion WriteCookie(converter, destinationFile); } converter.Dispose(); destinationFile.Dispose(); sourceFile.Dispose(); return(true); }
unsafe static void RenderAudio (CFUrl sourceUrl, CFUrl destinationUrl) { AudioStreamBasicDescription dataFormat; AudioQueueBuffer *buffer = null; long currentPacket = 0; int packetsToRead = 0; AudioStreamPacketDescription [] packetDescs = null; bool flushed = false; bool done = false; int bufferSize; using (var audioFile = AudioFile.Open (sourceUrl, AudioFilePermission.Read, (AudioFileType) 0)) { dataFormat = audioFile.StreamBasicDescription; using (var queue = new OutputAudioQueue (dataFormat, CFRunLoop.Current, CFRunLoop.CFRunLoopCommonModes)) { queue.OutputCompleted += (sender, e) => { HandleOutput (audioFile, queue, buffer, ref packetsToRead, ref currentPacket, ref done, ref flushed, ref packetDescs); }; // we need to calculate how many packets we read at a time and how big a buffer we need // we base this on the size of the packets in the file and an approximate duration for each buffer bool isVBR = dataFormat.BytesPerPacket == 0 || dataFormat.FramesPerPacket == 0; // first check to see what the max size of a packet is - if it is bigger // than our allocation default size, that needs to become larger // adjust buffer size to represent about a second of audio based on this format CalculateBytesForTime (dataFormat, audioFile.MaximumPacketSize, 1.0, out bufferSize, out packetsToRead); if (isVBR) { packetDescs = new AudioStreamPacketDescription [packetsToRead]; } else { packetDescs = null; // we don't provide packet descriptions for constant bit rate formats (like linear PCM) } if (audioFile.MagicCookie.Length != 0) queue.MagicCookie = audioFile.MagicCookie; // allocate the input read buffer queue.AllocateBuffer (bufferSize, out buffer); // prepare the capture format var captureFormat = AudioStreamBasicDescription.CreateLinearPCM (dataFormat.SampleRate, (uint) dataFormat.ChannelsPerFrame, 32); captureFormat.BytesPerFrame = captureFormat.BytesPerPacket = dataFormat.ChannelsPerFrame * 4; queue.SetOfflineRenderFormat (captureFormat, audioFile.ChannelLayout); // prepare the target format var dstFormat = AudioStreamBasicDescription.CreateLinearPCM (dataFormat.SampleRate, (uint) dataFormat.ChannelsPerFrame); using (var captureFile = ExtAudioFile.CreateWithUrl (destinationUrl, AudioFileType.CAF, dstFormat, AudioFileFlags.EraseFlags)) { captureFile.ClientDataFormat = captureFormat; int captureBufferSize = bufferSize / 2; AudioBuffers captureABL = new AudioBuffers (1); AudioQueueBuffer *captureBuffer; queue.AllocateBuffer (captureBufferSize, out captureBuffer); captureABL[0] = new AudioBuffer () { Data = captureBuffer->AudioData, NumberChannels = captureFormat.ChannelsPerFrame }; queue.Start (); double ts = 0; queue.RenderOffline (ts, captureBuffer, 0); HandleOutput (audioFile, queue, buffer, ref packetsToRead, ref currentPacket, ref done, ref flushed, ref packetDescs); while (true) { int reqFrames = captureBufferSize / captureFormat.BytesPerFrame; queue.RenderOffline (ts, captureBuffer, reqFrames); captureABL.SetData (0, captureBuffer->AudioData, (int) captureBuffer->AudioDataByteSize); var writeFrames = captureABL[0].DataByteSize / captureFormat.BytesPerFrame; // Console.WriteLine ("ts: {0} AudioQueueOfflineRender: req {1} frames / {2} bytes, got {3} frames / {4} bytes", // ts, reqFrames, captureBufferSize, writeFrames, captureABL.Buffers [0].DataByteSize); captureFile.WriteAsync ((uint) writeFrames, captureABL); if (flushed) break; ts += writeFrames; } CFRunLoop.Current.RunInMode (CFRunLoop.CFDefaultRunLoopMode, 1, false); } } } }
unsafe static void HandleOutput (AudioFile audioFile, AudioQueue queue, AudioQueueBuffer *audioQueueBuffer, ref int packetsToRead, ref long currentPacket, ref bool done, ref bool flushed, ref AudioStreamPacketDescription [] packetDescriptions) { int bytes; int packets; if (done) return; packets = packetsToRead; bytes = (int) audioQueueBuffer->AudioDataBytesCapacity; packetDescriptions = audioFile.ReadPacketData (false, currentPacket, ref packets, audioQueueBuffer->AudioData, ref bytes); if (packets > 0) { audioQueueBuffer->AudioDataByteSize = (uint) bytes; queue.EnqueueBuffer (audioQueueBuffer, packetDescriptions); currentPacket += packets; } else { if (!flushed) { queue.Flush (); flushed = true; } queue.Stop (false); done = true; } }
// Input data proc callback AudioConverterError EncoderDataProc (ref int numberDataPackets, AudioBuffers data, ref AudioStreamPacketDescription[] dataPacketDescription) { // figure out how much to read int maxPackets = afio.SrcBufferSize / afio.SrcSizePerPacket; if (numberDataPackets > maxPackets) numberDataPackets = maxPackets; // read from the file int outNumBytes; var res = afio.SourceFile.ReadPackets (false, out outNumBytes, afio.PacketDescriptions, afio.SrcFilePos, ref numberDataPackets, afio.SrcBuffer); if (res != 0) { throw new ApplicationException (res.ToString ()); } // advance input file packet position afio.SrcFilePos += numberDataPackets; // put the data pointer into the buffer list data.SetData (0, afio.SrcBuffer, outNumBytes); // don't forget the packet descriptions if required if (dataPacketDescription != null) { if (afio.PacketDescriptions != null) { dataPacketDescription = afio.PacketDescriptions; } else { dataPacketDescription = null; } } return AudioConverterError.None; }
bool DoConvertFile (CFUrl sourceURL, NSUrl destinationURL, AudioFormatType outputFormat, double outputSampleRate) { AudioStreamBasicDescription dstFormat = new AudioStreamBasicDescription (); // in this sample we should never be on the main thread here Debug.Assert (!NSThread.IsMain); // transition thread state to State::Running before continuing AppDelegate.ThreadStateSetRunning (); Debug.WriteLine ("DoConvertFile"); // get the source file var sourceFile = AudioFile.Open (sourceURL, AudioFilePermission.Read); // get the source data format var srcFormat = (AudioStreamBasicDescription)sourceFile.DataFormat; // setup the output file format dstFormat.SampleRate = (outputSampleRate == 0 ? srcFormat.SampleRate : outputSampleRate); // set sample rate if (outputFormat == AudioFormatType.LinearPCM) { // if the output format is PC create a 16-bit int PCM file format description as an example dstFormat.Format = outputFormat; dstFormat.ChannelsPerFrame = srcFormat.ChannelsPerFrame; dstFormat.BitsPerChannel = 16; dstFormat.BytesPerPacket = dstFormat.BytesPerFrame = 2 * dstFormat.ChannelsPerFrame; dstFormat.FramesPerPacket = 1; dstFormat.FormatFlags = AudioFormatFlags.LinearPCMIsPacked | AudioFormatFlags.LinearPCMIsSignedInteger; } else { // compressed format - need to set at least format, sample rate and channel fields for kAudioFormatProperty_FormatInfo dstFormat.Format = outputFormat; dstFormat.ChannelsPerFrame = (outputFormat == AudioFormatType.iLBC ? 1 : srcFormat.ChannelsPerFrame); // for iLBC num channels must be 1 // use AudioFormat API to fill out the rest of the description var fie = AudioStreamBasicDescription.GetFormatInfo (ref dstFormat); if (fie != AudioFormatError.None) { Debug.Print ("Cannot create destination format {0:x}", fie); AppDelegate.ThreadStateSetDone (); return false; } } // create the AudioConverter AudioConverterError ce; var converter = AudioConverter.Create (srcFormat, dstFormat, out ce); Debug.Assert (ce == AudioConverterError.None); converter.InputData += EncoderDataProc; // if the source has a cookie, get it and set it on the Audio Converter ReadCookie (sourceFile, converter); // get the actual formats back from the Audio Converter srcFormat = converter.CurrentInputStreamDescription; dstFormat = converter.CurrentOutputStreamDescription; // if encoding to AAC set the bitrate to 192k which is a nice value for this demo // kAudioConverterEncodeBitRate is a UInt32 value containing the number of bits per second to aim for when encoding data if (dstFormat.Format == AudioFormatType.MPEG4AAC) { uint outputBitRate = 192000; // 192k // ignore errors as setting may be invalid depending on format specifics such as samplerate try { converter.EncodeBitRate = outputBitRate; } catch { } // get it back and print it out outputBitRate = converter.EncodeBitRate; Debug.Print ("AAC Encode Bitrate: {0}", outputBitRate); } // can the Audio Converter resume conversion after an interruption? // this property may be queried at any time after construction of the Audio Converter after setting its output format // there's no clear reason to prefer construction time, interruption time, or potential resumption time but we prefer // construction time since it means less code to execute during or after interruption time bool canResumeFromInterruption; try { canResumeFromInterruption = converter.CanResumeFromInterruption; Debug.Print ("Audio Converter {0} continue after interruption!", canResumeFromInterruption ? "CAN" : "CANNOT"); } catch (Exception e) { // if the property is unimplemented (kAudioConverterErr_PropertyNotSupported, or paramErr returned in the case of PCM), // then the codec being used is not a hardware codec so we're not concerned about codec state // we are always going to be able to resume conversion after an interruption canResumeFromInterruption = false; Debug.Print ("CanResumeFromInterruption: {0}", e.Message); } // create the destination file var destinationFile = AudioFile.Create (destinationURL, AudioFileType.CAF, dstFormat, AudioFileFlags.EraseFlags); // set up source buffers and data proc info struct afio = new AudioFileIO (32768); afio.SourceFile = sourceFile; afio.SrcFormat = srcFormat; if (srcFormat.BytesPerPacket == 0) { // if the source format is VBR, we need to get the maximum packet size // use kAudioFilePropertyPacketSizeUpperBound which returns the theoretical maximum packet size // in the file (without actually scanning the whole file to find the largest packet, // as may happen with kAudioFilePropertyMaximumPacketSize) afio.SrcSizePerPacket = sourceFile.PacketSizeUpperBound; // how many packets can we read for our buffer size? afio.NumPacketsPerRead = afio.SrcBufferSize / afio.SrcSizePerPacket; // allocate memory for the PacketDescription structures describing the layout of each packet afio.PacketDescriptions = new AudioStreamPacketDescription [afio.NumPacketsPerRead]; } else { // CBR source format afio.SrcSizePerPacket = srcFormat.BytesPerPacket; afio.NumPacketsPerRead = afio.SrcBufferSize / afio.SrcSizePerPacket; } // set up output buffers int outputSizePerPacket = dstFormat.BytesPerPacket; // this will be non-zero if the format is CBR const int theOutputBufSize = 32768; var outputBuffer = Marshal.AllocHGlobal (theOutputBufSize); AudioStreamPacketDescription[] outputPacketDescriptions = null; if (outputSizePerPacket == 0) { // if the destination format is VBR, we need to get max size per packet from the converter outputSizePerPacket = (int)converter.MaximumOutputPacketSize; // allocate memory for the PacketDescription structures describing the layout of each packet outputPacketDescriptions = new AudioStreamPacketDescription [theOutputBufSize / outputSizePerPacket]; } int numOutputPackets = theOutputBufSize / outputSizePerPacket; // if the destination format has a cookie, get it and set it on the output file WriteCookie (converter, destinationFile); // write destination channel layout if (srcFormat.ChannelsPerFrame > 2) { WriteDestinationChannelLayout (converter, sourceFile, destinationFile); } long totalOutputFrames = 0; // used for debugging long outputFilePos = 0; AudioBuffers fillBufList = new AudioBuffers (1); bool error = false; // loop to convert data Debug.WriteLine ("Converting..."); while (true) { // set up output buffer list fillBufList [0] = new AudioBuffer () { NumberChannels = dstFormat.ChannelsPerFrame, DataByteSize = theOutputBufSize, Data = outputBuffer }; // this will block if we're interrupted var wasInterrupted = AppDelegate.ThreadStatePausedCheck(); if (wasInterrupted && !canResumeFromInterruption) { // this is our interruption termination condition // an interruption has occured but the Audio Converter cannot continue Debug.WriteLine ("Cannot resume from interruption"); error = true; break; } // convert data int ioOutputDataPackets = numOutputPackets; var fe = converter.FillComplexBuffer (ref ioOutputDataPackets, fillBufList, outputPacketDescriptions); // if interrupted in the process of the conversion call, we must handle the error appropriately if (fe != AudioConverterError.None) { Debug.Print ("FillComplexBuffer: {0}", fe); error = true; break; } if (ioOutputDataPackets == 0) { // this is the EOF conditon break; } // write to output file var inNumBytes = fillBufList [0].DataByteSize; var we = destinationFile.WritePackets (false, inNumBytes, outputPacketDescriptions, outputFilePos, ref ioOutputDataPackets, outputBuffer); if (we != 0) { Debug.Print ("WritePackets: {0}", we); error = true; break; } // advance output file packet position outputFilePos += ioOutputDataPackets; if (dstFormat.FramesPerPacket != 0) { // the format has constant frames per packet totalOutputFrames += (ioOutputDataPackets * dstFormat.FramesPerPacket); } else { // variable frames per packet require doing this for each packet (adding up the number of sample frames of data in each packet) for (var i = 0; i < ioOutputDataPackets; ++i) totalOutputFrames += outputPacketDescriptions [i].VariableFramesInPacket; } } Marshal.FreeHGlobal (outputBuffer); if (!error) { // write out any of the leading and trailing frames for compressed formats only if (dstFormat.BitsPerChannel == 0) { // our output frame count should jive with Debug.Print ("Total number of output frames counted: {0}", totalOutputFrames); WritePacketTableInfo (converter, destinationFile); } // write the cookie again - sometimes codecs will update cookies at the end of a conversion WriteCookie (converter, destinationFile); } converter.Dispose (); destinationFile.Dispose (); sourceFile.Dispose (); // transition thread state to State.Done before continuing AppDelegate.ThreadStateSetDone (); return !error; }
bool DoConvertFile(CFUrl sourceURL, NSUrl destinationURL, AudioFormatType outputFormat, double outputSampleRate) { AudioStreamBasicDescription dstFormat = new AudioStreamBasicDescription(); // in this sample we should never be on the main thread here Debug.Assert(!NSThread.IsMain); // transition thread state to State::Running before continuing AppDelegate.ThreadStateSetRunning(); Debug.WriteLine("DoConvertFile"); // get the source file var sourceFile = AudioFile.Open(sourceURL, AudioFilePermission.Read); // get the source data format var srcFormat = (AudioStreamBasicDescription)sourceFile.DataFormat; // setup the output file format dstFormat.SampleRate = (outputSampleRate == 0 ? srcFormat.SampleRate : outputSampleRate); // set sample rate if (outputFormat == AudioFormatType.LinearPCM) { // if the output format is PC create a 16-bit int PCM file format description as an example dstFormat.Format = outputFormat; dstFormat.ChannelsPerFrame = srcFormat.ChannelsPerFrame; dstFormat.BitsPerChannel = 16; dstFormat.BytesPerPacket = dstFormat.BytesPerFrame = 2 * dstFormat.ChannelsPerFrame; dstFormat.FramesPerPacket = 1; dstFormat.FormatFlags = AudioFormatFlags.LinearPCMIsPacked | AudioFormatFlags.LinearPCMIsSignedInteger; } else { // compressed format - need to set at least format, sample rate and channel fields for kAudioFormatProperty_FormatInfo dstFormat.Format = outputFormat; dstFormat.ChannelsPerFrame = (outputFormat == AudioFormatType.iLBC ? 1 : srcFormat.ChannelsPerFrame); // for iLBC num channels must be 1 // use AudioFormat API to fill out the rest of the description var fie = AudioStreamBasicDescription.GetFormatInfo(ref dstFormat); if (fie != AudioFormatError.None) { Debug.Print("Cannot create destination format {0:x}", fie); AppDelegate.ThreadStateSetDone(); return(false); } } // create the AudioConverter AudioConverterError ce; var converter = AudioConverter.Create(srcFormat, dstFormat, out ce); Debug.Assert(ce == AudioConverterError.None); converter.InputData += EncoderDataProc; // if the source has a cookie, get it and set it on the Audio Converter ReadCookie(sourceFile, converter); // get the actual formats back from the Audio Converter srcFormat = converter.CurrentInputStreamDescription; dstFormat = converter.CurrentOutputStreamDescription; // if encoding to AAC set the bitrate to 192k which is a nice value for this demo // kAudioConverterEncodeBitRate is a UInt32 value containing the number of bits per second to aim for when encoding data if (dstFormat.Format == AudioFormatType.MPEG4AAC) { uint outputBitRate = 192000; // 192k // ignore errors as setting may be invalid depending on format specifics such as samplerate try { converter.EncodeBitRate = outputBitRate; } catch { } // get it back and print it out outputBitRate = converter.EncodeBitRate; Debug.Print("AAC Encode Bitrate: {0}", outputBitRate); } // can the Audio Converter resume conversion after an interruption? // this property may be queried at any time after construction of the Audio Converter after setting its output format // there's no clear reason to prefer construction time, interruption time, or potential resumption time but we prefer // construction time since it means less code to execute during or after interruption time bool canResumeFromInterruption; try { canResumeFromInterruption = converter.CanResumeFromInterruption; Debug.Print("Audio Converter {0} continue after interruption!", canResumeFromInterruption ? "CAN" : "CANNOT"); } catch (Exception e) { // if the property is unimplemented (kAudioConverterErr_PropertyNotSupported, or paramErr returned in the case of PCM), // then the codec being used is not a hardware codec so we're not concerned about codec state // we are always going to be able to resume conversion after an interruption canResumeFromInterruption = false; Debug.Print("CanResumeFromInterruption: {0}", e.Message); } // create the destination file var destinationFile = AudioFile.Create(destinationURL, AudioFileType.CAF, dstFormat, AudioFileFlags.EraseFlags); // set up source buffers and data proc info struct afio = new AudioFileIO(32768); afio.SourceFile = sourceFile; afio.SrcFormat = srcFormat; if (srcFormat.BytesPerPacket == 0) { // if the source format is VBR, we need to get the maximum packet size // use kAudioFilePropertyPacketSizeUpperBound which returns the theoretical maximum packet size // in the file (without actually scanning the whole file to find the largest packet, // as may happen with kAudioFilePropertyMaximumPacketSize) afio.SrcSizePerPacket = sourceFile.PacketSizeUpperBound; // how many packets can we read for our buffer size? afio.NumPacketsPerRead = afio.SrcBufferSize / afio.SrcSizePerPacket; // allocate memory for the PacketDescription structures describing the layout of each packet afio.PacketDescriptions = new AudioStreamPacketDescription [afio.NumPacketsPerRead]; } else { // CBR source format afio.SrcSizePerPacket = srcFormat.BytesPerPacket; afio.NumPacketsPerRead = afio.SrcBufferSize / afio.SrcSizePerPacket; } // set up output buffers int outputSizePerPacket = dstFormat.BytesPerPacket; // this will be non-zero if the format is CBR const int theOutputBufSize = 32768; var outputBuffer = Marshal.AllocHGlobal(theOutputBufSize); AudioStreamPacketDescription[] outputPacketDescriptions = null; if (outputSizePerPacket == 0) { // if the destination format is VBR, we need to get max size per packet from the converter outputSizePerPacket = (int)converter.MaximumOutputPacketSize; // allocate memory for the PacketDescription structures describing the layout of each packet outputPacketDescriptions = new AudioStreamPacketDescription [theOutputBufSize / outputSizePerPacket]; } int numOutputPackets = theOutputBufSize / outputSizePerPacket; // if the destination format has a cookie, get it and set it on the output file WriteCookie(converter, destinationFile); // write destination channel layout if (srcFormat.ChannelsPerFrame > 2) { WriteDestinationChannelLayout(converter, sourceFile, destinationFile); } long totalOutputFrames = 0; // used for debugging long outputFilePos = 0; AudioBuffers fillBufList = new AudioBuffers(1); bool error = false; // loop to convert data Debug.WriteLine("Converting..."); while (true) { // set up output buffer list fillBufList [0] = new AudioBuffer() { NumberChannels = dstFormat.ChannelsPerFrame, DataByteSize = theOutputBufSize, Data = outputBuffer }; // this will block if we're interrupted var wasInterrupted = AppDelegate.ThreadStatePausedCheck(); if (wasInterrupted && !canResumeFromInterruption) { // this is our interruption termination condition // an interruption has occured but the Audio Converter cannot continue Debug.WriteLine("Cannot resume from interruption"); error = true; break; } // convert data int ioOutputDataPackets = numOutputPackets; var fe = converter.FillComplexBuffer(ref ioOutputDataPackets, fillBufList, outputPacketDescriptions); // if interrupted in the process of the conversion call, we must handle the error appropriately if (fe != AudioConverterError.None) { Debug.Print("FillComplexBuffer: {0}", fe); error = true; break; } if (ioOutputDataPackets == 0) { // this is the EOF conditon break; } // write to output file var inNumBytes = fillBufList [0].DataByteSize; var we = destinationFile.WritePackets(false, inNumBytes, outputPacketDescriptions, outputFilePos, ref ioOutputDataPackets, outputBuffer); if (we != 0) { Debug.Print("WritePackets: {0}", we); error = true; break; } // advance output file packet position outputFilePos += ioOutputDataPackets; if (dstFormat.FramesPerPacket != 0) { // the format has constant frames per packet totalOutputFrames += (ioOutputDataPackets * dstFormat.FramesPerPacket); } else { // variable frames per packet require doing this for each packet (adding up the number of sample frames of data in each packet) for (var i = 0; i < ioOutputDataPackets; ++i) { totalOutputFrames += outputPacketDescriptions [i].VariableFramesInPacket; } } } Marshal.FreeHGlobal(outputBuffer); if (!error) { // write out any of the leading and trailing frames for compressed formats only if (dstFormat.BitsPerChannel == 0) { // our output frame count should jive with Debug.Print("Total number of output frames counted: {0}", totalOutputFrames); WritePacketTableInfo(converter, destinationFile); } // write the cookie again - sometimes codecs will update cookies at the end of a conversion WriteCookie(converter, destinationFile); } converter.Dispose(); destinationFile.Dispose(); sourceFile.Dispose(); // transition thread state to State.Done before continuing AppDelegate.ThreadStateSetDone(); return(!error); }
unsafe static void RenderAudio(CFUrl sourceUrl, CFUrl destinationUrl) { AudioStreamBasicDescription dataFormat; AudioQueueBuffer * buffer = null; long currentPacket = 0; int packetsToRead = 0; AudioStreamPacketDescription[] packetDescs = null; bool flushed = false; bool done = false; int bufferSize; using (var audioFile = AudioFile.Open(sourceUrl, AudioFilePermission.Read, (AudioFileType)0)) { dataFormat = audioFile.StreamBasicDescription; using (var queue = new OutputAudioQueue(dataFormat, CFRunLoop.Current, CFRunLoop.ModeCommon)) { queue.BufferCompleted += (sender, e) => { HandleOutput(audioFile, queue, buffer, ref packetsToRead, ref currentPacket, ref done, ref flushed, ref packetDescs); }; // we need to calculate how many packets we read at a time and how big a buffer we need // we base this on the size of the packets in the file and an approximate duration for each buffer bool isVBR = dataFormat.BytesPerPacket == 0 || dataFormat.FramesPerPacket == 0; // first check to see what the max size of a packet is - if it is bigger // than our allocation default size, that needs to become larger // adjust buffer size to represent about a second of audio based on this format CalculateBytesForTime(dataFormat, audioFile.MaximumPacketSize, 1.0, out bufferSize, out packetsToRead); if (isVBR) { packetDescs = new AudioStreamPacketDescription [packetsToRead]; } else { packetDescs = null; // we don't provide packet descriptions for constant bit rate formats (like linear PCM) } if (audioFile.MagicCookie.Length != 0) { queue.MagicCookie = audioFile.MagicCookie; } // allocate the input read buffer queue.AllocateBuffer(bufferSize, out buffer); // prepare the capture format var captureFormat = AudioStreamBasicDescription.CreateLinearPCM(dataFormat.SampleRate, (uint)dataFormat.ChannelsPerFrame, 32); captureFormat.BytesPerFrame = captureFormat.BytesPerPacket = dataFormat.ChannelsPerFrame * 4; queue.SetOfflineRenderFormat(captureFormat, audioFile.ChannelLayout); // prepare the target format var dstFormat = AudioStreamBasicDescription.CreateLinearPCM(dataFormat.SampleRate, (uint)dataFormat.ChannelsPerFrame); using (var captureFile = ExtAudioFile.CreateWithUrl(destinationUrl, AudioFileType.CAF, dstFormat, AudioFileFlags.EraseFlags)) { captureFile.ClientDataFormat = captureFormat; int captureBufferSize = bufferSize / 2; AudioBuffers captureABL = new AudioBuffers(1); AudioQueueBuffer *captureBuffer; queue.AllocateBuffer(captureBufferSize, out captureBuffer); captureABL [0] = new AudioBuffer() { Data = captureBuffer->AudioData, NumberChannels = captureFormat.ChannelsPerFrame }; queue.Start(); double ts = 0; queue.RenderOffline(ts, captureBuffer, 0); HandleOutput(audioFile, queue, buffer, ref packetsToRead, ref currentPacket, ref done, ref flushed, ref packetDescs); while (true) { int reqFrames = captureBufferSize / captureFormat.BytesPerFrame; queue.RenderOffline(ts, captureBuffer, reqFrames); captureABL.SetData(0, captureBuffer->AudioData, (int)captureBuffer->AudioDataByteSize); var writeFrames = captureABL [0].DataByteSize / captureFormat.BytesPerFrame; // Console.WriteLine ("ts: {0} AudioQueueOfflineRender: req {1} frames / {2} bytes, got {3} frames / {4} bytes", // ts, reqFrames, captureBufferSize, writeFrames, captureABL.Buffers [0].DataByteSize); captureFile.WriteAsync((uint)writeFrames, captureABL); if (flushed) { break; } ts += writeFrames; } CFRunLoop.Current.RunInMode(CFRunLoop.ModeDefault, 1, false); } } } }
void Convert(string sourceFilePath, string destinationFilePath, AudioFormatType outputFormatType, int?sampleRate = null) { var destinationUrl = NSUrl.FromFilename(destinationFilePath); var sourceUrl = NSUrl.FromFilename(sourceFilePath); // get the source file var name = Path.GetFileNameWithoutExtension(destinationFilePath); using var sourceFile = AudioFile.Open(sourceUrl, AudioFilePermission.Read); var srcFormat = (AudioStreamBasicDescription)sourceFile.DataFormat; var dstFormat = new AudioStreamBasicDescription(); // setup the output file format dstFormat.SampleRate = sampleRate ?? srcFormat.SampleRate; if (outputFormatType == AudioFormatType.LinearPCM) { // if the output format is PCM create a 16 - bit int PCM file format dstFormat.Format = AudioFormatType.LinearPCM; dstFormat.ChannelsPerFrame = srcFormat.ChannelsPerFrame; dstFormat.BitsPerChannel = 16; dstFormat.BytesPerPacket = dstFormat.BytesPerFrame = 2 * dstFormat.ChannelsPerFrame; dstFormat.FramesPerPacket = 1; dstFormat.FormatFlags = AudioFormatFlags.LinearPCMIsPacked | AudioFormatFlags.LinearPCMIsSignedInteger; } else { // compressed format - need to set at least format, sample rate and channel fields for kAudioFormatProperty_FormatInfo dstFormat.Format = outputFormatType; dstFormat.ChannelsPerFrame = srcFormat.ChannelsPerFrame; // for iLBC num channels must be 1 // use AudioFormat API to fill out the rest of the description var afe = AudioStreamBasicDescription.GetFormatInfo(ref dstFormat); Assert.AreEqual(AudioFormatError.None, afe, $"GetFormatInfo: {name}"); } // create the AudioConverter using var converter = AudioConverter.Create(srcFormat, dstFormat, out var ce); Assert.AreEqual(AudioConverterError.None, ce, $"AudioConverterCreate: {name}"); // set up source buffers and data proc info struct var afio = new AudioFileIO(32 * 1024); // 32Kb converter.InputData += (ref int numberDataPackets, AudioBuffers data, ref AudioStreamPacketDescription [] dataPacketDescription) => { return(EncoderDataProc(afio, ref numberDataPackets, data, ref dataPacketDescription)); }; // Some audio formats have a magic cookie associated with them which is required to decompress audio data // When converting audio data you must check to see if the format of the data has a magic cookie // If the audio data format has a magic cookie associated with it, you must add this information to anAudio Converter // using AudioConverterSetProperty and kAudioConverterDecompressionMagicCookie to appropriately decompress the data // http://developer.apple.com/mac/library/qa/qa2001/qa1318.html var cookie = sourceFile.MagicCookie; // if there is an error here, then the format doesn't have a cookie - this is perfectly fine as some formats do not if (cookie?.Length > 0) { converter.DecompressionMagicCookie = cookie; } // get the actual formats back from the Audio Converter srcFormat = converter.CurrentInputStreamDescription; dstFormat = converter.CurrentOutputStreamDescription; // create the destination file using var destinationFile = AudioFile.Create(destinationUrl, AudioFileType.CAF, dstFormat, AudioFileFlags.EraseFlags); // set up source buffers and data proc info struct afio.SourceFile = sourceFile; afio.SrcFormat = srcFormat; if (srcFormat.BytesPerPacket == 0) { // if the source format is VBR, we need to get the maximum packet size // use kAudioFilePropertyPacketSizeUpperBound which returns the theoretical maximum packet size // in the file (without actually scanning the whole file to find the largest packet, // as may happen with kAudioFilePropertyMaximumPacketSize) afio.SrcSizePerPacket = sourceFile.PacketSizeUpperBound; // how many packets can we read for our buffer size? afio.NumPacketsPerRead = afio.SrcBufferSize / afio.SrcSizePerPacket; // allocate memory for the PacketDescription structures describing the layout of each packet afio.PacketDescriptions = new AudioStreamPacketDescription [afio.NumPacketsPerRead]; } else { // CBR source format afio.SrcSizePerPacket = srcFormat.BytesPerPacket; afio.NumPacketsPerRead = afio.SrcBufferSize / afio.SrcSizePerPacket; } // set up output buffers int outputSizePerPacket = dstFormat.BytesPerPacket; // this will be non-zero if the format is CBR const int theOutputBufSize = 32 * 1024; // 32Kb var outputBuffer = Marshal.AllocHGlobal(theOutputBufSize); AudioStreamPacketDescription [] outputPacketDescriptions = null; if (outputSizePerPacket == 0) { // if the destination format is VBR, we need to get max size per packet from the converter outputSizePerPacket = (int)converter.MaximumOutputPacketSize; // allocate memory for the PacketDescription structures describing the layout of each packet outputPacketDescriptions = new AudioStreamPacketDescription [theOutputBufSize / outputSizePerPacket]; } int numOutputPackets = theOutputBufSize / outputSizePerPacket; // if the destination format has a cookie, get it and set it on the output file WriteCookie(converter, destinationFile); long totalOutputFrames = 0; // used for debugging long outputFilePos = 0; AudioBuffers fillBufList = new AudioBuffers(1); // loop to convert data while (true) { // set up output buffer list fillBufList [0] = new AudioBuffer() { NumberChannels = dstFormat.ChannelsPerFrame, DataByteSize = theOutputBufSize, Data = outputBuffer }; // convert data int ioOutputDataPackets = numOutputPackets; var fe = converter.FillComplexBuffer(ref ioOutputDataPackets, fillBufList, outputPacketDescriptions); // if interrupted in the process of the conversion call, we must handle the error appropriately Assert.AreEqual(AudioConverterError.None, fe, $"FillComplexBuffer: {name}"); if (ioOutputDataPackets == 0) { // this is the EOF conditon break; } // write to output file var inNumBytes = fillBufList [0].DataByteSize; var we = destinationFile.WritePackets(false, inNumBytes, outputPacketDescriptions, outputFilePos, ref ioOutputDataPackets, outputBuffer); Assert.AreEqual(AudioFileError.Success, we, $"WritePackets: {name}"); // advance output file packet position outputFilePos += ioOutputDataPackets; // the format has constant frames per packet totalOutputFrames += (ioOutputDataPackets * dstFormat.FramesPerPacket); } Marshal.FreeHGlobal(outputBuffer); // write out any of the leading and trailing frames for compressed formats only if (dstFormat.BitsPerChannel == 0) { WritePacketTableInfo(converter, destinationFile); } // write the cookie again - sometimes codecs will update cookies at the end of a conversion WriteCookie(converter, destinationFile); }
// Input data proc callback AudioConverterError EncoderDataProc(ref int numberDataPackets, AudioBuffers data, ref AudioStreamPacketDescription[] dataPacketDescription) { // figure out how much to read int maxPackets = afio.SrcBufferSize / afio.SrcSizePerPacket; if (numberDataPackets > maxPackets) numberDataPackets = maxPackets; // read from the file int outNumBytes = 16384; // modified for iOS7 (ReadPackets depricated) afio.PacketDescriptions = afio.SourceFile.ReadPacketData(false, afio.SrcFilePos, ref numberDataPackets, afio.SrcBuffer, ref outNumBytes); if (afio.PacketDescriptions.Length == 0 && numberDataPackets > 0) throw new ApplicationException(afio.PacketDescriptions.ToString()); // advance input file packet position afio.SrcFilePos += numberDataPackets; // put the data pointer into the buffer list data.SetData(0, afio.SrcBuffer, outNumBytes); // don't forget the packet descriptions if required if (dataPacketDescription != null) dataPacketDescription = afio.PacketDescriptions; return AudioConverterError.None; }
public unsafe void ConvertData(AudioBuffers audioBufferList, uint frames, float[][] buffers, out AudioStreamPacketDescription packetDescriptions) { var totalArrays = buffers.Length; var arraySize = buffers [0].Length; var pSize = Marshal.SizeOf(typeof(IntPtr)); IntPtr pDataBuffers; IntPtr pPointerBuffers; IntPtrHelper.ConvertToIntPtr <float> (buffers, out pDataBuffers, out pPointerBuffers); _ConvertData((IntPtr)audioBufferList, frames, pPointerBuffers, out packetDescriptions); for (int i = 0; i < totalArrays; i++) { var pArray = Marshal.ReadIntPtr(IntPtr.Add(pPointerBuffers, i * pSize)); fixed(float *arrAddr = &buffers[i][0]) { IntPtrHelper.MemoryCopy((IntPtr)arrAddr, pArray, (nuint)arraySize); } } Marshal.FreeHGlobal(pDataBuffers); Marshal.FreeHGlobal(pPointerBuffers); }
static extern int AudioConverterFillComplexBuffer( IntPtr inAudioConverter, AudioConverterComplexInputDataProc inInputDataProc, IntPtr inInputDataProcUserData, ref uint ioOutputDataPacketSize, AudioBufferList outOutputData, AudioStreamPacketDescription[] outPacketDescription);