Skip to content

Commit 9aa5e2f

Browse files
authored
Merge pull request #11 from mutablelogic/5.1
Updated chromaprint
2 parents 121f02f + 7574861 commit 9aa5e2f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+710
-440
lines changed

Makefile

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
# Paths to packages
22
GO=$(shell which go)
3+
DOCKER=$(shell which docker)
34

45
# Paths to locations, etc
56
BUILD_DIR := "build"
67
CMD_DIR := $(filter-out cmd/README.md, $(wildcard cmd/*))
78

89
# Build flags
9-
BUILD_MODULE = "github.com/mutablelogic/go-media"
10+
BUILD_MODULE := $(shell go list -m)
1011
BUILD_LD_FLAGS += -X $(BUILD_MODULE)/pkg/config.GitSource=${BUILD_MODULE}
1112
BUILD_LD_FLAGS += -X $(BUILD_MODULE)/pkg/config.GitTag=$(shell git describe --tags)
1213
BUILD_LD_FLAGS += -X $(BUILD_MODULE)/pkg/config.GitBranch=$(shell git name-rev HEAD --name-only --always)
@@ -28,6 +29,15 @@ $(PLUGIN_DIR): FORCE
2829

2930
FORCE:
3031

32+
docker:
33+
@echo Build docker image
34+
@${DOCKER} build \
35+
--tag go-media:$(shell git describe --tags) \
36+
--build-arg PLATFORM=$(shell ${GO} env GOOS) \
37+
--build-arg ARCH=$(shell ${GO} env GOARCH) \
38+
--build-arg VERSION=kinetic \
39+
-f etc/docker/Dockerfile .
40+
3141
test: clean dependencies
3242
@echo Test sys/
3343
@${GO} test ./sys/...

chromaprint.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package media
2+
3+
import (
4+
"io"
5+
)
6+
7+
////////////////////////////////////////////////////////////////////////////////
8+
// TYPES
9+
10+
// Chromaprint is a wrapper around the chromaprint library. Create a new
11+
// Chromaprint object by calling chromaprint.New(sample_rate, channels)
12+
type Chromaprint interface {
13+
io.Closer
14+
15+
// Write sample data to the fingerprinter. Expects 16-bit signed integers
16+
// and returns number of samples written
17+
Write([]int16) (int64, error)
18+
19+
// Finish the fingerprinting, and compute the fingerprint, return as a
20+
// string
21+
Finish() (string, error)
22+
}

cmd/fingerprint/main.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
An example of fingerprinting audio and recognizing the any music tracks within the audio.
3+
*/
4+
package main
5+
6+
import (
7+
"encoding/binary"
8+
"errors"
9+
"flag"
10+
"fmt"
11+
"io"
12+
"os"
13+
"time"
14+
15+
// Packages
16+
chromaprint "github.com/mutablelogic/go-media/pkg/chromaprint"
17+
config "github.com/mutablelogic/go-media/pkg/config"
18+
)
19+
20+
var (
21+
flagVersion = flag.Bool("version", false, "Print version information")
22+
flagRate = flag.Int("rate", 22050, "Sample rate")
23+
flagChannels = flag.Int("channels", 1, "Number of channels")
24+
flagKey = flag.String("key", "${CHROMAPRINT_KEY}", "AcoustID API key")
25+
flagLength = flag.Duration("length", 2*time.Minute, "Restrict the duration of the processed input audio")
26+
flagDuration = flag.Duration("duration", 0, "The actual duration of the audio file")
27+
)
28+
29+
const (
30+
bufsize = 1024 * 64 // Number of bytes to read at a time
31+
)
32+
33+
func main() {
34+
flag.Parse()
35+
36+
// Check for version
37+
if *flagVersion {
38+
config.PrintVersion(flag.CommandLine.Output())
39+
chromaprint.PrintVersion(flag.CommandLine.Output())
40+
os.Exit(0)
41+
}
42+
43+
// Check arguments
44+
if flag.NArg() != 1 {
45+
flag.Usage()
46+
os.Exit(-1)
47+
}
48+
49+
// Open file
50+
info, err := os.Stat(flag.Arg(0))
51+
if err != nil {
52+
fmt.Fprintln(os.Stderr, err)
53+
os.Exit(-2)
54+
}
55+
if info.Size() == 0 {
56+
fmt.Fprintln(os.Stderr, "file is empty")
57+
os.Exit(-2)
58+
}
59+
60+
// Open the file
61+
r, err := os.Open(flag.Arg(0))
62+
if err != nil {
63+
fmt.Fprintln(os.Stderr, err)
64+
os.Exit(-2)
65+
}
66+
defer r.Close()
67+
68+
// Create fingerprinter, write samples
69+
size := int(info.Size() >> 1)
70+
fingerprint := chromaprint.New(*flagRate, *flagChannels, *flagLength)
71+
samples := make([]int16, bufsize)
72+
for {
73+
// Adjust buffer size
74+
sz := MinInt(bufsize, size)
75+
size -= sz
76+
if sz == 0 {
77+
break
78+
}
79+
80+
// Read samples
81+
if err := binary.Read(r, binary.LittleEndian, samples[:sz]); errors.Is(err, io.EOF) {
82+
break
83+
} else if err != nil {
84+
fmt.Fprintln(os.Stderr, "Unexpected error:", err)
85+
os.Exit(-2)
86+
}
87+
88+
// Write samples
89+
if _, err := fingerprint.Write(samples); err != nil {
90+
fmt.Fprintln(os.Stderr, err)
91+
os.Exit(-2)
92+
}
93+
}
94+
95+
// Get fingerprint
96+
str, err := fingerprint.Finish()
97+
if err != nil {
98+
fmt.Fprintln(os.Stderr, err)
99+
os.Exit(-2)
100+
}
101+
102+
// Get duration
103+
duration := fingerprint.Duration()
104+
if *flagDuration != 0 {
105+
duration = *flagDuration
106+
}
107+
108+
// Create client, make matches
109+
client := chromaprint.NewClient(*flagKey)
110+
if matches, err := client.Lookup(str, duration, chromaprint.META_ALL); err != nil {
111+
fmt.Fprintln(os.Stderr, err)
112+
os.Exit(-2)
113+
} else if len(matches) == 0 {
114+
fmt.Fprintln(os.Stderr, "No matches found")
115+
} else {
116+
for _, match := range matches {
117+
fmt.Println(match)
118+
}
119+
}
120+
}
121+
122+
func MinInt(a, b int) int {
123+
if a < b {
124+
return a
125+
}
126+
return b
127+
}

cmd/transcode/main.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,6 @@ func main() {
119119
os.Exit(-2)
120120
}
121121

122-
// Print the map
123-
media_map.PrintMap(os.Stdout)
124-
125122
// For the output, we can output to a device, a URL, or a file
126123
var out Media
127124
if reDeviceName.MatchString(flags.Out()) {
@@ -138,6 +135,25 @@ func main() {
138135
os.Exit(-2)
139136
}
140137

138+
// For each input stream, add an output
139+
for _, stream := range media_map.Streams(flags.MediaFlags()) {
140+
// TODO
141+
// If audio, then resample data
142+
switch {
143+
case stream.Flags().Is(MEDIA_FLAG_AUDIO):
144+
if err := media_map.Resample(AudioFormat{
145+
Rate: 11025,
146+
Format: SAMPLE_FORMAT_U8,
147+
}, stream); err != nil {
148+
fmt.Fprintln(os.Stderr, "Cannot resample audio stream:", err)
149+
os.Exit(-2)
150+
}
151+
}
152+
}
153+
154+
// Print the map
155+
media_map.PrintMap(os.Stdout)
156+
141157
// Create a cancellable context
142158
ctx := contextForSignal(os.Interrupt)
143159

etc/docker/Dockerfile

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
11
# recommended way to build is using:
22
# docker build \
3-
# --build-arg PLATFORM=linux --build-arg ARCH=amd64 --build-arg VERSION=bullseye \
3+
# --build-arg PLATFORM=linux --build-arg ARCH=amd64 --build-arg VERSION=focal \
44
# -f etc/docker/Dockerfile .
55
#
6+
# ${VERSION} should be "focal"
67
ARG PLATFORM
78
ARG ARCH
89
ARG VERSION
9-
FROM --platform=${PLATFORM}/${ARCH} golang:1.19-${VERSION} AS builder
10+
FROM --platform=${PLATFORM}/${ARCH} ubuntu:${VERSION} AS builder
11+
12+
# update the base packages
13+
ENV DEBIAN_FRONTEND="noninteractive" TZ="Europe/Berlin"
14+
RUN apt-get update -y && apt-get upgrade -y
15+
16+
# install packages
17+
RUN apt-get install -y apt-utils golang make pkg-config ca-certificates lsb-release software-properties-common
18+
19+
# install other build dependencies
20+
# note we need to install ffmpeg 4 from a different repo for bionic
21+
RUN apt-get install -y \
22+
libavcodec-dev libavdevice-dev libavfilter-dev \
23+
libavformat-dev libswresample-dev libavutil-dev libchromaprint-dev
1024

1125
# Run makefile to build command-line tools
1226
WORKDIR /usr/src/app

etc/test/audio_22050_1ch_5m35.s16le

2.52 MB
Binary file not shown.

etc/test/s16le_22050_1ch_audio.raw

-2.52 MB
Binary file not shown.

etc/test/s16le_44100_1ch_audio.raw

-5.05 MB
Binary file not shown.

etc/test/s16le_44100_2ch_audio.raw

-38.9 MB
Binary file not shown.

media.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,22 +105,24 @@ type Map interface {
105105
// Return input media
106106
Input() Media
107107

108-
// Return streams which are mapped for decoding
109-
Streams() []Stream
108+
// Return a single stream which is mapped for decoding, filtering by
109+
// stream type. Returns nil if there is no selection of that type
110+
Streams(MediaFlag) []Stream
110111

111112
// Print a summary of the mapping
112113
PrintMap(w io.Writer)
114+
115+
// Resample an audio stream
116+
Resample(AudioFormat, Stream) error
117+
118+
// Encode to output media using default codec from a specific stream
119+
//Encode(Media, Stream) error
113120
}
114121

115122
// Media is a source or destination of media
116123
type Media interface {
117124
io.Closer
118125

119-
// Return best streams for specific types (video, audio, subtitle, data or attachment)
120-
// or returns empty slice if no streams of that type are in the media file. Only returns
121-
// one stream of each type.
122-
//StreamsByType(MediaFlag) []Stream
123-
124126
// URL for the media
125127
URL() string
126128

@@ -130,6 +132,9 @@ type Media interface {
130132
// Return media flags for the media
131133
Flags() MediaFlag
132134

135+
// Return the format of the media
136+
Format() MediaFormat
137+
133138
// Return metadata for the media
134139
Metadata() Metadata
135140

0 commit comments

Comments
 (0)