i2s: Add playback output to a file

We needed a way to double check the outputs of our recording in a
loopback. This allows us to record our playback audio from the
TX FIFO and store to disk in a raw file.

Additionally, this adjusts the output file to /tmp/speaker.raw.

Change-Id: I20058fdd3afa1e34561cae13e0d9ebd03a72f3ba
diff --git a/shodan.resc b/shodan.resc
index 6919f04..d83a718 100644
--- a/shodan.resc
+++ b/shodan.resc
@@ -78,11 +78,11 @@
 # for sampling data. Note that the format for these files is raw
 # sample data, left channel followed by right.
 $i2s_mic_audio_file     ?= @sim/config/shodan_infrastructure/test.raw
-$i2s_speaker_audio_file ?= @out/speaker.raw
+$i2s_speaker_audio_file ?= @/tmp/speaker.raw
 
 # Uncomment these lines to enable audio
-# i2s0 InputFile $i2s_mic_audio_file
-# i2s0 OutputFile $i2s_speaker_audio_file
+#i2s0 InputFile $i2s_mic_audio_file
+#i2s0 OutputFile $i2s_speaker_audio_file
 
 # Start cpu0 at the bootrom reset vector, which is stored immediately after the
 # bootrom interrupt vector table at 0x8080.
@@ -109,3 +109,4 @@
 cpu0 IsHalted true
 cpu1 IsHalted true
 cpu2 IsHalted true
+
diff --git a/shodan_infrastructure/MatchaI2S.cs b/shodan_infrastructure/MatchaI2S.cs
index 5ef67e2..0591490 100644
--- a/shodan_infrastructure/MatchaI2S.cs
+++ b/shodan_infrastructure/MatchaI2S.cs
@@ -202,7 +202,17 @@
 
         private void StartTx()
         {
+            if (OutputFile == "") {
+                this.Log(LogLevel.Error, "Starting playback without an output file!");
+                return;
+            }
+
             this.Log(LogLevel.Info, "Starting playback.");
+
+            encoder = new PCMEncoder(SampleSizeBits, SampleFrequencyHz, NumChannels, false);
+            encoder.Output = OutputFile;
+            encoder.SetBufferingBySamplesCount(1);
+
             txThread = machine.ObtainManagedThread(TransmitFrame, SampleFrequencyHz);
             txThread.Start();
         }
@@ -225,7 +235,9 @@
                 return;
             }
 
-            txBuffer.Read();
+            ulong sample = txBuffer.Read();
+            encoder.AcceptSample((uint)((sample & 0xFFFF0000) >> 16)); // left
+            encoder.AcceptSample((uint)(sample & 0x0000FFFF));         // right
             this.Log(LogLevel.Noisy, "Removed sample. Level: {0}", txBuffer.Count);
 
             if (txBuffer.Count < mappedTxTriggerLevel)