Audio #1
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/qvest-digital/go-mxl/mxl"
|
"github.com/qvest-digital/go-mxl/mxl"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
"mxl-pattern-generator/internal/audio"
|
||||||
"mxl-pattern-generator/internal/flowdef"
|
"mxl-pattern-generator/internal/flowdef"
|
||||||
"mxl-pattern-generator/internal/generator"
|
"mxl-pattern-generator/internal/generator"
|
||||||
"mxl-pattern-generator/internal/video"
|
"mxl-pattern-generator/internal/video"
|
||||||
@@ -418,6 +419,42 @@ func buildVideoConfig(args appArgs) (video.Config, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildAudioConfig returns nil when audio is disabled. An explicit audio flow
|
||||||
|
// definition enables audio even when --channel is zero.
|
||||||
|
func buildAudioConfig(args appArgs) (*audio.Config, error) {
|
||||||
|
if args.audioFlowDefFile == "" && args.audioChannels == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var definition flowdef.Audio
|
||||||
|
if args.audioFlowDefFile != "" {
|
||||||
|
data, err := os.ReadFile(args.audioFlowDefFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read audio flow definition %q: %w", args.audioFlowDefFile, err)
|
||||||
|
}
|
||||||
|
definition, err = flowdef.ParseFloat32Audio(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parse audio flow definition %q: %w", args.audioFlowDefFile, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rate, ok := samplingRates[args.audioSamplingFreq]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unsupported audio sample rate %q", args.audioSamplingFreq)
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
definition, err = flowdef.NewFloat32Audio(
|
||||||
|
args.audioUUID,
|
||||||
|
uint(args.audioChannels),
|
||||||
|
flowdef.Rational{Numerator: uint(rate.Num), Denominator: uint(rate.Den)},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("build audio flow definition: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &audio.Config{Definition: definition}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
parsed, err := parseArgs(os.Args[1:], os.Stdout, os.Stderr)
|
parsed, err := parseArgs(os.Args[1:], os.Stdout, os.Stderr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -162,3 +162,70 @@ func TestBuildVideoConfigFromFile(t *testing.T) {
|
|||||||
t.Fatalf("rate = %d/%d, want file value 60000/1001", cfg.Rate().Num, cfg.Rate().Den)
|
t.Fatalf("rate = %d/%d, want file value 60000/1001", cfg.Rate().Num, cfg.Rate().Den)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildAudioConfigDisabled(t *testing.T) {
|
||||||
|
cfg, err := buildAudioConfig(appArgs{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("buildAudioConfig: %v", err)
|
||||||
|
}
|
||||||
|
if cfg != nil {
|
||||||
|
t.Fatalf("config = %+v, want nil for disabled audio", cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildAudioConfigFromArgs(t *testing.T) {
|
||||||
|
cfg, err := buildAudioConfig(appArgs{
|
||||||
|
audioUUID: "b3bb5be7-9fe9-4324-a5bb-4c70e1084449",
|
||||||
|
audioChannels: 2,
|
||||||
|
audioSamplingFreq: "48",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("buildAudioConfig: %v", err)
|
||||||
|
}
|
||||||
|
if cfg == nil {
|
||||||
|
t.Fatal("config is nil")
|
||||||
|
}
|
||||||
|
if cfg.Channels() != 2 {
|
||||||
|
t.Fatalf("channels = %d, want 2", cfg.Channels())
|
||||||
|
}
|
||||||
|
if cfg.Rate().Num != 48000 || cfg.Rate().Den != 1 {
|
||||||
|
t.Fatalf("rate = %d/%d, want 48000/1", cfg.Rate().Num, cfg.Rate().Den)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildAudioConfigFromFile(t *testing.T) {
|
||||||
|
definition, err := flowdef.NewFloat32Audio(
|
||||||
|
"b3bb5be7-9fe9-4324-a5bb-4c70e1084449",
|
||||||
|
8,
|
||||||
|
flowdef.Rational{Numerator: 96000, Denominator: 1},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewFloat32Audio: %v", err)
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(definition)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("json.Marshal: %v", err)
|
||||||
|
}
|
||||||
|
path := t.TempDir() + "/audio.json"
|
||||||
|
if err := os.WriteFile(path, data, 0o600); err != nil {
|
||||||
|
t.Fatalf("os.WriteFile: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := buildAudioConfig(appArgs{
|
||||||
|
audioFlowDefFile: path,
|
||||||
|
audioChannels: 2,
|
||||||
|
audioSamplingFreq: "48",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("buildAudioConfig: %v", err)
|
||||||
|
}
|
||||||
|
if cfg == nil {
|
||||||
|
t.Fatal("config is nil")
|
||||||
|
}
|
||||||
|
if cfg.Channels() != 8 {
|
||||||
|
t.Fatalf("channels = %d, want file value 8", cfg.Channels())
|
||||||
|
}
|
||||||
|
if cfg.Rate().Num != 96000 || cfg.Rate().Den != 1 {
|
||||||
|
t.Fatalf("rate = %d/%d, want file value 96000/1", cfg.Rate().Num, cfg.Rate().Den)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package audio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mxl-pattern-generator/internal/flowdef"
|
||||||
|
|
||||||
|
"github.com/qvest-digital/go-mxl/mxl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Definition flowdef.Audio
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) ID() string {
|
||||||
|
return c.Definition.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) Channels() uint {
|
||||||
|
return c.Definition.ChannelCount
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) Rate() mxl.Rational {
|
||||||
|
return mxl.Rational{
|
||||||
|
Num: int64(c.Definition.SampleRate.Numerator),
|
||||||
|
Den: int64(c.Definition.SampleRate.Denominator),
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user