Golang绑定bass.dll的实现与使用

Golang绑定bass.dll的实现与使用 我正在为bass.dll编写绑定(https://github.com/lexesv/gobass.dll),遇到了一些困难。

我无法封装这些函数:

typedef BOOL (CALLBACK RECORDPROC)(HRECORD handle, const void *buffer, DWORD length, void *user);
HRECORD BASSDEF(BASS_RecordStart)(DWORD freq, DWORD chans, DWORD flags, RECORDPROC *proc, void *user);

我尝试了:

func RecordStart(freq int, chans int, flags int, proc RecordCallback) (int, error) {
    	h := C.BASS_RecordStart(C.DWORD(freq), C.DWORD(chans), C.DWORD(flags), (*C.RECORDPROC)(unsafe.Pointer(&proc)), nil)
    	if h != 0 {
    		return int(h), nil
    	} else {
   		return 0, errMsg(int(C.BASS_ErrorGetCode()))
    	}
    }

type RecordCallback = func(handle C.HRECORD, buffer *C.char, length C.DWORD, user *C.char) bool

但在编译示例时,我遇到了错误: ./record.go:15: cannot use MyRecordCallback (type func(C.HRECORD, *C.char, C.DWORD, *C.char) bool) as type func(bass.C.HRECORD, *bass.C.char, bass.C.DWORD, *bass.C.char) bool in argument to bass.RecordStart

func main() {
 	fmt.Println(bass.RecordInit(-1))
 	fmt.Println(bass.RecordStart(44100, 2, 0, MyRecordCallback))
 	select {}
 }
func MyRecordCallback(handle C.HRECORD, buffer *C.char, length C.DWORD, user *C.char) bool {
 	fmt.Println("...")
 	return true
}

请帮助我解决如何正确封装回调函数的问题。


更多关于Golang绑定bass.dll的实现与使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang绑定bass.dll的实现与使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中封装C回调函数时,需要确保回调函数的签名与C函数期望的签名完全匹配。问题在于Go回调函数类型定义中的C类型与调用时使用的类型不一致。

以下是正确的实现方式:

package main

/*
#include "bass.h"
*/
import "C"
import (
	"fmt"
	"unsafe"
)

// 定义回调函数类型
type RecordCallback func(handle C.HRECORD, buffer unsafe.Pointer, length C.DWORD, user unsafe.Pointer) C.BOOL

// 全局变量存储回调函数引用
var recordCallback RecordCallback

// 导出给C调用的回调函数
//export goRecordProc
func goRecordProc(handle C.HRECORD, buffer unsafe.Pointer, length C.DWORD, user unsafe.Pointer) C.BOOL {
	if recordCallback != nil {
		return recordCallback(handle, buffer, length, user)
	}
	return C.BOOL(1)
}

func RecordStart(freq int, chans int, flags int, proc RecordCallback) (int, error) {
	// 保存回调函数引用
	recordCallback = proc
	
	h := C.BASS_RecordStart(
		C.DWORD(freq), 
		C.DWORD(chans), 
		C.DWORD(flags), 
		(C.RECORDPROC)(C.goRecordProc), 
		nil,
	)
	
	if h != 0 {
		return int(h), nil
	}
	return 0, fmt.Errorf("BASS error: %d", C.BASS_ErrorGetCode())
}

func RecordInit(device int) bool {
	return C.BASS_RecordInit(C.int(device)) != 0
}

使用示例:

package main

/*
#include "bass.h"
*/
import "C"
import (
	"fmt"
	"unsafe"
)

func main() {
	fmt.Println(RecordInit(-1))
	fmt.Println(RecordStart(44100, 2, 0, MyRecordCallback))
	select {}
}

func MyRecordCallback(handle C.HRECORD, buffer unsafe.Pointer, length C.DWORD, user unsafe.Pointer) C.BOOL {
	fmt.Printf("Record callback: handle=%v, length=%d\n", handle, length)
	return C.BOOL(1)
}

关键点说明:

  1. 使用//export指令导出Go函数供C调用
  2. 回调函数返回类型使用C.BOOL而不是bool
  3. 参数类型使用unsafe.Pointer来处理C的void*类型
  4. 通过全局变量保存回调函数引用,避免被垃圾回收
  5. 确保所有C类型在Go中的定义一致

编译时需要确保CGO正确配置,回调函数能够被C代码正确调用。这种方法可以避免类型不匹配的错误,并确保回调函数在整个录音过程中保持有效。

回到顶部