如何使用golang id3-go库获取和打印mp3流行度表框架

I would like to read the Popularimeter frame from a file with id3-go.

This is how the frame looks like when printing with mutagen-inspect:

$ mutagen-inspect samples/with_popm.mp3 | grep POPM
POPM=traktor@native-instruments.de=0 255/255

I would like to read the value (255/255) from the file. As I could not find any documentation, my naive approach is:

popFrame := mp3File.Frame("POPM")
log.Println(popFrame.String())

But when I run this (on a file with and also without a popularimeter tag), I get segmentation faults:

$ ./id3-go-popm-example samples/with_popm.mp3 
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x4ac302]

goroutine 1 [running]:
main.main()
    /home/ifischer/src/rivamp/rivamp-dist/id3-go-popm-example/main.go:21 +0xd2
$ ./id3-go-popm-example samples/without_popm.mp3 
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x4ac302]

goroutine 1 [running]:
main.main()
    /home/ifischer/src/rivamp/rivamp-dist/id3-go-popm-example/main.go:21 +0xd2

I setup a sample repository containing two sample files (one with, one without popularimeter frame) here: https://github.com/ifischer/id3-go-popm-example

How to I get the Popularimeter value (255/255) with id3-go library?

You're getting a segmentation fault because mp3File.Frame("POPM") is actually returning a nil value. I've narrowed it down to the V23FrameTypeMap in id3-go which lacks the TRDC frame. I'm really not familiar with the internals of ID3 but maybe you need to check if TRDC is really in V2.3 and if it is, add the missing ids in V23FrameTypeMap in id3-go.

Since it would be too much of a hassle to learn ID3, I've managed to get the POPM tag using another library https://github.com/dhowden/tag

f, _ := os.Open("with_popm.mp3")
m, _ := tag.ReadFrom(f)
fmt.Printf("%s
", m.Raw()["POPM"])

which outputs:

traktor@native-instruments.de

Edit: I mistook TDRC as TRDC, my mistake. Anyway, here is what you need to do to get the POPM field using id3-go

Add the following in V23FrameTypeMap in github.com/mikkyang/id3-go/v2/idv23.go

V23FrameTypeMap = map[string]FrameType{
    //...
    "TDRC": FrameType{id: "TDRC", description: "Recording Date", constructor: ParseTextFrame},
}

Then you can easily get the POPM field e.g.

package main

import (
    "fmt"
    id3 "github.com/mikkyang/id3-go"
)

func main() {

    f, _ := id3.Open("with_popm.mp3")
    p := f.Frame("POPM")
    fmt.Printf("%s
", p.Bytes())

}

Edit 2: I've added a way so that you can parse the POPM field. You can then access the rating 255/255 from Popularimeter.Rating:

import (
    "bytes"
    "encoding/binary"
    "errors"
    "fmt"

    id3 "github.com/mikkyang/id3-go"
)

func main() {

    f, _ := id3.Open("with_popm.mp3")
    popm := f.Frame("POPM")
    popularimeter, _ := FromData(popm.Bytes())
    fmt.Println(popularimeter)
}

type Rating uint8

func (r Rating) String() string {
    if r == 0 {
        return "unknown"
    }
    return fmt.Sprintf("%d/255", r)
}

type Popularimeter struct {
    Email   string
    Rating  Rating
    Counter uint32
}

func FromData(data []byte) (*Popularimeter, error) {

    tokens := bytes.SplitN(data, []byte{0x0}, 2)
    // Pupolarimeter: <string>, null, 1byte rating, 4bytes counter
    if len(tokens) != 2 || len(tokens[1]) != 5 {
        return nil, errors.New("invalid Popularimeter")
    }

    return &Popularimeter{
        Email:   string(tokens[0]),
        Rating:  Rating(tokens[1][0]),
        Counter: binary.BigEndian.Uint32(tokens[1][1:]),
    }, nil
}