Как я могу открыть звуковой файл, загруженный в память с помощью libsndfile?

#c #go #cgo #libsndfile

#c #Вперед #cgo #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 (amp;sfinfo, 0, sizeof (sfinfo));
        sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;


if((file = sf_open_virtual (amp;vio, SFM_READ, amp;sfinfo, amp;vio_data)) == NULL) {
            printf("Sndfile error: %sn", sf_strerror(file));
            exit(1);
//      return;
        }
        // !As an example I want to know the number of channels etc.
        printf("Channel: %in", 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)(amp;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

Комментарии:

1. «Я зашифровал аудиоданные с помощью AES». — но ПОЧЕМУ?

2. Я не хочу, чтобы мои аудиоданные использовались без разрешения. Эти звуки я купил и тщательно разработал. Кроме того, другие игровые компании шифруют свои данные.

3. Если у вас нет действительно надежного DRM, простое шифрование данных — пустая трата времени. И если у вас надежный DRM, вы не будете задавать вопросы такого уровня на SO. :/

4. Я не думаю, что это так. Я знаю, что многие проекты работают таким образом. Если я не думаю, что этих шагов достаточно, я могу создать другой формат файла. Но я снова вернусь к той же теме. В конечном итоге я должен обработать эти файлы с помощью libsndfile.

5. Ну, это ваш проект. Так что делайте, что хотите. Но я бы не стал тратить свое время.

Ответ №1:

Вот решение. Если вы хотите его использовать, вы должны поместить библиотеки libsndfile и openal в правильное расположение.
Протестировано с 64-разрядной версией Go 15.2.
Я пользователь Windows.
Файл считывается из виртуальных источников, информация печатается (канал, кадры, поиск и т. Д.) И воспроизводится с помощью OpenAL. И проблема на GitHub. Чтение может быть полезным. #16

основной код go:

 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 := amp;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, amp;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, amp;s.Source)
    C.alDeleteBuffers(1, amp;s.Buffer)
}

func (s* Source) Create(b uint32) {
        var source C.ALuint
    var buffer C.ALuint = C.ALuint(b)
    source = 0
    C.alGenSources(1, amp;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 amp;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, amp;buffer);
    alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate);

    free(membuf);
    sf_close(sndfile);

    err = alGetError();
    if(err != AL_NO_ERROR) {
        if(buffer amp;amp; alIsBuffer(buffer))
            alDeleteBuffers(1, amp;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