golang轻量级MP3解码器插件库minimp3的使用

Golang轻量级MP3解码器插件库minimp3的使用

minimp3是一个基于C语言minimp3库的Go语言封装,用于解码MP3音频文件。它是一个轻量级的解决方案,适合在Go项目中处理MP3音频。

安装

  1. 首先需要安装Go(要求Go 1.15+版本),然后使用以下命令安装minimp3:
$ go get -u github.com/tosone/minimp3
  1. 在代码中导入:
import "github.com/tosone/minimp3"

使用示例

示例1:解码整个MP3文件并播放

package main

import (
	"io/ioutil"
	"log"
	"time"

	"github.com/hajimehoshi/oto"
	"github.com/tosone/minimp3"
)

func main() {
	var err error

	var file []byte
	if file, err = ioutil.ReadFile("test.mp3"); err != nil {
		log.Fatal(err)
	}

	var dec *minimp3.Decoder
	var data []byte
	if dec, data, err = minimp3.DecodeFull(file); err != nil {
		log.Fatal(err)
	}

	var context *oto.Context
	if context, err = oto.NewContext(dec.SampleRate, dec.Channels, 2, 1024); err != nil {
		log.Fatal(err)
	}

	var player = context.NewPlayer()
	player.Write(data)

	<-time.After(time.Second)

	dec.Close()
	if err = player.Close(); err != nil {
		log.Fatal(err)
	}
}

示例2:流式解码并播放

package main

import (
	"io"
	"log"
	"os"
	"sync"
	"time"

	"github.com/hajimehoshi/oto"
	"github.com/tosone/minimp3"
)

func main() {
	var err error

	var file *os.File
	if file, err = os.Open("../test.mp3"); err != nil {
		log.Fatal(err)
	}

	var dec *minimp3.Decoder
	if dec, err = minimp3.NewDecoder(file); err != nil {
		log.Fatal(err)
	}
	started := dec.Started()
	<-started

	log.Printf("Convert audio sample rate: %d, channels: %d\n", dec.SampleRate, dec.Channels)

	var context *oto.Context
	if context, err = oto.NewContext(dec.SampleRate, dec.Channels, 2, 1024); err != nil {
		log.Fatal(err)
	}

	var waitForPlayOver = new(sync.WaitGroup)
	waitForPlayOver.Add(1)

	var player = context.NewPlayer()

	go func() {
		for {
			var data = make([]byte, 1024)
			_, err := dec.Read(data)
			if err == io.EOF {
				break
			}
			if err != nil {
				break
			}
			player.Write(data)
		}
		log.Println("over play.")
		waitForPlayOver.Done()
	}()
	waitForPlayOver.Wait()

	<-time.After(time.Second)
	dec.Close()
	if err = player.Close(); err != nil {
		log.Fatal(err)
	}
}

示例3:播放网络音频

package main

import (
	"io"
	"log"
	"net/http"
	"os"
	"sync"
	"time"

	"github.com/hajimehoshi/oto"
	"github.com/tosone/minimp3"
)

func main() {
	var err error

	var args = os.Args
	if len(args) != 2 {
		log.Fatal("Run test like this:\n\n\t./networkAudio.test [mp3url]\n\n")
	}

	var response *http.Response
	if response, err = http.Get(args[1]); err != nil {
		log.Fatal(err)
	}

	var dec *minimp3.Decoder
	if dec, err = minimp3.NewDecoder(response.Body); err != nil {
		log.Fatal(err)
	}
	<-dec.Started()

	log.Printf("Convert audio sample rate: %d, channels: %d\n", dec.SampleRate, dec.Channels)

	var context *oto.Context
	if context, err = oto.NewContext(dec.SampleRate, dec.Channels, 2, 4096); err != nil {
		log.Fatal(err)
	}

	var waitForPlayOver = new(sync.WaitGroup)
	waitForPlayOver.Add(1)

	var player = context.NewPlayer()

	go func() {
		defer response.Body.Close()
		for {
			var data = make([]byte, 512)
			_, err = dec.Read(data)
			if err == io.EOF {
				break
			}
			if err != nil {
				log.Fatal(err)
				break
			}
			player.Write(data)
		}
		log.Println("over play.")
		waitForPlayOver.Done()
	}()

	waitForPlayOver.Wait()

	<-time.After(time.Second)
	dec.Close()
	player.Close()
}

这些示例展示了如何使用minimp3库来解码和播放MP3音频文件,包括本地文件和网络流。注意这些示例都使用了oto库来处理音频播放。


更多关于golang轻量级MP3解码器插件库minimp3的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang轻量级MP3解码器插件库minimp3的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用minimp3在Go中进行轻量级MP3解码

minimp3是一个极简的MP3解码器实现,非常适合嵌入式系统和资源受限环境。在Go中,我们可以通过cgo来使用这个轻量级解码器。下面我将介绍如何在Go项目中使用minimp3进行MP3解码。

安装与准备

首先,你需要获取minimp3的源代码。可以从官方仓库获取:

git clone https://github.com/lieff/minimp3.git

基本使用方法

下面是一个完整的Go示例,展示如何使用minimp3解码MP3文件:

package main

/*
#cgo CFLAGS: -I./minimp3
#cgo LDFLAGS: -L. -lminimp3
#include "minimp3.h"
#include <stdlib.h>
*/
import "C"
import (
	"fmt"
	"io/ioutil"
	"os"
	"unsafe"
)

func main() {
	// 读取MP3文件
	data, err := ioutil.ReadFile("test.mp3")
	if err != nil {
		fmt.Println("Error reading file:", err)
		return
	}

	// 初始化解码器
	var dec C.mp3dec_t
	C.mp3dec_init(&dec)

	// 准备解码参数
	var frame C.mp3dec_frame_info_t
	var pcm [C.MINIMP3_MAX_SAMPLES_PER_FRAME]C.mp3d_sample_t

	// 转换为C可用的指针
	cData := (*C.uint8_t)(unsafe.Pointer(&data[0]))
	dataSize := C.int(len(data))

	var offset C.int
	var totalSamples int

	// 创建输出文件
	outFile, err := os.Create("output.raw")
	if err != nil {
		fmt.Println("Error creating output file:", err)
		return
	}
	defer outFile.Close()

	// 解码循环
	for offset < dataSize {
		samples := C.mp3dec_decode_frame(
			&dec,
			(*C.uint8_t)(unsafe.Pointer(uintptr(unsafe.Pointer(cData))+uintptr(offset))),
			dataSize-offset,
			&pcm[0],
			&frame,
		)

		if samples == 0 {
			break
		}

		// 写入解码后的PCM数据
		pcmData := (*[1 << 30]byte)(unsafe.Pointer(&pcm[0]))[:int(samples)*int(frame.channels)*2]
		if _, err := outFile.Write(pcmData); err != nil {
			fmt.Println("Error writing PCM data:", err)
			return
		}

		totalSamples += int(samples)
		offset += frame.frame_bytes
	}

	fmt.Printf("Decoded %d samples\n", totalSamples)
}

更高级的封装

为了更方便地在Go中使用,我们可以创建一个简单的封装:

package minimp3

/*
#cgo CFLAGS: -I./minimp3
#cgo LDFLAGS: -L. -lminimp3
#include "minimp3.h"
#include <stdlib.h>
*/
import "C"
import (
	"errors"
	"unsafe"
)

type Decoder struct {
	dec    C.mp3dec_t
	info   C.mp3dec_frame_info_t
	buffer [C.MINIMP3_MAX_SAMPLES_PER_FRAME]C.mp3d_sample_t
}

func NewDecoder() *Decoder {
	d := &Decoder{}
	C.mp3dec_init(&d.dec)
	return d
}

func (d *Decoder) Decode(data []byte) (pcm []int16, samples int, err error) {
	if len(data) == 0 {
		return nil, 0, errors.New("empty input data")
	}

	cData := (*C.uint8_t)(unsafe.Pointer(&data[0]))
	dataSize := C.int(len(data))

	samples = int(C.mp3dec_decode_frame(
		&d.dec,
		cData,
		dataSize,
		&d.buffer[0],
		&d.info,
	))

	if samples == 0 {
		return nil, 0, errors.New("no samples decoded")
	}

	pcm = make([]int16, samples*int(d.info.channels))
	copy(pcm, (*(*[1 << 30]int16)(unsafe.Pointer(&d.buffer[0]))[:samples*int(d.info.channels)])

	return pcm, samples, nil
}

func (d *Decoder) Info() (sampleRate, channels, bitrate int) {
	return int(d.info.hz), int(d.info.channels), int(d.info.bitrate_kbps)
}

使用封装后的解码器

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"your/path/to/minimp3"
)

func main() {
	// 读取MP3文件
	data, err := ioutil.ReadFile("test.mp3")
	if err != nil {
		fmt.Println("Error reading file:", err)
		return
	}

	decoder := minimp3.NewDecoder()

	// 创建输出文件
	outFile, err := os.Create("output.raw")
	if err != nil {
		fmt.Println("Error creating output file:", err)
		return
	}
	defer outFile.Close()

	var offset int
	var totalSamples int

	for offset < len(data) {
		pcm, samples, err := decoder.Decode(data[offset:])
		if err != nil {
			fmt.Println("Decoding error:", err)
			break
		}

		if samples == 0 {
			break
		}

		// 写入PCM数据
		if _, err := outFile.Write(int16ToBytes(pcm)); err != nil {
			fmt.Println("Error writing PCM data:", err)
			return
		}

		totalSamples += samples
		offset += int(decoder.info.frame_bytes)
	}

	fmt.Printf("Decoded %d samples, sample rate: %d, channels: %d\n",
		totalSamples, decoder.Info())
}

func int16ToBytes(pcm []int16) []byte {
	b := make([]byte, len(pcm)*2)
	for i, v := range pcm {
		b[i*2] = byte(v)
		b[i*2+1] = byte(v >> 8)
	}
	return b
}

性能与注意事项

  1. 性能:minimp3非常轻量,解码速度很快,适合实时音频处理
  2. 内存:内存占用极小,没有动态内存分配
  3. 功能:只支持MPEG1和MPEG2音频层3(MP3)格式
  4. 线程安全:解码器状态不是线程安全的,每个goroutine应该有自己的解码器实例

minimp3是一个出色的轻量级MP3解码解决方案,特别适合需要高效解码而不需要完整音频框架的Go应用程序。

回到顶部