Add live capture diagnostic logging to live.log
- Logs device name, audio format (bit depth, channels, sample rate) - Logs dataAvailable callbacks (every 100th): bytes, samples, buffer size - Logs FFT ticks (every 50th): melody energy, freq, intensity A/B - Warning if data arrives but buffer doesn't fill - Logs totals + any exception on stop - Writes to live.log next to .exe, cleared on each start
This commit is contained in:
+49
-2
@@ -1,6 +1,7 @@
|
|||||||
using FftSharp;
|
using FftSharp;
|
||||||
using NAudio.CoreAudioApi;
|
using NAudio.CoreAudioApi;
|
||||||
using NAudio.Wave;
|
using NAudio.Wave;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Substation;
|
namespace Substation;
|
||||||
|
|
||||||
@@ -21,6 +22,23 @@ public class LiveCapture : IDisposable
|
|||||||
int _fftIndexInTick;
|
int _fftIndexInTick;
|
||||||
readonly DrumDetector _drums = new();
|
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 event Action? Stopped;
|
||||||
|
|
||||||
public LiveCapture(State state, MMDevice device)
|
public LiveCapture(State state, MMDevice device)
|
||||||
@@ -36,6 +54,12 @@ public class LiveCapture : IDisposable
|
|||||||
_sampleRate = _capture.WaveFormat.SampleRate;
|
_sampleRate = _capture.WaveFormat.SampleRate;
|
||||||
_fftsPerTick = Math.Max(1, (int)Math.Round(MusicAnalyzer.TickDuration * _sampleRate / MusicAnalyzer.HopSize));
|
_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.DataAvailable += OnDataAvailable;
|
||||||
_capture.RecordingStopped += OnRecordingStopped;
|
_capture.RecordingStopped += OnRecordingStopped;
|
||||||
|
|
||||||
@@ -43,7 +67,8 @@ public class LiveCapture : IDisposable
|
|||||||
_processThread.Start();
|
_processThread.Start();
|
||||||
|
|
||||||
_capture.StartRecording();
|
_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()
|
public void Stop()
|
||||||
@@ -59,6 +84,12 @@ public class LiveCapture : IDisposable
|
|||||||
int frameSize = channels * bytesPerSample;
|
int frameSize = channels * bytesPerSample;
|
||||||
int sampleCount = e.BytesRecorded / frameSize;
|
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)
|
lock (_bufferLock)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < sampleCount; i++)
|
for (int i = 0; i < sampleCount; i++)
|
||||||
@@ -81,7 +112,9 @@ public class LiveCapture : IDisposable
|
|||||||
{
|
{
|
||||||
_running = false;
|
_running = false;
|
||||||
Stopped?.Invoke();
|
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()
|
void ProcessLoop()
|
||||||
@@ -91,6 +124,8 @@ public class LiveCapture : IDisposable
|
|||||||
var tf = new MusicAnalyzer.TickFeature();
|
var tf = new MusicAnalyzer.TickFeature();
|
||||||
double[]? overlap = null; // last HopSize samples from previous window
|
double[]? overlap = null; // last HopSize samples from previous window
|
||||||
|
|
||||||
|
Log("[live] process thread started");
|
||||||
|
|
||||||
while (_running)
|
while (_running)
|
||||||
{
|
{
|
||||||
double[]? windowData = null;
|
double[]? windowData = null;
|
||||||
@@ -120,6 +155,8 @@ public class LiveCapture : IDisposable
|
|||||||
|
|
||||||
if (windowData == null)
|
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);
|
Thread.Sleep(5);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -128,6 +165,8 @@ public class LiveCapture : IDisposable
|
|||||||
var spectrum = FFT.Forward(windowData);
|
var spectrum = FFT.Forward(windowData);
|
||||||
var mag = FFT.Magnitude(spectrum);
|
var mag = FFT.Magnitude(spectrum);
|
||||||
|
|
||||||
|
_fftCount++;
|
||||||
|
|
||||||
var (melodyEnergy, melodyFreq) = MusicAnalyzer.ExtractFeatures(mag, _sampleRate);
|
var (melodyEnergy, melodyFreq) = MusicAnalyzer.ExtractFeatures(mag, _sampleRate);
|
||||||
|
|
||||||
// Adaptive normalization: running max with slow decay
|
// Adaptive normalization: running max with slow decay
|
||||||
@@ -151,6 +190,14 @@ public class LiveCapture : IDisposable
|
|||||||
_state.EnqueueStream('A', new[] { frameA });
|
_state.EnqueueStream('A', new[] { frameA });
|
||||||
_state.EnqueueStream('B', new[] { frameB });
|
_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();
|
tf = new MusicAnalyzer.TickFeature();
|
||||||
_fftIndexInTick = 0;
|
_fftIndexInTick = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user