为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
米琪卡哇伊 2022-07-11 16:48:13
为了理解这一点,我想讲述以下场景:我设计了一个游戏并完成了它。我用 AES 加密了音频数据。然后我将在游戏打开时解密这些加密文件。这些文件在内存中以字节编码。例如,我想为 OpenAL 创建缓冲区。注意:这里没有加密功能。因为我的例子会很复杂和困难。项目文件夹:main.go鸟22.wavlibsndfile-1.dllsndfile.h我的构建命令:go build .我不是经验丰富的 C 开发人员。这就是我与 CGo 合作的原因。但我会尝试使 C 代码适应 CGO,这解释了如何做到这一点。我使用的源代码和我尝试调整的代码: libsndfile virtualio test gosndfile virtualio test
查看完整描述

1 回答

?
扬帆大鱼

TA贡献1799条经验 获得超9个赞

这是一个解决方案。如果要使用它,必须将 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 = seek

globVB.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 { // File



We'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.SNDFILE




func 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 readwrite

type FMode int


const (

    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() int64


type TVioSeek func(offset int64, whence int) int64


type TVioRead func(o []byte) int64


type TVioWrite func( ptr unsafe.Pointer, count int64, user_data unsafe.Pointer)


type TVioTell func() int64



type SVirtualIO struct {

    GetFileLen      TGetFileLen

    Seek        TVioSeek

    Read        TVioRead

    Write       TVioWrite

    Tell        TVioTell

    // Data     interface{}

}


var globVB  SVirtualIO


//export goVirtualRead

func goVirtualRead(buffPtr unsafe.Pointer, count int64, data unsafe.Pointer) int64 {

    byteBuff := (*[1 << 31]byte) (buffPtr)[0:count]


    return globVB.Read(byteBuff)

    }



//export goGetLen

func goGetLen(userData unsafe.Pointer) int64 {


    return globVB.GetFileLen()

}


//export goVirtualSeek

func goVirtualSeek(offset int64,  whence int, userData unsafe.Pointer) int64 {


    return globVB.Seek(offset, whence)

}


//export goVirtualTell

func 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_t

virtualSeek(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;

}




ALuint

CreateVirtualBuffer(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);


ALuint

CreateVirtualBuffer(SNDFILE *file, SF_INFO info);


#endif


查看完整回答
反对 回复 2022-07-11
  • 1 回答
  • 0 关注
  • 180 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信