1 //
2 // Copyright (c) 2010-2022 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 Antmicro.Renode.Core;
9 using Antmicro.Renode.Logging;
10 using Antmicro.Renode.Time;
11 
12 namespace Antmicro.Renode.Peripherals.Sound
13 {
14     public class LiteX_I2S_Slave : LiteX_I2S
15     {
LiteX_I2S_Slave(IMachine machine, DataFormat format, uint sampleWidthBits, uint samplingRateHz, uint numberOfChannels)16         public LiteX_I2S_Slave(IMachine machine, DataFormat format, uint sampleWidthBits, uint samplingRateHz, uint numberOfChannels) : base(machine, format, sampleWidthBits, samplingRateHz)
17         {
18             decoder = new PCMDecoder(sampleWidthBits, samplingRateHz, numberOfChannels, false, this);
19             SamplesPushFrequency = 100;
20         }
21 
Reset()22         public override void Reset()
23         {
24             base.Reset();
25             TryStopPusher();
26 
27             decoder.Reset();
28             globalEnabled = false;
29         }
30 
FeedSample(uint sample, int count = 1)31         public void FeedSample(uint sample, int count = 1)
32         {
33             for(var i = 0; i < count; i++)
34             {
35                 decoder.LoadSample(sample);
36             }
37 
38             TryStartPusher();
39         }
40 
LoadPCM(string path)41         public void LoadPCM(string path)
42         {
43             decoder.LoadFile(path);
44             TryStartPusher();
45         }
46 
ForceBufferClear()47         public void ForceBufferClear()
48         {
49             lock(buffer)
50             {
51                 buffer.Clear();
52                 UpdateInterrupts();
53             }
54         }
55 
56         public uint SamplesPushFrequency { get; set; }
57 
IsReady()58         protected override bool IsReady()
59         {
60             return (buffer.Count >= fifoIrqThreshold);
61         }
62 
HandleEnable(bool enabled)63         protected override void HandleEnable(bool enabled)
64         {
65             globalEnabled = enabled;
66             if(enabled)
67             {
68                 TryStartPusher();
69             }
70             else
71             {
72                 TryStopPusher();
73             }
74         }
75 
TryStartPusher()76         private bool TryStartPusher()
77         {
78             if(!globalEnabled)
79             {
80                 return false;
81             }
82 
83             // stop a previous thread (if any)
84             TryStopPusher();
85 
86             pusher = machine.ObtainManagedThread(PushSamples, SamplesPushFrequency);
87             pusher.Start();
88 
89             return true;
90         }
91 
TryStopPusher()92         private bool TryStopPusher()
93         {
94             if(pusher == null)
95             {
96                 return false;
97             }
98 
99             pusher.Stop();
100             pusher = null;
101             previousPushTimestamp = null;
102             return true;
103         }
104 
PushSamples()105         private void PushSamples()
106         {
107             var currentTimestamp = machine.LocalTimeSource.ElapsedVirtualTime;
108             if(!previousPushTimestamp.HasValue)
109             {
110                 previousPushTimestamp = currentTimestamp;
111                 return;
112             }
113 
114             var timeDiff = currentTimestamp - previousPushTimestamp.Value;
115             previousPushTimestamp = currentTimestamp;
116 
117             var any = false;
118             var samples = decoder.GetSamplesByTime((uint)(timeDiff.TotalMicroseconds / 1000));
119             foreach(var sample in samples)
120             {
121                 any = true;
122                 buffer.Enqueue(sample);
123             }
124             UpdateInterrupts();
125 
126             if(!any)
127             {
128                 TryStopPusher();
129             }
130         }
131 
132         private IManagedThread pusher;
133         private TimeInterval? previousPushTimestamp;
134         private bool globalEnabled;
135 
136         private readonly PCMDecoder decoder;
137     }
138 }
139