I'm writing a small tool, it can play audio file in the command/terminal
like sox. I'm using bass.dll
and Golang syscall
for Windows.
Here is my code, files can downloaded from comments, only run on Windows X64.
package main
import (
"fmt"
"syscall"
"time"
"unsafe"
)
/*
基于 [bass.dll](http://us2.un4seen.com/files/bass24.zip)
和 [Golang syscall](https://github.com/golang/go/wiki/WindowsDLLs)
实现的命令行版播放器。
*/
type BassLib struct {
libBass syscall.Handle
init uintptr
free uintptr
streamCreateFile uintptr
channelPlay uintptr
channelPause uintptr
channelStop uintptr
}
func (bass *BassLib) LoadBass(bassDllPath string) bool {
bass.libBass, _ = syscall.LoadLibrary(bassDllPath)
if bass.libBass == 0 {
fmt.Println("load library result")
fmt.Println(bass.libBass)
return false
}
bass.init, _ = syscall.GetProcAddress(bass.libBass, "BASS_Init")
// BASS_init(device, freq, flags, win, clsid)
// see http://www.un4seen.com/doc/#bass/BASS_Init.html
device := 1
syscall.Syscall6(bass.init, 5, uintptr(device), uintptr(44100), uintptr(0), uintptr(0), uintptr(0), 0)
return true
}
func StrPtr(s string) uintptr {
// return uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(s)))
p, _ := syscall.UTF16PtrFromString(s)
return uintptr(unsafe.Pointer(p))
}
func (bass *BassLib) PlayFile(filePath string) {
bass.streamCreateFile, _ = syscall.GetProcAddress(bass.libBass, "BASS_StreamCreateFile")
// hstream = BASS_StreamCreateFile(mem=0, &file, offset=0, length=0, flags=(A_IsUnicode ? 0x80000000 : 0x40000))
// see http://www.un4seen.com/doc/#bass/BASS_StreamCreateFile.html
var bassUnicode uint32 = 0x80000000
hstream, _, _ := syscall.Syscall6(bass.streamCreateFile, 5, uintptr(0), StrPtr(filePath), uintptr(0), uintptr(0), uintptr(bassUnicode), 0)
// bassErrorGetCode, _ := syscall.GetProcAddress(bass.libBass, "BASS_ErrorGetCode")
// errCode, _, _ := syscall.Syscall(uintptr(bassErrorGetCode), 0, 0, 0, 0)
// fmt.Println(errCode)
fmt.Println("hstream")
fmt.Println(hstream)
bass.channelPlay, _ = syscall.GetProcAddress(bass.libBass, "BASS_ChannelPlay")
// BASS_ChannelPlay(hstream)
// see http://www.un4seen.com/doc/#bass/BASS_ChannelPlay.html
ret, _, _ := syscall.Syscall(bass.channelPlay, 2, hstream, uintptr(0), 0)
bassErrorGetCode, _ := syscall.GetProcAddress(bass.libBass, "BASS_ErrorGetCode")
errCode, _, _ := syscall.Syscall(bassErrorGetCode, 0, 0, 0, 0)
fmt.Println(errCode)
fmt.Println(ret)
// sleep to wait playing mp3 file
time.Sleep(time.Second * 10)
// bass.channelPause, _ = syscall.GetProcAddress(bass.libBass, "BASS_ChannelPause")
// bass.channelStop, _ = syscall.GetProcAddress(bass.libBass, "BASS_ChannelStop")
// return true
}
func (bass *BassLib) UnLoad() {
if bass.libBass != 0 {
bass.free, _ = syscall.GetProcAddress(bass.libBass, "BASS_Free")
syscall.Syscall(bass.free, 0, 0, 0, 0)
// BASS_Free()
// see http://www.un4seen.com/doc/#bass/BASS_Free.html
syscall.FreeLibrary(bass.libBass)
}
}
func main() {
bass := &BassLib{}
bass.LoadBass("C:\\workspace\\play\\bass.dll")
bass.PlayFile("C:\\workspace\\play\\sample.mp3")
bass.UnLoad()
}
There is a big problem:
time.Sleep
code (bass.go
line 68) not added , no sound played with quickly quit out.time.Sleep(time.Second * 10)
code, maybe the audio duration more than 10 seconds.Is there any possibility that make the program automatically exit after audio played over?
Thanks everyone. Can solve the problem with BASS_ChannelGetLength
and BASS_ChannelGetPosition
functions.
Here is the code:
// +build windows
package main
import (
"fmt"
"syscall"
"time"
"unsafe"
)
/*
基于 [bass.dll](http://us2.un4seen.com/files/bass24.zip)
和 [Golang syscall](https://github.com/golang/go/wiki/WindowsDLLs)
实现的命令行版播放器。
*/
type (
BASSErrorGetCode int32
)
const (
BassUnicode uint32 = 0x80000000 // BASS_UNICODE
BassSampleFloat uint32 = 256 // BASS_SAMPLE_FLOAT
BassPosByte uint64 = 0 // BASS_POS_BYTE
)
type BassLib struct {
libBass syscall.Handle
init uintptr
free uintptr
streamCreateFile uintptr
channelPlay uintptr
channelPause uintptr
channelStop uintptr
channelGetLength uintptr
channelGetPosition uintptr
channelBytes2Seconds uintptr
}
func (bass *BassLib) LoadBass(bassDllPath string) bool {
bass.libBass, _ = syscall.LoadLibrary(bassDllPath)
if bass.libBass == 0 {
fmt.Println("Load `bass.dll` library failed!")
errCode := bass.GetBassErrorGetCode()
fmt.Println("Bass_Init failed!")
fmt.Println(errCode)
return false
}
bass.init, _ = syscall.GetProcAddress(bass.libBass, "BASS_Init")
// BASS_Init(device, freq, flags, win, clsid)
// see http://www.un4seen.com/doc/#bass/BASS_Init.html
device := 1
r, _, _ := syscall.Syscall6(bass.init, 5, uintptr(device), uintptr(44100), uintptr(0), uintptr(0), uintptr(0), 0)
// var ret = *(* int)(unsafe.Pointer(&r))
if r == 0 {
errCode := bass.GetBassErrorGetCode()
fmt.Println("Bass_Init failed!")
fmt.Println(errCode)
return false
}
return true
}
func StrPtr(s string) uintptr {
p, _ := syscall.UTF16PtrFromString(s)
return uintptr(unsafe.Pointer(p))
}
func (bass *BassLib) PlayFile(filePath string) {
bass.streamCreateFile, _ = syscall.GetProcAddress(bass.libBass, "BASS_StreamCreateFile")
// hStream = BASS_StreamCreateFile(mem=0, &file, offset=0, length=0, flags=(A_IsUnicode ? 0x80000000 : 0x40000))
// see http://www.un4seen.com/doc/#bass/BASS_StreamCreateFile.html
hStream, _, _ := syscall.Syscall6(bass.streamCreateFile, 5, uintptr(0), StrPtr(filePath), uintptr(0), uintptr(0), uintptr(BassUnicode|BassSampleFloat), 0)
bass.channelPlay, _ = syscall.GetProcAddress(bass.libBass, "BASS_ChannelPlay")
// BASS_ChannelPlay(hStream)
// see http://www.un4seen.com/doc/#bass/BASS_ChannelPlay.html
r, _, _ := syscall.Syscall(bass.channelPlay, 2, hStream, uintptr(0), 0)
if r == 1 {
totalDuration := bass.GetAudioByteLength(hStream)
// currentPos := bass.GetAudioCurrentBytePosition(hStream)
fmt.Println(totalDuration)
// fmt.Println(currentPos)
time.Sleep(time.Second*1)
for {
currentPos := bass.GetAudioCurrentBytePosition(hStream)
if currentPos >= totalDuration {
break
}
}
} else {
errCode := bass.GetBassErrorGetCode()
fmt.Println("Bass_ChannelPlay failed!")
fmt.Println(errCode)
}
}
func (bass *BassLib) GetBassErrorGetCode() BASSErrorGetCode {
bassErrorGetCode, _ := syscall.GetProcAddress(bass.libBass, "BASS_ErrorGetCode")
// BASS_ErrorGetCode()
// BASS_OK BASSErrorGetCode = 0 // all is OK
// BASS_ERROR_MEM BASSErrorGetCode = 1 // memory error
// ...
// see http://www.un4seen.com/doc/#bass/BASS_ErrorGetCode.html
errCode, _, _ := syscall.Syscall(bassErrorGetCode, 0, 0, 0, 0)
var iErrCode = *(*BASSErrorGetCode)(unsafe.Pointer(&errCode))
return iErrCode
}
func (bass *BassLib) GetAudioByteLength(handle uintptr) uintptr {
// (QWORD) BASS_ChannelGetLength(handle=hStream, mode)
// see http://www.un4seen.com/doc/#bass/BASS_ChannelGetLength.html
bass.channelGetLength, _ = syscall.GetProcAddress(bass.libBass, "BASS_ChannelGetLength")
len, _, _ := syscall.Syscall(bass.channelGetLength, 2, handle, uintptr(BassPosByte), 0)
return len
}
func (bass *BassLib) GetAudioCurrentBytePosition(handle uintptr) uintptr {
// BASS_ChannelGetPosition(handle=hStream, mode)
// see http://www.un4seen.com/doc/#bass/BASS_ChannelGetPosition.html
bass.channelGetPosition, _ = syscall.GetProcAddress(bass.libBass, "BASS_ChannelGetPosition")
pos, _, _ := syscall.Syscall(bass.channelGetPosition, 2, handle, uintptr(BassPosByte), 0)
return pos
}
func (bass *BassLib) GetChannelBytes2Seconds(handle uintptr, pos uintptr) uintptr {
// BASS_ChannelBytes2Seconds(handle=hStream, pos)
// see http://www.un4seen.com/doc/#bass/BASS_ChannelBytes2Seconds.html
// bass.channelBytes2Seconds, _ = syscall.GetProcAddress(bass.libBass, "BASS_ChannelBytes2Seconds")
len, _, _ := syscall.Syscall(bass.channelBytes2Seconds, 2, handle, pos, 0)
return len
}
func (bass *BassLib) UnLoad() {
if bass.libBass != 0 {
bass.free, _ = syscall.GetProcAddress(bass.libBass, "BASS_Free")
syscall.Syscall(bass.free, 0, 0, 0, 0)
// BASS_Free()
// see http://www.un4seen.com/doc/#bass/BASS_Free.html
syscall.FreeLibrary(bass.libBass)
}
}
func main() {
bass := &BassLib{}
bass.LoadBass("C:\\workspace\\play\\bass.dll")
bass.PlayFile("C:\\workspace\\play\\sample.mp3")
bass.UnLoad()
}
Also you can get at https://gist.github.com/ycrao/e7d1df181f870091b4a6d298d6ea2770#file-bass_play-go-L81-L91 .