godllsystem-callsbass

How can I make the program automatically exit after audio played over


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.

bass.go on github gist

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:

Is there any possibility that make the program automatically exit after audio played over?


Solution

  • 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 .