Skip to content

Commit

Permalink
Read sampleRate from audio files
Browse files Browse the repository at this point in the history
  • Loading branch information
deluan committed May 11, 2024
1 parent ed83c22 commit 472324e
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 12 deletions.
4 changes: 3 additions & 1 deletion scanner/metadata/ffmpeg/ffmpeg.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ var (

// Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 192 kb/s
// Stream #0:0: Audio: flac, 44100 Hz, stereo, s16
audioStreamRx = regexp.MustCompile(`^\s{2,4}Stream #\d+:\d+.*: Audio: (.*), (.* Hz), ([\w.]+),*(.*.,)*`)
// Stream #0:0: Audio: dsd_lsbf_planar, 352800 Hz, stereo, fltp, 5644 kb/s
audioStreamRx = regexp.MustCompile(`^\s{2,4}Stream #\d+:\d+.*: Audio: (.*), (.*) Hz, ([\w.]+),*(.*.,)*`)

// Stream #0:1: Video: mjpeg, yuvj444p(pc, bt470bg/unknown/unknown), 600x600 [SAR 1:1 DAR 1:1], 90k tbr, 90k tbn, 90k tbc`
coverRx = regexp.MustCompile(`^\s{2,4}Stream #\d+:.+: (Video):.*`)
Expand Down Expand Up @@ -166,6 +167,7 @@ func (e *Extractor) parseInfo(info string) map[string][]string {

match = audioStreamRx.FindStringSubmatch(line)
if len(match) > 0 {
tags["samplerate"] = []string{match[2]}
tags["channels"] = []string{e.parseChannels(match[3])}
}
}
Expand Down
18 changes: 18 additions & 0 deletions scanner/metadata/ffmpeg/ffmpeg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,24 @@ Input #0, flac, from '/Users/deluan/Music/iTunes/iTunes Media/Music/Compilations
Expect(md).To(HaveKeyWithValue("channels", []string{"2"}))
})

It("parse sampleRate from the stream", func() {
const output = `
Input #0, dsf, from '/Users/deluan/Downloads/06-04 Perpetual Change.dsf':
Duration: 00:14:19.46, start: 0.000000, bitrate: 5644 kb/s
Stream #0:0: Audio: dsd_lsbf_planar, 352800 Hz, stereo, fltp, 5644 kb/s`
md, _ := e.extractMetadata("tests/fixtures/test.mp3", output)
Expect(md).To(HaveKeyWithValue("samplerate", []string{"352800"}))
})

It("parse sampleRate from the stream", func() {
const output = `
Input #0, wav, from '/Users/deluan/Music/Music/Media/_/multichannel/Nums_7dot1_24_48000.wav':
Duration: 00:00:09.05, bitrate: 9216 kb/s
Stream #0:0: Audio: pcm_s24le ([1][0][0][0] / 0x0001), 48000 Hz, 7.1, s32 (24 bit), 9216 kb/s`
md, _ := e.extractMetadata("tests/fixtures/test.mp3", output)
Expect(md).To(HaveKeyWithValue("samplerate", []string{"48000"}))
})

It("parses stream level tags", func() {
const output = `
Input #0, ogg, from './01-02 Drive (Teku).opus':
Expand Down
25 changes: 14 additions & 11 deletions scanner/metadata/taglib/taglib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var _ = Describe("Extractor", func() {
Expect(err).NotTo(HaveOccurred())
Expect(mds).To(HaveLen(2))

// Test MP3
m := mds["tests/fixtures/test.mp3"]
Expect(m).To(HaveKeyWithValue("title", []string{"Song", "Song"}))
Expect(m).To(HaveKeyWithValue("album", []string{"Album", "Album"}))
Expand All @@ -44,6 +45,7 @@ var _ = Describe("Extractor", func() {
Expect(m).To(HaveKeyWithValue("duration", []string{"1.02"}))
Expect(m).To(HaveKeyWithValue("bitrate", []string{"192"}))
Expect(m).To(HaveKeyWithValue("channels", []string{"2"}))
Expect(m).To(HaveKeyWithValue("samplerate", []string{"44100"}))
Expect(m).To(HaveKeyWithValue("comment", []string{"Comment1\nComment2"}))
Expect(m).ToNot(HaveKey("lyrics"))
Expect(m).To(Or(HaveKeyWithValue("lyrics-eng", []string{
Expand All @@ -70,11 +72,13 @@ var _ = Describe("Extractor", func() {
m = m.Map(e.CustomMappings())
Expect(m).To(HaveKeyWithValue("tracknumber", []string{"2/10", "2/10", "2"}))

// Test OGG
m = mds["tests/fixtures/test.ogg"]
Expect(err).To(BeNil())
Expect(m).ToNot(HaveKey("has_picture"))
Expect(m).To(HaveKeyWithValue("duration", []string{"1.04"}))
Expect(m).To(HaveKeyWithValue("fbpm", []string{"141.7"}))
Expect(m).To(HaveKeyWithValue("samplerate", []string{"8000"}))

// TabLib 1.12 returns 18, previous versions return 39.
// See https://github.com/taglib/taglib/commit/2f238921824741b2cfe6fbfbfc9701d9827ab06b
Expand All @@ -83,7 +87,7 @@ var _ = Describe("Extractor", func() {
})

DescribeTable("Format-Specific tests",
func(file, duration, channels, albumGain, albumPeak, trackGain, trackPeak string, id3Lyrics bool) {
func(file, duration, channels, samplerate, albumGain, albumPeak, trackGain, trackPeak string, id3Lyrics bool) {
file = "tests/fixtures/" + file
mds, err := e.Parse(file)
Expect(err).NotTo(HaveOccurred())
Expand Down Expand Up @@ -122,6 +126,7 @@ var _ = Describe("Extractor", func() {
Expect(m).To(HaveKeyWithValue("duration", []string{duration}))

Expect(m).To(HaveKeyWithValue("channels", []string{channels}))
Expect(m).To(HaveKeyWithValue("samplerate", []string{samplerate}))
Expect(m).To(HaveKeyWithValue("comment", []string{"Comment1\nComment2"}))

if id3Lyrics {
Expand All @@ -147,26 +152,24 @@ var _ = Describe("Extractor", func() {
},

// ffmpeg -f lavfi -i "sine=frequency=1200:duration=1" test.flac
Entry("correctly parses flac tags", "test.flac", "1.00", "1", "+4.06 dB", "0.12496948", "+4.06 dB", "0.12496948", false),
Entry("correctly parses flac tags", "test.flac", "1.00", "1", "44100", "+4.06 dB", "0.12496948", "+4.06 dB", "0.12496948", false),

Entry("Correctly parses m4a (aac) gain tags", "01 Invisible (RED) Edit Version.m4a", "1.04", "2", "0.37", "0.48", "0.37", "0.48", false),
Entry("Correctly parses m4a (aac) gain tags (uppercase)", "test.m4a", "1.04", "2", "0.37", "0.48", "0.37", "0.48", false),

Entry("correctly parses ogg (vorbis) tags", "test.ogg", "1.04", "2", "+7.64 dB", "0.11772506", "+7.64 dB", "0.11772506", false),
Entry("Correctly parses m4a (aac) gain tags", "01 Invisible (RED) Edit Version.m4a", "1.04", "2", "44100", "0.37", "0.48", "0.37", "0.48", false),
Entry("Correctly parses m4a (aac) gain tags (uppercase)", "test.m4a", "1.04", "2", "44100", "0.37", "0.48", "0.37", "0.48", false),
Entry("correctly parses ogg (vorbis) tags", "test.ogg", "1.04", "2", "8000", "+7.64 dB", "0.11772506", "+7.64 dB", "0.11772506", false),

// ffmpeg -f lavfi -i "sine=frequency=900:duration=1" test.wma
// Weird note: for the tag parsing to work, the lyrics are actually stored in the reverse order
Entry("correctly parses wma/asf tags", "test.wma", "1.02", "1", "3.27 dB", "0.132914", "3.27 dB", "0.132914", false),
Entry("correctly parses wma/asf tags", "test.wma", "1.02", "1", "44100", "3.27 dB", "0.132914", "3.27 dB", "0.132914", false),

// ffmpeg -f lavfi -i "sine=frequency=800:duration=1" test.wv
Entry("correctly parses wv (wavpak) tags", "test.wv", "1.00", "1", "3.43 dB", "0.125061", "3.43 dB", "0.125061", false),
Entry("correctly parses wv (wavpak) tags", "test.wv", "1.00", "1", "44100", "3.43 dB", "0.125061", "3.43 dB", "0.125061", false),

// TODO - these break in the pipeline as it uses TabLib 1.11. Once Ubuntu 24.04 is released we can uncomment these tests
// ffmpeg -f lavfi -i "sine=frequency=1000:duration=1" test.wav
// Entry("correctly parses wav tags", "test.wav", "1.00", "1", "3.06 dB", "0.125056", "3.06 dB", "0.125056", true),
Entry("correctly parses wav tags", "test.wav", "1.00", "1", "44100", "3.06 dB", "0.125056", "3.06 dB", "0.125056", true),

// ffmpeg -f lavfi -i "sine=frequency=1400:duration=1" test.aiff
// Entry("correctly parses aiff tags", "test.aiff", "1.00", "1", "2.00 dB", "0.124972", "2.00 dB", "0.124972", true),
Entry("correctly parses aiff tags", "test.aiff", "1.00", "1", "44100", "2.00 dB", "0.124972", "2.00 dB", "0.124972", true),
)

// Skip these tests when running as root
Expand Down
1 change: 1 addition & 0 deletions scanner/metadata/taglib/taglib_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ int taglib_read(const FILENAME_CHAR_T *filename, unsigned long id) {
go_map_put_int(id, (char *)"lengthinmilliseconds", props->lengthInMilliseconds());
go_map_put_int(id, (char *)"bitrate", props->bitrate());
go_map_put_int(id, (char *)"channels", props->channels());
go_map_put_int(id, (char *)"samplerate", props->sampleRate());

// Create a map to collect all the tags
TagLib::PropertyMap tags = f.file()->properties();
Expand Down

0 comments on commit 472324e

Please sign in to comment.