如何打开使用 libsndfile 加载到内存中的声音文件?
How can I open a sound file loaded in memory with libsndfile?
为了理解这一点,我想讲述以下场景:
我设计了一个游戏并完成了它。我用 AES 加密了音频数据。然后我将在游戏打开时解密这些加密文件。这些文件在内存中以字节编码。
例如我想为 OpenAL 创建缓冲区。
注意:这里没有加密功能。因为我的例子会比较复杂和困难。
这是我想用 CGo 和 Go 做的简单草图:
package main
// #cgo windows CFLAGS: -DGO_WINDOWS -I.
// #cgo windows LDFLAGS: -L. -lsndfile-1
/*
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <math.h>
#include <string.h>
#include <errno.h>
#include "sndfile.h"
typedef struct {
sf_count_t offset, length ;
unsigned char data [16 * 1024] ;
} VIO_DATA;
static sf_count_t
vfget_filelen(void *user_data) {
VIO_DATA *vf = (VIO_DATA *) user_data;
return vf->length;
}
static sf_count_t
vfseek(sf_count_t offset, int whence, void *user_data) {
VIO_DATA *vf = (VIO_DATA *) user_data ;
switch (whence) {
case SEEK_SET :
vf->offset = offset ;
break ;
case SEEK_CUR :
vf->offset = vf->offset + offset ;
break ;
case SEEK_END :
vf->offset = vf->length + offset ;
break ;
default :
break ;
};
return vf->offset;
}
static sf_count_t
vfread (void *ptr, sf_count_t count, void *user_data) {
VIO_DATA *vf = (VIO_DATA *) user_data ;
if (vf->offset + count > vf->length)
count = vf->length - vf->offset ;
memcpy (ptr, vf->data + vf->offset, count) ;
vf->offset += count ;
return count ;
}
static sf_count_t
vfwrite (const void *ptr, sf_count_t count, void *user_data) {
VIO_DATA *vf = (VIO_DATA *) user_data ;
if(vf->offset >= sizeof(vf->data))
return 0;
if (vf->offset + count > sizeof(vf->data))
count = sizeof (vf->data) - vf->offset;
memcpy(vf->data + vf->offset, ptr, (size_t) count);
vf->offset += count;
if(vf->offset > vf->length)
vf->length = vf->offset ;
return count;
}
static sf_count_t
vftell(void *user_data) {
VIO_DATA *vf = (VIO_DATA *) user_data ;
return vf->offset;
}
void voidTest(void *data) {
printf("value in c: %s \n", (char*)data);
static VIO_DATA vio_data ;
// static short fdata [256];
SF_VIRTUAL_IO vio ;
SNDFILE * file ;
SF_INFO sfinfo ;
vio.get_filelen = vfget_filelen;
vio.seek = vfseek ;
vio.read = vfread;
vio.write = vfwrite;
vio.tell = vftell;
vio_data.offset = 0;
vio_data.length = 0;
memset (&sfinfo, 0, sizeof (sfinfo));
sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
if((file = sf_open_virtual (&vio, SFM_READ, &sfinfo, &vio_data)) == NULL) {
printf("Sndfile error: %s\n", sf_strerror(file));
exit(1);
// return;
}
// !As an example I want to know the number of channels etc.
printf("Channel: %i\n", sfinfo.channels);
sf_close(file);
}
*/
import "C"
import (
"io/ioutil"
"fmt"
"unsafe"
)
func main() {
byt, err := ioutil.ReadFile("birds22.wav")
if err != nil {
fmt.Println(err)
return
}
var v unsafe.Pointer
v = (unsafe.Pointer)(&byt[0])
C.voidTest(v)
// fmt.Println("byte:", string(byt))
fmt.Println("No crash: test successfuly")
}
项目文件夹:
- main.go
- birds22.wav
- libsndfile-1.dll
- sndfile.h
我的构建命令:go build .
我不是经验丰富的 C 开发人员。这就是我与 CGo 合作的原因。但我会尝试使 C 代码适应 CGO,这解释了如何做到这一点。
我使用的来源和我尝试改编的代码:
libsndfile virtualio test
gosndfile virtualio test
这是一个解决方案。如果你想使用它,你必须把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)
}
extras.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
为了理解这一点,我想讲述以下场景: 我设计了一个游戏并完成了它。我用 AES 加密了音频数据。然后我将在游戏打开时解密这些加密文件。这些文件在内存中以字节编码。 例如我想为 OpenAL 创建缓冲区。
注意:这里没有加密功能。因为我的例子会比较复杂和困难。
这是我想用 CGo 和 Go 做的简单草图:
package main
// #cgo windows CFLAGS: -DGO_WINDOWS -I.
// #cgo windows LDFLAGS: -L. -lsndfile-1
/*
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <math.h>
#include <string.h>
#include <errno.h>
#include "sndfile.h"
typedef struct {
sf_count_t offset, length ;
unsigned char data [16 * 1024] ;
} VIO_DATA;
static sf_count_t
vfget_filelen(void *user_data) {
VIO_DATA *vf = (VIO_DATA *) user_data;
return vf->length;
}
static sf_count_t
vfseek(sf_count_t offset, int whence, void *user_data) {
VIO_DATA *vf = (VIO_DATA *) user_data ;
switch (whence) {
case SEEK_SET :
vf->offset = offset ;
break ;
case SEEK_CUR :
vf->offset = vf->offset + offset ;
break ;
case SEEK_END :
vf->offset = vf->length + offset ;
break ;
default :
break ;
};
return vf->offset;
}
static sf_count_t
vfread (void *ptr, sf_count_t count, void *user_data) {
VIO_DATA *vf = (VIO_DATA *) user_data ;
if (vf->offset + count > vf->length)
count = vf->length - vf->offset ;
memcpy (ptr, vf->data + vf->offset, count) ;
vf->offset += count ;
return count ;
}
static sf_count_t
vfwrite (const void *ptr, sf_count_t count, void *user_data) {
VIO_DATA *vf = (VIO_DATA *) user_data ;
if(vf->offset >= sizeof(vf->data))
return 0;
if (vf->offset + count > sizeof(vf->data))
count = sizeof (vf->data) - vf->offset;
memcpy(vf->data + vf->offset, ptr, (size_t) count);
vf->offset += count;
if(vf->offset > vf->length)
vf->length = vf->offset ;
return count;
}
static sf_count_t
vftell(void *user_data) {
VIO_DATA *vf = (VIO_DATA *) user_data ;
return vf->offset;
}
void voidTest(void *data) {
printf("value in c: %s \n", (char*)data);
static VIO_DATA vio_data ;
// static short fdata [256];
SF_VIRTUAL_IO vio ;
SNDFILE * file ;
SF_INFO sfinfo ;
vio.get_filelen = vfget_filelen;
vio.seek = vfseek ;
vio.read = vfread;
vio.write = vfwrite;
vio.tell = vftell;
vio_data.offset = 0;
vio_data.length = 0;
memset (&sfinfo, 0, sizeof (sfinfo));
sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
if((file = sf_open_virtual (&vio, SFM_READ, &sfinfo, &vio_data)) == NULL) {
printf("Sndfile error: %s\n", sf_strerror(file));
exit(1);
// return;
}
// !As an example I want to know the number of channels etc.
printf("Channel: %i\n", sfinfo.channels);
sf_close(file);
}
*/
import "C"
import (
"io/ioutil"
"fmt"
"unsafe"
)
func main() {
byt, err := ioutil.ReadFile("birds22.wav")
if err != nil {
fmt.Println(err)
return
}
var v unsafe.Pointer
v = (unsafe.Pointer)(&byt[0])
C.voidTest(v)
// fmt.Println("byte:", string(byt))
fmt.Println("No crash: test successfuly")
}
项目文件夹:
- main.go
- birds22.wav
- libsndfile-1.dll
- sndfile.h
我的构建命令:go build .
我不是经验丰富的 C 开发人员。这就是我与 CGo 合作的原因。但我会尝试使 C 代码适应 CGO,这解释了如何做到这一点。
我使用的来源和我尝试改编的代码: libsndfile virtualio test gosndfile virtualio test
这是一个解决方案。如果你想使用它,你必须把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)
}
extras.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