添加一些关于midi播放的项目

This commit is contained in:
terryLP
2025-03-24 14:30:56 +08:00
parent e31eb22077
commit 498b4ef13b
699 changed files with 186162 additions and 1 deletions
@@ -0,0 +1,563 @@
package rtaudio
/*
#cgo CXXFLAGS: -g
#cgo LDFLAGS: -lstdc++ -g
#cgo linux CXXFLAGS: -D__LINUX_ALSA__
#cgo linux LDFLAGS: -lm -lasound -pthread
#cgo linux,pulseaudio CXXFLAGS: -D__LINUX_PULSE__
#cgo linux,pulseaudio LDFLAGS: -lpulse -lpulse-simple
#cgo jack CXXFLAGS: -D__UNIX_JACK__
#cgo jack LDFLAGS: -ljack
#cgo windows CXXFLAGS: -D__WINDOWS_WASAPI__
#cgo windows LDFLAGS: -lm -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid -lwinmm -lole32 -static
#cgo darwin CXXFLAGS: -D__MACOSX_CORE__
#cgo darwin LDFLAGS: -framework CoreAudio -framework CoreFoundation
#include <stdlib.h>
#include <stdint.h>
#include "rtaudio_stub.h"
extern int goCallback(void *out, void *in, unsigned int nFrames,
double stream_time, rtaudio_stream_status_t status, void *userdata);
static inline void cgoRtAudioOpenStream(rtaudio_t audio,
rtaudio_stream_parameters_t *output_params,
rtaudio_stream_parameters_t *input_params,
rtaudio_format_t format,
unsigned int sample_rate,
unsigned int *buffer_frames,
int cb_id,
rtaudio_stream_options_t *options) {
rtaudio_open_stream(audio, output_params, input_params,
format, sample_rate, buffer_frames,
goCallback, (void *)(uintptr_t)cb_id, options, NULL);
}
*/
import "C"
import (
"errors"
"sync"
"time"
"unsafe"
)
// API is an enumeration of available compiled APIs. Supported API include
// Alsa/PulseAudio/OSS, Jack, CoreAudio, WASAPI/ASIO/DS and dummy API.
type API C.rtaudio_api_t
const (
// APIUnspecified looks for a working compiled API.
APIUnspecified API = C.RTAUDIO_API_UNSPECIFIED
// APILinuxALSA uses the Advanced Linux Sound Architecture API.
APILinuxALSA = C.RTAUDIO_API_LINUX_ALSA
// APILinuxPulse uses the Linux PulseAudio API.
APILinuxPulse = C.RTAUDIO_API_LINUX_PULSE
// APILinuxOSS uses the Linux Open Sound System API.
APILinuxOSS = C.RTAUDIO_API_LINUX_OSS
// APIUnixJack uses the Jack Low-Latency Audio Server API.
APIUnixJack = C.RTAUDIO_API_UNIX_JACK
// APIMacOSXCore uses Macintosh OS-X Core Audio API.
APIMacOSXCore = C.RTAUDIO_API_MACOSX_CORE
// APIWindowsWASAPI uses the Microsoft WASAPI API.
APIWindowsWASAPI = C.RTAUDIO_API_WINDOWS_WASAPI
// APIWindowsASIO uses the Steinberg Audio Stream I/O API.
APIWindowsASIO = C.RTAUDIO_API_WINDOWS_ASIO
// APIWindowsDS uses the Microsoft DirectSound API.
APIWindowsDS = C.RTAUDIO_API_WINDOWS_DS
// APIDummy is a compilable but non-functional API.
APIDummy = C.RTAUDIO_API_DUMMY
)
func (api API) String() string {
switch api {
case APIUnspecified:
return "unspecified"
case APILinuxALSA:
return "alsa"
case APILinuxPulse:
return "pulse"
case APILinuxOSS:
return "oss"
case APIUnixJack:
return "jack"
case APIMacOSXCore:
return "coreaudio"
case APIWindowsWASAPI:
return "wasapi"
case APIWindowsASIO:
return "asio"
case APIWindowsDS:
return "directsound"
case APIDummy:
return "dummy"
}
return "?"
}
// StreamStatus defines over- or underflow flags in the audio callback.
type StreamStatus C.rtaudio_stream_status_t
const (
// StatusInputOverflow indicates that data was discarded because of an
// overflow condition at the driver.
StatusInputOverflow StreamStatus = C.RTAUDIO_STATUS_INPUT_OVERFLOW
// StatusOutputUnderflow indicates that the output buffer ran low, likely
// producing a break in the output sound.
StatusOutputUnderflow StreamStatus = C.RTAUDIO_STATUS_OUTPUT_UNDERFLOW
)
// Version returns current RtAudio library version string.
func Version() string {
return C.GoString(C.rtaudio_version())
}
// CompiledAPI determines the available compiled audio APIs.
func CompiledAPI() (apis []API) {
capis := (*[1 << 27]C.rtaudio_api_t)(unsafe.Pointer(C.rtaudio_compiled_api()))
for i := 0; ; i++ {
api := capis[i]
if api == C.RTAUDIO_API_UNSPECIFIED {
break
}
apis = append(apis, API(api))
}
return apis
}
// DeviceInfo is the public device information structure for returning queried values.
type DeviceInfo struct {
Name string
Probed bool
NumOutputChannels int
NumInputChannels int
NumDuplexChannels int
IsDefaultOutput bool
IsDefaultInput bool
//rtaudio_format_t native_formats;
PreferredSampleRate uint
SampleRates []int
}
// StreamParams is the structure for specifying input or output stream parameters.
type StreamParams struct {
DeviceID uint
NumChannels uint
FirstChannel uint
}
// StreamFlags is a set of RtAudio stream option flags.
type StreamFlags C.rtaudio_stream_flags_t
const (
// FlagsNoninterleaved is set to use non-interleaved buffers (default = interleaved).
FlagsNoninterleaved = C.RTAUDIO_FLAGS_NONINTERLEAVED
// FlagsMinimizeLatency when set attempts to configure stream parameters for lowest possible latency.
FlagsMinimizeLatency = C.RTAUDIO_FLAGS_MINIMIZE_LATENCY
// FlagsHogDevice when set attempts to grab device for exclusive use.
FlagsHogDevice = C.RTAUDIO_FLAGS_HOG_DEVICE
// FlagsScheduleRealtime is set in attempt to select realtime scheduling (round-robin) for the callback thread.
FlagsScheduleRealtime = C.RTAUDIO_FLAGS_SCHEDULE_REALTIME
// FlagsAlsaUseDefault is set to use the "default" PCM device (ALSA only).
FlagsAlsaUseDefault = C.RTAUDIO_FLAGS_ALSA_USE_DEFAULT
)
// StreamOptions is the structure for specifying stream options.
type StreamOptions struct {
Flags StreamFlags
NumBuffers uint
Priotity int
Name string
}
// RtAudio is a "controller" used to select an available audio i/o interface.
type RtAudio interface {
Destroy()
CurrentAPI() API
Devices() ([]DeviceInfo, error)
DefaultOutputDevice() int
DefaultInputDevice() int
Open(out, in *StreamParams, format Format, sampleRate uint, frames uint, cb Callback, opts *StreamOptions) error
Close()
Start() error
Stop() error
Abort() error
IsOpen() bool
IsRunning() bool
Latency() (int, error)
SampleRate() (uint, error)
Time() (time.Duration, error)
SetTime(time.Duration) error
ShowWarnings(bool)
}
type rtaudio struct {
audio C.rtaudio_t
cb Callback
inputChannels int
outputChannels int
format Format
}
var _ RtAudio = &rtaudio{}
// Create a new RtAudio instance using the given API.
func Create(api API) (RtAudio, error) {
audio := C.rtaudio_create(C.rtaudio_api_t(api))
if C.rtaudio_error(audio) != nil {
return nil, errors.New(C.GoString(C.rtaudio_error(audio)))
}
return &rtaudio{audio: audio}, nil
}
func (audio *rtaudio) Destroy() {
C.rtaudio_destroy(audio.audio)
}
func (audio *rtaudio) CurrentAPI() API {
return API(C.rtaudio_current_api(audio.audio))
}
func (audio *rtaudio) DefaultInputDevice() int {
return int(C.rtaudio_get_default_input_device(audio.audio))
}
func (audio *rtaudio) DefaultOutputDevice() int {
return int(C.rtaudio_get_default_output_device(audio.audio))
}
func (audio *rtaudio) Devices() ([]DeviceInfo, error) {
n := C.rtaudio_device_count(audio.audio)
devices := []DeviceInfo{}
for i := C.int(0); i < n; i++ {
cinfo := C.rtaudio_get_device_info(audio.audio, i)
if C.rtaudio_error(audio.audio) != nil {
return nil, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
}
sr := []int{}
for _, r := range cinfo.sample_rates {
if r == 0 {
break
}
sr = append(sr, int(r))
}
devices = append(devices, DeviceInfo{
Name: C.GoString(&cinfo.name[0]),
Probed: cinfo.probed != 0,
NumInputChannels: int(cinfo.input_channels),
NumOutputChannels: int(cinfo.output_channels),
NumDuplexChannels: int(cinfo.duplex_channels),
IsDefaultOutput: cinfo.is_default_output != 0,
IsDefaultInput: cinfo.is_default_input != 0,
PreferredSampleRate: uint(cinfo.preferred_sample_rate),
SampleRates: sr,
})
// TODO: formats
}
return devices, nil
}
// Format defines RtAudio data format type.
type Format int
const (
// FormatInt8 uses 8-bit signed integer.
FormatInt8 Format = C.RTAUDIO_FORMAT_SINT8
// FormatInt16 uses 16-bit signed integer.
FormatInt16 = C.RTAUDIO_FORMAT_SINT16
// FormatInt24 uses 24-bit signed integer.
FormatInt24 = C.RTAUDIO_FORMAT_SINT24
// FormatInt32 uses 32-bit signed integer.
FormatInt32 = C.RTAUDIO_FORMAT_SINT32
// FormatFloat32 uses 32-bit floating point values normalized between (-1..1).
FormatFloat32 = C.RTAUDIO_FORMAT_FLOAT32
// FormatFloat64 uses 64-bit floating point values normalized between (-1..1).
FormatFloat64 = C.RTAUDIO_FORMAT_FLOAT64
)
// Buffer is a common interface for audio buffers of various data format types.
type Buffer interface {
Len() int
Int8() []int8
Int16() []int16
Int24() []Int24
Int32() []int32
Float32() []float32
Float64() []float64
}
// Int24 is a helper type to convert int32 values to int24 and back.
type Int24 [3]byte
// Set Int24 value using the least significant bytes of the given number n.
func (i *Int24) Set(n int32) {
(*i)[0], (*i)[1], (*i)[2] = byte(n&0xff), byte((n&0xff00)>>8), byte((n&0xff0000)>>16)
}
// Get Int24 value as int32.
func (i Int24) Get() int32 {
n := int32(i[0]) | int32(i[1])<<8 | int32(i[2])<<16
if n&0x800000 != 0 {
n |= ^0xffffff
}
return n
}
type buffer struct {
format Format
length int
numChannels int
ptr unsafe.Pointer
}
func (b *buffer) Len() int {
if b.ptr == nil {
return 0
}
return b.length
}
func (b *buffer) Int8() []int8 {
if b.format != FormatInt8 {
return nil
}
if b.ptr == nil {
return nil
}
return (*[1 << 30]int8)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
}
func (b *buffer) Int16() []int16 {
if b.format != FormatInt16 {
return nil
}
if b.ptr == nil {
return nil
}
return (*[1 << 29]int16)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
}
func (b *buffer) Int24() []Int24 {
if b.format != FormatInt24 {
return nil
}
if b.ptr == nil {
return nil
}
return (*[1 << 28]Int24)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
}
func (b *buffer) Int32() []int32 {
if b.format != FormatInt32 {
return nil
}
if b.ptr == nil {
return nil
}
return (*[1 << 27]int32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
}
func (b *buffer) Float32() []float32 {
if b.format != FormatFloat32 {
return nil
}
if b.ptr == nil {
return nil
}
return (*[1 << 27]float32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
}
func (b *buffer) Float64() []float64 {
if b.format != FormatFloat64 {
return nil
}
if b.ptr == nil {
return nil
}
return (*[1 << 23]float64)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
}
// Callback is a client-defined function that will be invoked when input data
// is available and/or output data is needed.
type Callback func(out Buffer, in Buffer, dur time.Duration, status StreamStatus) int
var (
mu sync.Mutex
audios = map[int]*rtaudio{}
)
func registerAudio(a *rtaudio) int {
mu.Lock()
defer mu.Unlock()
for i := 0; ; i++ {
if _, ok := audios[i]; !ok {
audios[i] = a
return i
}
}
}
func unregisterAudio(a *rtaudio) {
mu.Lock()
defer mu.Unlock()
for i := 0; i < len(audios); i++ {
if audios[i] == a {
delete(audios, i)
return
}
}
}
func findAudio(k int) *rtaudio {
mu.Lock()
defer mu.Unlock()
return audios[k]
}
//export goCallback
func goCallback(out, in unsafe.Pointer, frames C.uint, sec C.double,
status C.rtaudio_stream_status_t, userdata unsafe.Pointer) C.int {
k := int(uintptr(userdata))
audio := findAudio(k)
dur := time.Duration(time.Microsecond * time.Duration(sec*1000000.0))
inbuf := &buffer{audio.format, int(frames), audio.inputChannels, in}
outbuf := &buffer{audio.format, int(frames), audio.outputChannels, out}
return C.int(audio.cb(outbuf, inbuf, dur, StreamStatus(status)))
}
func (audio *rtaudio) Open(out, in *StreamParams, format Format, sampleRate uint,
frames uint, cb Callback, opts *StreamOptions) error {
var (
cInPtr *C.rtaudio_stream_parameters_t
cOutPtr *C.rtaudio_stream_parameters_t
cOptsPtr *C.rtaudio_stream_options_t
cIn C.rtaudio_stream_parameters_t
cOut C.rtaudio_stream_parameters_t
cOpts C.rtaudio_stream_options_t
)
audio.inputChannels = 0
audio.outputChannels = 0
if out != nil {
audio.outputChannels = int(out.NumChannels)
cOut.device_id = C.uint(out.DeviceID)
cOut.num_channels = C.uint(out.NumChannels)
cOut.first_channel = C.uint(out.FirstChannel)
cOutPtr = &cOut
}
if in != nil {
audio.inputChannels = int(in.NumChannels)
cIn.device_id = C.uint(in.DeviceID)
cIn.num_channels = C.uint(in.NumChannels)
cIn.first_channel = C.uint(in.FirstChannel)
cInPtr = &cIn
}
if opts != nil {
cOpts.flags = C.rtaudio_stream_flags_t(opts.Flags)
cOpts.num_buffers = C.uint(opts.NumBuffers)
cOpts.priority = C.int(opts.Priotity)
cOptsPtr = &cOpts
}
framesCount := C.uint(frames)
audio.format = format
audio.cb = cb
k := registerAudio(audio)
C.cgoRtAudioOpenStream(audio.audio, cOutPtr, cInPtr,
C.rtaudio_format_t(format), C.uint(sampleRate), &framesCount, C.int(k), cOptsPtr)
if C.rtaudio_error(audio.audio) != nil {
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
}
return nil
}
func (audio *rtaudio) Close() {
unregisterAudio(audio)
C.rtaudio_close_stream(audio.audio)
}
func (audio *rtaudio) Start() error {
C.rtaudio_start_stream(audio.audio)
if C.rtaudio_error(audio.audio) != nil {
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
}
return nil
}
func (audio *rtaudio) Stop() error {
C.rtaudio_stop_stream(audio.audio)
if C.rtaudio_error(audio.audio) != nil {
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
}
return nil
}
func (audio *rtaudio) Abort() error {
C.rtaudio_abort_stream(audio.audio)
if C.rtaudio_error(audio.audio) != nil {
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
}
return nil
}
func (audio *rtaudio) IsOpen() bool {
return C.rtaudio_is_stream_open(audio.audio) != 0
}
func (audio *rtaudio) IsRunning() bool {
return C.rtaudio_is_stream_running(audio.audio) != 0
}
func (audio *rtaudio) Latency() (int, error) {
latency := C.rtaudio_get_stream_latency(audio.audio)
if C.rtaudio_error(audio.audio) != nil {
return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
}
return int(latency), nil
}
func (audio *rtaudio) SampleRate() (uint, error) {
sampleRate := C.rtaudio_get_stream_sample_rate(audio.audio)
if C.rtaudio_error(audio.audio) != nil {
return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
}
return uint(sampleRate), nil
}
func (audio *rtaudio) Time() (time.Duration, error) {
sec := C.rtaudio_get_stream_time(audio.audio)
if C.rtaudio_error(audio.audio) != nil {
return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
}
return time.Duration(time.Microsecond * time.Duration(sec*1000000.0)), nil
}
func (audio *rtaudio) SetTime(t time.Duration) error {
sec := float64(t) * 1000000.0 / float64(time.Microsecond)
C.rtaudio_set_stream_time(audio.audio, C.double(sec))
if C.rtaudio_error(audio.audio) != nil {
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
}
return nil
}
func (audio *rtaudio) ShowWarnings(show bool) {
if show {
C.rtaudio_show_warnings(audio.audio, 1)
} else {
C.rtaudio_show_warnings(audio.audio, 0)
}
}
@@ -0,0 +1,4 @@
#include "../../../RtAudio.h"
#include "../../../RtAudio.cpp"
#include "../../../rtaudio_c.cpp"
@@ -0,0 +1 @@
#include "../../../rtaudio_c.h"
@@ -0,0 +1,71 @@
package rtaudio
import (
"log"
"math"
"time"
)
func ExampleCompiledAPI() {
log.Println("RtAudio version: ", Version())
for _, api := range CompiledAPI() {
log.Println("Compiled API: ", api)
}
}
func ExampleRtAudio_Devices() {
audio, err := Create(APIUnspecified)
if err != nil {
log.Fatal(err)
}
defer audio.Destroy()
devices, err := audio.Devices()
if err != nil {
log.Fatal(err)
}
for _, d := range devices {
log.Printf("Audio device: %#v\n", d)
}
}
func ExampleRtAudio_Open() {
const (
sampleRate = 44100
bufSz = 512
freq = 440.0
)
phase := 0.0
audio, err := Create(APIUnspecified)
if err != nil {
log.Fatal(err)
}
defer audio.Destroy()
params := StreamParams{
DeviceID: uint(audio.DefaultOutputDevice()),
NumChannels: 2,
FirstChannel: 0,
}
options := StreamOptions{
Flags: FlagsAlsaUseDefault,
}
cb := func(out, in Buffer, dur time.Duration, status StreamStatus) int {
samples := out.Float32()
for i := 0; i < len(samples)/2; i++ {
sample := float32(math.Sin(2 * math.Pi * phase))
phase += freq / sampleRate
samples[i*2] = sample
samples[i*2+1] = sample
}
return 0
}
err = audio.Open(&params, nil, FormatFloat32, sampleRate, bufSz, cb, &options)
if err != nil {
log.Fatal(err)
}
defer audio.Close()
audio.Start()
defer audio.Stop()
time.Sleep(3 * time.Second)
}