如何让程序播放完后自动退出
How can I make the program automatically exit after audio played over
我正在写一个小工具,它可以像 sox 一样播放 command/terminal
中的音频文件。我正在为 Windows.
使用 bass.dll
和 Golang syscall
这是我的代码,文件可以从评论中下载,仅运行 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()
}
有个大问题:
- 如果
time.Sleep
代码(bass.go
第68行)没有添加,没有声音播放很快退出。
- 当我添加
time.Sleep(time.Second * 10)
代码时,音频持续时间可能超过 10 秒。
有没有办法让程序播放完后自动退出?
我认为你可以使用 golang 的 defer
关键字来在播放功能完成后触发退出。
您可以参考这里:A Tour of Go | Defer
或者在这里:Golang Blog | Defer
package main
import "fmt"
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
==========
$ go run main.go
hello
world
我强烈建议通过 Effective Go on the golang.org website (it's not a long read, I am sure you can go through all the ideas in a single day), paying special attention to the concurrency section。
Go 背后的整个想法是让并发和异步编程变得简单,它使用了多种语言结构(通道、goroutines)专门设计来帮助您处理这些情况。
例如,您可以使用一个频道来发信号:
func main() {
// end signal
finished := make(chan bool)
// create and run a goroutine
go func() {
// do your bass stuff here
...
// send a signal
finished <- true
}()
// wait
<-finished
}
一个常见的模式也是将通道传递给执行此工作的函数:
func main() {
// end signal
finished := make(chan bool)
// PlayFile is responsible for
// signalling 'finished' when done
go PlayFile(someFile, finished);
// wait
<-finished
}
或者,如果您有多个例程,您将使用 WaitGroup
:
func main() {
// create the waitgroup
var wg sync.WaitGroup
// number of semaphores
wg.Add(1)
go func() {
// notify WaitGroup when done
// (the 'defer' keyword means
// this call will be executed before
// returning from the method)
defer wg.Done()
// do your bass stuff here
...
}()
wg.Wait()
}
谢谢大家。可以用BASS_ChannelGetLength
和BASS_ChannelGetPosition
函数解决问题。
代码如下:
// +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()
}
您也可以在 https://gist.github.com/ycrao/e7d1df181f870091b4a6d298d6ea2770#file-bass_play-go-L81-L91 获得。
我正在写一个小工具,它可以像 sox 一样播放 command/terminal
中的音频文件。我正在为 Windows.
bass.dll
和 Golang syscall
这是我的代码,文件可以从评论中下载,仅运行 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()
}
有个大问题:
- 如果
time.Sleep
代码(bass.go
第68行)没有添加,没有声音播放很快退出。 - 当我添加
time.Sleep(time.Second * 10)
代码时,音频持续时间可能超过 10 秒。
有没有办法让程序播放完后自动退出?
我认为你可以使用 golang 的 defer
关键字来在播放功能完成后触发退出。
您可以参考这里:A Tour of Go | Defer 或者在这里:Golang Blog | Defer
package main
import "fmt"
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
==========
$ go run main.go
hello
world
我强烈建议通过 Effective Go on the golang.org website (it's not a long read, I am sure you can go through all the ideas in a single day), paying special attention to the concurrency section。
Go 背后的整个想法是让并发和异步编程变得简单,它使用了多种语言结构(通道、goroutines)专门设计来帮助您处理这些情况。
例如,您可以使用一个频道来发信号:
func main() {
// end signal
finished := make(chan bool)
// create and run a goroutine
go func() {
// do your bass stuff here
...
// send a signal
finished <- true
}()
// wait
<-finished
}
一个常见的模式也是将通道传递给执行此工作的函数:
func main() {
// end signal
finished := make(chan bool)
// PlayFile is responsible for
// signalling 'finished' when done
go PlayFile(someFile, finished);
// wait
<-finished
}
或者,如果您有多个例程,您将使用 WaitGroup
:
func main() {
// create the waitgroup
var wg sync.WaitGroup
// number of semaphores
wg.Add(1)
go func() {
// notify WaitGroup when done
// (the 'defer' keyword means
// this call will be executed before
// returning from the method)
defer wg.Done()
// do your bass stuff here
...
}()
wg.Wait()
}
谢谢大家。可以用BASS_ChannelGetLength
和BASS_ChannelGetPosition
函数解决问题。
代码如下:
// +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()
}
您也可以在 https://gist.github.com/ycrao/e7d1df181f870091b4a6d298d6ea2770#file-bass_play-go-L81-L91 获得。