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