Skip to content

Commit

Permalink
[ISSUE-28] Update preamp fetch (#29)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
indiependente authored Sep 30, 2022
1 parent 2256248 commit 2c1b61d
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 44 deletions.
23 changes: 17 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/indiependente/autoEqMac)](https://goreportcard.com/report/github.com/indiependente/autoEqMac)
<a href='https://github.com/jpoles1/gopherbadger' target='_blank'>![gopherbadger-tag-do-not-edit](https://img.shields.io/badge/Go%20Coverage-63%25-brightgreen.svg?longCache=true&style=flat)</a>
[![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 [<flags>]
Expand All @@ -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

- <https://github.com/jaakkopasanen/AutoEq>
- <https://github.com/bitgapp/eqMac/>
- <https://github.com/c-bata/go-prompt>
59 changes: 44 additions & 15 deletions autoeq/fixed_band.go
Original file line number Diff line number Diff line change
@@ -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]))
Expand All @@ -48,13 +75,15 @@ 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,
}
i++
}

return eqs[:i], nil
fbEQ.Filters = fbEQ.Filters[:i]

return fbEQ, nil
}
23 changes: 19 additions & 4 deletions autoeq/fixed_band_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
{
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions autoeq/mdparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
8 changes: 4 additions & 4 deletions eqmac/mapping/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion eqmac/mapping/mapper_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions eqmac/mapping/mapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,7 @@ func NewExecutor(srv *server.HTTPServer) func(string) {
return
}
fmt.Printf("📝 Preset saved to %s\n", f.Name())

os.Exit(0)
}
}
16 changes: 9 additions & 7 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
30 changes: 28 additions & 2 deletions server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)),
Expand Down

0 comments on commit 2c1b61d

Please sign in to comment.