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.IO; 9 using System.Collections.Generic; 10 using Antmicro.Renode.Exceptions; 11 using Antmicro.Renode.Utilities; 12 using Antmicro.Renode.Logging; 13 14 namespace Antmicro.Renode.Peripherals.Sound 15 { 16 public class PCMDecoder 17 { PCMDecoder(uint sampleWidthBits, uint samplingRateHz, uint numberOfChannels, bool concatenatedChannels, IPeripheral loggingParent = null)18 public PCMDecoder(uint sampleWidthBits, uint samplingRateHz, uint numberOfChannels, bool concatenatedChannels, IPeripheral loggingParent = null) 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 bits are currently supported."); 28 } 29 30 samples = new Queue<uint>(); 31 32 this.sampleWidthBits = sampleWidthBits; 33 this.numberOfChannels = numberOfChannels; 34 this.samplingRateHz = samplingRateHz; 35 36 this.loggingParent = loggingParent; 37 } 38 Reset()39 public void Reset() 40 { 41 lock(samples) 42 { 43 samples.Clear(); 44 } 45 } 46 LoadFile(ReadFilePath path)47 public void LoadFile(ReadFilePath path) 48 { 49 var sampleSize = (int)(sampleWidthBits / 8); 50 51 lock(samples) 52 { 53 using(var br = new BinaryReader(File.Open(path, FileMode.Open))) 54 { 55 while(true) 56 { 57 var bytes = br.ReadBytes(sampleSize); 58 if(bytes.Length == 0) 59 { 60 break; 61 } 62 63 var sample = BitHelper.ToUInt32(bytes, 0, bytes.Length, false); 64 samples.Enqueue(sample); 65 } 66 } 67 } 68 } 69 LoadSample(uint sample)70 public void LoadSample(uint sample) 71 { 72 var croppedSample = BitHelper.GetMaskedValue(sample, 0, (int)sampleWidthBits); 73 if(croppedSample != sample) 74 { 75 loggingParent?.Log(LogLevel.Warning, "Cropping the sample from 0x{0:X} to 0x{1:X} to fit to the sample width ({2} bits)", sample, croppedSample, sampleWidthBits); 76 } 77 78 lock(samples) 79 { 80 samples.Enqueue(croppedSample); 81 } 82 } 83 GetSingleSample()84 public uint GetSingleSample() 85 { 86 return samples.TryDequeue(out var sample) ? sample : 0u; 87 } 88 GetSamplesByCount(uint samplesCountPerChannel)89 public IEnumerable<uint> GetSamplesByCount(uint samplesCountPerChannel) 90 { 91 lock(samples) 92 { 93 return samples.DequeueRange((int)(samplesCountPerChannel * numberOfChannels)); 94 } 95 } 96 GetSamplesByTime(uint ms)97 public IEnumerable<uint> GetSamplesByTime(uint ms) 98 { 99 lock(samples) 100 { 101 var samplesPerChannel = ms * (samplingRateHz / 1000); 102 return GetSamplesByCount(samplesPerChannel); 103 } 104 } 105 106 private readonly uint samplingRateHz; 107 private readonly uint numberOfChannels; 108 private readonly uint sampleWidthBits; 109 110 private readonly IPeripheral loggingParent; 111 private readonly Queue<uint> samples; 112 } 113 } 114