From 2c1b61d2f64549ba0a4a38f845112b78abd2a312 Mon Sep 17 00:00:00 2001
From: Francesco Farina <3093285+indiependente@users.noreply.github.com>
Date: Sat, 1 Oct 2022 00:03:31 +0100
Subject: [PATCH] [ISSUE-28] Update preamp fetch (#29)
* updated preamp fetching code to match new fixed band eq file format
* minor improvement: parse preamp row before filters
* update README
* update README with link to latest release
* add boundary check on preamp fields
---
README.md | 23 ++++++++++----
autoeq/fixed_band.go | 59 +++++++++++++++++++++++++++---------
autoeq/fixed_band_test.go | 23 +++++++++++---
autoeq/mdparser.go | 1 +
eqmac/mapping/mapper.go | 8 ++---
eqmac/mapping/mapper_mock.go | 2 +-
eqmac/mapping/mapper_test.go | 11 ++++---
executor.go | 2 ++
server/server.go | 16 +++++-----
server/server_test.go | 30 ++++++++++++++++--
10 files changed, 131 insertions(+), 44 deletions(-)
diff --git a/README.md b/README.md
index 9364f87..bf5f7cd 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,30 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/indiependente/autoEqMac)](https://goreportcard.com/report/github.com/indiependente/autoEqMac)
![gopherbadger-tag-do-not-edit](https://img.shields.io/badge/Go%20Coverage-63%25-brightgreen.svg?longCache=true&style=flat)
[![Workflow Status](https://github.com/indiependente/autoEqMac/workflows/lint-test/badge.svg)](https://github.com/indiependente/autoEqMac/actions)
+
# autoEqMac
+
An interactive CLI that retrieves headphones EQ data from the [AutoEq Project](https://github.com/jaakkopasanen/AutoEq) and produces a JSON preset ready to be imported into [EqMac](https://github.com/bitgapp/eqMac/).
## Dependencies
- - Go
+
+- Go
## How to
### Install
-`go install github.com/indiependente/autoEqMac`
+Download the latest [release](https://github.com/indiependente/autoEqMac/releases/latest).
-### Supported commands
+### Install with Go
+```bash
+go install github.com/indiependente/autoEqMac
```
+
+### Supported commands
+
+```plaintext
▶ autoEqMac --help
usage: autoEqMac []
@@ -42,11 +51,13 @@ By default `autoEqMac` saves a JSON file with the same name of the headphones mo
You can provide a different path by passing it using the `-f, --file` flag.
## TODO
+
- [ ] GUI
## Credits
Thanks to:
- - https://github.com/jaakkopasanen/AutoEq
- - https://github.com/bitgapp/eqMac/
- - https://github.com/c-bata/go-prompt
+
+-
+-
+-
diff --git a/autoeq/fixed_band.go b/autoeq/fixed_band.go
index 64b024b..df20263 100644
--- a/autoeq/fixed_band.go
+++ b/autoeq/fixed_band.go
@@ -1,39 +1,66 @@
package autoeq
import (
+ "errors"
"fmt"
"strconv"
"strings"
)
-const preampFields = 12
+const (
+ preampFields = 3
+ fixedBandFields = 12
+)
-// FixedBandEQ represents a single EQ band.
-type FixedBandEQ struct {
+// ErrBadPreampFormat is returned when the preamp line can't be parsed.
+var ErrBadPreampFormat = errors.New("bad preamp line format")
+
+// FixedBandFilter represents a single EQ band.
+type FixedBandFilter struct {
Frequency int // Hz
- Gain float64 // Db
+ Gain float64 // dB
Q float64 // fixed
}
-// FixedBandEQs represents a simple fixed bands EQ.
-type FixedBandEQs []*FixedBandEQ
+// FixedBandEQ represents a simple fixed bands EQ.
+type FixedBandEQ struct {
+ Filters []*FixedBandFilter
+ Preamp float64
+}
// ToFixedBandEQs transforms the raw EQ data into a fixed band EQ.
// Returns an error if any.
-func ToFixedBandEQs(data []byte) (FixedBandEQs, error) {
+func ToFixedBandEQs(data []byte) (*FixedBandEQ, error) {
rows := strings.Split(string(data), "\n")
- eqs := make(FixedBandEQs, len(rows))
+
+ fbEQ := &FixedBandEQ{
+ Filters: make([]*FixedBandFilter, len(rows)),
+ }
+
+ startIdx := 0 // rows index, increment if first row is preamp
+
+ // parse preamp
+ if strings.HasPrefix(rows[0], "Preamp") {
+ fields := strings.Fields(rows[0])
+ if len(fields) != preampFields {
+ return nil, ErrBadPreampFormat
+ }
+ preamp, err := strconv.ParseFloat(strings.TrimSpace(fields[1]), bitSize)
+ if err != nil {
+ return nil, err
+ }
+ fbEQ.Preamp = preamp
+ startIdx++
+ }
i := 0
- for _, row := range rows {
+ for _, row := range rows[startIdx:] {
if row == "" {
continue
}
- if strings.HasPrefix(row, "Preamp") {
- continue
- }
+
eqFields := strings.Fields(row)
- if len(eqFields) < preampFields {
+ if len(eqFields) < fixedBandFields {
return nil, fmt.Errorf("could not parse : %s", row)
}
freq, err := strconv.Atoi(strings.TrimSpace(eqFields[5]))
@@ -48,7 +75,7 @@ func ToFixedBandEQs(data []byte) (FixedBandEQs, error) {
if err != nil {
return nil, fmt.Errorf("could not parse Q: %w", err)
}
- eqs[i] = &FixedBandEQ{
+ fbEQ.Filters[i] = &FixedBandFilter{
Frequency: freq,
Gain: gain,
Q: q,
@@ -56,5 +83,7 @@ func ToFixedBandEQs(data []byte) (FixedBandEQs, error) {
i++
}
- return eqs[:i], nil
+ fbEQ.Filters = fbEQ.Filters[:i]
+
+ return fbEQ, nil
}
diff --git a/autoeq/fixed_band_test.go b/autoeq/fixed_band_test.go
index 42286e6..8929163 100644
--- a/autoeq/fixed_band_test.go
+++ b/autoeq/fixed_band_test.go
@@ -12,13 +12,22 @@ func TestToFixedBandEQs(t *testing.T) {
tests := []struct {
name string
data []byte
- want FixedBandEQs
+ want *FixedBandEQ
wantErr bool
}{
{
- name: "Happy path",
- data: []byte("Filter 1: ON PK Fc 31 Hz Gain 5.8 dB Q 1.41\n"),
- want: FixedBandEQs{{Frequency: 31, Gain: 5.8, Q: 1.41}},
+ name: "Happy path",
+ data: []byte("Preamp: -6.1 dB\nFilter 1: ON PK Fc 31 Hz Gain 5.8 dB Q 1.41\n"),
+ want: &FixedBandEQ{
+ Filters: []*FixedBandFilter{{Frequency: 31, Gain: 5.8, Q: 1.41}},
+ Preamp: -6.1,
+ },
+ wantErr: false,
+ },
+ {
+ name: "Happy path - No data",
+ data: nil,
+ want: &FixedBandEQ{Filters: []*FixedBandFilter{}, Preamp: 0},
wantErr: false,
},
{
@@ -39,6 +48,12 @@ func TestToFixedBandEQs(t *testing.T) {
want: nil,
wantErr: true,
},
+ {
+ name: "Sad path - Bad Preamp Line Format",
+ data: []byte("Preamp: -6.1\nFilter 1: ON PK Fc AB Hz Gain 5.8 dB Q 1.41"),
+ want: nil,
+ wantErr: true,
+ },
}
for _, tt := range tests {
tt := tt
diff --git a/autoeq/mdparser.go b/autoeq/mdparser.go
index fdd7f84..e29499c 100644
--- a/autoeq/mdparser.go
+++ b/autoeq/mdparser.go
@@ -21,6 +21,7 @@ type EQMetadata struct {
Name string
Author string
Link string
+ // Deprecated: this field has been deprecated in favor of autoeq.FixedBandEQ.Preamp.
Global float64
}
diff --git a/eqmac/mapping/mapper.go b/eqmac/mapping/mapper.go
index 76e964b..e7af7fb 100644
--- a/eqmac/mapping/mapper.go
+++ b/eqmac/mapping/mapper.go
@@ -22,7 +22,7 @@ func (g WrappedGenerator) UUID() string {
// Mapper defines the behavior of a component capable of mapping AutoEQ data into EqMac format.
type Mapper interface {
- MapFixedBand(autoeq.FixedBandEQs, *autoeq.EQMetadata) (eqmac.EQPreset, error)
+ MapFixedBand(*autoeq.FixedBandEQ, *autoeq.EQMetadata) (eqmac.EQPreset, error)
}
// compile time interface implementation check.
@@ -43,17 +43,17 @@ func NewAutoEQMapper(gen UUIDGenerator) AutoEQMapper {
// MapFixedBand maps fixed bands EQ data into an EqMac preset.
// Returns an error if any.
-func (m AutoEQMapper) MapFixedBand(fbeq autoeq.FixedBandEQs, meta *autoeq.EQMetadata) (eqmac.EQPreset, error) {
+func (m AutoEQMapper) MapFixedBand(fbeq *autoeq.FixedBandEQ, meta *autoeq.EQMetadata) (eqmac.EQPreset, error) {
var preset eqmac.EQPreset
preset.ID = m.gen.UUID()
preset.IsDefault = false
preset.Name = meta.Name
preset.Gains = eqmac.Gains{
- Global: meta.Global,
+ Global: fbeq.Preamp,
Bands: []float64{},
}
bands := []float64{}
- for _, band := range fbeq {
+ for _, band := range fbeq.Filters {
bands = append(bands, band.Gain)
}
preset.Gains.Bands = bands
diff --git a/eqmac/mapping/mapper_mock.go b/eqmac/mapping/mapper_mock.go
index b32b9e9..d5436c4 100644
--- a/eqmac/mapping/mapper_mock.go
+++ b/eqmac/mapping/mapper_mock.go
@@ -70,7 +70,7 @@ func (m *MockMapper) EXPECT() *MockMapperMockRecorder {
}
// MapFixedBand mocks base method
-func (m *MockMapper) MapFixedBand(arg0 autoeq.FixedBandEQs, arg1 autoeq.EQMetadata) (eqmac.EQPreset, error) {
+func (m *MockMapper) MapFixedBand(arg0 autoeq.FixedBandEQ, arg1 autoeq.EQMetadata) (eqmac.EQPreset, error) {
ret := m.ctrl.Call(m, "MapFixedBand", arg0, arg1)
ret0, _ := ret[0].(eqmac.EQPreset)
ret1, _ := ret[1].(error)
diff --git a/eqmac/mapping/mapper_test.go b/eqmac/mapping/mapper_test.go
index 785a74f..4ba95d4 100644
--- a/eqmac/mapping/mapper_test.go
+++ b/eqmac/mapping/mapper_test.go
@@ -11,11 +11,11 @@ import (
"github.com/indiependente/autoEqMac/eqmac"
)
-func TestAutoEQMapper_MapFixedBand(t *testing.T) {
+func TestAutoEQMapper_MapFixedBand(t *testing.T) { //nolint: funlen
t.Parallel()
id := uuid.New().String()
type args struct {
- fbeq autoeq.FixedBandEQs
+ fbeq *autoeq.FixedBandEQ
meta *autoeq.EQMetadata
}
tests := []struct {
@@ -28,12 +28,13 @@ func TestAutoEQMapper_MapFixedBand(t *testing.T) {
{
name: "Happy path",
args: args{
- fbeq: autoeq.FixedBandEQs{
- {
+ fbeq: &autoeq.FixedBandEQ{
+ Preamp: -6.4,
+ Filters: []*autoeq.FixedBandFilter{{
Frequency: 31,
Gain: 5.8,
Q: 1.41,
- },
+ }},
},
meta: &autoeq.EQMetadata{
ID: "0",
diff --git a/executor.go b/executor.go
index 895e2e6..a0955dc 100644
--- a/executor.go
+++ b/executor.go
@@ -58,5 +58,7 @@ func NewExecutor(srv *server.HTTPServer) func(string) {
return
}
fmt.Printf("📝 Preset saved to %s\n", f.Name())
+
+ os.Exit(0)
}
}
diff --git a/server/server.go b/server/server.go
index 0b50fa6..aea6eaf 100644
--- a/server/server.go
+++ b/server/server.go
@@ -91,16 +91,18 @@ func (s *HTTPServer) GetFixedBandEQPreset(id string) (eqmac.EQPreset, error) {
if err != nil {
return eqmac.EQPreset{}, fmt.Errorf("could not get raw EQ data: %w", err)
}
- globalPreamp, err := s.eqGetter.GetFixedBandGlobalPreamp(eqMeta)
- if err != nil {
- return eqmac.EQPreset{}, fmt.Errorf("could not get global EQ preamp data: %w", err)
- }
- eqMeta.Global = globalPreamp
- fbEQs, err := autoeq.ToFixedBandEQs(rawEQ)
+ fbEQ, err := autoeq.ToFixedBandEQs(rawEQ)
if err != nil {
return eqmac.EQPreset{}, fmt.Errorf("could not map raw EQ data: %w", err)
}
- eqPreset, err := s.mapper.MapFixedBand(fbEQs, eqMeta)
+ if fbEQ.Preamp == 0 { // fallback mechanism for Preamp
+ globalPreamp, err := s.eqGetter.GetFixedBandGlobalPreamp(eqMeta) //nolint:govet // error shadowing doesn't impact its handling.
+ if err != nil {
+ return eqmac.EQPreset{}, fmt.Errorf("could not get global EQ preamp data: %w", err)
+ }
+ fbEQ.Preamp = globalPreamp
+ }
+ eqPreset, err := s.mapper.MapFixedBand(fbEQ, eqMeta)
if err != nil {
return eqmac.EQPreset{}, fmt.Errorf("could not map raw EQ datato eqMac preset: %w", err)
}
diff --git a/server/server_test.go b/server/server_test.go
index e1fae9c..4bae7f4 100644
--- a/server/server_test.go
+++ b/server/server_test.go
@@ -22,8 +22,10 @@ from the same source.
- [Audio-Technica ATH-M50x](./oratory1990/harman_over-ear_2018/Audio-Technica%20ATH-M50x) by oratory1990
`)
- rawEqData = []byte(`Filter 1: ON PK Fc 31 Hz Gain 5.8 dB Q 1.41`)
- rawGlobalData = []byte(`# Audio-Technica ATH-M50x
+ rawEqData = []byte(`Preamp: -6.4 dB
+ Filter 1: ON PK Fc 31 Hz Gain 5.8 dB Q 1.41`)
+ rawEqDataNoPreamp = []byte(`Filter 1: ON PK Fc 31 Hz Gain 5.8 dB Q 1.41`)
+ rawGlobalData = []byte(`# Audio-Technica ATH-M50x
See [usage instructions](https://github.com/jaakkopasanen/AutoEq#usage) for more options and info.
### Parametric EQs
@@ -105,6 +107,30 @@ func TestHTTPServer(t *testing.T) { //nolint:funlen
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewReader(rawEqData)),
}, nil)
+ },
+ want: struct {
+ meta []*autoeq.EQMetadata
+ preset eqmac.EQPreset
+ }{
+ meta: []*autoeq.EQMetadata{
+ eqMeta,
+ },
+ preset: eqPreset,
+ },
+
+ wantErr: false,
+ },
+ {
+ name: "Happy path - Missing Global Preamp requires extra HTTP call",
+ setupExpectations: func(doer *MockDoer) {
+ doer.EXPECT().Do(gomock.Any()).Return(&http.Response{
+ StatusCode: http.StatusOK,
+ Body: io.NopCloser(bytes.NewReader(rawEqList)),
+ }, nil)
+ doer.EXPECT().Do(gomock.Any()).Return(&http.Response{
+ StatusCode: http.StatusOK,
+ Body: io.NopCloser(bytes.NewReader(rawEqDataNoPreamp)),
+ }, nil)
doer.EXPECT().Do(gomock.Any()).Return(&http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewReader(rawGlobalData)),