如何使用 libsndfile 打开加载到内存中的声音文件?

为了理解这一点,我想讲述以下场景:我设计了一个游戏并完成了它。我用 AES 加密了音频数据。然后我将在游戏打开时解密这些加密文件。这些文件在内存中以字节编码。例如,我想为 OpenAL 创建缓冲区。

注意:这里没有加密功能。因为我的例子会很复杂和困难。

项目文件夹:

  • main.go

  • 鸟22.wav

  • libsndfile-1.dll

  • sndfile.h

我的构建命令:go build .

我不是经验丰富的 C 开发人员。这就是我与 CGo 合作的原因。但我会尝试使 C 代码适应 CGO,这解释了如何做到这一点。

我使用的源代码和我尝试调整的代码: libsndfile virtualio test gosndfile virtualio test


米琪卡哇伊
浏览 182回答 1
1回答

扬帆大鱼

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

相关分类

Go