1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 8 using System; 9 using System.Collections.Generic; 10 using System.IO; 11 using Antmicro.Renode.Exceptions; 12 using Antmicro.Renode.Utilities; 13 14 namespace Antmicro.Renode.Sound 15 { 16 public class PCMEncoder : IDisposable 17 { PCMEncoder(uint sampleWidthBits, uint samplingRateHz, uint numberOfChannels, bool concatenatedChannels)18 public PCMEncoder(uint sampleWidthBits, uint samplingRateHz, uint numberOfChannels, bool concatenatedChannels) 19 { 20 if(concatenatedChannels) 21 { 22 throw new ConstructionException("Concatenated channels are currently not supported"); 23 } 24 25 if(sampleWidthBits != 8u && sampleWidthBits != 16u && sampleWidthBits != 24u && sampleWidthBits != 32u) 26 { 27 throw new ConstructionException($"Not supported sample width: {0}. Only 8/16/24/32 values are currently supported."); 28 } 29 30 this.samplingRateHz = samplingRateHz; 31 this.sampleWidthBits = sampleWidthBits; 32 this.numberOfChannels = numberOfChannels; 33 34 buffer = new Queue<byte>(); 35 } 36 Dispose()37 public void Dispose() 38 { 39 // flush and close the output file (if any) 40 Output = null; 41 } 42 AcceptSample(uint sample)43 public void AcceptSample(uint sample) 44 { 45 lock(buffer) 46 { 47 for(var i = 0; i < (sampleWidthBits / 8); i++) 48 { 49 buffer.Enqueue((byte)BitHelper.GetValue(sample, (int)(sampleWidthBits - 8 * (i + 1)), 8)); 50 } 51 TryFlushBuffer(); 52 } 53 } 54 FlushBuffer()55 public void FlushBuffer() 56 { 57 byte[] data; 58 lock(buffer) 59 { 60 data = buffer.DequeueAll(); 61 } 62 63 file.Write(data, 0, data.Length); 64 file.Flush(); 65 } 66 SetBufferingBySamplesCount(uint samplesCount)67 public void SetBufferingBySamplesCount(uint samplesCount) 68 { 69 bufferingThreshold = (int)(samplesCount * numberOfChannels * (sampleWidthBits / 8)); 70 TryFlushBuffer(); 71 } 72 SetBufferingByTime(uint milliseconds)73 public void SetBufferingByTime(uint milliseconds) 74 { 75 bufferingThreshold = (int)(milliseconds * (samplingRateHz / 1000) * numberOfChannels * (sampleWidthBits / 8)); 76 TryFlushBuffer(); 77 } 78 79 public string Output 80 { 81 get => outputPath; 82 83 set 84 { 85 outputPath = value; 86 if(value == null) 87 { 88 if(file != null) 89 { 90 file.Dispose(); 91 file = null; 92 } 93 } 94 else 95 { 96 file = File.OpenWrite(value); 97 } 98 } 99 } 100 TryFlushBuffer()101 private void TryFlushBuffer() 102 { 103 if(buffer.Count >= bufferingThreshold) 104 { 105 FlushBuffer(); 106 } 107 } 108 109 private int bufferingThreshold; 110 private string outputPath; 111 private FileStream file; 112 113 private readonly uint samplingRateHz; 114 private readonly uint numberOfChannels; 115 private readonly uint sampleWidthBits; 116 117 private readonly Queue<byte> buffer; 118 } 119 } 120