Git Product home page Git Product logo

Comments (12)

mkb218 avatar mkb218 commented on August 18, 2024

Hmm, I think you could use OpenVirtual There's an example in the tests: https://github.com/mkb218/gosndfile/blob/master/sndfile/virtual_test.go which you could probably pattern off of. I would use a bytes.Reader to encapsulate your byte array.

from gosndfile.

SeanTolstoyevski avatar SeanTolstoyevski commented on August 18, 2024

Thank you very much for your answer.

I read the tests. It's too complexed for me.

Can you write a little example?

from gosndfile.

mkb218 avatar mkb218 commented on August 18, 2024

I'll try later tonight.

from gosndfile.

SeanTolstoyevski avatar SeanTolstoyevski commented on August 18, 2024

that would be great.

I'm waiting for your answer.

from gosndfile.

mkb218 avatar mkb218 commented on August 18, 2024

Argh, I'm finding that the tests aren't passing against current libsndfile. It might be some time before I can get you a working example. Here's where I would start, but keep in mind that this panics for reasons I'm not sure about: https://gist.github.com/mkb218/fb442d05488aee5cd4b4fafc7e8275e0

from gosndfile.

SeanTolstoyevski avatar SeanTolstoyevski commented on August 18, 2024

Thank you. I will work this topic.

from gosndfile.

SeanTolstoyevski avatar SeanTolstoyevski commented on August 18, 2024

Now I have written a sample that really works but this does not work correctly for Go 1.5 and above.

Many things have changed in that release.

We can pass any Go expression to C, but c is prohibited from storing it.
How can we write callbacks better for Go 1.6>?

from gosndfile.

mkb218 avatar mkb218 commented on August 18, 2024

That's frustrating. I may have some time to look at after this weekend.

from gosndfile.

hoffie avatar hoffie commented on August 18, 2024

I'm currently trying to get the same accomplished. I found a hint on StackOverflow that there are disadvantages when using sf_open_virtual() as it would make libsndfile assume that the input is seekable (which it might not be). One is supposed to use sf_open_fd() instead.

Due to this (assumed) disadvantage in seekability and the fact that there still seems to be an issue using gosndfile's OpenVirtual (as described by the previous comments), I'm currently looking to implement my use case using memfds.

Note: I haven't verified any of this, I'm just sharing what I've found so far in the hope that it might help you as well.

from gosndfile.

SeanTolstoyevski avatar SeanTolstoyevski commented on August 18, 2024

Go's rules are very strict. I think we should rewrite the callback system.

If the problem is with reading bytes, then I don't know what can be done.

Is it possible to understand where the problem is?

panic does not print a good output.

from gosndfile.

SeanTolstoyevski avatar SeanTolstoyevski commented on August 18, 2024

Hi @mkb218 and all,

I found a solution. The problem seemed to be sending interfaces to C.
Sory. English is not my native language :) .

I used it with OpenAL. You will also see other things in the code. However, if you delete OpenAL, you can still use it normally.

And my advice is that we shouldn't use interfaces. We need to use some global variables.
Is such a PR acceptable?
I will consider to implement this.

And I used some code in this package. I hope licensing is ok.

main go code:

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 (header file):

#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

from gosndfile.

SeanTolstoyevski avatar SeanTolstoyevski commented on August 18, 2024

Hey @mkb218, I'll send pr if this is applicable. Otherwise, I will close the issue.

from gosndfile.

Related Issues (13)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.