From 89b19990484e3a6cb9f69c3f5bfb9814b9955295 Mon Sep 17 00:00:00 2001 From: Alex Savin Date: Fri, 15 Aug 2025 19:08:38 -0400 Subject: [PATCH] Refactor main.go to remove slog logger integration and replace with standard log package --- main.go | 91 +++++++++++++-------------------------------------------- 1 file changed, 20 insertions(+), 71 deletions(-) diff --git a/main.go b/main.go index 46523d5..ec42ee2 100644 --- a/main.go +++ b/main.go @@ -3,7 +3,7 @@ package main import ( "flag" "fmt" - "log/slog" + "log" "math" "os" "time" @@ -22,7 +22,6 @@ var ( hopMs = flag.Int("hop", 50, "Hop size (ms)") ratioThresh = flag.Float64("ratio", 0.65, "Power ratio threshold for tone detection") rmsThresh = flag.Float64("rms", 300.0, "Minimum RMS for valid signal") - verbose = flag.Bool("verbose", false, "Enable debug logging") ) // Goertzel struct for frequency detection @@ -33,7 +32,6 @@ type goertzel struct { coeff float64 } -// newGoertzel initializes and returns a new instance of the Goertzel algorithm for detecting a specific target frequency. func newGoertzel(targetHz float64, fs float64, N int) *goertzel { g := &goertzel{N: N, fs: fs} g.k = int(0.5 + (float64(g.N)*targetHz)/fs) @@ -42,7 +40,6 @@ func newGoertzel(targetHz float64, fs float64, N int) *goertzel { return g } -// Power computes the power of the target frequency in the input signal x using the Goertzel algorithm. func (g *goertzel) Power(x []float64) float64 { var s0, s1, s2 float64 for i := 0; i < g.N; i++ { @@ -56,7 +53,6 @@ func (g *goertzel) Power(x []float64) float64 { return real*real + imag*imag } -// windowHann applies a Hann window to the input slice x in-place. func windowHann(x []float64) { n := float64(len(x)) for i := range x { @@ -64,7 +60,6 @@ func windowHann(x []float64) { } } -// pcmToFloat converts a slice of 16-bit PCM audio samples to a slice of float64 values. func pcmToFloat(buf []int16, N int) []float64 { out := make([]float64, N) for i := 0; i < N && i < len(buf); i++ { @@ -73,7 +68,6 @@ func pcmToFloat(buf []int16, N int) []float64 { return out } -// rmsPCM calculates the root mean square (RMS) value of a slice of 16-bit PCM audio samples. func rmsPCM(buf []int16) float64 { var s float64 for _, v := range buf { @@ -102,16 +96,16 @@ type twoToneDetector struct { aFreq float64 aAccumMs int aStart time.Time + aEnd time.Time waitingB bool bFreq float64 bAccumMs int bStart time.Time + bEnd time.Time gapRemainMs int - logger *slog.Logger } -// newTwoToneDetector creates and initializes a twoToneDetector instance with the specified parameters. -func newTwoToneDetector(fs, winN, hopN int, ratioThresh, rmsThresh float64, minAms, minBms, gapMaxMs int, logger *slog.Logger) *twoToneDetector { +func newTwoToneDetector(fs, winN, hopN int, ratioThresh, rmsThresh float64, minAms, minBms, gapMaxMs int) *twoToneDetector { freqs := make([]float64, 0) for f := 300.0; f <= 3000.0; f += 10.0 { freqs = append(freqs, f) @@ -131,11 +125,9 @@ func newTwoToneDetector(fs, winN, hopN int, ratioThresh, rmsThresh float64, minA gapMaxMs: gapMaxMs, freqs: freqs, gzBank: gzBank, - logger: logger, } } -// stepWindow processes a window of PCM audio samples to detect a two-tone event. func (d *twoToneDetector) stepWindow(pcms []int16, t0 time.Time) (event string, aFreq, aDur, bFreq, bDur float64, timestamp time.Time) { xi := pcmToFloat(pcms, d.winN) windowHann(xi) @@ -150,10 +142,6 @@ func (d *twoToneDetector) stepWindow(pcms []int16, t0 time.Time) (event string, now := t0 if r < d.rmsThresh { - d.logger.Debug("RMS below threshold, resetting", - "time", now.Format(time.RFC3339), - "rms", fmt.Sprintf("%.2f", r), - "threshold", d.rmsThresh) d.reset() return "", 0, 0, 0, 0, time.Time{} } @@ -170,10 +158,6 @@ func (d *twoToneDetector) stepWindow(pcms []int16, t0 time.Time) (event string, } ratio := bestPow / (total + 1e-12) if ratio < d.ratioThresh { - d.logger.Debug("Ratio below threshold, resetting", - "time", now.Format(time.RFC3339), - "ratio", fmt.Sprintf("%.3f", ratio), - "threshold", d.ratioThresh) d.reset() return "", 0, 0, 0, 0, time.Time{} } @@ -183,30 +167,23 @@ func (d *twoToneDetector) stepWindow(pcms []int16, t0 time.Time) (event string, // Looking for Tone A d.inA = true d.aFreq = freq - d.aAccumMs = int(hopDur.Milliseconds()) d.aStart = now } else if d.inA && !d.waitingB { // Confirming Tone A if math.Abs(freq-d.aFreq) <= 10.0 { d.aAccumMs += int(hopDur.Milliseconds()) + d.aEnd = now.Add(hopDur) if d.aAccumMs >= d.minAms { d.inA = false d.waitingB = true d.gapRemainMs = d.gapMaxMs } } else { - d.logger.Debug("Frequency differs from Tone A, resetting", - "time", now.Format(time.RFC3339), - "freq", fmt.Sprintf("%.1f", freq), - "tone_a_freq", fmt.Sprintf("%.1f", d.aFreq)) d.reset() } } else if d.waitingB { d.gapRemainMs -= int(hopDur.Milliseconds()) if d.gapRemainMs <= 0 { - d.logger.Debug("Gap exceeded max duration, resetting", - "time", now.Format(time.RFC3339), - "gap_max_ms", d.gapMaxMs) d.reset() } else if math.Abs(freq-d.aFreq) > 10.0 { // Check for Tone B @@ -214,91 +191,66 @@ func (d *twoToneDetector) stepWindow(pcms []int16, t0 time.Time) (event string, d.bFreq = freq d.bStart = now } else if math.Abs(freq-d.bFreq) > 10.0 { - d.logger.Debug("Frequency differs from Tone B, resetting B", - "time", now.Format(time.RFC3339), - "freq", fmt.Sprintf("%.1f", freq), - "tone_b_freq", fmt.Sprintf("%.1f", d.bFreq)) d.bFreq = freq d.bAccumMs = 0 d.bStart = now } d.bAccumMs += int(hopDur.Milliseconds()) + d.bEnd = now.Add(hopDur) if d.bAccumMs >= d.minBms { event = "TWO_TONE_DETECTED" - d.logger.Info("Two-tone detected", - "time", now.Format(time.RFC3339), - "tone_a_freq", fmt.Sprintf("%.1f", d.aFreq), - "tone_a_duration_ms", d.aAccumMs, - "tone_b_freq", fmt.Sprintf("%.1f", d.bFreq), - "tone_b_duration_ms", d.bAccumMs) - return event, d.aFreq, float64(d.aAccumMs), d.bFreq, float64(d.bAccumMs), now + aDurMs := float64(d.aEnd.Sub(d.aStart).Milliseconds()) + bDurMs := float64(d.bEnd.Sub(d.bStart).Milliseconds()) + return event, d.aFreq, aDurMs, d.bFreq, bDurMs, now } } } return "", 0, 0, 0, 0, time.Time{} } -// reset reinitializes all internal state fields of the twoToneDetector. func (d *twoToneDetector) reset() { d.inA = false d.aFreq = 0 d.aAccumMs = 0 d.aStart = time.Time{} + d.aEnd = time.Time{} d.waitingB = false d.bFreq = 0 d.bAccumMs = 0 d.bStart = time.Time{} + d.bEnd = time.Time{} d.gapRemainMs = 0 } func main() { flag.Parse() - - // Initialize slog logger - logLevel := &slog.LevelVar{} - logLevel.Set(slog.LevelInfo) - if *verbose { - logLevel.Set(slog.LevelDebug) - } - logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ - Level: logLevel, - })) - if *wavFile == "" { - logger.Error("WAV file path is required", "flag", "-wav") - os.Exit(1) + log.Fatal("WAV file path is required (use -wav flag)") } file, err := os.Open(*wavFile) if err != nil { - logger.Error("Failed to open WAV file", "error", err) - os.Exit(1) + log.Fatalf("Failed to open WAV file: %v", err) } defer file.Close() decoder := wav.NewDecoder(file) if !decoder.IsValidFile() { - logger.Error("Invalid WAV file") - os.Exit(1) + log.Fatal("Invalid WAV file") } if decoder.Format().SampleRate != 8000 || decoder.Format().NumChannels != 1 { - logger.Error("WAV file must be mono 8kHz", - "sample_rate", decoder.Format().SampleRate, - "channels", decoder.Format().NumChannels) - os.Exit(1) + log.Fatalf("WAV file must be mono 8kHz, got %d Hz, %d channels", + decoder.Format().SampleRate, decoder.Format().NumChannels) } const fs = 8000 winN := int(float64(fs) * float64(*winMs) / 1000.0) hopN := int(float64(fs) * float64(*hopMs) / 1000.0) if winN <= 0 || hopN <= 0 || hopN > winN { - logger.Error("Invalid window/hop parameters", - "winN", winN, - "hopN", hopN) - os.Exit(1) + log.Fatalf("Invalid window/hop: winN=%d, hopN=%d", winN, hopN) } - det := newTwoToneDetector(fs, winN, hopN, *ratioThresh, *rmsThresh, *minAms, *minBms, *gapMaxMs, logger) + det := newTwoToneDetector(fs, winN, hopN, *ratioThresh, *rmsThresh, *minAms, *minBms, *gapMaxMs) buf := &audio.IntBuffer{ Format: &audio.Format{SampleRate: fs, NumChannels: 1}, @@ -308,13 +260,11 @@ func main() { sampleCount := 0 startTime := time.Now() - logger.Info("Processing WAV file") + log.Println("Processing WAV file...") for { n, err := decoder.PCMBuffer(buf) if err != nil || n == 0 || len(buf.Data) == 0 { - logger.Info("Finished processing", - "samples", sampleCount, - "duration_sec", fmt.Sprintf("%.2f", float64(sampleCount)/float64(fs))) + log.Printf("Finished processing %d samples (%.2f seconds)", sampleCount, float64(sampleCount)/float64(fs)) break } @@ -338,7 +288,6 @@ func main() { } } -// min returns the smaller of two integer values a and b. func min(a, b int) int { if a < b { return a