diff --git a/LiveCapture.cs b/LiveCapture.cs index d1b7a79..ab48f61 100644 --- a/LiveCapture.cs +++ b/LiveCapture.cs @@ -1,6 +1,7 @@ using FftSharp; using NAudio.CoreAudioApi; using NAudio.Wave; +using System.IO; namespace Substation; @@ -21,6 +22,23 @@ public class LiveCapture : IDisposable int _fftIndexInTick; readonly DrumDetector _drums = new(); + int _dataAvailableCount; + int _totalSamplesReceived; + int _fftCount; + int _tickCount; + + static readonly string LogPath = Path.Combine(AppContext.BaseDirectory, "live.log"); + static readonly object LogLock = new(); + + static void Log(string msg) + { + var line = $"{DateTime.Now:HH:mm:ss.fff} {msg}"; + lock (LogLock) + { + try { File.AppendAllText(LogPath, line + Environment.NewLine); } catch { } + } + } + public event Action? Stopped; public LiveCapture(State state, MMDevice device) @@ -36,6 +54,12 @@ public class LiveCapture : IDisposable _sampleRate = _capture.WaveFormat.SampleRate; _fftsPerTick = Math.Max(1, (int)Math.Round(MusicAnalyzer.TickDuration * _sampleRate / MusicAnalyzer.HopSize)); + try { File.WriteAllText(LogPath, ""); } catch { } + + Log($"[live] device: {_device.FriendlyName}"); + Log($"[live] format: {_capture.WaveFormat} ({_capture.WaveFormat.BitsPerSample}bit, {_capture.WaveFormat.Channels}ch, {_sampleRate}Hz)"); + Log($"[live] fftsPerTick: {_fftsPerTick}"); + _capture.DataAvailable += OnDataAvailable; _capture.RecordingStopped += OnRecordingStopped; @@ -43,7 +67,8 @@ public class LiveCapture : IDisposable _processThread.Start(); _capture.StartRecording(); - Console.WriteLine($"[live] capture started: {_device.FriendlyName} ({_sampleRate}Hz, {_capture.WaveFormat.Channels}ch)"); + Log($"[live] capture started"); + Log($"[live] log file: {LogPath}"); } public void Stop() @@ -59,6 +84,12 @@ public class LiveCapture : IDisposable int frameSize = channels * bytesPerSample; int sampleCount = e.BytesRecorded / frameSize; + _dataAvailableCount++; + _totalSamplesReceived += sampleCount; + + if (_dataAvailableCount % 100 == 1) + Log($"[live] dataAvailable #{_dataAvailableCount}: {e.BytesRecorded} bytes, {sampleCount} samples, total={_totalSamplesReceived}, buffer={_sampleBuffer.Count}"); + lock (_bufferLock) { for (int i = 0; i < sampleCount; i++) @@ -81,7 +112,9 @@ public class LiveCapture : IDisposable { _running = false; Stopped?.Invoke(); - Console.WriteLine("[live] capture stopped"); + Log($"[live] capture stopped. dataAvailable={_dataAvailableCount}, totalSamples={_totalSamplesReceived}, ffts={_fftCount}, ticks={_tickCount}"); + if (e?.Exception != null) + Log($"[live] ERROR stop exception: {e.Exception.Message}"); } void ProcessLoop() @@ -91,6 +124,8 @@ public class LiveCapture : IDisposable var tf = new MusicAnalyzer.TickFeature(); double[]? overlap = null; // last HopSize samples from previous window + Log("[live] process thread started"); + while (_running) { double[]? windowData = null; @@ -120,6 +155,8 @@ public class LiveCapture : IDisposable if (windowData == null) { + if (_fftCount == 0 && _dataAvailableCount > 0 && _dataAvailableCount % 200 == 0) + Log($"[live] WARNING: data available ({_dataAvailableCount} callbacks, {_totalSamplesReceived} samples) but buffer has only {_sampleBuffer.Count} samples (need {MusicAnalyzer.WindowSize})"); Thread.Sleep(5); continue; } @@ -128,6 +165,8 @@ public class LiveCapture : IDisposable var spectrum = FFT.Forward(windowData); var mag = FFT.Magnitude(spectrum); + _fftCount++; + var (melodyEnergy, melodyFreq) = MusicAnalyzer.ExtractFeatures(mag, _sampleRate); // Adaptive normalization: running max with slow decay @@ -151,6 +190,14 @@ public class LiveCapture : IDisposable _state.EnqueueStream('A', new[] { frameA }); _state.EnqueueStream('B', new[] { frameB }); + _tickCount++; + if (_tickCount % 50 == 1) + { + int intA = (frameA.Intensity[0] + frameA.Intensity[1] + frameA.Intensity[2] + frameA.Intensity[3]) / 4; + int intB = (frameB.Intensity[0] + frameB.Intensity[1] + frameB.Intensity[2] + frameB.Intensity[3]) / 4; + Log($"[live] tick #{_tickCount}: ffts={_fftCount}, melodyEnergy={melodyEnergy:F4}, maxMelody={_liveMaxMelodyEnergy:F4}, freq={melodyFreq:F0}Hz, intA={intA}, intB={intB}, freqA={frameA.Freq[0]}, freqB={frameB.Freq[0]}"); + } + tf = new MusicAnalyzer.TickFeature(); _fftIndexInTick = 0; }