Closed LCRERGO closed 1 year ago
Hi @LCRERGO, thanks for submitting an issue! We haven't figured out a way to allow users to easily pass user data yet as it requires C code but this method could work by creating a C function that Go code can use.
audio.h
typedef struct UserData {
int toneHz;
int sampleHz;
float dPhase;
} UserData;
UserData* CreateUserData(int toneHz, int sampleHz, float dPhase);
void DestroyUserData(UserData*);
audio.c
#include <audio.h>
#include <stdlib.h>
UserData* CreateUserData(int toneHz, int sampleHz, float dPhase)
{
UserData* userdata = malloc(sizeof(UserData));
userdata->toneHz = toneHz;
userdata->sampleHz = sampleHz;
userdata->dPhase = dPhase;
return userdata;
}
void DestroyUserData(UserData* userdata)
{
if (userdata)
free(userdata);
}
audio.go
package main
/*
#include <audio.h>
typedef unsigned char Uint8;
void SineWave(void *userdata, Uint8 *stream, int len);
*/
import "C"
import (
"log"
"math"
"reflect"
"unsafe"
"github.com/veandco/go-sdl2/sdl"
)
const (
toneHz = 440
sampleHz = 48000
dPhase = 2 * math.Pi * toneHz / sampleHz
)
//export SineWave
func SineWave(userdata unsafe.Pointer, stream *C.Uint8, length C.int) {
n := int(length)
hdr := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(stream)), Len: n, Cap: n}
buf := *(*[]C.Uint8)(unsafe.Pointer(&hdr))
myUserData := (*C.UserData)(userdata)
log.Println("ToneHZ:", myUserData.toneHz)
log.Println("SampleHZ:", myUserData.sampleHz)
log.Println("DPhase:", myUserData.dPhase)
var phase float64
for i := 0; i < n; i += 2 {
phase += dPhase
sample := C.Uint8((math.Sin(phase) + 0.999999) * 128)
buf[i] = sample
buf[i+1] = sample
}
}
func main() {
if err := sdl.Init(sdl.INIT_AUDIO); err != nil {
log.Println(err)
return
}
defer sdl.Quit()
userData := C.CreateUserData(toneHz, sampleHz, dPhase)
defer C.DestroyUserData(userData)
spec := &sdl.AudioSpec{
Freq: sampleHz,
Format: sdl.AUDIO_U8,
Channels: 2,
Samples: sampleHz,
Callback: sdl.AudioCallback(C.SineWave),
UserData: unsafe.Pointer(userData),
}
if err := sdl.OpenAudio(spec, nil); err != nil {
log.Println(err)
return
}
sdl.PauseAudio(false)
sdl.Delay(5000) // play audio for long enough to understand whether it works
sdl.CloseAudio()
}
Thanks it worked. I've adapted your code to work in a single gofile and I'll post here for completness
package audio
// #include <stdlib.h>
// #include <stdint.h>
// #include <math.h>
// typedef unsigned char Uint8;
// typedef uint32_t Uint32;
// void SineWave(void *userdata, Uint8 *stream, int len);
//
// typedef struct UserData {
// int frequency;
// int sampleRate;
// float dPhase;
// } UserData;
//
// __attribute__((weak))
// UserData* NewUserData(Uint32 frequency, Uint32 sampleRate) {
// UserData* userdata = malloc(sizeof(UserData));
// userdata->frequency = frequency;
// userdata->sampleRate = sampleRate;
// userdata->dPhase = 2 * M_PI * frequency / sampleRate;
//
// return userdata;
// }
//
// __attribute__((weak))
// void DestroyUserData(UserData* userdata)
// {
// if (userdata)
// free(userdata);
// }
import "C"
import (
"math"
"reflect"
"unsafe"
"github.com/veandco/go-sdl2/sdl"
)
type AudioSubsystem struct {
SampleRate int
}
func NewAudioSubsystem(sampleRate int) *AudioSubsystem {
if err := sdl.Init(sdl.INIT_AUDIO); err != nil {
panic(err)
}
return &AudioSubsystem{
SampleRate: sampleRate,
}
}
func Beep(audio AudioSubsystem, frequency, duration int) {
userData := C.NewUserData(C.Uint32(frequency), C.Uint32(audio.SampleRate))
defer C.DestroyUserData(userData)
spec := sdl.AudioSpec{
Freq: int32(audio.SampleRate),
Format: sdl.AUDIO_U8,
Channels: 2,
Samples: 512,
Callback: sdl.AudioCallback(C.SineWave),
UserData: unsafe.Pointer(userData),
}
if err := sdl.OpenAudio(&spec, nil); err != nil {
panic(err)
}
sdl.PauseAudio(false)
sdl.Delay(uint32(duration))
}
func Destroy(audio *AudioSubsystem) {
sdl.CloseAudio()
}
//export SineWave
func SineWave(userdata unsafe.Pointer, stream *C.Uint8, len C.int) {
n := int(len)
hdr := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(stream)), Len: n, Cap: n}
buf := *(*[]C.Uint8)(unsafe.Pointer(&hdr))
data := (*C.UserData)(userdata)
var phase float64
for i := 0; i < n; i += 2 {
phase += float64(data.dPhase)
sample := C.Uint8((math.Sin(phase) + 0.999999) * 128)
buf[i] = sample
buf[i+1] = sample
}
}
I've been trying to adapt
audio/audio.go
to pass toneHz, sampleHz, dPhase into SineWave, but I have no clue how it should be done