扬帆大鱼
这是一个解决方案。如果要使用它,必须将 libsndfile 和 openal '库放在正确的位置。使用 Go 15.2 64 位测试。我是 Windows 用户。从虚拟源读取文件,打印信息(通道、帧、可搜索等)并使用 OpenAL 播放。还有 GitHub 上的问题。阅读可能会有所帮助。#16主要代码:package main// #cgo CFLAGS: -Wall -O3 -Iinclude// #cgo LDFLAGS: -O3 -L. -llibsndfile-1 -lopenal32/* #include "extras.h" #include "sndfile.h" #include "stdlib.h" #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h"*/import "C"import ( "fmt" "unsafe" "io/ioutil" "bytes" "time" "os" "./oal")type MyData struct { MyBytes *bytes.Reader Count int64}func main() {fullFileByte, err := ioutil.ReadFile(os.Args[1]); errHandler(err) reader := bytes.NewReader(fullFileByte) // file info (Channels, frames, seekable etc...) var myInfo Info data := &MyData{MyBytes: reader, Count: 0} getLen := func() int64 { l := data.MyBytes.Len() println("Lenght:", l) return int64(l) } vRead := func(o []byte) int64 { //println("Read:", data.Count) i, _ := data.MyBytes.Read(o) // ; errHandler(err) data.Count += int64(i) return int64(i) }seek := func(offset int64, whence int) int64 { println("Seek:", data.Count) goWhence := whence data.Count, _ = data.MyBytes.Seek(offset, goWhence) // ; errHandler(err) return data.Count }tell := func() int64 { println("Tell: ", data.Count) return data.Count }globVB.GetFileLen = getLen globVB.Read = vRead globVB.Seek = seekglobVB.Tell = tell f := OpenVirtual(ModeRead, &myInfo) fmt.Println("Channel:", myInfo.Channels, "\nFrames:", myInfo.Frames, "\nFormat:", myInfo.Format, "\nSections:", myInfo.Sections, "\nSample Rate:", myInfo.Samplerate, "\nSeekable:", myInfo.Seekable) s := Source{} s.Create(uint32(C.CreateVirtualBuffer(f.SFile, *myInfo.fromGoToCInfo()))) s.Play() for { time.Sleep(500 * time.Millisecond) }}func OpenVirtual(mode FMode, info* Info) File { // FileWe're tricking the libsndfile. It's actually unnecessary code. var vb *C.VirtualCallbacks var file File // Go → C cInfo := info.fromGoToCInfo() cVirtualIO := C.NewVirtualIO() file.SFile = C.sf_open_virtual(cVirtualIO, C.int(mode), cInfo, (unsafe.Pointer)(vb)) if file.SFile == nil { panic(C.GoString(C.sf_strerror(file.SFile))) } *info = fromCToGo(cInfo) return file}type File struct { SFile* C.SNDFILE}type Info struct { Frames int64 Samplerate int Channels int Format int Sections int Seekable int}func (s Info) fromGoToCInfo() *C.SF_INFO { val := new(C.SF_INFO) val.frames = C.sf_count_t(s.Frames) val.samplerate = C.int(s.Samplerate) val.channels = C.int(s.Channels) val.format = C.int(s.Format) val.sections = C.int(s.Sections) val.seekable = C.int(s.Seekable) return val}type SFile C.SNDFILEfunc fromCToGo(info* C.SF_INFO) Info { val := Info{} val.Frames = int64(info.frames) val.Samplerate = int(info.samplerate) val.Channels = int(info.channels) val.Format = int(info.format) val.Sections = int(info.sections) val.Seekable = int(info.seekable) return val}// File modes: read, write and readwritetype FMode intconst ( ModeRead FMode = C.SFM_READ ModeWrite FMode = C.SFM_WRITE ModeReadWrite FMode = C.SFM_RDWR)func errHandler(e error) {if e != nil { panic(e)}}func init() { device, err := oal.OpenDevice("") errHandler(err) ctx, err := oal.CreateContext(device, nil) errHandler(err) oal.MakeContextCurrent(ctx)}type TGetFileLen func() int64type TVioSeek func(offset int64, whence int) int64type TVioRead func(o []byte) int64type TVioWrite func( ptr unsafe.Pointer, count int64, user_data unsafe.Pointer)type TVioTell func() int64type SVirtualIO struct { GetFileLen TGetFileLen Seek TVioSeek Read TVioRead Write TVioWrite Tell TVioTell // Data interface{}}var globVB SVirtualIO//export goVirtualReadfunc goVirtualRead(buffPtr unsafe.Pointer, count int64, data unsafe.Pointer) int64 { byteBuff := (*[1 << 31]byte) (buffPtr)[0:count] return globVB.Read(byteBuff) }//export goGetLenfunc goGetLen(userData unsafe.Pointer) int64 { return globVB.GetFileLen()}//export goVirtualSeekfunc goVirtualSeek(offset int64, whence int, userData unsafe.Pointer) int64 { return globVB.Seek(offset, whence)}//export goVirtualTellfunc goVirtualTell(userData unsafe.Pointer) int64 { return globVB.Tell()}type Source struct { Source C.ALuint Buffer C.ALuint}func (s Source) Delete() { C.alDeleteSources(1, &s.Source) C.alDeleteBuffers(1, &s.Buffer)}func (s* Source) Create(b uint32) { var source C.ALuint var buffer C.ALuint = C.ALuint(b) source = 0 C.alGenSources(1, &source) C.alSourcei(source, C.AL_BUFFER, C.ALint(buffer)) s.Source = source s.Buffer = buffer}func (s Source) Play() { C.alSourcePlay(s.Source)}额外的.c:#include <stdio.h>#include <string.h>#include <stdlib.h>#include "extras.h"#include "_cgo_export.h" #include "sndfile.h"#include "AL/al.h"#include "AL/alc.h" sf_count_t virtualRead(void *ptr, sf_count_t count, void *userData) { return goVirtualRead(ptr, count, userData); } sf_count_t virtualGetFileLen(void *udata) { return goGetLen(udata); } sf_count_tvirtualSeek(sf_count_t offset, int whence, void *user_data) { return goVirtualSeek(offset, whence, user_data);}sf_count_t virtualTell(void *userData) { return goVirtualTell(userData);}SF_VIRTUAL_IO*NewVirtualIO() { static SF_VIRTUAL_IO sndVirtualIO; sndVirtualIO.read = virtualRead; sndVirtualIO.get_filelen = virtualGetFileLen; sndVirtualIO.seek = virtualSeek; sndVirtualIO.tell = virtualTell;// sndVirtualIO.write= virtualWrite; return &sndVirtualIO;}ALuintCreateVirtualBuffer(SNDFILE *file, SF_INFO info) { ALenum err, format; ALuint buffer; SNDFILE *sndfile; SF_INFO sfinfo; sfinfo = info; short *membuf; sf_count_t num_frames; ALsizei num_bytes; sndfile = file; if(!sndfile) { return 0; } if(sfinfo.channels == 1) format = AL_FORMAT_MONO16; else if(sfinfo.channels == 2) format = AL_FORMAT_STEREO16; else { sf_close(sndfile); return 0; } membuf = malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(short)); num_frames = sf_readf_short(sndfile, membuf, sfinfo.frames); if(num_frames < 1) { free(membuf); sf_close(sndfile); return 0; } num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(short); buffer = 0; alGenBuffers(1, &buffer); alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate); free(membuf); sf_close(sndfile); err = alGetError(); if(err != AL_NO_ERROR) { if(buffer && alIsBuffer(buffer)) alDeleteBuffers(1, &buffer); return 0; } return buffer;}extras.h(头文件):#ifndef EXTRAS_H#define EXTRAS_H#include "sndfile.h"#include "AL/al.h"#include "AL/alc.h"// **redundant code. Because NULL is not accepted. :)typedef sf_count_t (*goreadfunc)(void* sf_count_t, void*);struct VirtualCallbacks { goreadfunc vRead; sf_vio_get_filelen vGetFileLen; sf_vio_seek vSeek; sf_vio_write vWrite;};typedef struct VirtualCallbacks VirtualCallbacks;sf_count_t goVirtualRead(void *ptr, sf_count_t count, void *user_data);SF_VIRTUAL_IO*NewVirtualIO(void);ALuintCreateVirtualBuffer(SNDFILE *file, SF_INFO info);#endif