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