// DataFlow baseline. Similar CPU & throughput results, but memory keeps growing. public void RunDataFlowPipeline <T>(DeliveryPolicy policy, Func <int, T> create, Func <int, T, T> initialize, Func <T, T, T> increment, Func <T, T, T, T> add, Func <T, int> extract, bool validateNoLoss, bool validateSync) { int resultCount = 0; var dfo = new DataflowLinkOptions(); dfo.Append = true; List <object> saved = new List <object>(); // create several parallel branches of components var branches = new ISourceBlock <Wrap <T> > [ParallelBranchCount]; var sources = new Time.TimerDelegate[SourceCount]; for (int i = 0; i < SourceCount; i++) { // make a timer for each source var timerSeqId = 0; var timer = new TransformBlock <int, int>(ts => timerSeqId++); sources[i] = new Time.TimerDelegate((uint timerID, uint msg, UIntPtr userCtx, UIntPtr dw1, UIntPtr dw2) => timer.Post(i)); saved.Add(timer); // branch and generate data for (int k = 0; k < ParallelBranchMultiplier; k++) { int b = (i * ParallelBranchMultiplier) + k; var initInst = new Wrap <T>(create(b), 0); var init = new TransformBlock <int, Wrap <T> >(seqId => initInst = new Wrap <T>(initialize(seqId, initInst.Inner), seqId).DeepClone()); timer.LinkTo(init, dfo); branches[b] = init; saved.Add(init); // apply a sequence of transforms for (int j = 0; j < TransformCount; j++) { var incInst = new Wrap <T>(create(b), 0); var inc = new TransformBlock <Wrap <T>, Wrap <T> >(src => incInst = new Wrap <T>(increment(incInst.Inner, src.Inner), src.ExpectedResult + 1).DeepClone()); branches[b].LinkTo(inc, dfo); branches[b] = inc; saved.Add(inc); } // make sure we didn't lose messages // branches[b] = branches[b].DoT(m => CheckMessageId(m.SequenceId + TransformCount, m.Data.ExpectedResult, validateNoLoss), true, true); } } // join all var fullJoin = branches[0]; for (int i = 1; i < ParallelBranchCount; i++) { var joinGo = new GroupingDataflowBlockOptions(); joinGo.Greedy = false; var join = new JoinBlock <Wrap <T>, Wrap <T> >(joinGo); fullJoin.LinkTo(join.Target1, dfo); branches[i].LinkTo(join.Target2, dfo); var addInst = new Wrap <T>(create(i), 0); var select = new TransformBlock <Tuple <Wrap <T>, Wrap <T> >, Wrap <T> >(tpl => addInst = new Wrap <T>(add(addInst.Inner, tpl.Item1.Inner, tpl.Item2.Inner), tpl.Item1.ExpectedResult + tpl.Item2.ExpectedResult).DeepClone()); join.LinkTo(select, dfo); fullJoin = select; saved.Add(join); saved.Add(select); } // extract final result var result = new TransformBlock <Wrap <T>, Wrap <long> >(w => new Wrap <long>(extract(w.Inner), w.ExpectedResult)); fullJoin.LinkTo(result, dfo); saved.Add(result); // validate result int actionSeqId = 0; var final = new ActionBlock <Wrap <long> >(w => { resultCount++; this.CheckMessageId(++actionSeqId, resultCount, validateNoLoss); if (w.Inner != w.ExpectedResult) { throw new Exception("Unexpected computation result."); } }); result.LinkTo(final, dfo); saved.Add(final); // run the pipeline for (int i = 0; i < SourceCount; i++) { Platform.Specific.TimerStart(1000 / this.frequency, sources[i]); } while (!final.Completion.Wait(1000)) { Console.WriteLine(resultCount); if (sources.Length == 0) { throw new Exception("This was here just to keeo source alive in release mode, why did it hit?"); } } Console.WriteLine("Stopped"); Assert.AreNotEqual(0, resultCount); }
/// <summary> /// The frame shift operator. /// </summary> /// <param name="source">A stream containing the input data.</param> /// <param name="frameSizeInBytes">The frame size in bytes.</param> /// <param name="frameShiftInBytes">The number of bytes by which to shift the data.</param> /// <param name="bytesPerSec">The sampling frequency in bytes per second.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>A stream containing the frame-shifted data.</returns> public static IProducer <byte[]> FrameShift(this IProducer <byte[]> source, int frameSizeInBytes, int frameShiftInBytes, double bytesPerSec, DeliveryPolicy deliveryPolicy = null) { return(source.PipeTo(new FrameShift(source.Out.Pipeline, frameSizeInBytes, frameShiftInBytes, bytesPerSec), deliveryPolicy)); }
public void RunPipeline <T>(DeliveryPolicy policy, Func <int, T> create, Func <T, int, T> initialize, Func <T, T, T> increment, Func <T, T, T, T> add, Func <T, int> extract, bool validateNoLoss, bool validateSync) { int resultCount = 0; var p = Pipeline.Create(nameof(FunctionalTests)); Console.WriteLine($"Running {ParallelBranchCount} branches and {TransformCount} transforms at {this.frequency}Hz"); // create several parallel branches of components var branches = new IProducer <Wrap <T> > [ParallelBranchCount]; for (int i = 0; i < SourceCount; i++) { // make a timer for each source var timer = Generators.Timer(p, TimeSpan.FromMilliseconds(1000 / this.frequency)).Select((d, e) => e.SequenceId); // branch and generate data for (int k = 0; k < ParallelBranchMultiplier; k++) { int b = (i * ParallelBranchMultiplier) + k; branches[b] = timer.Aggregate(new Wrap <T>(create(b), 0), (tgt, seqId) => new Wrap <T>(initialize(tgt.Inner, seqId), seqId)); // apply a sequence of transforms for (int j = 0; j < TransformCount; j++) { branches[b] = branches[b].Aggregate(new Wrap <T>(create(b), 0), (tgt, src) => new Wrap <T>(increment(tgt.Inner, src.Inner), src.ExpectedResult + 1)); } // make sure we didn't lose messages branches[b] = branches[b].Do((d, e) => this.CheckMessageId(e.SequenceId + TransformCount, d.ExpectedResult, validateNoLoss)); } } // join all var fullJoin = branches[0]; for (int i = 1; i < ParallelBranchCount; i++) { var join = fullJoin.Join(branches[i], TimeSpan.MaxValue); fullJoin = join.Aggregate(new Wrap <T>(create(i), 0), (tgt, tpl) => new Wrap <T>(add(tgt.Inner, tpl.Item1.Inner, tpl.Item2.Inner), tpl.Item1.ExpectedResult + tpl.Item2.ExpectedResult)); } // extract final result var result = fullJoin.Select(w => new Wrap <long>(extract(w.Inner), w.ExpectedResult)); // validate result var final = result.Do((d, e) => { resultCount++; this.CheckMessageId(e.SequenceId, resultCount, validateNoLoss); if (d.Inner != d.ExpectedResult) { throw new Exception("Unexpected computation result."); } }); // run the pipeline using (p) { var now = Time.GetCurrentTime(); p.RunAsync(new ReplayDescriptor(now, now + this.duration)); while (!p.WaitAll(1000)) { Console.WriteLine(resultCount); } } Console.WriteLine(resultCount); Assert.AreNotEqual(0, resultCount); }
/// <summary> /// Creates a gzipped byte array of the depth image. /// </summary> /// <param name="depthImage">Depth image to compress.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>Byte array containing the compressed depth map.</returns> public static IProducer <byte[]> GZipCompressDepthImage(this IProducer <Shared <Image> > depthImage, DeliveryPolicy deliveryPolicy = null) { var memoryStream = new MemoryStream(); var memoryStreamLo = new MemoryStream(); var memoryStreamHi = new MemoryStream(); byte[] buffer = null; return(depthImage.Select( image => { if (buffer == null) { buffer = new byte[image.Resource.Size]; } image.Resource.CopyTo(buffer); memoryStream.Seek(0, SeekOrigin.Begin); using (var compressedStream = new GZipStream(memoryStream, CompressionLevel.Optimal, true)) { compressedStream.Write(buffer, 0, buffer.Length); } var output = new byte[memoryStream.Position]; memoryStream.Seek(0, SeekOrigin.Begin); memoryStream.Read(output, 0, output.Length); return output; }, deliveryPolicy)); }
/// <summary> /// Converts the source image to a different pixel format. /// </summary> /// <param name="source">The source stream.</param> /// <param name="pixelFormat">The pixel format to convert to.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>The resulting stream.</returns> public static IProducer <Shared <Image> > Convert(this IProducer <Shared <Image> > source, PixelFormat pixelFormat, DeliveryPolicy deliveryPolicy = null) { return(source.PipeTo(new ToPixelFormat(source.Out.Pipeline, pixelFormat), deliveryPolicy)); }
/// <summary> /// Converts a stream of depth images into a stream of <see cref="PixelFormat.Gray_16bpp"/> format images. /// </summary> /// <param name="source">A producer of depth images.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <param name="sharedDepthImageAllocator ">Optional image allocator for creating new shared depth image.</param> /// <returns>A corresponding stream of images.</returns> public static IProducer <Shared <Image> > ToImage(this IProducer <Shared <DepthImage> > source, DeliveryPolicy <Shared <DepthImage> > deliveryPolicy = null, Func <int, int, Shared <Image> > sharedDepthImageAllocator = null) { sharedDepthImageAllocator ??= (width, height) => ImagePool.GetOrCreate(width, height, PixelFormat.Gray_16bpp); return(source.Process <Shared <DepthImage>, Shared <Image> >( (sharedDepthImage, envelope, emitter) => { using var sharedImage = sharedDepthImageAllocator(sharedDepthImage.Resource.Width, sharedDepthImage.Resource.Height); sharedImage.Resource.CopyFrom(sharedDepthImage.Resource); emitter.Post(sharedImage, envelope.OriginatingTime); }, deliveryPolicy)); }
/// <summary> /// Converts a shared image to a different pixel format using the specified transformer. /// </summary> /// <param name="source">Source image to compress.</param> /// <param name="transformer">Method for converting an image sample.</param> /// <param name="pixelFormat">Pixel format to use for converted image.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <param name="sharedImageAllocator">Optional image allocator for creating new shared image.</param> /// <returns>Returns a producer that generates the transformed images.</returns> public static IProducer <Shared <Image> > Transform(this IProducer <Shared <Image> > source, TransformDelegate transformer, PixelFormat pixelFormat, DeliveryPolicy <Shared <Image> > deliveryPolicy = null, Func <int, int, PixelFormat, Shared <Image> > sharedImageAllocator = null) { return(source.PipeTo(new ImageTransformer(source.Out.Pipeline, transformer, pixelFormat, sharedImageAllocator), deliveryPolicy)); }
/// <summary> /// Returns the position of a given joint in the body. /// </summary> /// <param name="source">The stream of kinect body.</param> /// <param name="jointType">The type of joint.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>The joint position as a 3D point in Kinect camera space.</returns> public static IProducer <Point3D> GetJointPosition(this IProducer <KinectBody> source, JointType jointType, DeliveryPolicy deliveryPolicy = null) { return(source.Select(kb => kb.Joints[jointType].Position.ToPoint3D(), deliveryPolicy)); }
/// <summary> /// Here we define an Psi extension. This extension will take a stream of images (source) /// and create a new stream of converted images. /// </summary> /// <param name="source">Our source producer (source stream of image samples)</param> /// <param name="deliveryPolicy">Our delivery policy (null means use the default)</param> /// <returns>The new stream of converted images.</returns> public static IProducer <Shared <Image> > ToGrayViaOpenCV(this IProducer <Shared <Image> > source, DeliveryPolicy deliveryPolicy = null) { // Process informs the pipeline that we want to call our lambda ("(srcImage, env, e) =>{...}") with each image // from the stream. return(source.Process <Shared <Image>, Shared <Image> >( (srcImage, env, e) => { // Our lambda here is called with each image sample from our stream and calls OpenCV to convert // the image into a grayscale image. We then post the resulting gray scale image to our event queu // so that the Psi pipeline will send it to the next component. // Have Psi allocate a new image. We will convert the current image ('srcImage') into this new image. using (var destImage = ImagePool.GetOrCreate(srcImage.Resource.Width, srcImage.Resource.Height, PixelFormat.Gray_8bpp)) { // Call into our OpenCV wrapper to convert the source image ('srcImage') into the newly created image ('destImage') // Note: since srcImage & destImage are Shared<> we need to access the Microsoft.Psi.Imaging.Image data via the Resource member OpenCVMethods.ToGray(srcImage.ToImageBuffer(), destImage.ToImageBuffer()); e.Post(destImage, env.OriginatingTime); } }, deliveryPolicy)); }
/// <summary> /// Resamples an audio stream. /// </summary> /// <param name="source">A stream of audio to be resampled.</param> /// <param name="configuration">The resampler configuration.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>A stream of resampled audio.</returns> public static IProducer <AudioBuffer> Resample(this IProducer <AudioBuffer> source, AudioResamplerConfiguration configuration, DeliveryPolicy deliveryPolicy = null) { return(source.PipeTo(new AudioResampler(source.Out.Pipeline, configuration), deliveryPolicy)); }
/// <summary> /// Resamples an audio stream. /// </summary> /// <param name="source">A stream audio to be resampled.</param> /// <param name="outputFormat">The desired audio output format for the resampled stream.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>A stream of resampled audio.</returns> public static IProducer <AudioBuffer> Resample(this IProducer <AudioBuffer> source, WaveFormat outputFormat, DeliveryPolicy deliveryPolicy = null) { return(Resample(source, new AudioResamplerConfiguration() { OutputFormat = outputFormat }, deliveryPolicy)); }
/// <summary> /// Performs object detection on a stream of images via the <a href="https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/">Microsoft Cognitive Services Vision API.</a>. /// </summary> /// <param name="source">The source stream of images.</param> /// <param name="subscriptionKey">The Azure subscription key to use.</param> /// <param name="region">The region for the Azure subscription.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>A stream of detected objects for each image.</returns> /// <remarks> /// A <a href="https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/">Microsoft Cognitive Services Vision API</a> /// subscription key is required to use this operator. For more information, see the full direct API for. /// <a href="https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/">Microsoft Cognitive Services Vision API</a> /// </remarks> public static IProducer <IList <DetectedObject> > DetectObjects(this IProducer <Shared <Imaging.Image> > source, string subscriptionKey, string region, DeliveryPolicy deliveryPolicy = null) { var imageAnalyzer = new ImageAnalyzer(source.Out.Pipeline, new ImageAnalyzerConfiguration(subscriptionKey, region, VisualFeatureTypes.Objects)); source.PipeTo(imageAnalyzer.In, deliveryPolicy); return(imageAnalyzer.Out.Select(ia => ia?.Objects)); }
/// <summary> /// Applies dithering to input sample values. /// </summary> /// <param name="source">A stream of floating point input sample values.</param> /// <param name="scaleFactor">The scale factor of the dither.</param> /// <param name="randomSeed">An initial random seed value.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>A stream of floating point sample values with dithering.</returns> public static IProducer <float[]> Dither(this IProducer <float[]> source, float scaleFactor, int randomSeed = 0, DeliveryPolicy deliveryPolicy = null) { float[] dithered = null; Random random = new Random(randomSeed); return(source.Select( values => { if (dithered == null || dithered.Length != values.Length) { dithered = new float[values.Length]; } for (int i = 0; i < values.Length; ++i) { dithered[i] = values[i] + (((((float)random.Next() / (float)int.MaxValue) * 2.0f) - 1.0f) * scaleFactor); } return dithered; }, deliveryPolicy)); }
/// <summary> /// Converts a stream of audio data to a stream of floating point values. /// </summary> /// <param name="source">A stream containing the input audio data.</param> /// <param name="format">The audio format of the input audio.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>A stream of floating point audio sample values.</returns> public static IProducer <float[]> ToFloat(this IProducer <byte[]> source, WaveFormat format, DeliveryPolicy deliveryPolicy = null) { return(source.PipeTo(new ToFloat(source.Out.Pipeline, format), deliveryPolicy)); }
/// <summary> /// Performs face recognition over a stream of images via <a href="https://azure.microsoft.com/en-us/services/cognitive-services/face/">Microsoft Cognitive Services Face API</a>. /// </summary> /// <param name="source">The source stream of images.</param> /// <param name="configuration">The face recognizer configuration.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>A stream of messages containing a dictionary that represents the set of identity alternates and their corresponding scores.</returns> /// <remarks> /// A <a href="https://azure.microsoft.com/en-us/services/cognitive-services/face/">Microsoft Cognitive Services Face API</a> /// subscription key is required to use this operators. In addition, a person group needs to be created ahead of time, and the id of the person group /// passed to the operator via the configuration. For more information, and to see how to create person groups, see the full direct API for /// <a href="https://azure.microsoft.com/en-us/services/cognitive-services/face/">Microsoft Cognitive Services Face API</a> /// </remarks> public static IProducer <Dictionary <string, double> > RecognizeFace(this IProducer <Shared <Imaging.Image> > source, FaceRecognizerConfiguration configuration, DeliveryPolicy deliveryPolicy = null) { var faceRecognizer = new FaceRecognizer(source.Out.Pipeline, configuration); source.PipeTo(faceRecognizer, deliveryPolicy); return(faceRecognizer.Out); }
/// <summary> /// Writes the specified stream to a multi-stream JSON store. /// </summary> /// <typeparam name="TIn">The type of messages in the stream</typeparam> /// <param name="source">The source stream to write</param> /// <param name="name">The name of the persisted stream.</param> /// <param name="writer">The store writer, created by e.g. <see cref="Create(Pipeline, string, string, bool)"/></param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>The input stream</returns> public static IProducer <TIn> Write <TIn>(this IProducer <TIn> source, string name, JsonExporter writer, DeliveryPolicy deliveryPolicy = null) { writer.Write(source.Out, name, deliveryPolicy); return(source); }
/// <summary> /// Writes the specified stream to this multi-stream store. /// </summary> /// <param name="source">The source stream to write</param> /// <param name="metadata">The stream metadata of the stream.</param> /// <param name="policy">An optional delivery policy</param> internal void Write(Emitter <Message <JToken> > source, JsonStreamMetadata metadata, DeliveryPolicy policy = null) { var mergeInput = this.merger.Add(metadata.Name); // this checks for duplicates this.writer.OpenStream(metadata); Operators.PipeTo(source, mergeInput, policy); }
/// <summary> /// Operator converts from an Image to a compressed (encoded) image /// </summary> /// <param name="source">Source image to encode</param> /// <param name="encoderFn">Method to perform encoding</param> /// <param name="deliveryPolicy">Delivery policy</param> /// <returns>Returns a producer that generates the encoded images</returns> public static IProducer <Shared <EncodedImage> > Encode(this IProducer <Shared <Image> > source, Func <BitmapEncoder> encoderFn, DeliveryPolicy deliveryPolicy = null) { return(source.PipeTo(new ImageEncoder(source.Out.Pipeline, encoderFn), deliveryPolicy)); }
/// <summary> /// Converts the source image to a different pixel format. /// </summary> /// <param name="source">The source stream.</param> /// <param name="pixelFormat">The pixel format to convert to.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <param name="sharedImageAllocator">Optional image allocator for creating new shared image.</param> /// <returns>The resulting stream.</returns> public static IProducer <Shared <Image> > ToPixelFormat(this IProducer <Shared <Image> > source, PixelFormat pixelFormat, DeliveryPolicy <Shared <Image> > deliveryPolicy = null, Func <int, int, PixelFormat, Shared <Image> > sharedImageAllocator = null) { return(source.PipeTo(new ToPixelFormat(source.Out.Pipeline, pixelFormat, sharedImageAllocator), deliveryPolicy)); }
/// <summary> /// Operator converts from an Image to a compressed JPEG image /// </summary> /// <param name="source">Source image to compress</param> /// <param name="quality">JPEG quality to use</param> /// <param name="deliveryPolicy">Delivery policy</param> /// <returns>Returns a producer that generates the JPEG images</returns> public static IProducer <Shared <EncodedImage> > EncodeJpeg(this IProducer <Shared <Image> > source, int quality = 90, DeliveryPolicy deliveryPolicy = null) { return(Encode(source, () => new JpegBitmapEncoder { QualityLevel = quality }, deliveryPolicy)); }
/// <summary> /// Simple producer for converting from depth map to colored version of depth map. /// </summary> /// <param name="depthImage">Depth image to convert.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>Returns colored representation of the depth map.</returns> public static IProducer <Shared <Image> > ToColor(this IProducer <Shared <Image> > depthImage, DeliveryPolicy deliveryPolicy = null) { return(depthImage.PipeTo(new DepthToColorConverter(depthImage.Out.Pipeline), deliveryPolicy)); }
/// <summary> /// Operator converts from an Image to a compressed PNG image /// </summary> /// <param name="source">Source image to compress</param> /// <param name="deliveryPolicy">Delivery policy</param> /// <returns>Returns a producer that generates the PNG images</returns> public static IProducer <Shared <EncodedImage> > EncodePng(this IProducer <Shared <Image> > source, DeliveryPolicy deliveryPolicy = null) { return(Encode(source, () => new PngBitmapEncoder(), deliveryPolicy)); }
/// <summary> /// Uncompressed a depth map that was previously compressed with GZip. /// </summary> /// <param name="compressedDepthBytes">Byte array of compressed depth values.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>Uncompressed depth map as an image.</returns> public static IProducer <Shared <Image> > GZipUncompressDepthImage(this IProducer <byte[]> compressedDepthBytes, DeliveryPolicy deliveryPolicy = null) { var buffer = new byte[424 * 512 * 2]; return(compressedDepthBytes.Select( bytes => { using (var compressedStream = new GZipStream(new MemoryStream(bytes), CompressionMode.Decompress)) { compressedStream.Read(buffer, 0, buffer.Length); } var psiImage = ImagePool.GetOrCreate(512, 424, PixelFormat.Gray_16bpp); psiImage.Resource.CopyFrom(buffer); return psiImage; }, deliveryPolicy)); }
/// <summary> /// Operator decodes a sample that was previously encoded /// </summary> /// <param name="source">Source image to compress</param> /// <param name="deliveryPolicy">Delivery policy</param> /// <returns>Returns a producer that generates the decoded images</returns> public static IProducer <Shared <Image> > Decode(this IProducer <Shared <EncodedImage> > source, DeliveryPolicy deliveryPolicy = null) { return(source.PipeTo(new ImageDecoder(source.Out.Pipeline), deliveryPolicy)); }
/// <summary> /// Converts an image to a different pixel format using the specified transformer. /// </summary> /// <param name="source">Source image to compress.</param> /// <param name="transformer">Method for converting an image sample.</param> /// <param name="pixelFormat">Pixel format to use for converted image.</param> /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>Returns a producer that generates the transformed images.</returns> public static IProducer <Shared <Image> > Transform(this IProducer <Shared <Image> > source, TransformDelegate transformer, PixelFormat pixelFormat, DeliveryPolicy deliveryPolicy = null) { return(source.PipeTo(new ImageTransformer(source.Out.Pipeline, transformer, pixelFormat), deliveryPolicy)); }
/// <summary> /// Transforms a stream of byte arrays containing raw audio to an <see cref="AudioBuffer"/> stream. /// </summary> /// <param name="source">A stream of raw audio byte arrays.</param> /// <param name="audioFormat">The audio format of the raw audio contained within the byte arrays.</param> /// /// <param name="deliveryPolicy">An optional delivery policy.</param> /// <returns>A stream of audio buffers.</returns> public static IProducer <AudioBuffer> ToAudioBuffer(this IProducer <byte[]> source, WaveFormat audioFormat, DeliveryPolicy deliveryPolicy = null) { return(source.Select(x => new AudioBuffer(x, audioFormat), deliveryPolicy)); }