├── .gitignore ├── LICENSE ├── portaudio ├── _example-c │ ├── Makefile │ └── example.c ├── amalg.sh ├── example │ └── main.go ├── pa_amalg.c ├── portaudio.go ├── portaudio.h └── portaudio_cb.go ├── portmidi ├── _example-c │ ├── Makefile │ └── example.c ├── amalg.sh ├── example │ └── main.go ├── pm_amalg.c ├── portmidi.go ├── portmidi.h └── stream.go ├── rtaudio ├── RtAudio.cpp ├── RtAudio.h ├── _example-c │ ├── Makefile │ └── example.c ├── example │ └── main.go ├── rtaudio.go ├── rtaudio_c.cpp └── rtaudio_c.h ├── rtmidi ├── RtMidi.cpp ├── RtMidi.h ├── example │ └── main.go ├── rtmidi.go ├── rtmidi_c.cpp └── rtmidi_c.h └── soundio ├── _example-c ├── Makefile └── example.c ├── amalg.sh ├── soundio.h └── soundio_amalg.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | *.su 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 NaiveSound 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /portaudio/_example-c/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(jack),1) 2 | CFLAGS += -DPA_USE_JACK=1 3 | LDFLAGS += -lm -ljack -pthread 4 | endif 5 | ifeq ($(linux),1) 6 | CFLAGS += -DPA_USE_ALSA=1 7 | LDFLAGS += -lm -lasound -pthread 8 | endif 9 | ifeq ($(macos),1) 10 | CFLAGS += -DPA_USE_COREAUDIO=1 11 | LDFLAGS += -lm -framework CoreAudio -framework CoreMIDI -framework CoreFoundation 12 | endif 13 | ifeq ($(windows),1) 14 | CFLAGS += -DPA_USE_WASAPI=1 15 | LDFLAGS += -lm -lole32 -lksuser -lwinmm -lws2_32 -mwindows -static 16 | endif 17 | 18 | example: example.o ../pa_amalg.o 19 | $(CC) $^ $(LDFLAGS) -o $@ 20 | clean: 21 | rm -f example example.o ../pa_amalg.o 22 | -------------------------------------------------------------------------------- /portaudio/_example-c/example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../portaudio.h" 5 | 6 | #define SAMPLE_RATE 44100 7 | #define SINE_HZ 440 8 | #define SINE_DURATION_SEC 3 9 | 10 | struct context { 11 | float phase; 12 | }; 13 | 14 | static int cb(const void *in, void *out, unsigned long nframes, 15 | const PaStreamCallbackTimeInfo *ti, PaStreamCallbackFlags flags, 16 | void *arg) { 17 | float *buf = (float *)out; 18 | struct context *context = (struct context *)arg; 19 | 20 | for (int i = 0; i < nframes; i++) { 21 | buf[i * 2] = buf[i * 2 + 1] = sinf(context->phase); 22 | context->phase += SINE_HZ * 2 * 3.1415926f / SAMPLE_RATE; 23 | } 24 | return 0; 25 | } 26 | 27 | int main() { 28 | PaStream *stream; 29 | PaError err; 30 | 31 | struct context context = {0}; 32 | 33 | err = Pa_Initialize(); 34 | if (err != paNoError) { 35 | goto error; 36 | } 37 | 38 | err = Pa_OpenDefaultStream(&stream, 0, 2, paFloat32, SAMPLE_RATE, 256, cb, 39 | &context); 40 | if (err != paNoError) { 41 | goto error; 42 | } 43 | 44 | err = Pa_StartStream(stream); 45 | if (err != paNoError) { 46 | goto error; 47 | } 48 | 49 | Pa_Sleep(SINE_DURATION_SEC * 1000); 50 | 51 | err = Pa_StopStream(stream); 52 | if (err != paNoError) { 53 | goto error; 54 | } 55 | err = Pa_CloseStream(stream); 56 | if (err != paNoError) { 57 | goto error; 58 | } 59 | error: 60 | if (err != 0) { 61 | fprintf(stderr, "error: %s\n", Pa_GetErrorText(err)); 62 | } 63 | Pa_Terminate(); 64 | return err; 65 | } 66 | -------------------------------------------------------------------------------- /portaudio/amalg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | AMALG_H=portaudio.h 4 | AMALG_C=pa_amalg.c 5 | 6 | echo "Downloading PortAudio sources..." 7 | git clone https://git.assembla.com/portaudio.git 8 | 9 | cd portaudio 10 | 11 | echo "Amalgamating to $AMALG_C and $AMALG_H..." 12 | 13 | sed 's!\(#include ".*.h"\)!// (amalg) \1!' >> $AMALG_H << EOF 14 | $(cat include/portaudio.h) 15 | 16 | #if PA_USE_DS || PA_USE_WASAPI || PA_USE_WMME 17 | $(cat include/pa_win_waveformat.h) 18 | #endif 19 | 20 | #if PA_USE_ASIO 21 | $(cat include/pa_asio.h) 22 | #endif 23 | 24 | #if PA_USE_JACK 25 | $(cat include/pa_jack.h) 26 | #endif 27 | 28 | #if PA_USE_ALSA 29 | $(cat include/pa_linux_alsa.h) 30 | #endif 31 | 32 | #if PA_USE_COREAUDIO 33 | $(cat include/pa_mac_core.h) 34 | #endif 35 | 36 | #if PA_USE_DS 37 | $(cat include/pa_win_ds.h) 38 | #endif 39 | 40 | #if PA_USE_WASAPI 41 | $(cat include/pa_win_wasapi.h) 42 | #endif 43 | 44 | #if PA_USE_WDMKS 45 | $(cat include/pa_win_wdmks.h) 46 | #endif 47 | 48 | #if PA_USE_WMME 49 | $(cat include/pa_win_wmme.h) 50 | #endif 51 | 52 | EOF 53 | 54 | echo "#include \"$AMALG_H\"" > $AMALG_C 55 | sed 's!\(#include ".*.h"\)!// (amalg) \1!' >> $AMALG_C << EOF 56 | 57 | $(cat src/common/pa_types.h) 58 | 59 | $(cat src/common/pa_allocation.h) 60 | $(cat src/common/pa_converters.h) 61 | $(cat src/common/pa_cpuload.h) 62 | $(cat src/common/pa_debugprint.h) 63 | $(cat src/common/pa_dither.h) 64 | $(cat src/common/pa_endianness.h) 65 | $(cat src/common/pa_gitrevision.h) 66 | $(cat src/common/pa_hostapi.h) 67 | $(cat src/common/pa_memorybarrier.h) 68 | $(cat src/common/pa_process.h) 69 | $(cat src/common/pa_ringbuffer.h) 70 | $(cat src/common/pa_stream.h) 71 | $(cat src/common/pa_trace.h) 72 | $(cat src/common/pa_util.h) 73 | 74 | $(cat src/common/*.c) 75 | 76 | #ifdef WIN32 77 | $(cat src/os/win/pa_win_coinitialize.h) 78 | $(cat src/os/win/pa_win_coinitialize.c) 79 | $(cat src/os/win/pa_win_hostapis.c) 80 | $(cat src/os/win/pa_win_util.c) 81 | $(cat src/os/win/pa_win_waveformat.c) 82 | #if 0 83 | $(cat src/os/win/pa_x86_plain_converters.h) 84 | $(cat src/os/win/pa_x86_plain_converters.c) 85 | #endif 86 | #if PA_USE_WDMKS 87 | $(cat src/os/win/pa_win_wdmks_utils.c) 88 | $(cat src/os/win/pa_win_wdmks_utils.h) 89 | #endif /* PA_USE_WDMKS */ 90 | #else 91 | $(cat src/os/unix/*.h) 92 | $(cat src/os/unix/*.c) 93 | #endif /* WIN32/UNIX */ 94 | 95 | #if PA_USE_ALSA 96 | $(cat src/hostapi/alsa/*.h) 97 | $(cat src/hostapi/alsa/*.c) 98 | #endif /* PA_USE_ALSA */ 99 | 100 | /* PA_USE_ASIHPI */ 101 | #if 0 102 | $(cat src/hostapi/asihpi/*.h) 103 | $(cat src/hostapi/asihpi/*.c) 104 | #endif /* PA_USE_ASIHPI */ 105 | 106 | /* PA_USE_ASIO */ 107 | #if 0 108 | // This uses C++, not C! 109 | $(cat src/hostapi/asio/*.h) 110 | $(cat src/hostapi/asio/*.cpp) 111 | #endif /* PA_USE_ASIO */ 112 | 113 | #if PA_USE_COREAUDIO 114 | $(cat src/hostapi/coreaudio/pa_mac_core_utilities.h) 115 | $(cat src/hostapi/coreaudio/pa_mac_core_blocking.h) 116 | $(cat src/hostapi/coreaudio/pa_mac_core_internal.h) 117 | $(cat src/hostapi/coreaudio/pa_mac_core_blocking.c) 118 | $(cat src/hostapi/coreaudio/pa_mac_core_utilities.c) 119 | $(cat src/hostapi/coreaudio/pa_mac_core.c) 120 | #endif /* PA_USE_COREAUDIO */ 121 | 122 | #if PA_USE_DS 123 | $(cat src/hostapi/dsound/*.h) 124 | $(cat src/hostapi/dsound/*.c) 125 | #endif /* PA_USE_DS */ 126 | 127 | #if PA_USE_JACK 128 | $(cat src/hostapi/jack/*.h) 129 | $(cat src/hostapi/jack/*.c) 130 | #endif /* PA_USE_JACK */ 131 | 132 | #if PA_USE_OSS 133 | $(cat src/hostapi/oss/*.h) 134 | $(cat src/hostapi/oss/*.c) 135 | #endif /* PA_USE_OSS */ 136 | 137 | #if PA_USE_WASAPI 138 | /* This is inspired by the patch from MXE to make it work with mingw */ 139 | $(sed \ 140 | -e 's!\(#if defined(_MSC_VER) && (_MSC_VER >= 1400)\)!\1 || defined(__MINGW64_VERSION_MAJOR)!' \ 141 | -e 's!!!' \ 142 | -e 's!!!' \ 143 | -e 's!!!' \ 144 | -e '//a #include ' \ 145 | src/hostapi/wasapi/*.c) 146 | #endif /* PA_USE_WASAPI */ 147 | 148 | #if PA_USE_WDMKS 149 | $(cat src/hostapi/wdmks/*.h) 150 | $(cat src/hostapi/wdmks/*.c) 151 | #endif /* PA_USE_WDMKS */ 152 | 153 | #if PA_USE_WMME 154 | $(cat src/hostapi/wmme/*.h) 155 | $(cat src/hostapi/wmme/*.c) 156 | #endif /* PA_USE_WMME */ 157 | EOF 158 | 159 | mv $AMALG_C $AMALG_H .. 160 | cd .. 161 | 162 | #rm -rf portaudio 163 | 164 | echo "Done." 165 | -------------------------------------------------------------------------------- /portaudio/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math" 6 | "time" 7 | 8 | "github.com/naivesound/audio/portaudio" 9 | ) 10 | 11 | const ( 12 | SampleRate = 44100.0 13 | Freq = 440.0 14 | ) 15 | 16 | func main() { 17 | portaudio.Initialize() 18 | defer portaudio.Terminate() 19 | 20 | phase := float64(0) 21 | 22 | stream, err := portaudio.OpenDefaultStream(0, 2, SampleRate, 0, func(out [][]float32) { 23 | numCh := len(out) 24 | for i := range out[0] { 25 | sample := float32(math.Sin(2 * math.Pi * phase)) 26 | phase += Freq / SampleRate 27 | for ch := 0; ch < numCh; ch++ { 28 | out[ch][i] = sample 29 | } 30 | } 31 | }) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | defer stream.Close() 36 | stream.Start() 37 | defer stream.Stop() 38 | 39 | time.Sleep(2 * time.Second) 40 | } 41 | -------------------------------------------------------------------------------- /portaudio/pa_amalg.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naivesound/audio/f928e9a79db0259efaaab0a207e77fd2d6be964c/portaudio/pa_amalg.c -------------------------------------------------------------------------------- /portaudio/portaudio.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package portaudio applies Go bindings to the PortAudio library. 3 | 4 | For the most part, these bindings parallel the underlying PortAudio API; please 5 | refer to http://www.portaudio.com/docs.html for details. Differences 6 | introduced by the bindings are documented here: 7 | 8 | Instead of passing a flag to OpenStream, audio sample formats are inferred from 9 | the signature of the stream callback or, for a blocking stream, from the types 10 | of the buffers. See the StreamCallback and Buffer types for details. 11 | 12 | Blocking I/O: Read and Write do not accept buffer arguments; instead they use 13 | the buffers (or pointers to buffers) provided to OpenStream. The number of 14 | samples to read or write is determined by the size of the buffers. 15 | 16 | The StreamParameters struct combines parameters for both the input and the 17 | output device as well as the sample rate, buffer size, and flags. 18 | */ 19 | package portaudio 20 | 21 | /* 22 | #cgo linux CFLAGS: -DPA_USE_ALSA=1 -Wno-deprecated-declarations 23 | #cgo linux LDFLAGS: -lm -lasound -pthread 24 | 25 | #cgo windows CFLAGS: -DPA_USE_WASAPI=1 26 | #cgo windows LDFLAGS: -lm -luuid -lksuser -lwinmm -lole32 27 | 28 | #cgo darwin CFLAGS: -DPA_USE_COREAUDIO=1 -Wno-deprecated-declarations 29 | #cgo darwin LDFLAGS: -framework CoreServices -framework CoreAudio -framework CoreFoundation 30 | #cgo darwin LDFLAGS: -framework AudioUnit -framework AudioToolbox 31 | 32 | #include "portaudio.h" 33 | 34 | int CgoStreamCallback(const void *inputBuffer, 35 | void *outputBuffer, unsigned long frames, 36 | const PaStreamCallbackTimeInfo *timeInfo, 37 | PaStreamCallbackFlags statusFlags, void *userData); 38 | */ 39 | import "C" 40 | 41 | import ( 42 | "fmt" 43 | "os" 44 | "reflect" 45 | "runtime" 46 | "sync" 47 | "time" 48 | "unsafe" 49 | ) 50 | 51 | //#xxxcgo pkg-config: portaudio-2.0 52 | 53 | // Version returns the release number of PortAudio. 54 | func Version() int { 55 | return int(C.Pa_GetVersion()) 56 | } 57 | 58 | // VersionText returns the textual description of the PortAudio release. 59 | func VersionText() string { 60 | return C.GoString(C.Pa_GetVersionText()) 61 | } 62 | 63 | // Error wraps over PaError. 64 | type Error C.PaError 65 | 66 | func (err Error) Error() string { 67 | return C.GoString(C.Pa_GetErrorText(C.PaError(err))) 68 | } 69 | 70 | // PortAudio Errors. 71 | const ( 72 | NotInitialized Error = C.paNotInitialized 73 | InvalidChannelCount Error = C.paInvalidChannelCount 74 | InvalidSampleRate Error = C.paInvalidSampleRate 75 | InvalidDevice Error = C.paInvalidDevice 76 | InvalidFlag Error = C.paInvalidFlag 77 | SampleFormatNotSupported Error = C.paSampleFormatNotSupported 78 | BadIODeviceCombination Error = C.paBadIODeviceCombination 79 | InsufficientMemory Error = C.paInsufficientMemory 80 | BufferTooBig Error = C.paBufferTooBig 81 | BufferTooSmall Error = C.paBufferTooSmall 82 | NullCallback Error = C.paNullCallback 83 | BadStreamPtr Error = C.paBadStreamPtr 84 | TimedOut Error = C.paTimedOut 85 | InternalError Error = C.paInternalError 86 | DeviceUnavailable Error = C.paDeviceUnavailable 87 | IncompatibleHostApiSpecificStreamInfo Error = C.paIncompatibleHostApiSpecificStreamInfo 88 | StreamIsStopped Error = C.paStreamIsStopped 89 | StreamIsNotStopped Error = C.paStreamIsNotStopped 90 | InputOverflowed Error = C.paInputOverflowed 91 | OutputUnderflowed Error = C.paOutputUnderflowed 92 | HostApiNotFound Error = C.paHostApiNotFound 93 | InvalidHostApi Error = C.paInvalidHostApi 94 | CanNotReadFromACallbackStream Error = C.paCanNotReadFromACallbackStream 95 | CanNotWriteToACallbackStream Error = C.paCanNotWriteToACallbackStream 96 | CanNotReadFromAnOutputOnlyStream Error = C.paCanNotReadFromAnOutputOnlyStream 97 | CanNotWriteToAnInputOnlyStream Error = C.paCanNotWriteToAnInputOnlyStream 98 | IncompatibleStreamHostApi Error = C.paIncompatibleStreamHostApi 99 | BadBufferPtr Error = C.paBadBufferPtr 100 | ) 101 | 102 | // UnanticipatedHostError contains details for ApiHost related errors. 103 | type UnanticipatedHostError struct { 104 | HostApiType HostApiType 105 | Code int 106 | Text string 107 | } 108 | 109 | func (err UnanticipatedHostError) Error() string { 110 | return err.Text 111 | } 112 | 113 | func newError(err C.PaError) error { 114 | switch err { 115 | case C.paUnanticipatedHostError: 116 | hostErr := C.Pa_GetLastHostErrorInfo() 117 | return UnanticipatedHostError{ 118 | HostApiType(hostErr.hostApiType), 119 | int(hostErr.errorCode), 120 | C.GoString(hostErr.errorText), 121 | } 122 | case C.paNoError: 123 | return nil 124 | } 125 | return Error(err) 126 | } 127 | 128 | var initialized = 0 129 | 130 | // Initialize initializes internal data structures and 131 | // prepares underlying host APIs for use. With the exception 132 | // of Version(), VersionText(), and ErrorText(), this function 133 | // MUST be called before using any other PortAudio API functions. 134 | // 135 | // If Initialize() is called multiple times, each successful call 136 | // must be matched with a corresponding call to Terminate(). Pairs of 137 | // calls to Initialize()/Terminate() may overlap, and are not required to be fully nested. 138 | // 139 | // Note that if Initialize() returns an error code, Terminate() should NOT be called. 140 | func Initialize() error { 141 | paErr := C.Pa_Initialize() 142 | if paErr != C.paNoError { 143 | return newError(paErr) 144 | } 145 | initialized++ 146 | return nil 147 | } 148 | 149 | // Terminate deallocates all resources allocated by PortAudio 150 | // since it was initialized by a call to Initialize(). 151 | // 152 | // In cases where Initialize() has been called multiple times, 153 | // each call must be matched with a corresponding call to Pa_Terminate(). 154 | // The final matching call to Pa_Terminate() will automatically 155 | // close any PortAudio streams that are still open.. 156 | // 157 | // Terminate MUST be called before exiting a program which uses PortAudio. 158 | // Failure to do so may result in serious resource leaks, such as audio devices 159 | // not being available until the next reboot. 160 | func Terminate() error { 161 | paErr := C.Pa_Terminate() 162 | if paErr != C.paNoError { 163 | return newError(paErr) 164 | } 165 | initialized-- 166 | if initialized <= 0 { 167 | initialized = 0 168 | cached = false 169 | } 170 | return nil 171 | } 172 | 173 | // HostApiType maps ints to HostApi modes. 174 | type HostApiType int 175 | 176 | func (t HostApiType) String() string { 177 | return hostApiStrings[t] 178 | } 179 | 180 | var hostApiStrings = [...]string{ 181 | InDevelopment: "InDevelopment", 182 | DirectSound: "DirectSound", 183 | MME: "MME", 184 | ASIO: "ASIO", 185 | SoundManager: "SoundManager", 186 | CoreAudio: "CoreAudio", 187 | OSS: "OSS", 188 | ALSA: "ALSA", 189 | AL: "AL", 190 | BeOS: "BeOS", 191 | WDMkS: "WDMKS", 192 | JACK: "JACK", 193 | WASAPI: "WASAPI", 194 | AudioScienceHPI: "AudioScienceHPI", 195 | } 196 | 197 | // PortAudio Api types. 198 | const ( 199 | InDevelopment HostApiType = C.paInDevelopment 200 | DirectSound HostApiType = C.paDirectSound 201 | MME HostApiType = C.paMME 202 | ASIO HostApiType = C.paASIO 203 | SoundManager HostApiType = C.paSoundManager 204 | CoreAudio HostApiType = C.paCoreAudio 205 | OSS HostApiType = C.paOSS 206 | ALSA HostApiType = C.paALSA 207 | AL HostApiType = C.paAL 208 | BeOS HostApiType = C.paBeOS 209 | WDMkS HostApiType = C.paWDMKS 210 | JACK HostApiType = C.paJACK 211 | WASAPI HostApiType = C.paWASAPI 212 | AudioScienceHPI HostApiType = C.paAudioScienceHPI 213 | ) 214 | 215 | // HostApiInfo contains information for a HostApi. 216 | type HostApiInfo struct { 217 | Type HostApiType 218 | Name string 219 | DefaultInputDevice *DeviceInfo 220 | DefaultOutputDevice *DeviceInfo 221 | Devices []*DeviceInfo 222 | } 223 | 224 | // DeviceInfo contains information for an audio device. 225 | type DeviceInfo struct { 226 | index C.PaDeviceIndex 227 | Name string 228 | MaxInputChannels int 229 | MaxOutputChannels int 230 | DefaultLowInputLatency time.Duration 231 | DefaultLowOutputLatency time.Duration 232 | DefaultHighInputLatency time.Duration 233 | DefaultHighOutputLatency time.Duration 234 | DefaultSampleRate float64 235 | HostApi *HostApiInfo 236 | } 237 | 238 | // HostApis returns all information available for HostApis. 239 | func HostApis() ([]*HostApiInfo, error) { 240 | hosts, _, err := hostsAndDevices() 241 | if err != nil { 242 | return nil, err 243 | } 244 | return hosts, nil 245 | } 246 | 247 | // HostApi returns information for a requested HostApiType. 248 | func HostApi(apiType HostApiType) (*HostApiInfo, error) { 249 | hosts, err := HostApis() 250 | if err != nil { 251 | return nil, err 252 | } 253 | i := C.Pa_HostApiTypeIdToHostApiIndex(C.PaHostApiTypeId(apiType)) 254 | if i < 0 { 255 | return nil, newError(C.PaError(i)) 256 | } 257 | return hosts[i], nil 258 | } 259 | 260 | // DefaultHostApi returns information of the default HostApi available on the system. 261 | // 262 | // The default host API will be the lowest common denominator host API 263 | // on the current platform and is unlikely to provide the best performance. 264 | func DefaultHostApi() (*HostApiInfo, error) { 265 | hosts, err := HostApis() 266 | if err != nil { 267 | return nil, err 268 | } 269 | i := C.Pa_GetDefaultHostApi() 270 | if i < 0 { 271 | return nil, newError(C.PaError(i)) 272 | } 273 | return hosts[i], nil 274 | } 275 | 276 | // Devices returns information for all available devices on the system. 277 | func Devices() ([]*DeviceInfo, error) { 278 | _, devs, err := hostsAndDevices() 279 | if err != nil { 280 | return nil, err 281 | } 282 | return devs, nil 283 | } 284 | 285 | // DefaultInputDevice returns information for the default 286 | // input device on the system. 287 | func DefaultInputDevice() (*DeviceInfo, error) { 288 | devs, err := Devices() 289 | if err != nil { 290 | return nil, err 291 | } 292 | i := C.Pa_GetDefaultInputDevice() 293 | if i < 0 { 294 | return nil, newError(C.PaError(i)) 295 | } 296 | return devs[i], nil 297 | } 298 | 299 | // DefaultOutputDevice returns information for the default 300 | // output device on the system. 301 | func DefaultOutputDevice() (*DeviceInfo, error) { 302 | devs, err := Devices() 303 | if err != nil { 304 | return nil, err 305 | } 306 | i := C.Pa_GetDefaultOutputDevice() 307 | if i < 0 { 308 | return nil, newError(C.PaError(i)) 309 | } 310 | return devs[i], nil 311 | } 312 | 313 | /* 314 | Cache the HostApi/Device list to simplify the enumeration code. 315 | Note that portaudio itself caches the lists, so these won't go stale. 316 | 317 | However, there is talk of extending the portaudio API to allow clients 318 | to rescan available devices without calling Pa_Terminate() followed by 319 | Pa_Initialize() - our caching strategy will have to change if this 320 | goes ahead. See https://www.assembla.com/spaces/portaudio/tickets/11 321 | */ 322 | var ( 323 | cached bool 324 | hostApis []*HostApiInfo 325 | devices []*DeviceInfo 326 | ) 327 | 328 | func hostsAndDevices() ([]*HostApiInfo, []*DeviceInfo, error) { 329 | if !cached { 330 | nhosts := C.Pa_GetHostApiCount() 331 | ndevs := C.Pa_GetDeviceCount() 332 | if nhosts < 0 { 333 | return nil, nil, newError(C.PaError(nhosts)) 334 | } 335 | if ndevs < 0 { 336 | return nil, nil, newError(C.PaError(ndevs)) 337 | } 338 | devices = make([]*DeviceInfo, ndevs) 339 | hosti := make([]C.PaHostApiIndex, ndevs) 340 | for i := range devices { 341 | i := C.PaDeviceIndex(i) 342 | paDev := C.Pa_GetDeviceInfo(i) 343 | devices[i] = &DeviceInfo{ 344 | index: i, 345 | Name: C.GoString(paDev.name), 346 | MaxInputChannels: int(paDev.maxInputChannels), 347 | MaxOutputChannels: int(paDev.maxOutputChannels), 348 | DefaultLowInputLatency: duration(paDev.defaultLowInputLatency), 349 | DefaultLowOutputLatency: duration(paDev.defaultLowOutputLatency), 350 | DefaultHighInputLatency: duration(paDev.defaultHighInputLatency), 351 | DefaultHighOutputLatency: duration(paDev.defaultHighOutputLatency), 352 | DefaultSampleRate: float64(paDev.defaultSampleRate), 353 | } 354 | hosti[i] = paDev.hostApi 355 | } 356 | hostApis = make([]*HostApiInfo, nhosts) 357 | for i := range hostApis { 358 | i := C.PaHostApiIndex(i) 359 | paHost := C.Pa_GetHostApiInfo(i) 360 | devs := make([]*DeviceInfo, paHost.deviceCount) 361 | for j := range devs { 362 | devs[j] = devices[C.Pa_HostApiDeviceIndexToDeviceIndex(i, C.int(j))] 363 | } 364 | hostApis[i] = &HostApiInfo{ 365 | Type: HostApiType(paHost._type), 366 | Name: C.GoString(paHost.name), 367 | DefaultInputDevice: lookupDevice(devices, paHost.defaultInputDevice), 368 | DefaultOutputDevice: lookupDevice(devices, paHost.defaultOutputDevice), 369 | Devices: devs, 370 | } 371 | } 372 | for i := range devices { 373 | devices[i].HostApi = hostApis[hosti[i]] 374 | } 375 | cached = true 376 | } 377 | return hostApis, devices, nil 378 | } 379 | 380 | func duration(paTime C.PaTime) time.Duration { 381 | return time.Duration(paTime * C.PaTime(time.Second)) 382 | } 383 | 384 | func lookupDevice(d []*DeviceInfo, i C.PaDeviceIndex) *DeviceInfo { 385 | if i >= 0 { 386 | return d[i] 387 | } 388 | return nil 389 | } 390 | 391 | // StreamParameters includes all parameters required to 392 | // open a stream except for the callback or buffers. 393 | type StreamParameters struct { 394 | Input, Output StreamDeviceParameters 395 | SampleRate float64 396 | FramesPerBuffer int 397 | Flags StreamFlags 398 | } 399 | 400 | // StreamDeviceParameters specifies parameters for 401 | // one device (either input or output) in a stream. 402 | // A nil Device indicates that no device is to be used 403 | // -- i.e., for an input- or output-only stream. 404 | type StreamDeviceParameters struct { 405 | Device *DeviceInfo 406 | Channels int 407 | Latency time.Duration 408 | } 409 | 410 | // FramesPerBufferUnspecified ... 411 | const FramesPerBufferUnspecified = C.paFramesPerBufferUnspecified 412 | 413 | // StreamFlags ... 414 | type StreamFlags C.PaStreamFlags 415 | 416 | const ( 417 | NoFlag StreamFlags = C.paNoFlag 418 | ClipOff StreamFlags = C.paClipOff 419 | DitherOff StreamFlags = C.paDitherOff 420 | NeverDropInput StreamFlags = C.paNeverDropInput 421 | PrimeOutputBuffersUsingStreamCallback StreamFlags = C.paPrimeOutputBuffersUsingStreamCallback 422 | PlatformSpecificFlags StreamFlags = C.paPlatformSpecificFlags 423 | ) 424 | 425 | // HighLatencyParameters are mono in, stereo out (if supported), 426 | // high latency, the smaller of the default sample rates of the two devices, 427 | // and FramesPerBufferUnspecified. One of the devices may be nil. 428 | func HighLatencyParameters(in, out *DeviceInfo) (p StreamParameters) { 429 | sampleRate := 0.0 430 | if in != nil { 431 | p := &p.Input 432 | p.Device = in 433 | p.Channels = 1 434 | if in.MaxInputChannels < 1 { 435 | p.Channels = in.MaxInputChannels 436 | } 437 | p.Latency = in.DefaultHighInputLatency 438 | sampleRate = in.DefaultSampleRate 439 | } 440 | if out != nil { 441 | p := &p.Output 442 | p.Device = out 443 | p.Channels = 2 444 | if out.MaxOutputChannels < 2 { 445 | p.Channels = out.MaxOutputChannels 446 | } 447 | p.Latency = out.DefaultHighOutputLatency 448 | if r := out.DefaultSampleRate; r < sampleRate || sampleRate == 0 { 449 | sampleRate = r 450 | } 451 | } 452 | p.SampleRate = sampleRate 453 | p.FramesPerBuffer = FramesPerBufferUnspecified 454 | return p 455 | } 456 | 457 | // LowLatencyParameters are mono in, stereo out (if supported), 458 | // low latency, the larger of the default sample rates of the two devices, 459 | // and FramesPerBufferUnspecified. One of the devices may be nil. 460 | func LowLatencyParameters(in, out *DeviceInfo) (p StreamParameters) { 461 | sampleRate := 0.0 462 | if in != nil { 463 | p := &p.Input 464 | p.Device = in 465 | p.Channels = 1 466 | if in.MaxInputChannels < 1 { 467 | p.Channels = in.MaxInputChannels 468 | } 469 | p.Latency = in.DefaultLowInputLatency 470 | sampleRate = in.DefaultSampleRate 471 | } 472 | if out != nil { 473 | p := &p.Output 474 | p.Device = out 475 | p.Channels = 2 476 | if out.MaxOutputChannels < 2 { 477 | p.Channels = out.MaxOutputChannels 478 | } 479 | p.Latency = out.DefaultLowOutputLatency 480 | if r := out.DefaultSampleRate; r > sampleRate { 481 | sampleRate = r 482 | } 483 | } 484 | p.SampleRate = sampleRate 485 | p.FramesPerBuffer = FramesPerBufferUnspecified 486 | return p 487 | } 488 | 489 | // IsFormatSupported Returns nil if the format is supported, otherwise an error. 490 | // The args parameter has the same meaning as in OpenStream. 491 | func IsFormatSupported(p StreamParameters, args ...interface{}) error { 492 | s := &Stream{} 493 | err := s.init(p, args...) 494 | if err != nil { 495 | return err 496 | } 497 | return newError(C.Pa_IsFormatSupported(s.inParams, s.outParams, C.double(p.SampleRate))) 498 | } 499 | 500 | // Int24 ... 501 | type Int24 [3]byte 502 | 503 | // Stream provides access to audio hardware represented 504 | // by one or more PaDevices. Depending on the underlying 505 | // Host API, it may be possible to open multiple streams 506 | // using the same device, however this behavior is 507 | // implementation defined. 508 | // 509 | // Portable applications should assume that a Device may be simultaneously used by at most one Stream. 510 | type Stream struct { 511 | id uintptr 512 | paStream unsafe.Pointer 513 | inParams, outParams *C.PaStreamParameters 514 | in, out *reflect.SliceHeader 515 | timeInfo StreamCallbackTimeInfo 516 | flags StreamCallbackFlags 517 | args []reflect.Value 518 | callback reflect.Value 519 | closed bool 520 | } 521 | 522 | /* 523 | Since Go 1.6, if a Go pointer is passed to C then the Go memory it points to 524 | may not contain any Go pointers: https://golang.org/cmd/cgo/#hdr-Passing_pointers 525 | To deal with this, we maintain an id-keyed map of active streams. 526 | */ 527 | var ( 528 | mu sync.RWMutex 529 | streams = map[uintptr]*Stream{} 530 | nextID uintptr 531 | ) 532 | 533 | func newStream() *Stream { 534 | mu.Lock() 535 | defer mu.Unlock() 536 | s := &Stream{id: nextID} 537 | streams[nextID] = s 538 | nextID++ 539 | return s 540 | } 541 | 542 | func getStream(id uintptr) *Stream { 543 | mu.RLock() 544 | defer mu.RUnlock() 545 | return streams[id] 546 | } 547 | 548 | func delStream(s *Stream) { 549 | mu.Lock() 550 | defer mu.Unlock() 551 | delete(streams, s.id) 552 | } 553 | 554 | /* 555 | StreamCallback exists for documentation purposes only. 556 | 557 | A StreamCallback is a func whose signature resembles 558 | 559 | func(in Buffer, out Buffer, timeInfo StreamCallbackTimeInfo, flags StreamCallbackFlags) 560 | 561 | where the final one or two parameters may be omitted. For an input- or 562 | output-only stream, one of the Buffer parameters may also be omitted. The two 563 | Buffer types may be different. 564 | */ 565 | type StreamCallback interface{} 566 | 567 | /* 568 | Buffer exists for documentation purposes only. 569 | 570 | A Buffer is of the form [][]SampleType or []SampleType 571 | where SampleType is float32, int32, Int24, int16, int8, or uint8. 572 | 573 | In the first form, channels are non-interleaved: 574 | len(buf) == numChannels and len(buf[i]) == framesPerBuffer 575 | 576 | In the second form, channels are interleaved: 577 | len(buf) == numChannels * framesPerBuffer 578 | */ 579 | type Buffer interface{} 580 | 581 | // StreamCallbackTimeInfo contains timing information for the 582 | // buffers passed to the stream callback. 583 | type StreamCallbackTimeInfo struct { 584 | InputBufferAdcTime, CurrentTime, OutputBufferDacTime time.Duration 585 | } 586 | 587 | // StreamCallbackFlags are flag bit constants for the statusFlags to StreamCallback. 588 | type StreamCallbackFlags C.PaStreamCallbackFlags 589 | 590 | // PortAudio stream callback flags. 591 | const ( 592 | // In a stream opened with FramesPerBufferUnspecified, 593 | // InputUnderflow indicates that input data is all silence (zeros) 594 | // because no real data is available. 595 | // 596 | // In a stream opened without FramesPerBufferUnspecified, 597 | // InputUnderflow indicates that one or more zero samples have been inserted 598 | // into the input buffer to compensate for an input underflow. 599 | InputUnderflow StreamCallbackFlags = C.paInputUnderflow 600 | 601 | // In a stream opened with FramesPerBufferUnspecified, 602 | // indicates that data prior to the first sample of the 603 | // input buffer was discarded due to an overflow, possibly 604 | // because the stream callback is using too much CPU time. 605 | // 606 | // Otherwise indicates that data prior to one or more samples 607 | // in the input buffer was discarded. 608 | InputOverflow StreamCallbackFlags = C.paInputOverflow 609 | 610 | // Indicates that output data (or a gap) was inserted, 611 | // possibly because the stream callback is using too much CPU time. 612 | OutputUnderflow StreamCallbackFlags = C.paOutputUnderflow 613 | 614 | // Indicates that output data will be discarded because no room is available. 615 | OutputOverflow StreamCallbackFlags = C.paOutputOverflow 616 | 617 | // Some of all of the output data will be used to prime the stream, 618 | // input data may be zero. 619 | PrimingOutput StreamCallbackFlags = C.paPrimingOutput 620 | ) 621 | 622 | // OpenStream creates an instance of a Stream. 623 | // 624 | // For an input- or output-only stream, p.Output.Device or p.Input.Device must be nil, respectively. 625 | // 626 | // The args may consist of either a single StreamCallback or, 627 | // for a blocking stream, two Buffers or pointers to Buffers. 628 | // 629 | // For an input- or output-only stream, one of the Buffer args may be omitted. 630 | func OpenStream(p StreamParameters, args ...interface{}) (*Stream, error) { 631 | if initialized <= 0 { 632 | return nil, NotInitialized 633 | } 634 | 635 | s := newStream() 636 | err := s.init(p, args...) 637 | if err != nil { 638 | delStream(s) 639 | return nil, err 640 | } 641 | cb := (*C.PaStreamCallback)(unsafe.Pointer(C.CgoStreamCallback)) 642 | if !s.callback.IsValid() { 643 | cb = nil 644 | } 645 | paErr := C.Pa_OpenStream(&s.paStream, s.inParams, s.outParams, 646 | C.double(p.SampleRate), C.ulong(p.FramesPerBuffer), C.PaStreamFlags(p.Flags), 647 | cb, unsafe.Pointer(s.id)) 648 | if paErr != C.paNoError { 649 | delStream(s) 650 | return nil, newError(paErr) 651 | } 652 | return s, nil 653 | } 654 | 655 | // OpenDefaultStream is a simplified version of OpenStream that 656 | // opens the default input and/or output devices. 657 | // 658 | // The args parameter has the same meaning as in OpenStream. 659 | func OpenDefaultStream(numInputChannels, numOutputChannels int, 660 | sampleRate float64, framesPerBuffer int, args ...interface{}) (*Stream, error) { 661 | if initialized <= 0 { 662 | return nil, NotInitialized 663 | } 664 | 665 | var inDev, outDev *DeviceInfo 666 | var err error 667 | if numInputChannels > 0 { 668 | inDev, err = DefaultInputDevice() 669 | if err != nil { 670 | return nil, err 671 | } 672 | } 673 | if numOutputChannels > 0 { 674 | outDev, err = DefaultOutputDevice() 675 | if err != nil { 676 | return nil, err 677 | } 678 | } 679 | p := HighLatencyParameters(inDev, outDev) 680 | p.Input.Channels = numInputChannels 681 | p.Output.Channels = numOutputChannels 682 | p.SampleRate = sampleRate 683 | p.FramesPerBuffer = framesPerBuffer 684 | return OpenStream(p, args...) 685 | } 686 | 687 | func (s *Stream) init(p StreamParameters, args ...interface{}) error { 688 | switch len(args) { 689 | case 0: 690 | return fmt.Errorf("too few args") 691 | case 1, 2: 692 | if fun := reflect.ValueOf(args[0]); fun.Kind() == reflect.Func { 693 | return s.initCallback(p, fun) 694 | } 695 | return s.initBuffers(p, args...) 696 | default: 697 | return fmt.Errorf("too many args") 698 | } 699 | } 700 | 701 | func (s *Stream) initCallback(p StreamParameters, fun reflect.Value) error { 702 | t := fun.Type() 703 | if t.IsVariadic() { 704 | return fmt.Errorf("StreamCallback must not be variadic") 705 | } 706 | nArgs := t.NumIn() 707 | if nArgs == 0 { 708 | return fmt.Errorf("too few parameters in StreamCallback") 709 | } 710 | args := make([]reflect.Value, nArgs) 711 | i := 0 712 | bothBufs := nArgs > 1 && t.In(1).Kind() == reflect.Slice 713 | bufArg := func(p StreamDeviceParameters) (*C.PaStreamParameters, *reflect.SliceHeader, error) { 714 | if p.Device != nil || bothBufs { 715 | if i >= nArgs { 716 | return nil, nil, fmt.Errorf("too few Buffer parameters in StreamCallback") 717 | } 718 | t := t.In(i) 719 | sampleFmt := sampleFormat(t) 720 | if sampleFmt == 0 { 721 | return nil, nil, fmt.Errorf("expected Buffer type in StreamCallback, got %v", t) 722 | } 723 | buf := reflect.New(t) 724 | args[i] = buf.Elem() 725 | i++ 726 | if p.Device != nil { 727 | pap := paStreamParameters(p, sampleFmt) 728 | if pap.sampleFormat&C.paNonInterleaved != 0 { 729 | n := int(pap.channelCount) 730 | buf.Elem().Set(reflect.MakeSlice(t, n, n)) 731 | } 732 | return pap, (*reflect.SliceHeader)(unsafe.Pointer(buf.Pointer())), nil 733 | } 734 | } 735 | return nil, nil, nil 736 | } 737 | var err error 738 | s.inParams, s.in, err = bufArg(p.Input) 739 | if err != nil { 740 | return err 741 | } 742 | s.outParams, s.out, err = bufArg(p.Output) 743 | if err != nil { 744 | return err 745 | } 746 | if i < nArgs { 747 | t := t.In(i) 748 | if t != reflect.TypeOf(StreamCallbackTimeInfo{}) { 749 | return fmt.Errorf("invalid StreamCallback") 750 | } 751 | args[i] = reflect.ValueOf(&s.timeInfo).Elem() 752 | i++ 753 | } 754 | if i < nArgs { 755 | t := t.In(i) 756 | if t != reflect.TypeOf(StreamCallbackFlags(0)) { 757 | return fmt.Errorf("invalid StreamCallback") 758 | } 759 | args[i] = reflect.ValueOf(&s.flags).Elem() 760 | i++ 761 | } 762 | if i < nArgs { 763 | return fmt.Errorf("too many parameters in StreamCallback") 764 | } 765 | if t.NumOut() > 0 { 766 | return fmt.Errorf("too many results in StreamCallback") 767 | } 768 | s.callback = fun 769 | s.args = args 770 | return nil 771 | } 772 | 773 | func (s *Stream) initBuffers(p StreamParameters, args ...interface{}) error { 774 | bothBufs := len(args) == 2 775 | bufArg := func(p StreamDeviceParameters) (*C.PaStreamParameters, *reflect.SliceHeader, error) { 776 | if p.Device != nil || bothBufs { 777 | if len(args) == 0 { 778 | return nil, nil, fmt.Errorf("too few Buffer args") 779 | } 780 | arg := reflect.ValueOf(args[0]) 781 | args = args[1:] 782 | t := arg.Type() 783 | if t.Kind() == reflect.Ptr { 784 | t = t.Elem() 785 | } else { 786 | argPtr := reflect.New(t) 787 | argPtr.Elem().Set(arg) 788 | arg = argPtr 789 | } 790 | sampleFmt := sampleFormat(t) 791 | if sampleFmt == 0 { 792 | return nil, nil, fmt.Errorf("invalid Buffer type %v", t) 793 | } 794 | if arg.IsNil() { 795 | return nil, nil, fmt.Errorf("nil Buffer pointer") 796 | } 797 | if p.Device != nil { 798 | return paStreamParameters(p, sampleFmt), (*reflect.SliceHeader)(unsafe.Pointer(arg.Pointer())), nil 799 | } 800 | } 801 | return nil, nil, nil 802 | } 803 | var err error 804 | s.inParams, s.in, err = bufArg(p.Input) 805 | if err != nil { 806 | return err 807 | } 808 | s.outParams, s.out, err = bufArg(p.Output) 809 | if err != nil { 810 | return err 811 | } 812 | return nil 813 | } 814 | 815 | func sampleFormat(b reflect.Type) (f C.PaSampleFormat) { 816 | if b.Kind() != reflect.Slice { 817 | return 0 818 | } 819 | b = b.Elem() 820 | if b.Kind() == reflect.Slice { 821 | f = C.paNonInterleaved 822 | b = b.Elem() 823 | } 824 | switch b.Kind() { 825 | case reflect.Float32: 826 | f |= C.paFloat32 827 | case reflect.Int32: 828 | f |= C.paInt32 829 | default: 830 | if b == reflect.TypeOf(Int24{}) { 831 | f |= C.paInt24 832 | } else { 833 | return 0 834 | } 835 | case reflect.Int16: 836 | f |= C.paInt16 837 | case reflect.Int8: 838 | f |= C.paInt8 839 | case reflect.Uint8: 840 | f |= C.paUInt8 841 | } 842 | return f 843 | } 844 | 845 | func paStreamParameters(p StreamDeviceParameters, fmt C.PaSampleFormat) *C.PaStreamParameters { 846 | return &C.PaStreamParameters{ 847 | device: p.Device.index, 848 | channelCount: C.int(p.Channels), 849 | sampleFormat: fmt, 850 | suggestedLatency: C.PaTime(p.Latency.Seconds()), 851 | } 852 | } 853 | 854 | // Close terminates the stream. 855 | func (s *Stream) Close() error { 856 | if !s.closed { 857 | s.closed = true 858 | err := newError(C.Pa_CloseStream(s.paStream)) 859 | delStream(s) 860 | return err 861 | } 862 | return nil 863 | } 864 | 865 | // Start commences audio processing. 866 | func (s *Stream) Start() error { 867 | return newError(C.Pa_StartStream(s.paStream)) 868 | } 869 | 870 | //export streamCallback 871 | func streamCallback(inputBuffer, outputBuffer unsafe.Pointer, frames C.ulong, 872 | timeInfo *C.PaStreamCallbackTimeInfo, statusFlags C.PaStreamCallbackFlags, 873 | userData unsafe.Pointer) { 874 | defer func() { 875 | // Don't let PortAudio silently swallow panics. 876 | if x := recover(); x != nil { 877 | buf := make([]byte, 1<<10) 878 | for runtime.Stack(buf, true) == len(buf) { 879 | buf = make([]byte, 2*len(buf)) 880 | } 881 | fmt.Fprintf(os.Stderr, "panic in portaudio stream callback: %s\n\n%s", x, buf) 882 | os.Exit(2) 883 | } 884 | }() 885 | 886 | s := getStream(uintptr(userData)) 887 | s.timeInfo = StreamCallbackTimeInfo{duration(timeInfo.inputBufferAdcTime), 888 | duration(timeInfo.currentTime), duration(timeInfo.outputBufferDacTime)} 889 | s.flags = StreamCallbackFlags(statusFlags) 890 | updateBuffer(s.in, uintptr(inputBuffer), s.inParams, int(frames)) 891 | updateBuffer(s.out, uintptr(outputBuffer), s.outParams, int(frames)) 892 | s.callback.Call(s.args) 893 | } 894 | 895 | func updateBuffer(buf *reflect.SliceHeader, p uintptr, params *C.PaStreamParameters, frames int) { 896 | if p == 0 { 897 | return 898 | } 899 | if params.sampleFormat&C.paNonInterleaved == 0 { 900 | setSlice(buf, p, frames*int(params.channelCount)) 901 | } else { 902 | setChannels(buf, p, frames) 903 | } 904 | } 905 | 906 | func setChannels(s *reflect.SliceHeader, p uintptr, frames int) { 907 | sp := s.Data 908 | for i := 0; i < s.Len; i++ { 909 | setSlice((*reflect.SliceHeader)(unsafe.Pointer(sp)), *(*uintptr)(unsafe.Pointer(p)), frames) 910 | sp += unsafe.Sizeof(reflect.SliceHeader{}) 911 | p += unsafe.Sizeof(uintptr(0)) 912 | } 913 | } 914 | 915 | func setSlice(s *reflect.SliceHeader, data uintptr, n int) { 916 | s.Data = data 917 | s.Len = n 918 | s.Cap = n 919 | } 920 | 921 | // Stop terminates audio processing. It waits until all pending 922 | // audio buffers have been played before it returns. 923 | func (s *Stream) Stop() error { 924 | return newError(C.Pa_StopStream(s.paStream)) 925 | } 926 | 927 | // Abort terminates audio processing immediately 928 | // without waiting for pending buffers to complete. 929 | func (s *Stream) Abort() error { 930 | return newError(C.Pa_AbortStream(s.paStream)) 931 | } 932 | 933 | // Info returns information about the Stream instance. 934 | func (s *Stream) Info() *StreamInfo { 935 | i := C.Pa_GetStreamInfo(s.paStream) 936 | if i == nil { 937 | return nil 938 | } 939 | return &StreamInfo{duration(i.inputLatency), duration(i.outputLatency), float64(i.sampleRate)} 940 | } 941 | 942 | // StreamInfo contains information about the stream. 943 | type StreamInfo struct { 944 | InputLatency, OutputLatency time.Duration 945 | SampleRate float64 946 | } 947 | 948 | // Time returns the current time in seconds for a lifespan of a stream. 949 | // Starting and stopping the stream does not affect the passage of time. 950 | func (s *Stream) Time() time.Duration { 951 | return duration(C.Pa_GetStreamTime(s.paStream)) 952 | } 953 | 954 | // CpuLoad returns the CPU usage information for the specified stream, 955 | // where 0.0 is 0% usage and 1.0 is 100% usage. 956 | // 957 | // The "CPU Load" is a fraction of total CPU time consumed by a 958 | // callback stream's audio processing routines including, 959 | // but not limited to the client supplied stream callback. 960 | // 961 | // This function does not work with blocking read/write streams. 962 | // 963 | // This function may be called from the stream callback function or the application. 964 | func (s *Stream) CpuLoad() float64 { 965 | return float64(C.Pa_GetStreamCpuLoad(s.paStream)) 966 | } 967 | 968 | // AvailableToRead returns the number of frames that 969 | // can be read from the stream without waiting. 970 | func (s *Stream) AvailableToRead() (int, error) { 971 | n := C.Pa_GetStreamReadAvailable(s.paStream) 972 | if n < 0 { 973 | return 0, newError(C.PaError(n)) 974 | } 975 | return int(n), nil 976 | } 977 | 978 | // AvailableToWrite returns the number of frames that 979 | // can be written from the stream without waiting. 980 | func (s *Stream) AvailableToWrite() (int, error) { 981 | n := C.Pa_GetStreamWriteAvailable(s.paStream) 982 | if n < 0 { 983 | return 0, newError(C.PaError(n)) 984 | } 985 | return int(n), nil 986 | } 987 | 988 | // Read uses the buffer provided to OpenStream. 989 | // The number of samples to read is determined by the size of the buffer. 990 | func (s *Stream) Read() error { 991 | if s.callback.IsValid() { 992 | return CanNotReadFromACallbackStream 993 | } 994 | if s.in == nil { 995 | return CanNotReadFromAnOutputOnlyStream 996 | } 997 | buf, frames, err := getBuffer(s.in, s.inParams) 998 | if err != nil { 999 | return err 1000 | } 1001 | return newError(C.Pa_ReadStream(s.paStream, buf, C.ulong(frames))) 1002 | } 1003 | 1004 | // Write uses the buffer provided to OpenStream. 1005 | // The number of samples to write is determined by the size of the buffer. 1006 | func (s *Stream) Write() error { 1007 | if s.callback.IsValid() { 1008 | return CanNotWriteToACallbackStream 1009 | } 1010 | if s.out == nil { 1011 | return CanNotWriteToAnInputOnlyStream 1012 | } 1013 | buf, frames, err := getBuffer(s.out, s.outParams) 1014 | if err != nil { 1015 | return err 1016 | } 1017 | return newError(C.Pa_WriteStream(s.paStream, buf, C.ulong(frames))) 1018 | } 1019 | 1020 | func getBuffer(s *reflect.SliceHeader, p *C.PaStreamParameters) (unsafe.Pointer, int, error) { 1021 | if p.sampleFormat&C.paNonInterleaved == 0 { 1022 | n := int(p.channelCount) 1023 | if s.Len%n != 0 { 1024 | return nil, 0, fmt.Errorf("length of interleaved buffer not divisible by number of channels") 1025 | } 1026 | return unsafe.Pointer(s.Data), s.Len / n, nil 1027 | } else { 1028 | if s.Len != int(p.channelCount) { 1029 | return nil, 0, fmt.Errorf("buffer has wrong number of channels") 1030 | } 1031 | buf := make([]uintptr, s.Len) 1032 | frames := -1 1033 | sp := s.Data 1034 | for i := range buf { 1035 | ch := (*reflect.SliceHeader)(unsafe.Pointer(sp)) 1036 | if frames == -1 { 1037 | frames = ch.Len 1038 | } else if ch.Len != frames { 1039 | return nil, 0, fmt.Errorf("channels have different lengths") 1040 | } 1041 | buf[i] = ch.Data 1042 | sp += unsafe.Sizeof(reflect.SliceHeader{}) 1043 | } 1044 | return unsafe.Pointer(&buf[0]), frames, nil 1045 | } 1046 | } 1047 | -------------------------------------------------------------------------------- /portaudio/portaudio_cb.go: -------------------------------------------------------------------------------- 1 | package portaudio 2 | 3 | // #include "portaudio.h" 4 | // 5 | // extern void streamCallback(void*, void*, unsigned long, 6 | // PaStreamCallbackTimeInfo*, PaStreamCallbackFlags, void*); 7 | // 8 | // int CgoStreamCallback(const void *inputBuffer, 9 | // void *outputBuffer, unsigned long frames, 10 | // const PaStreamCallbackTimeInfo *timeInfo, 11 | // PaStreamCallbackFlags statusFlags, void *userData) { 12 | // 13 | // streamCallback((void*)inputBuffer, 14 | // outputBuffer, frames, 15 | // (PaStreamCallbackTimeInfo*)timeInfo, statusFlags, userData); 16 | // return paContinue; 17 | // } 18 | import "C" 19 | -------------------------------------------------------------------------------- /portmidi/_example-c/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(linux),1) 2 | LDFLAGS += -lasound -pthread 3 | endif 4 | ifeq ($(macos),1) 5 | LDFLAGS += -framework CoreAudio -framework CoreMIDI -framework CoreFoundation -framework CoreServices 6 | endif 7 | ifeq ($(windows),1) 8 | LDFLAGS += -lm -lole32 -lksuser -lwinmm -lws2_32 -mwindows -static 9 | endif 10 | 11 | example: example.o ../pm_amalg.o 12 | $(CC) $^ $(LDFLAGS) -o $@ 13 | clean: 14 | rm -f example example.o ../pm_amalg.o 15 | -------------------------------------------------------------------------------- /portmidi/_example-c/example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../portmidi.h" 5 | 6 | int main() { 7 | int r = Pm_Initialize(); 8 | if (r != 0) { 9 | goto error; 10 | } 11 | printf("%d\n", Pm_CountDevices()); 12 | Pt_Start(1, NULL, NULL); 13 | Pt_Stop(); 14 | error: 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /portmidi/amalg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | AMALG_H=portmidi.h 4 | AMALG_C=pm_amalg.c 5 | 6 | echo "Downloading PortMidi sources..." 7 | svn checkout http://svn.code.sf.net/p/portmedia/code/portmidi/trunk/ portmidi 8 | 9 | cd portmidi 10 | 11 | echo "Amalgamating to $AMALG_C and $AMALG_H..." 12 | 13 | sed 's!\(#include ".*.h"\)!// (amalg) \1!' >> $AMALG_H << EOF 14 | #include 15 | #include 16 | $(cat pm_common/portmidi.h) 17 | $(cat pm_common/pmutil.h) 18 | $(cat pm_common/pminternal.h) 19 | $(cat porttime/porttime.h) 20 | 21 | #if __linux__ 22 | #define PMALSA 23 | $(cat pm_linux/pmlinux.h) 24 | $(cat pm_linux/pmlinuxalsa.h) 25 | #elif _WIN32 26 | #include 27 | $(cat pm_win/pmwinmm.h) 28 | #elif __APPLE__ 29 | $(cat pm_mac/pmmac.h) 30 | $(cat pm_mac/pmmacosxcm.h) 31 | $(cat pm_mac/readbinaryplist.h) 32 | #else 33 | #error "Unknown platform" 34 | #endif 35 | EOF 36 | 37 | echo "#include \"$AMALG_H\"" > $AMALG_C 38 | sed 's!\(#include ".*.h"\)!// (amalg) \1!' >> $AMALG_C << EOF 39 | $(cat pm_common/pmutil.c) 40 | $(cat pm_common/portmidi.c) 41 | $(cat porttime/porttime.c) 42 | #if __linux__ 43 | #include 44 | #include 45 | #include 46 | $(cat porttime/ptlinux.c) 47 | $(cat pm_linux/pmlinux.c) 48 | $(cat pm_linux/pmlinuxalsa.c) 49 | $(cat pm_linux/finddefault.c) 50 | #elif _WIN32 51 | $(cat porttime/ptwinmm.c) 52 | $(cat pm_win/pmwin.c) 53 | $(cat pm_win/pmwinmm.c) 54 | #elif __APPLE__ 55 | $(cat porttime/ptmacosx_cf.c) 56 | $(cat pm_mac/pmmac.c) 57 | $(cat pm_mac/pmmacosxcm.c) 58 | $(cat pm_mac/readbinaryplist.c) 59 | $(cat pm_mac/finddefault.c) 60 | #endif 61 | EOF 62 | 63 | mv $AMALG_C $AMALG_H .. 64 | cd .. 65 | 66 | rm -rf portmidi 67 | 68 | echo "Done." 69 | -------------------------------------------------------------------------------- /portmidi/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/naivesound/audio/portmidi" 7 | ) 8 | 9 | func main() { 10 | in, err := portmidi.NewInputStream(portmidi.DefaultInputDeviceID(), 1024) 11 | if err != nil { 12 | log.Fatal(err) 13 | } 14 | 15 | defer in.Close() 16 | 17 | for e := range in.Listen() { 18 | log.Println(e) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /portmidi/portmidi.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package portmidi provides PortMidi bindings. 16 | package portmidi 17 | 18 | // #cgo linux LDFLAGS: -lasound -pthread 19 | // #cgo windows LDFLAGS: -luuid -lksuser -lwinmm -lole32 20 | // #cgo darwin LDFLAGS: -framework CoreServices -framework CoreAudio -framework CoreMIDI -framework CoreFoundation 21 | // 22 | // #include 23 | // #include "portmidi.h" 24 | import "C" 25 | 26 | import ( 27 | "errors" 28 | ) 29 | 30 | // DeviceID is a MIDI device ID. 31 | type DeviceID int 32 | 33 | // DeviceInfo provides info about a MIDI device. 34 | type DeviceInfo struct { 35 | Interface string 36 | Name string 37 | IsInputAvailable bool 38 | IsOutputAvailable bool 39 | IsOpened bool 40 | } 41 | 42 | type Timestamp int64 43 | 44 | // Initialize initializes the portmidi. Needs to be called before 45 | // making any other call from the portmidi package. 46 | // Once portmidi package is no longer required, Terminate should be 47 | // called to free the underlying resources. 48 | func Initialize() error { 49 | if code := C.Pm_Initialize(); code != 0 { 50 | return convertToError(code) 51 | } 52 | C.Pt_Start(C.int(1), nil, nil) 53 | return nil 54 | } 55 | 56 | // Terminate terminates and cleans up the midi streams. 57 | func Terminate() error { 58 | C.Pt_Stop() 59 | return convertToError(C.Pm_Terminate()) 60 | } 61 | 62 | // DefaultInputDeviceID returns the default input device's ID. 63 | func DefaultInputDeviceID() DeviceID { 64 | return DeviceID(C.Pm_GetDefaultInputDeviceID()) 65 | } 66 | 67 | // DefaultOutputDeviceID returns the default output device's ID. 68 | func DefaultOutputDeviceID() DeviceID { 69 | return DeviceID(C.Pm_GetDefaultOutputDeviceID()) 70 | } 71 | 72 | // CountDevices returns the number of MIDI devices. 73 | func CountDevices() int { 74 | return int(C.Pm_CountDevices()) 75 | } 76 | 77 | // Info returns the device info for the device indentified with deviceID. 78 | // If deviceID is out of range, Info returns nil. 79 | func Info(deviceID DeviceID) *DeviceInfo { 80 | info := C.Pm_GetDeviceInfo(C.PmDeviceID(deviceID)) 81 | if info == nil { 82 | return nil 83 | } 84 | return &DeviceInfo{ 85 | Interface: C.GoString(info.interf), 86 | Name: C.GoString(info.name), 87 | IsInputAvailable: info.input > 0, 88 | IsOutputAvailable: info.output > 0, 89 | IsOpened: info.opened > 0, 90 | } 91 | } 92 | 93 | // Time returns the portmidi timer's current time. 94 | func Time() Timestamp { 95 | return Timestamp(C.Pt_Time()) 96 | } 97 | 98 | // convertToError converts a portmidi error code to a Go error. 99 | func convertToError(code C.PmError) error { 100 | if code >= 0 { 101 | return nil 102 | } 103 | return errors.New(C.GoString(C.Pm_GetErrorText(code))) 104 | } 105 | -------------------------------------------------------------------------------- /portmidi/stream.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package portmidi 16 | 17 | // #include "portmidi.h" 18 | import "C" 19 | 20 | import ( 21 | "encoding/hex" 22 | "errors" 23 | "strings" 24 | "time" 25 | "unsafe" 26 | ) 27 | 28 | const ( 29 | minEventBufferSize = 1 30 | maxEventBufferSize = 1024 31 | ) 32 | 33 | var ( 34 | ErrMaxBuffer = errors.New("portmidi: max event buffer size is 1024") 35 | ErrMinBuffer = errors.New("portmidi: min event buffer size is 1") 36 | ErrInputUnavailable = errors.New("portmidi: input is unavailable") 37 | ErrOutputUnavailable = errors.New("portmidi: output is unavailable") 38 | ) 39 | 40 | // Channel represent a MIDI channel. It should be between 1-16. 41 | type Channel int 42 | 43 | // Event represents a MIDI event. 44 | type Event struct { 45 | Timestamp Timestamp 46 | Status int64 47 | Data1 int64 48 | Data2 int64 49 | } 50 | 51 | // Stream represents a portmidi stream. 52 | type Stream struct { 53 | deviceID DeviceID 54 | pmStream *C.PmStream 55 | } 56 | 57 | // NewInputStream initializes a new input stream. 58 | func NewInputStream(id DeviceID, bufferSize int64) (stream *Stream, err error) { 59 | var str *C.PmStream 60 | errCode := C.Pm_OpenInput( 61 | (*unsafe.Pointer)(unsafe.Pointer(&str)), 62 | C.PmDeviceID(id), nil, C.int32_t(bufferSize), nil, nil) 63 | if errCode != 0 { 64 | return nil, convertToError(errCode) 65 | } 66 | if info := Info(id); !info.IsInputAvailable { 67 | return nil, ErrInputUnavailable 68 | } 69 | return &Stream{deviceID: id, pmStream: str}, nil 70 | } 71 | 72 | // NewOutputStream initializes a new output stream. 73 | func NewOutputStream(id DeviceID, bufferSize int64, latency int64) (stream *Stream, err error) { 74 | var str *C.PmStream 75 | errCode := C.Pm_OpenOutput( 76 | (*unsafe.Pointer)(unsafe.Pointer(&str)), 77 | C.PmDeviceID(id), nil, C.int32_t(bufferSize), nil, nil, C.int32_t(latency)) 78 | if errCode != 0 { 79 | return nil, convertToError(errCode) 80 | } 81 | if info := Info(id); !info.IsOutputAvailable { 82 | return nil, ErrOutputUnavailable 83 | } 84 | return &Stream{deviceID: id, pmStream: str}, nil 85 | } 86 | 87 | // Close closes the MIDI stream. 88 | func (s *Stream) Close() error { 89 | if s.pmStream == nil { 90 | return nil 91 | } 92 | return convertToError(C.Pm_Close(unsafe.Pointer(s.pmStream))) 93 | } 94 | 95 | // Abort aborts the MIDI stream. 96 | func (s *Stream) Abort() error { 97 | if s.pmStream == nil { 98 | return nil 99 | } 100 | return convertToError(C.Pm_Abort(unsafe.Pointer(s.pmStream))) 101 | } 102 | 103 | // Write writes a buffer of MIDI events to the output stream. 104 | func (s *Stream) Write(events []Event) error { 105 | size := len(events) 106 | if size > maxEventBufferSize { 107 | return ErrMaxBuffer 108 | } 109 | buffer := make([]C.PmEvent, size) 110 | for i, evt := range events { 111 | var event C.PmEvent 112 | event.timestamp = C.PmTimestamp(evt.Timestamp) 113 | event.message = C.PmMessage((((evt.Data2 << 16) & 0xFF0000) | ((evt.Data1 << 8) & 0xFF00) | (evt.Status & 0xFF))) 114 | buffer[i] = event 115 | } 116 | return convertToError(C.Pm_Write(unsafe.Pointer(s.pmStream), &buffer[0], C.int32_t(size))) 117 | } 118 | 119 | // WriteShort writes a MIDI event of three bytes immediately to the output stream. 120 | func (s *Stream) WriteShort(status int64, data1 int64, data2 int64) error { 121 | evt := Event{ 122 | Timestamp: Timestamp(C.Pt_Time()), 123 | Status: status, 124 | Data1: data1, 125 | Data2: data2, 126 | } 127 | return s.Write([]Event{evt}) 128 | } 129 | 130 | // WriteSysExBytes writes a system exclusive MIDI message given as a []byte to the output stream. 131 | func (s *Stream) WriteSysExBytes(when Timestamp, msg []byte) error { 132 | return convertToError(C.Pm_WriteSysEx(unsafe.Pointer(s.pmStream), C.PmTimestamp(when), (*C.uchar)(unsafe.Pointer(&msg[0])))) 133 | } 134 | 135 | // WriteSysEx writes a system exclusive MIDI message given as a string of hexadecimal characters to 136 | // the output stream. The string must only consist of hex digits (0-9A-F) and optional spaces. This 137 | // function is case-insenstive. 138 | func (s *Stream) WriteSysEx(when Timestamp, msg string) error { 139 | buf, err := hex.DecodeString(strings.Replace(msg, " ", "", -1)) 140 | if err != nil { 141 | return err 142 | } 143 | 144 | return s.WriteSysExBytes(when, buf) 145 | } 146 | 147 | // SetChannelMask filters incoming stream based on channel. 148 | // In order to filter from more than a single channel, or multiple channels. 149 | // s.SetChannelMask(Channel(1) | Channel(10)) will both filter input 150 | // from channel 1 and 10. 151 | func (s *Stream) SetChannelMask(mask int) error { 152 | return convertToError(C.Pm_SetChannelMask(unsafe.Pointer(s.pmStream), C.int(mask))) 153 | } 154 | 155 | // Reads from the input stream, the max number events to be read are 156 | // determined by max. 157 | func (s *Stream) Read(max int) (events []Event, err error) { 158 | if max > maxEventBufferSize { 159 | return nil, ErrMaxBuffer 160 | } 161 | if max < minEventBufferSize { 162 | return nil, ErrMinBuffer 163 | } 164 | buffer := make([]C.PmEvent, max) 165 | numEvents := C.Pm_Read(unsafe.Pointer(s.pmStream), &buffer[0], C.int32_t(max)) 166 | if numEvents < 0 { 167 | return nil, convertToError(C.PmError(numEvents)) 168 | } 169 | events = make([]Event, numEvents) 170 | for i := 0; i < int(numEvents); i++ { 171 | events[i] = Event{ 172 | Timestamp: Timestamp(buffer[i].timestamp), 173 | Status: int64(buffer[i].message) & 0xFF, 174 | Data1: (int64(buffer[i].message) >> 8) & 0xFF, 175 | Data2: (int64(buffer[i].message) >> 16) & 0xFF, 176 | } 177 | } 178 | return 179 | } 180 | 181 | // ReadSysExBytes reads 4*max sysex bytes from the input stream. 182 | func (s *Stream) ReadSysExBytes(max int) ([]byte, error) { 183 | if max > maxEventBufferSize { 184 | return nil, ErrMaxBuffer 185 | } 186 | if max < minEventBufferSize { 187 | return nil, ErrMinBuffer 188 | } 189 | buffer := make([]C.PmEvent, max) 190 | numEvents := C.Pm_Read(unsafe.Pointer(s.pmStream), &buffer[0], C.int32_t(max)) 191 | if numEvents < 0 { 192 | return nil, convertToError(C.PmError(numEvents)) 193 | } 194 | msg := make([]byte, 4*numEvents) 195 | for i := 0; i < int(numEvents); i++ { 196 | msg[4*i+0] = byte(buffer[i].message & 0xFF) 197 | msg[4*i+1] = byte((buffer[i].message >> 8) & 0xFF) 198 | msg[4*i+2] = byte((buffer[i].message >> 16) & 0xFF) 199 | msg[4*i+3] = byte((buffer[i].message >> 24) & 0xFF) 200 | } 201 | return msg, nil 202 | } 203 | 204 | // Listen input stream for MIDI events. 205 | func (s *Stream) Listen() <-chan Event { 206 | ch := make(chan Event) 207 | go func(s *Stream, ch chan Event) { 208 | for { 209 | // sleep for a while before the new polling tick, 210 | // otherwise operation is too intensive and blocking 211 | time.Sleep(10 * time.Millisecond) 212 | events, err := s.Read(1024) 213 | // Note: It's not very reasonable to push sliced data into 214 | // a channel, several perf penalities there are. 215 | // This function is added as a handy utility. 216 | if err != nil { 217 | continue 218 | } 219 | for i := range events { 220 | ch <- events[i] 221 | } 222 | } 223 | }(s, ch) 224 | return ch 225 | } 226 | 227 | // Poll reports whether there is input available in the stream. 228 | func (s *Stream) Poll() (bool, error) { 229 | poll := C.Pm_Poll(unsafe.Pointer(s.pmStream)) 230 | if poll < 0 { 231 | return false, convertToError(C.PmError(poll)) 232 | } 233 | return poll > 0, nil 234 | } 235 | 236 | // TODO: add bindings for Pm_SetFilter 237 | -------------------------------------------------------------------------------- /rtaudio/RtAudio.h: -------------------------------------------------------------------------------- 1 | /************************************************************************/ 2 | /*! \class RtAudio 3 | \brief Realtime audio i/o C++ classes. 4 | 5 | RtAudio provides a common API (Application Programming Interface) 6 | for realtime audio input/output across Linux (native ALSA, Jack, 7 | and OSS), Macintosh OS X (CoreAudio and Jack), and Windows 8 | (DirectSound, ASIO and WASAPI) operating systems. 9 | 10 | RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ 11 | 12 | RtAudio: realtime audio i/o C++ classes 13 | Copyright (c) 2001-2017 Gary P. Scavone 14 | 15 | Permission is hereby granted, free of charge, to any person 16 | obtaining a copy of this software and associated documentation files 17 | (the "Software"), to deal in the Software without restriction, 18 | including without limitation the rights to use, copy, modify, merge, 19 | publish, distribute, sublicense, and/or sell copies of the Software, 20 | and to permit persons to whom the Software is furnished to do so, 21 | subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be 24 | included in all copies or substantial portions of the Software. 25 | 26 | Any person wishing to distribute modifications to the Software is 27 | asked to send the modifications to the original developer so that 28 | they can be incorporated into the canonical version. This is, 29 | however, not a binding provision of this license. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 32 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 33 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 34 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 35 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 36 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 37 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 38 | */ 39 | /************************************************************************/ 40 | 41 | /*! 42 | \file RtAudio.h 43 | */ 44 | 45 | #ifndef __RTAUDIO_H 46 | #define __RTAUDIO_H 47 | 48 | #define RTAUDIO_VERSION "5.0.0" 49 | 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | /*! \typedef typedef unsigned long RtAudioFormat; 56 | \brief RtAudio data format type. 57 | 58 | Support for signed integers and floats. Audio data fed to/from an 59 | RtAudio stream is assumed to ALWAYS be in host byte order. The 60 | internal routines will automatically take care of any necessary 61 | byte-swapping between the host format and the soundcard. Thus, 62 | endian-ness is not a concern in the following format definitions. 63 | 64 | - \e RTAUDIO_SINT8: 8-bit signed integer. 65 | - \e RTAUDIO_SINT16: 16-bit signed integer. 66 | - \e RTAUDIO_SINT24: 24-bit signed integer. 67 | - \e RTAUDIO_SINT32: 32-bit signed integer. 68 | - \e RTAUDIO_FLOAT32: Normalized between plus/minus 1.0. 69 | - \e RTAUDIO_FLOAT64: Normalized between plus/minus 1.0. 70 | */ 71 | typedef unsigned long RtAudioFormat; 72 | static const RtAudioFormat RTAUDIO_SINT8 = 0x1; // 8-bit signed integer. 73 | static const RtAudioFormat RTAUDIO_SINT16 = 0x2; // 16-bit signed integer. 74 | static const RtAudioFormat RTAUDIO_SINT24 = 0x4; // 24-bit signed integer. 75 | static const RtAudioFormat RTAUDIO_SINT32 = 0x8; // 32-bit signed integer. 76 | static const RtAudioFormat RTAUDIO_FLOAT32 = 0x10; // Normalized between plus/minus 1.0. 77 | static const RtAudioFormat RTAUDIO_FLOAT64 = 0x20; // Normalized between plus/minus 1.0. 78 | 79 | /*! \typedef typedef unsigned long RtAudioStreamFlags; 80 | \brief RtAudio stream option flags. 81 | 82 | The following flags can be OR'ed together to allow a client to 83 | make changes to the default stream behavior: 84 | 85 | - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved). 86 | - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency. 87 | - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use. 88 | - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only). 89 | - \e RTAUDIO_JACK_DONT_CONNECT: Do not automatically connect ports (JACK only). 90 | 91 | By default, RtAudio streams pass and receive audio data from the 92 | client in an interleaved format. By passing the 93 | RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio 94 | data will instead be presented in non-interleaved buffers. In 95 | this case, each buffer argument in the RtAudioCallback function 96 | will point to a single array of data, with \c nFrames samples for 97 | each channel concatenated back-to-back. For example, the first 98 | sample of data for the second channel would be located at index \c 99 | nFrames (assuming the \c buffer pointer was recast to the correct 100 | data type for the stream). 101 | 102 | Certain audio APIs offer a number of parameters that influence the 103 | I/O latency of a stream. By default, RtAudio will attempt to set 104 | these parameters internally for robust (glitch-free) performance 105 | (though some APIs, like Windows Direct Sound, make this difficult). 106 | By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream() 107 | function, internal stream settings will be influenced in an attempt 108 | to minimize stream latency, though possibly at the expense of stream 109 | performance. 110 | 111 | If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to 112 | open the input and/or output stream device(s) for exclusive use. 113 | Note that this is not possible with all supported audio APIs. 114 | 115 | If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt 116 | to select realtime scheduling (round-robin) for the callback thread. 117 | 118 | If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to 119 | open the "default" PCM device when using the ALSA API. Note that this 120 | will override any specified input or output device id. 121 | 122 | If the RTAUDIO_JACK_DONT_CONNECT flag is set, RtAudio will not attempt 123 | to automatically connect the ports of the client to the audio device. 124 | */ 125 | typedef unsigned int RtAudioStreamFlags; 126 | static const RtAudioStreamFlags RTAUDIO_NONINTERLEAVED = 0x1; // Use non-interleaved buffers (default = interleaved). 127 | static const RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY = 0x2; // Attempt to set stream parameters for lowest possible latency. 128 | static const RtAudioStreamFlags RTAUDIO_HOG_DEVICE = 0x4; // Attempt grab device and prevent use by others. 129 | static const RtAudioStreamFlags RTAUDIO_SCHEDULE_REALTIME = 0x8; // Try to select realtime scheduling for callback thread. 130 | static const RtAudioStreamFlags RTAUDIO_ALSA_USE_DEFAULT = 0x10; // Use the "default" PCM device (ALSA only). 131 | static const RtAudioStreamFlags RTAUDIO_JACK_DONT_CONNECT = 0x20; // Do not automatically connect ports (JACK only). 132 | 133 | /*! \typedef typedef unsigned long RtAudioStreamStatus; 134 | \brief RtAudio stream status (over- or underflow) flags. 135 | 136 | Notification of a stream over- or underflow is indicated by a 137 | non-zero stream \c status argument in the RtAudioCallback function. 138 | The stream status can be one of the following two options, 139 | depending on whether the stream is open for output and/or input: 140 | 141 | - \e RTAUDIO_INPUT_OVERFLOW: Input data was discarded because of an overflow condition at the driver. 142 | - \e RTAUDIO_OUTPUT_UNDERFLOW: The output buffer ran low, likely producing a break in the output sound. 143 | */ 144 | typedef unsigned int RtAudioStreamStatus; 145 | static const RtAudioStreamStatus RTAUDIO_INPUT_OVERFLOW = 0x1; // Input data was discarded because of an overflow condition at the driver. 146 | static const RtAudioStreamStatus RTAUDIO_OUTPUT_UNDERFLOW = 0x2; // The output buffer ran low, likely causing a gap in the output sound. 147 | 148 | //! RtAudio callback function prototype. 149 | /*! 150 | All RtAudio clients must create a function of type RtAudioCallback 151 | to read and/or write data from/to the audio stream. When the 152 | underlying audio system is ready for new input or output data, this 153 | function will be invoked. 154 | 155 | \param outputBuffer For output (or duplex) streams, the client 156 | should write \c nFrames of audio sample frames into this 157 | buffer. This argument should be recast to the datatype 158 | specified when the stream was opened. For input-only 159 | streams, this argument will be NULL. 160 | 161 | \param inputBuffer For input (or duplex) streams, this buffer will 162 | hold \c nFrames of input audio sample frames. This 163 | argument should be recast to the datatype specified when the 164 | stream was opened. For output-only streams, this argument 165 | will be NULL. 166 | 167 | \param nFrames The number of sample frames of input or output 168 | data in the buffers. The actual buffer size in bytes is 169 | dependent on the data type and number of channels in use. 170 | 171 | \param streamTime The number of seconds that have elapsed since the 172 | stream was started. 173 | 174 | \param status If non-zero, this argument indicates a data overflow 175 | or underflow condition for the stream. The particular 176 | condition can be determined by comparison with the 177 | RtAudioStreamStatus flags. 178 | 179 | \param userData A pointer to optional data provided by the client 180 | when opening the stream (default = NULL). 181 | 182 | To continue normal stream operation, the RtAudioCallback function 183 | should return a value of zero. To stop the stream and drain the 184 | output buffer, the function should return a value of one. To abort 185 | the stream immediately, the client should return a value of two. 186 | */ 187 | typedef int (*RtAudioCallback)( void *outputBuffer, void *inputBuffer, 188 | unsigned int nFrames, 189 | double streamTime, 190 | RtAudioStreamStatus status, 191 | void *userData ); 192 | 193 | /************************************************************************/ 194 | /*! \class RtAudioError 195 | \brief Exception handling class for RtAudio. 196 | 197 | The RtAudioError class is quite simple but it does allow errors to be 198 | "caught" by RtAudioError::Type. See the RtAudio documentation to know 199 | which methods can throw an RtAudioError. 200 | */ 201 | /************************************************************************/ 202 | 203 | class RtAudioError : public std::runtime_error 204 | { 205 | public: 206 | //! Defined RtAudioError types. 207 | enum Type { 208 | WARNING, /*!< A non-critical error. */ 209 | DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ 210 | UNSPECIFIED, /*!< The default, unspecified error type. */ 211 | NO_DEVICES_FOUND, /*!< No devices found on system. */ 212 | INVALID_DEVICE, /*!< An invalid device ID was specified. */ 213 | MEMORY_ERROR, /*!< An error occured during memory allocation. */ 214 | INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ 215 | INVALID_USE, /*!< The function was called incorrectly. */ 216 | DRIVER_ERROR, /*!< A system driver error occured. */ 217 | SYSTEM_ERROR, /*!< A system error occured. */ 218 | THREAD_ERROR /*!< A thread error occured. */ 219 | }; 220 | 221 | //! The constructor. 222 | RtAudioError( const std::string& message, 223 | Type type = RtAudioError::UNSPECIFIED ) 224 | : std::runtime_error(message), type_(type) {} 225 | 226 | //! Prints thrown error message to stderr. 227 | virtual void printMessage( void ) const 228 | { std::cerr << '\n' << what() << "\n\n"; } 229 | 230 | //! Returns the thrown error message type. 231 | virtual const Type& getType(void) const { return type_; } 232 | 233 | //! Returns the thrown error message string. 234 | virtual const std::string getMessage(void) const 235 | { return std::string(what()); } 236 | 237 | protected: 238 | Type type_; 239 | }; 240 | 241 | //! RtAudio error callback function prototype. 242 | /*! 243 | \param type Type of error. 244 | \param errorText Error description. 245 | */ 246 | typedef void (*RtAudioErrorCallback)( RtAudioError::Type type, const std::string &errorText ); 247 | 248 | // **************************************************************** // 249 | // 250 | // RtAudio class declaration. 251 | // 252 | // RtAudio is a "controller" used to select an available audio i/o 253 | // interface. It presents a common API for the user to call but all 254 | // functionality is implemented by the class RtApi and its 255 | // subclasses. RtAudio creates an instance of an RtApi subclass 256 | // based on the user's API choice. If no choice is made, RtAudio 257 | // attempts to make a "logical" API selection. 258 | // 259 | // **************************************************************** // 260 | 261 | class RtApi; 262 | 263 | class RtAudio 264 | { 265 | public: 266 | 267 | //! Audio API specifier arguments. 268 | enum Api { 269 | UNSPECIFIED, /*!< Search for a working compiled API. */ 270 | LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ 271 | LINUX_PULSE, /*!< The Linux PulseAudio API. */ 272 | LINUX_OSS, /*!< The Linux Open Sound System API. */ 273 | UNIX_JACK, /*!< The Jack Low-Latency Audio Server API. */ 274 | MACOSX_CORE, /*!< Macintosh OS-X Core Audio API. */ 275 | WINDOWS_WASAPI, /*!< The Microsoft WASAPI API. */ 276 | WINDOWS_ASIO, /*!< The Steinberg Audio Stream I/O API. */ 277 | WINDOWS_DS, /*!< The Microsoft Direct Sound API. */ 278 | RTAUDIO_DUMMY /*!< A compilable but non-functional API. */ 279 | }; 280 | 281 | //! The public device information structure for returning queried values. 282 | struct DeviceInfo { 283 | bool probed; /*!< true if the device capabilities were successfully probed. */ 284 | std::string name; /*!< Character string device identifier. */ 285 | unsigned int outputChannels; /*!< Maximum output channels supported by device. */ 286 | unsigned int inputChannels; /*!< Maximum input channels supported by device. */ 287 | unsigned int duplexChannels; /*!< Maximum simultaneous input/output channels supported by device. */ 288 | bool isDefaultOutput; /*!< true if this is the default output device. */ 289 | bool isDefaultInput; /*!< true if this is the default input device. */ 290 | std::vector sampleRates; /*!< Supported sample rates (queried from list of standard rates). */ 291 | unsigned int preferredSampleRate; /*!< Preferred sample rate, eg. for WASAPI the system sample rate. */ 292 | RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */ 293 | 294 | // Default constructor. 295 | DeviceInfo() 296 | :probed(false), outputChannels(0), inputChannels(0), duplexChannels(0), 297 | isDefaultOutput(false), isDefaultInput(false), preferredSampleRate(0), nativeFormats(0) {} 298 | }; 299 | 300 | //! The structure for specifying input or ouput stream parameters. 301 | struct StreamParameters { 302 | unsigned int deviceId; /*!< Device index (0 to getDeviceCount() - 1). */ 303 | unsigned int nChannels; /*!< Number of channels. */ 304 | unsigned int firstChannel; /*!< First channel index on device (default = 0). */ 305 | 306 | // Default constructor. 307 | StreamParameters() 308 | : deviceId(0), nChannels(0), firstChannel(0) {} 309 | }; 310 | 311 | //! The structure for specifying stream options. 312 | /*! 313 | The following flags can be OR'ed together to allow a client to 314 | make changes to the default stream behavior: 315 | 316 | - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved). 317 | - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency. 318 | - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use. 319 | - \e RTAUDIO_SCHEDULE_REALTIME: Attempt to select realtime scheduling for callback thread. 320 | - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only). 321 | 322 | By default, RtAudio streams pass and receive audio data from the 323 | client in an interleaved format. By passing the 324 | RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio 325 | data will instead be presented in non-interleaved buffers. In 326 | this case, each buffer argument in the RtAudioCallback function 327 | will point to a single array of data, with \c nFrames samples for 328 | each channel concatenated back-to-back. For example, the first 329 | sample of data for the second channel would be located at index \c 330 | nFrames (assuming the \c buffer pointer was recast to the correct 331 | data type for the stream). 332 | 333 | Certain audio APIs offer a number of parameters that influence the 334 | I/O latency of a stream. By default, RtAudio will attempt to set 335 | these parameters internally for robust (glitch-free) performance 336 | (though some APIs, like Windows Direct Sound, make this difficult). 337 | By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream() 338 | function, internal stream settings will be influenced in an attempt 339 | to minimize stream latency, though possibly at the expense of stream 340 | performance. 341 | 342 | If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to 343 | open the input and/or output stream device(s) for exclusive use. 344 | Note that this is not possible with all supported audio APIs. 345 | 346 | If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt 347 | to select realtime scheduling (round-robin) for the callback thread. 348 | The \c priority parameter will only be used if the RTAUDIO_SCHEDULE_REALTIME 349 | flag is set. It defines the thread's realtime priority. 350 | 351 | If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to 352 | open the "default" PCM device when using the ALSA API. Note that this 353 | will override any specified input or output device id. 354 | 355 | The \c numberOfBuffers parameter can be used to control stream 356 | latency in the Windows DirectSound, Linux OSS, and Linux Alsa APIs 357 | only. A value of two is usually the smallest allowed. Larger 358 | numbers can potentially result in more robust stream performance, 359 | though likely at the cost of stream latency. The value set by the 360 | user is replaced during execution of the RtAudio::openStream() 361 | function by the value actually used by the system. 362 | 363 | The \c streamName parameter can be used to set the client name 364 | when using the Jack API. By default, the client name is set to 365 | RtApiJack. However, if you wish to create multiple instances of 366 | RtAudio with Jack, each instance must have a unique client name. 367 | */ 368 | struct StreamOptions { 369 | RtAudioStreamFlags flags; /*!< A bit-mask of stream flags (RTAUDIO_NONINTERLEAVED, RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE, RTAUDIO_ALSA_USE_DEFAULT). */ 370 | unsigned int numberOfBuffers; /*!< Number of stream buffers. */ 371 | std::string streamName; /*!< A stream name (currently used only in Jack). */ 372 | int priority; /*!< Scheduling priority of callback thread (only used with flag RTAUDIO_SCHEDULE_REALTIME). */ 373 | 374 | // Default constructor. 375 | StreamOptions() 376 | : flags(0), numberOfBuffers(0), priority(0) {} 377 | }; 378 | 379 | //! A static function to determine the current RtAudio version. 380 | static std::string getVersion( void ); 381 | 382 | //! A static function to determine the available compiled audio APIs. 383 | /*! 384 | The values returned in the std::vector can be compared against 385 | the enumerated list values. Note that there can be more than one 386 | API compiled for certain operating systems. 387 | */ 388 | static void getCompiledApi( std::vector &apis ); 389 | 390 | //! The class constructor. 391 | /*! 392 | The constructor performs minor initialization tasks. An exception 393 | can be thrown if no API support is compiled. 394 | 395 | If no API argument is specified and multiple API support has been 396 | compiled, the default order of use is JACK, ALSA, OSS (Linux 397 | systems) and ASIO, DS (Windows systems). 398 | */ 399 | RtAudio( RtAudio::Api api=UNSPECIFIED ); 400 | 401 | //! The destructor. 402 | /*! 403 | If a stream is running or open, it will be stopped and closed 404 | automatically. 405 | */ 406 | ~RtAudio(); 407 | 408 | //! Returns the audio API specifier for the current instance of RtAudio. 409 | RtAudio::Api getCurrentApi( void ); 410 | 411 | //! A public function that queries for the number of audio devices available. 412 | /*! 413 | This function performs a system query of available devices each time it 414 | is called, thus supporting devices connected \e after instantiation. If 415 | a system error occurs during processing, a warning will be issued. 416 | */ 417 | unsigned int getDeviceCount( void ); 418 | 419 | //! Return an RtAudio::DeviceInfo structure for a specified device number. 420 | /*! 421 | 422 | Any device integer between 0 and getDeviceCount() - 1 is valid. 423 | If an invalid argument is provided, an RtAudioError (type = INVALID_USE) 424 | will be thrown. If a device is busy or otherwise unavailable, the 425 | structure member "probed" will have a value of "false" and all 426 | other members are undefined. If the specified device is the 427 | current default input or output device, the corresponding 428 | "isDefault" member will have a value of "true". 429 | */ 430 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 431 | 432 | //! A function that returns the index of the default output device. 433 | /*! 434 | If the underlying audio API does not provide a "default 435 | device", or if no devices are available, the return value will be 436 | 0. Note that this is a valid device identifier and it is the 437 | client's responsibility to verify that a device is available 438 | before attempting to open a stream. 439 | */ 440 | unsigned int getDefaultOutputDevice( void ); 441 | 442 | //! A function that returns the index of the default input device. 443 | /*! 444 | If the underlying audio API does not provide a "default 445 | device", or if no devices are available, the return value will be 446 | 0. Note that this is a valid device identifier and it is the 447 | client's responsibility to verify that a device is available 448 | before attempting to open a stream. 449 | */ 450 | unsigned int getDefaultInputDevice( void ); 451 | 452 | //! A public function for opening a stream with the specified parameters. 453 | /*! 454 | An RtAudioError (type = SYSTEM_ERROR) is thrown if a stream cannot be 455 | opened with the specified parameters or an error occurs during 456 | processing. An RtAudioError (type = INVALID_USE) is thrown if any 457 | invalid device ID or channel number parameters are specified. 458 | 459 | \param outputParameters Specifies output stream parameters to use 460 | when opening a stream, including a device ID, number of channels, 461 | and starting channel number. For input-only streams, this 462 | argument should be NULL. The device ID is an index value between 463 | 0 and getDeviceCount() - 1. 464 | \param inputParameters Specifies input stream parameters to use 465 | when opening a stream, including a device ID, number of channels, 466 | and starting channel number. For output-only streams, this 467 | argument should be NULL. The device ID is an index value between 468 | 0 and getDeviceCount() - 1. 469 | \param format An RtAudioFormat specifying the desired sample data format. 470 | \param sampleRate The desired sample rate (sample frames per second). 471 | \param *bufferFrames A pointer to a value indicating the desired 472 | internal buffer size in sample frames. The actual value 473 | used by the device is returned via the same pointer. A 474 | value of zero can be specified, in which case the lowest 475 | allowable value is determined. 476 | \param callback A client-defined function that will be invoked 477 | when input data is available and/or output data is needed. 478 | \param userData An optional pointer to data that can be accessed 479 | from within the callback function. 480 | \param options An optional pointer to a structure containing various 481 | global stream options, including a list of OR'ed RtAudioStreamFlags 482 | and a suggested number of stream buffers that can be used to 483 | control stream latency. More buffers typically result in more 484 | robust performance, though at a cost of greater latency. If a 485 | value of zero is specified, a system-specific median value is 486 | chosen. If the RTAUDIO_MINIMIZE_LATENCY flag bit is set, the 487 | lowest allowable value is used. The actual value used is 488 | returned via the structure argument. The parameter is API dependent. 489 | \param errorCallback A client-defined function that will be invoked 490 | when an error has occured. 491 | */ 492 | void openStream( RtAudio::StreamParameters *outputParameters, 493 | RtAudio::StreamParameters *inputParameters, 494 | RtAudioFormat format, unsigned int sampleRate, 495 | unsigned int *bufferFrames, RtAudioCallback callback, 496 | void *userData = NULL, RtAudio::StreamOptions *options = NULL, RtAudioErrorCallback errorCallback = NULL ); 497 | 498 | //! A function that closes a stream and frees any associated stream memory. 499 | /*! 500 | If a stream is not open, this function issues a warning and 501 | returns (no exception is thrown). 502 | */ 503 | void closeStream( void ); 504 | 505 | //! A function that starts a stream. 506 | /*! 507 | An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs 508 | during processing. An RtAudioError (type = INVALID_USE) is thrown if a 509 | stream is not open. A warning is issued if the stream is already 510 | running. 511 | */ 512 | void startStream( void ); 513 | 514 | //! Stop a stream, allowing any samples remaining in the output queue to be played. 515 | /*! 516 | An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs 517 | during processing. An RtAudioError (type = INVALID_USE) is thrown if a 518 | stream is not open. A warning is issued if the stream is already 519 | stopped. 520 | */ 521 | void stopStream( void ); 522 | 523 | //! Stop a stream, discarding any samples remaining in the input/output queue. 524 | /*! 525 | An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs 526 | during processing. An RtAudioError (type = INVALID_USE) is thrown if a 527 | stream is not open. A warning is issued if the stream is already 528 | stopped. 529 | */ 530 | void abortStream( void ); 531 | 532 | //! Returns true if a stream is open and false if not. 533 | bool isStreamOpen( void ) const; 534 | 535 | //! Returns true if the stream is running and false if it is stopped or not open. 536 | bool isStreamRunning( void ) const; 537 | 538 | //! Returns the number of elapsed seconds since the stream was started. 539 | /*! 540 | If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown. 541 | */ 542 | double getStreamTime( void ); 543 | 544 | //! Set the stream time to a time in seconds greater than or equal to 0.0. 545 | /*! 546 | If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown. 547 | */ 548 | void setStreamTime( double time ); 549 | 550 | //! Returns the internal stream latency in sample frames. 551 | /*! 552 | The stream latency refers to delay in audio input and/or output 553 | caused by internal buffering by the audio system and/or hardware. 554 | For duplex streams, the returned value will represent the sum of 555 | the input and output latencies. If a stream is not open, an 556 | RtAudioError (type = INVALID_USE) will be thrown. If the API does not 557 | report latency, the return value will be zero. 558 | */ 559 | long getStreamLatency( void ); 560 | 561 | //! Returns actual sample rate in use by the stream. 562 | /*! 563 | On some systems, the sample rate used may be slightly different 564 | than that specified in the stream parameters. If a stream is not 565 | open, an RtAudioError (type = INVALID_USE) will be thrown. 566 | */ 567 | unsigned int getStreamSampleRate( void ); 568 | 569 | //! Specify whether warning messages should be printed to stderr. 570 | void showWarnings( bool value = true ); 571 | 572 | protected: 573 | 574 | void openRtApi( RtAudio::Api api ); 575 | RtApi *rtapi_; 576 | }; 577 | 578 | // Operating system dependent thread functionality. 579 | #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__) 580 | 581 | #ifndef NOMINMAX 582 | #define NOMINMAX 583 | #endif 584 | #include 585 | #include 586 | 587 | typedef uintptr_t ThreadHandle; 588 | typedef CRITICAL_SECTION StreamMutex; 589 | 590 | #elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__) 591 | // Using pthread library for various flavors of unix. 592 | #include 593 | 594 | typedef pthread_t ThreadHandle; 595 | typedef pthread_mutex_t StreamMutex; 596 | 597 | #else // Setup for "dummy" behavior 598 | 599 | #define __RTAUDIO_DUMMY__ 600 | typedef int ThreadHandle; 601 | typedef int StreamMutex; 602 | 603 | #endif 604 | 605 | // This global structure type is used to pass callback information 606 | // between the private RtAudio stream structure and global callback 607 | // handling functions. 608 | struct CallbackInfo { 609 | void *object; // Used as a "this" pointer. 610 | ThreadHandle thread; 611 | void *callback; 612 | void *userData; 613 | void *errorCallback; 614 | void *apiInfo; // void pointer for API specific callback information 615 | bool isRunning; 616 | bool doRealtime; 617 | int priority; 618 | 619 | // Default constructor. 620 | CallbackInfo() 621 | :object(0), callback(0), userData(0), errorCallback(0), apiInfo(0), isRunning(false), doRealtime(false), priority(0) {} 622 | }; 623 | 624 | // **************************************************************** // 625 | // 626 | // RtApi class declaration. 627 | // 628 | // Subclasses of RtApi contain all API- and OS-specific code necessary 629 | // to fully implement the RtAudio API. 630 | // 631 | // Note that RtApi is an abstract base class and cannot be 632 | // explicitly instantiated. The class RtAudio will create an 633 | // instance of an RtApi subclass (RtApiOss, RtApiAlsa, 634 | // RtApiJack, RtApiCore, RtApiDs, or RtApiAsio). 635 | // 636 | // **************************************************************** // 637 | 638 | #pragma pack(push, 1) 639 | class S24 { 640 | 641 | protected: 642 | unsigned char c3[3]; 643 | 644 | public: 645 | S24() {} 646 | 647 | S24& operator = ( const int& i ) { 648 | c3[0] = (i & 0x000000ff); 649 | c3[1] = (i & 0x0000ff00) >> 8; 650 | c3[2] = (i & 0x00ff0000) >> 16; 651 | return *this; 652 | } 653 | 654 | S24( const S24& v ) { *this = v; } 655 | S24( const double& d ) { *this = (int) d; } 656 | S24( const float& f ) { *this = (int) f; } 657 | S24( const signed short& s ) { *this = (int) s; } 658 | S24( const char& c ) { *this = (int) c; } 659 | 660 | int asInt() { 661 | int i = c3[0] | (c3[1] << 8) | (c3[2] << 16); 662 | if (i & 0x800000) i |= ~0xffffff; 663 | return i; 664 | } 665 | }; 666 | #pragma pack(pop) 667 | 668 | #if defined( HAVE_GETTIMEOFDAY ) 669 | #include 670 | #endif 671 | 672 | #include 673 | 674 | class RtApi 675 | { 676 | public: 677 | 678 | RtApi(); 679 | virtual ~RtApi(); 680 | virtual RtAudio::Api getCurrentApi( void ) = 0; 681 | virtual unsigned int getDeviceCount( void ) = 0; 682 | virtual RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) = 0; 683 | virtual unsigned int getDefaultInputDevice( void ); 684 | virtual unsigned int getDefaultOutputDevice( void ); 685 | void openStream( RtAudio::StreamParameters *outputParameters, 686 | RtAudio::StreamParameters *inputParameters, 687 | RtAudioFormat format, unsigned int sampleRate, 688 | unsigned int *bufferFrames, RtAudioCallback callback, 689 | void *userData, RtAudio::StreamOptions *options, 690 | RtAudioErrorCallback errorCallback ); 691 | virtual void closeStream( void ); 692 | virtual void startStream( void ) = 0; 693 | virtual void stopStream( void ) = 0; 694 | virtual void abortStream( void ) = 0; 695 | long getStreamLatency( void ); 696 | unsigned int getStreamSampleRate( void ); 697 | virtual double getStreamTime( void ); 698 | virtual void setStreamTime( double time ); 699 | bool isStreamOpen( void ) const { return stream_.state != STREAM_CLOSED; } 700 | bool isStreamRunning( void ) const { return stream_.state == STREAM_RUNNING; } 701 | void showWarnings( bool value ) { showWarnings_ = value; } 702 | 703 | 704 | protected: 705 | 706 | static const unsigned int MAX_SAMPLE_RATES; 707 | static const unsigned int SAMPLE_RATES[]; 708 | 709 | enum { FAILURE, SUCCESS }; 710 | 711 | enum StreamState { 712 | STREAM_STOPPED, 713 | STREAM_STOPPING, 714 | STREAM_RUNNING, 715 | STREAM_CLOSED = -50 716 | }; 717 | 718 | enum StreamMode { 719 | OUTPUT, 720 | INPUT, 721 | DUPLEX, 722 | UNINITIALIZED = -75 723 | }; 724 | 725 | // A protected structure used for buffer conversion. 726 | struct ConvertInfo { 727 | int channels; 728 | int inJump, outJump; 729 | RtAudioFormat inFormat, outFormat; 730 | std::vector inOffset; 731 | std::vector outOffset; 732 | }; 733 | 734 | // A protected structure for audio streams. 735 | struct RtApiStream { 736 | unsigned int device[2]; // Playback and record, respectively. 737 | void *apiHandle; // void pointer for API specific stream handle information 738 | StreamMode mode; // OUTPUT, INPUT, or DUPLEX. 739 | StreamState state; // STOPPED, RUNNING, or CLOSED 740 | char *userBuffer[2]; // Playback and record, respectively. 741 | char *deviceBuffer; 742 | bool doConvertBuffer[2]; // Playback and record, respectively. 743 | bool userInterleaved; 744 | bool deviceInterleaved[2]; // Playback and record, respectively. 745 | bool doByteSwap[2]; // Playback and record, respectively. 746 | unsigned int sampleRate; 747 | unsigned int bufferSize; 748 | unsigned int nBuffers; 749 | unsigned int nUserChannels[2]; // Playback and record, respectively. 750 | unsigned int nDeviceChannels[2]; // Playback and record channels, respectively. 751 | unsigned int channelOffset[2]; // Playback and record, respectively. 752 | unsigned long latency[2]; // Playback and record, respectively. 753 | RtAudioFormat userFormat; 754 | RtAudioFormat deviceFormat[2]; // Playback and record, respectively. 755 | StreamMutex mutex; 756 | CallbackInfo callbackInfo; 757 | ConvertInfo convertInfo[2]; 758 | double streamTime; // Number of elapsed seconds since the stream started. 759 | 760 | #if defined(HAVE_GETTIMEOFDAY) 761 | struct timeval lastTickTimestamp; 762 | #endif 763 | 764 | RtApiStream() 765 | :apiHandle(0), deviceBuffer(0) { device[0] = 11111; device[1] = 11111; } 766 | }; 767 | 768 | typedef S24 Int24; 769 | typedef signed short Int16; 770 | typedef signed int Int32; 771 | typedef float Float32; 772 | typedef double Float64; 773 | 774 | std::ostringstream errorStream_; 775 | std::string errorText_; 776 | bool showWarnings_; 777 | RtApiStream stream_; 778 | bool firstErrorOccurred_; 779 | 780 | /*! 781 | Protected, api-specific method that attempts to open a device 782 | with the given parameters. This function MUST be implemented by 783 | all subclasses. If an error is encountered during the probe, a 784 | "warning" message is reported and FAILURE is returned. A 785 | successful probe is indicated by a return value of SUCCESS. 786 | */ 787 | virtual bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 788 | unsigned int firstChannel, unsigned int sampleRate, 789 | RtAudioFormat format, unsigned int *bufferSize, 790 | RtAudio::StreamOptions *options ); 791 | 792 | //! A protected function used to increment the stream time. 793 | void tickStreamTime( void ); 794 | 795 | //! Protected common method to clear an RtApiStream structure. 796 | void clearStreamInfo(); 797 | 798 | /*! 799 | Protected common method that throws an RtAudioError (type = 800 | INVALID_USE) if a stream is not open. 801 | */ 802 | void verifyStream( void ); 803 | 804 | //! Protected common error method to allow global control over error handling. 805 | void error( RtAudioError::Type type ); 806 | 807 | /*! 808 | Protected method used to perform format, channel number, and/or interleaving 809 | conversions between the user and device buffers. 810 | */ 811 | void convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info ); 812 | 813 | //! Protected common method used to perform byte-swapping on buffers. 814 | void byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format ); 815 | 816 | //! Protected common method that returns the number of bytes for a given format. 817 | unsigned int formatBytes( RtAudioFormat format ); 818 | 819 | //! Protected common method that sets up the parameters for buffer conversion. 820 | void setConvertInfo( StreamMode mode, unsigned int firstChannel ); 821 | }; 822 | 823 | // **************************************************************** // 824 | // 825 | // Inline RtAudio definitions. 826 | // 827 | // **************************************************************** // 828 | 829 | inline RtAudio::Api RtAudio :: getCurrentApi( void ) { return rtapi_->getCurrentApi(); } 830 | inline unsigned int RtAudio :: getDeviceCount( void ) { return rtapi_->getDeviceCount(); } 831 | inline RtAudio::DeviceInfo RtAudio :: getDeviceInfo( unsigned int device ) { return rtapi_->getDeviceInfo( device ); } 832 | inline unsigned int RtAudio :: getDefaultInputDevice( void ) { return rtapi_->getDefaultInputDevice(); } 833 | inline unsigned int RtAudio :: getDefaultOutputDevice( void ) { return rtapi_->getDefaultOutputDevice(); } 834 | inline void RtAudio :: closeStream( void ) { return rtapi_->closeStream(); } 835 | inline void RtAudio :: startStream( void ) { return rtapi_->startStream(); } 836 | inline void RtAudio :: stopStream( void ) { return rtapi_->stopStream(); } 837 | inline void RtAudio :: abortStream( void ) { return rtapi_->abortStream(); } 838 | inline bool RtAudio :: isStreamOpen( void ) const { return rtapi_->isStreamOpen(); } 839 | inline bool RtAudio :: isStreamRunning( void ) const { return rtapi_->isStreamRunning(); } 840 | inline long RtAudio :: getStreamLatency( void ) { return rtapi_->getStreamLatency(); } 841 | inline unsigned int RtAudio :: getStreamSampleRate( void ) { return rtapi_->getStreamSampleRate(); } 842 | inline double RtAudio :: getStreamTime( void ) { return rtapi_->getStreamTime(); } 843 | inline void RtAudio :: setStreamTime( double time ) { return rtapi_->setStreamTime( time ); } 844 | inline void RtAudio :: showWarnings( bool value ) { rtapi_->showWarnings( value ); } 845 | 846 | // RtApi Subclass prototypes. 847 | 848 | #if defined(__MACOSX_CORE__) 849 | 850 | #include 851 | 852 | class RtApiCore: public RtApi 853 | { 854 | public: 855 | 856 | RtApiCore(); 857 | ~RtApiCore(); 858 | RtAudio::Api getCurrentApi( void ) { return RtAudio::MACOSX_CORE; } 859 | unsigned int getDeviceCount( void ); 860 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 861 | unsigned int getDefaultOutputDevice( void ); 862 | unsigned int getDefaultInputDevice( void ); 863 | void closeStream( void ); 864 | void startStream( void ); 865 | void stopStream( void ); 866 | void abortStream( void ); 867 | long getStreamLatency( void ); 868 | 869 | // This function is intended for internal use only. It must be 870 | // public because it is called by the internal callback handler, 871 | // which is not a member of RtAudio. External use of this function 872 | // will most likely produce highly undesireable results! 873 | bool callbackEvent( AudioDeviceID deviceId, 874 | const AudioBufferList *inBufferList, 875 | const AudioBufferList *outBufferList ); 876 | 877 | private: 878 | 879 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 880 | unsigned int firstChannel, unsigned int sampleRate, 881 | RtAudioFormat format, unsigned int *bufferSize, 882 | RtAudio::StreamOptions *options ); 883 | static const char* getErrorCode( OSStatus code ); 884 | }; 885 | 886 | #endif 887 | 888 | #if defined(__UNIX_JACK__) 889 | 890 | class RtApiJack: public RtApi 891 | { 892 | public: 893 | 894 | RtApiJack(); 895 | ~RtApiJack(); 896 | RtAudio::Api getCurrentApi( void ) { return RtAudio::UNIX_JACK; } 897 | unsigned int getDeviceCount( void ); 898 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 899 | void closeStream( void ); 900 | void startStream( void ); 901 | void stopStream( void ); 902 | void abortStream( void ); 903 | long getStreamLatency( void ); 904 | 905 | // This function is intended for internal use only. It must be 906 | // public because it is called by the internal callback handler, 907 | // which is not a member of RtAudio. External use of this function 908 | // will most likely produce highly undesireable results! 909 | bool callbackEvent( unsigned long nframes ); 910 | 911 | private: 912 | 913 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 914 | unsigned int firstChannel, unsigned int sampleRate, 915 | RtAudioFormat format, unsigned int *bufferSize, 916 | RtAudio::StreamOptions *options ); 917 | 918 | bool shouldAutoconnect_; 919 | }; 920 | 921 | #endif 922 | 923 | #if defined(__WINDOWS_ASIO__) 924 | 925 | class RtApiAsio: public RtApi 926 | { 927 | public: 928 | 929 | RtApiAsio(); 930 | ~RtApiAsio(); 931 | RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_ASIO; } 932 | unsigned int getDeviceCount( void ); 933 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 934 | void closeStream( void ); 935 | void startStream( void ); 936 | void stopStream( void ); 937 | void abortStream( void ); 938 | long getStreamLatency( void ); 939 | 940 | // This function is intended for internal use only. It must be 941 | // public because it is called by the internal callback handler, 942 | // which is not a member of RtAudio. External use of this function 943 | // will most likely produce highly undesireable results! 944 | bool callbackEvent( long bufferIndex ); 945 | 946 | private: 947 | 948 | std::vector devices_; 949 | void saveDeviceInfo( void ); 950 | bool coInitialized_; 951 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 952 | unsigned int firstChannel, unsigned int sampleRate, 953 | RtAudioFormat format, unsigned int *bufferSize, 954 | RtAudio::StreamOptions *options ); 955 | }; 956 | 957 | #endif 958 | 959 | #if defined(__WINDOWS_DS__) 960 | 961 | class RtApiDs: public RtApi 962 | { 963 | public: 964 | 965 | RtApiDs(); 966 | ~RtApiDs(); 967 | RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_DS; } 968 | unsigned int getDeviceCount( void ); 969 | unsigned int getDefaultOutputDevice( void ); 970 | unsigned int getDefaultInputDevice( void ); 971 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 972 | void closeStream( void ); 973 | void startStream( void ); 974 | void stopStream( void ); 975 | void abortStream( void ); 976 | long getStreamLatency( void ); 977 | 978 | // This function is intended for internal use only. It must be 979 | // public because it is called by the internal callback handler, 980 | // which is not a member of RtAudio. External use of this function 981 | // will most likely produce highly undesireable results! 982 | void callbackEvent( void ); 983 | 984 | private: 985 | 986 | bool coInitialized_; 987 | bool buffersRolling; 988 | long duplexPrerollBytes; 989 | std::vector dsDevices; 990 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 991 | unsigned int firstChannel, unsigned int sampleRate, 992 | RtAudioFormat format, unsigned int *bufferSize, 993 | RtAudio::StreamOptions *options ); 994 | }; 995 | 996 | #endif 997 | 998 | #if defined(__WINDOWS_WASAPI__) 999 | 1000 | struct IMMDeviceEnumerator; 1001 | 1002 | class RtApiWasapi : public RtApi 1003 | { 1004 | public: 1005 | RtApiWasapi(); 1006 | ~RtApiWasapi(); 1007 | 1008 | RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_WASAPI; } 1009 | unsigned int getDeviceCount( void ); 1010 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 1011 | unsigned int getDefaultOutputDevice( void ); 1012 | unsigned int getDefaultInputDevice( void ); 1013 | void closeStream( void ); 1014 | void startStream( void ); 1015 | void stopStream( void ); 1016 | void abortStream( void ); 1017 | 1018 | private: 1019 | bool coInitialized_; 1020 | IMMDeviceEnumerator* deviceEnumerator_; 1021 | 1022 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 1023 | unsigned int firstChannel, unsigned int sampleRate, 1024 | RtAudioFormat format, unsigned int* bufferSize, 1025 | RtAudio::StreamOptions* options ); 1026 | 1027 | static DWORD WINAPI runWasapiThread( void* wasapiPtr ); 1028 | static DWORD WINAPI stopWasapiThread( void* wasapiPtr ); 1029 | static DWORD WINAPI abortWasapiThread( void* wasapiPtr ); 1030 | void wasapiThread(); 1031 | }; 1032 | 1033 | #endif 1034 | 1035 | #if defined(__LINUX_ALSA__) 1036 | 1037 | class RtApiAlsa: public RtApi 1038 | { 1039 | public: 1040 | 1041 | RtApiAlsa(); 1042 | ~RtApiAlsa(); 1043 | RtAudio::Api getCurrentApi() { return RtAudio::LINUX_ALSA; } 1044 | unsigned int getDeviceCount( void ); 1045 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 1046 | void closeStream( void ); 1047 | void startStream( void ); 1048 | void stopStream( void ); 1049 | void abortStream( void ); 1050 | 1051 | // This function is intended for internal use only. It must be 1052 | // public because it is called by the internal callback handler, 1053 | // which is not a member of RtAudio. External use of this function 1054 | // will most likely produce highly undesireable results! 1055 | void callbackEvent( void ); 1056 | 1057 | private: 1058 | 1059 | std::vector devices_; 1060 | void saveDeviceInfo( void ); 1061 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 1062 | unsigned int firstChannel, unsigned int sampleRate, 1063 | RtAudioFormat format, unsigned int *bufferSize, 1064 | RtAudio::StreamOptions *options ); 1065 | }; 1066 | 1067 | #endif 1068 | 1069 | #if defined(__LINUX_PULSE__) 1070 | 1071 | class RtApiPulse: public RtApi 1072 | { 1073 | public: 1074 | ~RtApiPulse(); 1075 | RtAudio::Api getCurrentApi() { return RtAudio::LINUX_PULSE; } 1076 | unsigned int getDeviceCount( void ); 1077 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 1078 | void closeStream( void ); 1079 | void startStream( void ); 1080 | void stopStream( void ); 1081 | void abortStream( void ); 1082 | 1083 | // This function is intended for internal use only. It must be 1084 | // public because it is called by the internal callback handler, 1085 | // which is not a member of RtAudio. External use of this function 1086 | // will most likely produce highly undesireable results! 1087 | void callbackEvent( void ); 1088 | 1089 | private: 1090 | 1091 | std::vector devices_; 1092 | void saveDeviceInfo( void ); 1093 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 1094 | unsigned int firstChannel, unsigned int sampleRate, 1095 | RtAudioFormat format, unsigned int *bufferSize, 1096 | RtAudio::StreamOptions *options ); 1097 | }; 1098 | 1099 | #endif 1100 | 1101 | #if defined(__LINUX_OSS__) 1102 | 1103 | class RtApiOss: public RtApi 1104 | { 1105 | public: 1106 | 1107 | RtApiOss(); 1108 | ~RtApiOss(); 1109 | RtAudio::Api getCurrentApi() { return RtAudio::LINUX_OSS; } 1110 | unsigned int getDeviceCount( void ); 1111 | RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); 1112 | void closeStream( void ); 1113 | void startStream( void ); 1114 | void stopStream( void ); 1115 | void abortStream( void ); 1116 | 1117 | // This function is intended for internal use only. It must be 1118 | // public because it is called by the internal callback handler, 1119 | // which is not a member of RtAudio. External use of this function 1120 | // will most likely produce highly undesireable results! 1121 | void callbackEvent( void ); 1122 | 1123 | private: 1124 | 1125 | bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 1126 | unsigned int firstChannel, unsigned int sampleRate, 1127 | RtAudioFormat format, unsigned int *bufferSize, 1128 | RtAudio::StreamOptions *options ); 1129 | }; 1130 | 1131 | #endif 1132 | 1133 | #if defined(__RTAUDIO_DUMMY__) 1134 | 1135 | class RtApiDummy: public RtApi 1136 | { 1137 | public: 1138 | 1139 | RtApiDummy() { errorText_ = "RtApiDummy: This class provides no functionality."; error( RtAudioError::WARNING ); } 1140 | RtAudio::Api getCurrentApi( void ) { return RtAudio::RTAUDIO_DUMMY; } 1141 | unsigned int getDeviceCount( void ) { return 0; } 1142 | RtAudio::DeviceInfo getDeviceInfo( unsigned int /*device*/ ) { RtAudio::DeviceInfo info; return info; } 1143 | void closeStream( void ) {} 1144 | void startStream( void ) {} 1145 | void stopStream( void ) {} 1146 | void abortStream( void ) {} 1147 | 1148 | private: 1149 | 1150 | bool probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/, 1151 | unsigned int /*firstChannel*/, unsigned int /*sampleRate*/, 1152 | RtAudioFormat /*format*/, unsigned int * /*bufferSize*/, 1153 | RtAudio::StreamOptions * /*options*/ ) { return false; } 1154 | }; 1155 | 1156 | #endif 1157 | 1158 | #endif 1159 | 1160 | // Indentation settings for Vim and Emacs 1161 | // 1162 | // Local Variables: 1163 | // c-basic-offset: 2 1164 | // indent-tabs-mode: nil 1165 | // End: 1166 | // 1167 | // vim: et sts=2 sw=2 1168 | -------------------------------------------------------------------------------- /rtaudio/_example-c/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(jack),1) 2 | CXXFLAGS += -D__UNIX_JACK__ 3 | LDFLAGS += -lm -ljack -pthread 4 | endif 5 | ifeq ($(linux),1) 6 | CXXFLAGS += -D__LINUX_ALSA__ 7 | LDFLAGS += -lm -lasound -pthread 8 | endif 9 | ifeq ($(macos),1) 10 | CXXFLAGS += -D__MAXOSX_CORE__ 11 | LDFLAGS += -lm -framework CoreAudio -framework CoreMIDI -framework CoreFoundation 12 | endif 13 | ifeq ($(windows),1) 14 | CXXFLAGS += -D__WINDOWS_WASAPI__ 15 | LDFLAGS += -lm -lole32 -lksuser -lwinmm -lws2_32 -mwindows -static 16 | endif 17 | 18 | CFLAGS += -g 19 | CXXFLAGS += -g 20 | LDFLAGS += -lstdc++ 21 | 22 | example: example.o ../RtAudio.o ../rtaudio_c.o 23 | $(CC) $^ $(LDFLAGS) -o $@ 24 | clean: 25 | rm -f example example.exe example.o ../RtAudio.o ../rtaudio_c.o 26 | -------------------------------------------------------------------------------- /rtaudio/_example-c/example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../rtaudio_c.h" 6 | 7 | #define SAMPLE_RATE 44100 8 | #define SINE_HZ 440 9 | #define SINE_DURATION_SEC 3 10 | 11 | struct context { 12 | float phase; 13 | }; 14 | 15 | int cb(void *out, void *in, unsigned int nframes, double stream_time, 16 | rtaudio_stream_status_t status, void *userdata) { 17 | float *buf = (float *)out; 18 | struct context *context = (struct context *)userdata; 19 | for (int i = 0; i < nframes; i++) { 20 | buf[i * 2] = buf[i * 2 + 1] = sinf(context->phase); 21 | context->phase += SINE_HZ * 2 * 3.1415926f / SAMPLE_RATE; 22 | } 23 | return 0; 24 | } 25 | 26 | int main() { 27 | int status = 0; 28 | struct context context = {0}; 29 | rtaudio_t audio = rtaudio_create(RTAUDIO_API_UNSPECIFIED); 30 | if (rtaudio_error(audio) != NULL) { 31 | fprintf(stderr, "error: %s\n", rtaudio_error(audio)); 32 | status = 1; 33 | goto done; 34 | } 35 | 36 | #if 0 37 | /* Print list of device names and native sample rates */ 38 | for (int i = 0; i < rtaudio_device_count(audio); i++) { 39 | rtaudio_device_info_t info = rtaudio_get_device_info(audio, i); 40 | if (rtaudio_error(audio) != NULL) { 41 | fprintf(stderr, "error: %s\n", rtaudio_error(audio)); 42 | status = 1; 43 | goto done; 44 | } 45 | printf("%c%d: %s: %d\n", 46 | (info.is_default_input || info.is_default_output) ? '*' : ' ', i, 47 | info.name, info.preferred_sample_rate); 48 | } 49 | #endif 50 | rtaudio_stream_parameters_t out_params = { 51 | .device_id = rtaudio_get_default_output_device(audio), 52 | .num_channels = 2, 53 | .first_channel = 0, 54 | }; 55 | rtaudio_stream_options_t options = { 56 | .flags = RTAUDIO_FLAGS_ALSA_USE_DEFAULT, 57 | }; 58 | unsigned int bufsz = 2048; 59 | rtaudio_open_stream(audio, &out_params, NULL, RTAUDIO_FORMAT_FLOAT32, 60 | SAMPLE_RATE, &bufsz, cb, &context, &options, NULL); 61 | if (rtaudio_error(audio) != NULL) { 62 | fprintf(stderr, "error: %s\n", rtaudio_error(audio)); 63 | status = 1; 64 | goto done; 65 | } 66 | 67 | rtaudio_start_stream(audio); 68 | if (rtaudio_error(audio) != NULL) { 69 | fprintf(stderr, "error: %s\n", rtaudio_error(audio)); 70 | status = 1; 71 | goto done; 72 | } 73 | 74 | sleep(SINE_DURATION_SEC); 75 | 76 | rtaudio_stop_stream(audio); 77 | 78 | rtaudio_close_stream(audio); 79 | 80 | done: 81 | rtaudio_destroy(audio); 82 | return status; 83 | } 84 | -------------------------------------------------------------------------------- /rtaudio/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math" 6 | "time" 7 | 8 | "github.com/naivesound/audio/rtaudio" 9 | ) 10 | 11 | const ( 12 | SampleRate = 44100.0 13 | Freq = 440.0 14 | ) 15 | 16 | var phase = float64(0) 17 | 18 | func cb(out, in rtaudio.Buffer, dur time.Duration, status rtaudio.StreamStatus) int { 19 | samples := out.Float32() 20 | for i := 0; i < len(samples)/2; i++ { 21 | sample := float32(math.Sin(2 * math.Pi * phase)) 22 | phase += Freq / SampleRate 23 | 24 | samples[i*2] = sample 25 | samples[i*2+1] = sample 26 | } 27 | return 0 28 | } 29 | 30 | func main() { 31 | log.Println(rtaudio.Version()) 32 | log.Println(rtaudio.CompiledAPI()) 33 | 34 | a, err := rtaudio.Create(rtaudio.APIUnspecified) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | defer a.Destroy() 39 | 40 | log.Println(a.CurrentAPI()) 41 | 42 | devices, err := a.Devices() 43 | if err != nil { 44 | log.Fatal(err) 45 | } 46 | 47 | for _, device := range devices { 48 | log.Println(device) 49 | } 50 | 51 | params := rtaudio.StreamParams{ 52 | DeviceID: a.DefaultOutputDevice(), 53 | NumChannels: 2, 54 | FirstChannel: 0, 55 | } 56 | options := rtaudio.StreamOptions{ 57 | Flags: rtaudio.FlagsAlsaUseDefault, 58 | } 59 | err = a.Open(¶ms, nil, rtaudio.FormatFloat32, SampleRate, 2048, cb, &options) 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | defer a.Close() 64 | 65 | a.Start() 66 | defer a.Stop() 67 | 68 | time.Sleep(3 * time.Second) 69 | } 70 | -------------------------------------------------------------------------------- /rtaudio/rtaudio.go: -------------------------------------------------------------------------------- 1 | package rtaudio 2 | 3 | /* 4 | 5 | #cgo CXXFLAGS: -g 6 | #cgo LDFLAGS: -lstdc++ -g 7 | 8 | #cgo linux CXXFLAGS: -D__LINUX_ALSA__ 9 | #cgo linux LDFLAGS: -lm -lasound -pthread 10 | 11 | #cgo windows CXXFLAGS: -D__WINDOWS_WASAPI__ 12 | #cgo windows LDFLAGS: -lm -luuid -lksuser -lwinmm -lole32 13 | 14 | #cgo darwin CXXFLAGS: -D__MACOSX_CORE__ 15 | #cgo darwin LDFLAGS: -framework CoreAudio -framework CoreFoundation 16 | 17 | #include "rtaudio_c.h" 18 | 19 | extern int goCallback(void *out, void *in, unsigned int nFrames, 20 | double stream_time, rtaudio_stream_status_t status, 21 | void *userdata); 22 | 23 | */ 24 | import "C" 25 | import ( 26 | "errors" 27 | "sync" 28 | "time" 29 | "unsafe" 30 | ) 31 | 32 | type API C.rtaudio_api_t 33 | 34 | const ( 35 | APIUnspecified API = C.RTAUDIO_API_UNSPECIFIED 36 | APILinuxALSA = C.RTAUDIO_API_LINUX_ALSA 37 | APILinuxPulse = C.RTAUDIO_API_LINUX_PULSE 38 | APILinuxOSS = C.RTAUDIO_API_LINUX_OSS 39 | APIUnixJack = C.RTAUDIO_API_UNIX_JACK 40 | APIMacOSXCore = C.RTAUDIO_API_MACOSX_CORE 41 | APIWindowsWASAPI = C.RTAUDIO_API_WINDOWS_WASAPI 42 | APIWindowsASIO = C.RTAUDIO_API_WINDOWS_ASIO 43 | APIWindowsDS = C.RTAUDIO_API_WINDOWS_DS 44 | APIDummy = C.RTAUDIO_API_DUMMY 45 | ) 46 | 47 | func (api API) String() string { 48 | switch api { 49 | case APIUnspecified: 50 | return "unspecified" 51 | case APILinuxALSA: 52 | return "alsa" 53 | case APILinuxPulse: 54 | return "pulse" 55 | case APILinuxOSS: 56 | return "oss" 57 | case APIUnixJack: 58 | return "jack" 59 | case APIMacOSXCore: 60 | return "coreaudio" 61 | case APIWindowsWASAPI: 62 | return "wasapi" 63 | case APIWindowsASIO: 64 | return "asio" 65 | case APIWindowsDS: 66 | return "directsound" 67 | case APIDummy: 68 | return "dummy" 69 | } 70 | return "?" 71 | } 72 | 73 | type StreamStatus C.rtaudio_stream_status_t 74 | 75 | const ( 76 | StatusInputOverflow StreamStatus = C.RTAUDIO_STATUS_INPUT_OVERFLOW 77 | StatusOutputUnderflow StreamStatus = C.RTAUDIO_STATUS_OUTPUT_UNDERFLOW 78 | ) 79 | 80 | func Version() string { 81 | return C.GoString(C.rtaudio_version()) 82 | } 83 | 84 | func CompiledAPI() (apis []API) { 85 | capis := (*[1 << 30]C.rtaudio_api_t)(unsafe.Pointer(C.rtaudio_compiled_api())) 86 | for i := 0; ; i++ { 87 | api := capis[i] 88 | if api == C.RTAUDIO_API_UNSPECIFIED { 89 | break 90 | } 91 | apis = append(apis, API(api)) 92 | } 93 | return apis 94 | } 95 | 96 | type DeviceInfo struct { 97 | Name string 98 | Probed bool 99 | NumOutputChannels int 100 | NumInputChannels int 101 | NumDuplexChannels int 102 | IsDefaultOutput bool 103 | IsDefaultInput bool 104 | 105 | //rtaudio_format_t native_formats; 106 | 107 | PreferredSampleRate uint 108 | SampleRates []int 109 | } 110 | 111 | type StreamParams struct { 112 | DeviceID uint 113 | NumChannels uint 114 | FirstChannel uint 115 | } 116 | 117 | type StreamFlags C.rtaudio_stream_flags_t 118 | 119 | const ( 120 | FlagsNoninterleages = C.RTAUDIO_FLAGS_NONINTERLEAVED 121 | FlagsMinimizeLatency = C.RTAUDIO_FLAGS_MINIMIZE_LATENCY 122 | FlagsHogDevice = C.RTAUDIO_FLAGS_HOG_DEVICE 123 | FlagsScheduleRealtime = C.RTAUDIO_FLAGS_SCHEDULE_REALTIME 124 | FlagsAlsaUseDefault = C.RTAUDIO_FLAGS_ALSA_USE_DEFAULT 125 | ) 126 | 127 | type StreamOptions struct { 128 | Flags StreamFlags 129 | NumBuffers uint 130 | Priotity int 131 | Name string 132 | } 133 | 134 | type RtAudio interface { 135 | Destroy() 136 | CurrentAPI() API 137 | Devices() ([]DeviceInfo, error) 138 | DefaultOutputDevice() int 139 | DefaultInputDevice() int 140 | 141 | Open(out, in *StreamParams, format Format, sampleRate uint, frames uint, cb Callback, opts *StreamOptions) error 142 | Close() 143 | Start() error 144 | Stop() error 145 | Abort() error 146 | 147 | IsOpen() bool 148 | IsRunning() bool 149 | 150 | Latency() (int, error) 151 | SampleRate() (uint, error) 152 | Time() (time.Duration, error) 153 | SetTime(time.Duration) error 154 | 155 | ShowWarnings(bool) 156 | } 157 | 158 | type rtaudio struct { 159 | audio C.rtaudio_t 160 | cb Callback 161 | inputChannels int 162 | outputChannels int 163 | format Format 164 | } 165 | 166 | var _ RtAudio = &rtaudio{} 167 | 168 | func Create(api API) (*rtaudio, error) { 169 | audio := C.rtaudio_create(C.rtaudio_api_t(api)) 170 | if C.rtaudio_error(audio) != nil { 171 | return nil, errors.New(C.GoString(C.rtaudio_error(audio))) 172 | } 173 | return &rtaudio{audio: audio}, nil 174 | } 175 | 176 | func (audio *rtaudio) Destroy() { 177 | C.rtaudio_destroy(audio.audio) 178 | } 179 | 180 | func (audio *rtaudio) CurrentAPI() API { 181 | return API(C.rtaudio_current_api(audio.audio)) 182 | } 183 | 184 | func (audio *rtaudio) DefaultInputDevice() int { 185 | return int(C.rtaudio_get_default_input_device(audio.audio)) 186 | } 187 | 188 | func (audio *rtaudio) DefaultOutputDevice() int { 189 | return int(C.rtaudio_get_default_output_device(audio.audio)) 190 | } 191 | 192 | func (audio *rtaudio) Devices() ([]DeviceInfo, error) { 193 | n := C.rtaudio_device_count(audio.audio) 194 | devices := []DeviceInfo{} 195 | for i := C.int(0); i < n; i++ { 196 | cinfo := C.rtaudio_get_device_info(audio.audio, i) 197 | if C.rtaudio_error(audio.audio) != nil { 198 | return nil, errors.New(C.GoString(C.rtaudio_error(audio.audio))) 199 | } 200 | sr := []int{} 201 | for _, r := range cinfo.sample_rates { 202 | if r == 0 { 203 | break 204 | } 205 | sr = append(sr, int(r)) 206 | } 207 | devices = append(devices, DeviceInfo{ 208 | Name: C.GoString(&cinfo.name[0]), 209 | Probed: cinfo.probed != 0, 210 | NumInputChannels: int(cinfo.input_channels), 211 | NumOutputChannels: int(cinfo.output_channels), 212 | NumDuplexChannels: int(cinfo.duplex_channels), 213 | IsDefaultOutput: cinfo.is_default_output != 0, 214 | IsDefaultInput: cinfo.is_default_input != 0, 215 | PreferredSampleRate: uint(cinfo.preferred_sample_rate), 216 | SampleRates: sr, 217 | }) 218 | // TODO: formats 219 | } 220 | return devices, nil 221 | } 222 | 223 | type Format int 224 | 225 | const ( 226 | FormatInt8 Format = C.RTAUDIO_FORMAT_SINT8 227 | FormatInt16 = C.RTAUDIO_FORMAT_SINT16 228 | FormatInt24 = C.RTAUDIO_FORMAT_SINT24 229 | FormatInt32 = C.RTAUDIO_FORMAT_SINT32 230 | FormatFloat32 = C.RTAUDIO_FORMAT_FLOAT32 231 | FormatFloat64 = C.RTAUDIO_FORMAT_FLOAT64 232 | ) 233 | 234 | type Buffer interface { 235 | Len() int 236 | Int8() []int8 237 | Int16() []int16 238 | Int24() []Int24 239 | Int32() []int32 240 | Float32() []float32 241 | Float64() []float64 242 | } 243 | 244 | type Int24 [3]byte 245 | 246 | func (i *Int24) Set(n int32) { 247 | (*i)[0], (*i)[1], (*i)[2] = byte(n&0xff), byte((n&0xff00)>>8), byte((n&0xff0000)>>16) 248 | } 249 | 250 | func (i Int24) Get() int32 { 251 | n := int32(i[0]) | int32(i[1])<<8 | int32(i[2])<<16 252 | if n&0x800000 != 0 { 253 | n |= ^0xffffff 254 | } 255 | return n 256 | } 257 | 258 | type buffer struct { 259 | format Format 260 | length int 261 | numChannels int 262 | ptr unsafe.Pointer 263 | } 264 | 265 | func (b *buffer) Len() int { 266 | if b.ptr == nil { 267 | return 0 268 | } 269 | return b.length 270 | } 271 | 272 | func (b *buffer) Int8() []int8 { 273 | if b.format != FormatInt8 { 274 | return nil 275 | } 276 | if b.ptr == nil { 277 | return nil 278 | } 279 | return (*[1 << 30]int8)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels] 280 | } 281 | 282 | func (b *buffer) Int16() []int16 { 283 | if b.format != FormatInt16 { 284 | return nil 285 | } 286 | if b.ptr == nil { 287 | return nil 288 | } 289 | return (*[1 << 30]int16)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels] 290 | } 291 | 292 | func (b *buffer) Int24() []Int24 { 293 | if b.format != FormatInt24 { 294 | return nil 295 | } 296 | if b.ptr == nil { 297 | return nil 298 | } 299 | return (*[1 << 30]Int24)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels] 300 | } 301 | 302 | func (b *buffer) Int32() []int32 { 303 | if b.format != FormatInt32 { 304 | return nil 305 | } 306 | if b.ptr == nil { 307 | return nil 308 | } 309 | return (*[1 << 30]int32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels] 310 | } 311 | 312 | func (b *buffer) Float32() []float32 { 313 | if b.format != FormatFloat32 { 314 | return nil 315 | } 316 | if b.ptr == nil { 317 | return nil 318 | } 319 | return (*[1 << 30]float32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels] 320 | } 321 | 322 | func (b *buffer) Float64() []float64 { 323 | if b.format != FormatFloat64 { 324 | return nil 325 | } 326 | if b.ptr == nil { 327 | return nil 328 | } 329 | return (*[1 << 30]float64)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels] 330 | } 331 | 332 | type Callback func(out Buffer, in Buffer, dur time.Duration, status StreamStatus) int 333 | 334 | var ( 335 | mu sync.Mutex 336 | audios = map[int]*rtaudio{} 337 | ) 338 | 339 | func registerAudio(a *rtaudio) int { 340 | mu.Lock() 341 | defer mu.Unlock() 342 | for i := 0; ; i++ { 343 | if _, ok := audios[i]; !ok { 344 | audios[i] = a 345 | return i 346 | } 347 | } 348 | } 349 | 350 | func unregisterAudio(a *rtaudio) { 351 | mu.Lock() 352 | defer mu.Unlock() 353 | for i := 0; i < len(audios); i++ { 354 | if audios[i] == a { 355 | delete(audios, i) 356 | return 357 | } 358 | } 359 | } 360 | 361 | func findAudio(k int) *rtaudio { 362 | mu.Lock() 363 | defer mu.Unlock() 364 | return audios[k] 365 | } 366 | 367 | //export goCallback 368 | func goCallback(out, in unsafe.Pointer, frames C.uint, sec C.double, 369 | status C.rtaudio_stream_status_t, userdata unsafe.Pointer) C.int { 370 | 371 | k := int(uintptr(userdata)) 372 | audio := findAudio(k) 373 | dur := time.Duration(time.Microsecond * time.Duration(sec*1000000.0)) 374 | inbuf := &buffer{audio.format, int(frames), audio.inputChannels, in} 375 | outbuf := &buffer{audio.format, int(frames), audio.outputChannels, out} 376 | return C.int(audio.cb(outbuf, inbuf, dur, StreamStatus(status))) 377 | } 378 | 379 | func (audio *rtaudio) Open(out, in *StreamParams, format Format, sampleRate uint, 380 | frames uint, cb Callback, opts *StreamOptions) error { 381 | var ( 382 | c_in_ptr *C.rtaudio_stream_parameters_t 383 | c_out_ptr *C.rtaudio_stream_parameters_t 384 | c_opts_ptr *C.rtaudio_stream_options_t 385 | c_in C.rtaudio_stream_parameters_t 386 | c_out C.rtaudio_stream_parameters_t 387 | c_opts C.rtaudio_stream_options_t 388 | ) 389 | 390 | audio.inputChannels = 0 391 | audio.outputChannels = 0 392 | if out != nil { 393 | audio.outputChannels = int(out.NumChannels) 394 | c_out.device_id = C.uint(out.DeviceID) 395 | c_out.num_channels = C.uint(out.NumChannels) 396 | c_out.first_channel = C.uint(out.FirstChannel) 397 | c_out_ptr = &c_out 398 | } 399 | if in != nil { 400 | audio.inputChannels = int(in.NumChannels) 401 | c_in.device_id = C.uint(in.DeviceID) 402 | c_in.num_channels = C.uint(in.NumChannels) 403 | c_in.first_channel = C.uint(in.FirstChannel) 404 | c_in_ptr = &c_in 405 | } 406 | if opts != nil { 407 | c_opts.flags = C.rtaudio_stream_flags_t(opts.Flags) 408 | c_opts.num_buffers = C.uint(opts.NumBuffers) 409 | c_opts.priority = C.int(opts.Priotity) 410 | c_opts_ptr = &c_opts 411 | } 412 | frames_count := C.uint(frames) 413 | audio.format = format 414 | audio.cb = cb 415 | 416 | k := registerAudio(audio) 417 | C.rtaudio_open_stream(audio.audio, c_out_ptr, c_in_ptr, 418 | C.rtaudio_format_t(format), C.uint(sampleRate), &frames_count, 419 | C.rtaudio_cb_t(C.goCallback), unsafe.Pointer(uintptr(k)), c_opts_ptr, nil) 420 | if C.rtaudio_error(audio.audio) != nil { 421 | return errors.New(C.GoString(C.rtaudio_error(audio.audio))) 422 | } 423 | return nil 424 | } 425 | 426 | func (audio *rtaudio) Close() { 427 | unregisterAudio(audio) 428 | C.rtaudio_close_stream(audio.audio) 429 | } 430 | 431 | func (audio *rtaudio) Start() error { 432 | C.rtaudio_start_stream(audio.audio) 433 | if C.rtaudio_error(audio.audio) != nil { 434 | return errors.New(C.GoString(C.rtaudio_error(audio.audio))) 435 | } 436 | return nil 437 | } 438 | 439 | func (audio *rtaudio) Stop() error { 440 | C.rtaudio_stop_stream(audio.audio) 441 | if C.rtaudio_error(audio.audio) != nil { 442 | return errors.New(C.GoString(C.rtaudio_error(audio.audio))) 443 | } 444 | return nil 445 | } 446 | 447 | func (audio *rtaudio) Abort() error { 448 | C.rtaudio_abort_stream(audio.audio) 449 | if C.rtaudio_error(audio.audio) != nil { 450 | return errors.New(C.GoString(C.rtaudio_error(audio.audio))) 451 | } 452 | return nil 453 | } 454 | 455 | func (audio *rtaudio) IsOpen() bool { 456 | return C.rtaudio_is_stream_open(audio.audio) != 0 457 | } 458 | 459 | func (audio *rtaudio) IsRunning() bool { 460 | return C.rtaudio_is_stream_running(audio.audio) != 0 461 | } 462 | 463 | func (audio *rtaudio) Latency() (int, error) { 464 | latency := C.rtaudio_get_stream_latency(audio.audio) 465 | if C.rtaudio_error(audio.audio) != nil { 466 | return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio))) 467 | } 468 | return int(latency), nil 469 | } 470 | 471 | func (audio *rtaudio) SampleRate() (uint, error) { 472 | sampleRate := C.rtaudio_get_stream_sample_rate(audio.audio) 473 | if C.rtaudio_error(audio.audio) != nil { 474 | return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio))) 475 | } 476 | return uint(sampleRate), nil 477 | } 478 | 479 | func (audio *rtaudio) Time() (time.Duration, error) { 480 | sec := C.rtaudio_get_stream_time(audio.audio) 481 | if C.rtaudio_error(audio.audio) != nil { 482 | return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio))) 483 | } 484 | return time.Duration(time.Microsecond * time.Duration(sec*1000000.0)), nil 485 | } 486 | 487 | func (audio *rtaudio) SetTime(t time.Duration) error { 488 | sec := float64(t) * 1000000.0 / float64(time.Microsecond) 489 | C.rtaudio_set_stream_time(audio.audio, C.double(sec)) 490 | if C.rtaudio_error(audio.audio) != nil { 491 | return errors.New(C.GoString(C.rtaudio_error(audio.audio))) 492 | } 493 | return nil 494 | } 495 | 496 | func (audio *rtaudio) ShowWarnings(show bool) { 497 | if show { 498 | C.rtaudio_show_warnings(audio.audio, 1) 499 | } else { 500 | C.rtaudio_show_warnings(audio.audio, 0) 501 | } 502 | } 503 | -------------------------------------------------------------------------------- /rtaudio/rtaudio_c.cpp: -------------------------------------------------------------------------------- 1 | #include "rtaudio_c.h" 2 | #include "RtAudio.h" 3 | 4 | #include 5 | 6 | #define MAX_ERROR_MESSAGE_LENGTH 512 7 | 8 | struct rtaudio { 9 | RtAudio *audio; 10 | 11 | rtaudio_cb_t cb; 12 | void *userdata; 13 | 14 | int has_error; 15 | char errmsg[MAX_ERROR_MESSAGE_LENGTH]; 16 | }; 17 | 18 | static const rtaudio_api_t compiled_api[] = { 19 | #if defined(__UNIX_JACK__) 20 | RTAUDIO_API_UNIX_JACK, 21 | #endif 22 | #if defined(__LINUX_ALSA__) 23 | RTAUDIO_API_LINUX_ALSA, 24 | #endif 25 | #if defined(__LINUX_PULSE__) 26 | RTAUDIO_API_LINUX_PULSE, 27 | #endif 28 | #if defined(__LINUX_OSS__) 29 | RTAUDIO_API_LINUX_OSS, 30 | #endif 31 | #if defined(__WINDOWS_ASIO__) 32 | RTAUDIO_API_WINDOWS_ASIO, 33 | #endif 34 | #if defined(__WINDOWS_WASAPI__) 35 | RTAUDIO_API_WINDOWS_WASAPI, 36 | #endif 37 | #if defined(__WINDOWS_DS__) 38 | RTAUDIO_API_WINDOWS_DS, 39 | #endif 40 | #if defined(__MACOSX_CORE__) 41 | RTAUDIO_API_MACOSX_CORE, 42 | #endif 43 | #if defined(__RTAUDIO_DUMMY__) 44 | RTAUDIO_API_DUMMY, 45 | #endif 46 | RTAUDIO_API_UNSPECIFIED, 47 | }; 48 | 49 | const char *rtaudio_version() { return RTAUDIO_VERSION; } 50 | 51 | const rtaudio_api_t *rtaudio_compiled_api() { return compiled_api; } 52 | 53 | const char *rtaudio_error(rtaudio_t audio) { 54 | if (audio->has_error) { 55 | return audio->errmsg; 56 | } 57 | return NULL; 58 | } 59 | 60 | rtaudio_t rtaudio_create(rtaudio_api_t api) { 61 | rtaudio_t audio = new struct rtaudio(); 62 | try { 63 | audio->audio = new RtAudio((RtAudio::Api)api); 64 | } catch (RtAudioError &err) { 65 | audio->has_error = 1; 66 | strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1); 67 | } 68 | return audio; 69 | } 70 | 71 | void rtaudio_destroy(rtaudio_t audio) { delete audio->audio; } 72 | 73 | rtaudio_api_t rtaudio_current_api(rtaudio_t audio) { 74 | return (rtaudio_api_t)audio->audio->getCurrentApi(); 75 | } 76 | 77 | int rtaudio_device_count(rtaudio_t audio) { 78 | return audio->audio->getDeviceCount(); 79 | } 80 | 81 | rtaudio_device_info_t rtaudio_get_device_info(rtaudio_t audio, int i) { 82 | rtaudio_device_info_t result = {}; 83 | try { 84 | audio->has_error = 0; 85 | RtAudio::DeviceInfo info = audio->audio->getDeviceInfo(i); 86 | result.probed = info.probed; 87 | result.output_channels = info.outputChannels; 88 | result.input_channels = info.inputChannels; 89 | result.duplex_channels = info.duplexChannels; 90 | result.is_default_output = info.isDefaultOutput; 91 | result.is_default_input = info.isDefaultInput; 92 | result.native_formats = info.nativeFormats; 93 | result.preferred_sample_rate = info.preferredSampleRate; 94 | strncpy(result.name, info.name.c_str(), sizeof(result.name) - 1); 95 | for (unsigned int j = 0; j < info.sampleRates.size(); j++) { 96 | if (j < sizeof(result.sample_rates) / sizeof(result.sample_rates[0])) { 97 | result.sample_rates[j] = info.sampleRates[j]; 98 | } 99 | } 100 | } catch (RtAudioError &err) { 101 | audio->has_error = 1; 102 | strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1); 103 | } 104 | return result; 105 | } 106 | 107 | unsigned int rtaudio_get_default_output_device(rtaudio_t audio) { 108 | return audio->audio->getDefaultOutputDevice(); 109 | } 110 | 111 | unsigned int rtaudio_get_default_input_device(rtaudio_t audio) { 112 | return audio->audio->getDefaultInputDevice(); 113 | } 114 | 115 | static int proxy_cb_func(void *out, void *in, unsigned int nframes, double time, 116 | RtAudioStreamStatus status, void *userdata) { 117 | rtaudio_t audio = (rtaudio_t)userdata; 118 | return audio->cb(out, in, nframes, time, (rtaudio_stream_status_t)status, 119 | audio->userdata); 120 | } 121 | 122 | int rtaudio_open_stream(rtaudio_t audio, 123 | rtaudio_stream_parameters_t *output_params, 124 | rtaudio_stream_parameters_t *input_params, 125 | rtaudio_format_t format, unsigned int sample_rate, 126 | unsigned int *buffer_frames, rtaudio_cb_t cb, 127 | void *userdata, rtaudio_stream_options_t *options, 128 | rtaudio_error_cb_t errcb) { 129 | try { 130 | audio->has_error = 0; 131 | RtAudio::StreamParameters *in = NULL; 132 | RtAudio::StreamParameters *out = NULL; 133 | RtAudio::StreamOptions *opts = NULL; 134 | 135 | RtAudio::StreamParameters inparams; 136 | RtAudio::StreamParameters outparams; 137 | RtAudio::StreamOptions stream_opts; 138 | 139 | if (input_params != NULL) { 140 | inparams.deviceId = input_params->device_id; 141 | inparams.nChannels = input_params->num_channels; 142 | inparams.firstChannel = input_params->first_channel; 143 | in = &inparams; 144 | } 145 | if (output_params != NULL) { 146 | outparams.deviceId = output_params->device_id; 147 | outparams.nChannels = output_params->num_channels; 148 | outparams.firstChannel = output_params->first_channel; 149 | out = &outparams; 150 | } 151 | 152 | if (options != NULL) { 153 | stream_opts.flags = (RtAudioStreamFlags)options->flags; 154 | stream_opts.numberOfBuffers = options->num_buffers; 155 | stream_opts.priority = options->priority; 156 | if (strlen(options->name) > 0) { 157 | stream_opts.streamName = std::string(options->name); 158 | } 159 | opts = &stream_opts; 160 | } 161 | audio->cb = cb; 162 | audio->userdata = userdata; 163 | audio->audio->openStream(out, in, (RtAudioFormat)format, sample_rate, 164 | buffer_frames, proxy_cb_func, (void *)audio, opts, 165 | NULL); 166 | return 0; 167 | } catch (RtAudioError &err) { 168 | audio->has_error = 1; 169 | strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1); 170 | return -1; 171 | } 172 | } 173 | 174 | void rtaudio_close_stream(rtaudio_t audio) { audio->audio->closeStream(); } 175 | 176 | int rtaudio_start_stream(rtaudio_t audio) { 177 | try { 178 | audio->has_error = 0; 179 | audio->audio->startStream(); 180 | } catch (RtAudioError &err) { 181 | audio->has_error = 1; 182 | strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1); 183 | } 184 | return 0; 185 | } 186 | 187 | int rtaudio_stop_stream(rtaudio_t audio) { 188 | try { 189 | audio->has_error = 0; 190 | audio->audio->stopStream(); 191 | } catch (RtAudioError &err) { 192 | audio->has_error = 1; 193 | strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1); 194 | } 195 | return 0; 196 | } 197 | 198 | int rtaudio_abort_stream(rtaudio_t audio) { 199 | try { 200 | audio->has_error = 0; 201 | audio->audio->abortStream(); 202 | } catch (RtAudioError &err) { 203 | audio->has_error = 1; 204 | strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1); 205 | } 206 | return 0; 207 | } 208 | 209 | int rtaudio_is_stream_open(rtaudio_t audio) { 210 | return !!audio->audio->isStreamOpen(); 211 | } 212 | 213 | int rtaudio_is_stream_running(rtaudio_t audio) { 214 | return !!audio->audio->isStreamRunning(); 215 | } 216 | 217 | double rtaudio_get_stream_time(rtaudio_t audio) { 218 | try { 219 | audio->has_error = 0; 220 | return audio->audio->getStreamTime(); 221 | } catch (RtAudioError &err) { 222 | audio->has_error = 1; 223 | strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1); 224 | return 0; 225 | } 226 | } 227 | 228 | void rtaudio_set_stream_time(rtaudio_t audio, double time) { 229 | try { 230 | audio->has_error = 0; 231 | audio->audio->setStreamTime(time); 232 | } catch (RtAudioError &err) { 233 | audio->has_error = 1; 234 | strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1); 235 | } 236 | } 237 | 238 | int rtaudio_get_stream_latency(rtaudio_t audio) { 239 | try { 240 | audio->has_error = 0; 241 | return audio->audio->getStreamLatency(); 242 | } catch (RtAudioError &err) { 243 | audio->has_error = 1; 244 | strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1); 245 | return -1; 246 | } 247 | } 248 | 249 | unsigned int rtaudio_get_stream_sample_rate(rtaudio_t audio) { 250 | try { 251 | return audio->audio->getStreamSampleRate(); 252 | } catch (RtAudioError &err) { 253 | audio->has_error = 1; 254 | strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1); 255 | return -1; 256 | } 257 | } 258 | 259 | void rtaudio_show_warnings(rtaudio_t audio, int show) { 260 | audio->audio->showWarnings(!!show); 261 | } 262 | -------------------------------------------------------------------------------- /rtaudio/rtaudio_c.h: -------------------------------------------------------------------------------- 1 | #ifndef RTAUDIO_C_H 2 | #define RTAUDIO_C_H 3 | 4 | #if defined(RTAUDIO_EXPORT) 5 | #define RTAUDIOAPI __declspec(dllexport) 6 | #else 7 | #define RTAUDIOAPI //__declspec(dllimport) 8 | #endif 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | typedef unsigned long rtaudio_format_t; 15 | 16 | #define RTAUDIO_FORMAT_SINT8 0x01 17 | #define RTAUDIO_FORMAT_SINT16 0x02 18 | #define RTAUDIO_FORMAT_SINT24 0x04 19 | #define RTAUDIO_FORMAT_SINT32 0x08 20 | #define RTAUDIO_FORMAT_FLOAT32 0x10 21 | #define RTAUDIO_FORMAT_FLOAT64 0x20 22 | 23 | typedef unsigned int rtaudio_stream_flags_t; 24 | 25 | #define RTAUDIO_FLAGS_NONINTERLEAVED 0x1 26 | #define RTAUDIO_FLAGS_MINIMIZE_LATENCY 0x2 27 | #define RTAUDIO_FLAGS_HOG_DEVICE 0x4 28 | #define RTAUDIO_FLAGS_SCHEDULE_REALTIME 0x8 29 | #define RTAUDIO_FLAGS_ALSA_USE_DEFAULT 0x10 30 | 31 | typedef unsigned int rtaudio_stream_status_t; 32 | 33 | #define RTAUDIO_STATUS_INPUT_OVERFLOW 0x1 34 | #define RTAUDIO_STATUS_OUTPUT_UNDERFLOW 0x2 35 | 36 | typedef int (*rtaudio_cb_t)(void *out, void *in, unsigned int nFrames, 37 | double stream_time, rtaudio_stream_status_t status, 38 | void *userdata); 39 | 40 | typedef enum rtaudio_error { 41 | RTAUDIO_ERROR_WARNING, 42 | RTAUDIO_ERROR_DEBUG_WARNING, 43 | RTAUDIO_ERROR_UNSPECIFIED, 44 | RTAUDIO_ERROR_NO_DEVICES_FOUND, 45 | RTAUDIO_ERROR_INVALID_DEVICE, 46 | RTAUDIO_ERROR_MEMORY_ERROR, 47 | RTAUDIO_ERROR_INVALID_PARAMETER, 48 | RTAUDIO_ERROR_INVALID_USE, 49 | RTAUDIO_ERROR_DRIVER_ERROR, 50 | RTAUDIO_ERROR_SYSTEM_ERROR, 51 | RTAUDIO_ERROR_THREAD_ERROR, 52 | } rtaudio_error_t; 53 | 54 | typedef void (*rtaudio_error_cb_t)(rtaudio_error_t err, const char *msg); 55 | 56 | typedef enum rtaudio_api { 57 | RTAUDIO_API_UNSPECIFIED, 58 | RTAUDIO_API_LINUX_ALSA, 59 | RTAUDIO_API_LINUX_PULSE, 60 | RTAUDIO_API_LINUX_OSS, 61 | RTAUDIO_API_UNIX_JACK, 62 | RTAUDIO_API_MACOSX_CORE, 63 | RTAUDIO_API_WINDOWS_WASAPI, 64 | RTAUDIO_API_WINDOWS_ASIO, 65 | RTAUDIO_API_WINDOWS_DS, 66 | RTAUDIO_API_DUMMY, 67 | } rtaudio_api_t; 68 | 69 | #define NUM_SAMPLE_RATES 16 70 | #define MAX_NAME_LENGTH 512 71 | typedef struct rtaudio_device_info { 72 | int probed; 73 | unsigned int output_channels; 74 | unsigned int input_channels; 75 | unsigned int duplex_channels; 76 | 77 | int is_default_output; 78 | int is_default_input; 79 | 80 | rtaudio_format_t native_formats; 81 | 82 | unsigned int preferred_sample_rate; 83 | int sample_rates[NUM_SAMPLE_RATES]; 84 | 85 | char name[MAX_NAME_LENGTH]; 86 | } rtaudio_device_info_t; 87 | 88 | typedef struct rtaudio_stream_parameters { 89 | unsigned int device_id; 90 | unsigned int num_channels; 91 | unsigned int first_channel; 92 | } rtaudio_stream_parameters_t; 93 | 94 | typedef struct rtaudio_stream_options { 95 | rtaudio_stream_flags_t flags; 96 | unsigned int num_buffers; 97 | int priority; 98 | char name[MAX_NAME_LENGTH]; 99 | } rtaudio_stream_options_t; 100 | 101 | typedef struct rtaudio *rtaudio_t; 102 | 103 | RTAUDIOAPI const char *rtaudio_version(); 104 | RTAUDIOAPI const rtaudio_api_t *rtaudio_compiled_api(); 105 | 106 | RTAUDIOAPI const char *rtaudio_error(rtaudio_t audio); 107 | 108 | RTAUDIOAPI rtaudio_t rtaudio_create(rtaudio_api_t api); 109 | RTAUDIOAPI void rtaudio_destroy(rtaudio_t audio); 110 | 111 | RTAUDIOAPI rtaudio_api_t rtaudio_current_api(rtaudio_t audio); 112 | 113 | RTAUDIOAPI int rtaudio_device_count(rtaudio_t audio); 114 | RTAUDIOAPI rtaudio_device_info_t rtaudio_get_device_info(rtaudio_t audio, 115 | int i); 116 | RTAUDIOAPI unsigned int rtaudio_get_default_output_device(rtaudio_t audio); 117 | RTAUDIOAPI unsigned int rtaudio_get_default_input_device(rtaudio_t audio); 118 | 119 | RTAUDIOAPI int 120 | rtaudio_open_stream(rtaudio_t audio, rtaudio_stream_parameters_t *output_params, 121 | rtaudio_stream_parameters_t *input_params, 122 | rtaudio_format_t format, unsigned int sample_rate, 123 | unsigned int *buffer_frames, rtaudio_cb_t cb, 124 | void *userdata, rtaudio_stream_options_t *options, 125 | rtaudio_error_cb_t errcb); 126 | RTAUDIOAPI void rtaudio_close_stream(rtaudio_t audio); 127 | RTAUDIOAPI int rtaudio_start_stream(rtaudio_t audio); 128 | RTAUDIOAPI int rtaudio_stop_stream(rtaudio_t audio); 129 | RTAUDIOAPI int rtaudio_abort_stream(rtaudio_t audio); 130 | 131 | RTAUDIOAPI int rtaudio_is_stream_open(rtaudio_t audio); 132 | RTAUDIOAPI int rtaudio_is_stream_running(rtaudio_t audio); 133 | 134 | RTAUDIOAPI double rtaudio_get_stream_time(rtaudio_t audio); 135 | RTAUDIOAPI void rtaudio_set_stream_time(rtaudio_t audio, double time); 136 | RTAUDIOAPI int rtaudio_get_stream_latency(rtaudio_t audio); 137 | RTAUDIOAPI unsigned int rtaudio_get_stream_sample_rate(rtaudio_t audio); 138 | 139 | RTAUDIOAPI void rtaudio_show_warnings(rtaudio_t audio, int show); 140 | 141 | #ifdef __cplusplus 142 | } 143 | #endif 144 | #endif /* RTAUDIO_C_H */ 145 | -------------------------------------------------------------------------------- /rtmidi/RtMidi.h: -------------------------------------------------------------------------------- 1 | /**********************************************************************/ 2 | /*! \class RtMidi 3 | \brief An abstract base class for realtime MIDI input/output. 4 | 5 | This class implements some common functionality for the realtime 6 | MIDI input/output subclasses RtMidiIn and RtMidiOut. 7 | 8 | RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ 9 | 10 | RtMidi: realtime MIDI i/o C++ classes 11 | Copyright (c) 2003-2017 Gary P. Scavone 12 | 13 | Permission is hereby granted, free of charge, to any person 14 | obtaining a copy of this software and associated documentation files 15 | (the "Software"), to deal in the Software without restriction, 16 | including without limitation the rights to use, copy, modify, merge, 17 | publish, distribute, sublicense, and/or sell copies of the Software, 18 | and to permit persons to whom the Software is furnished to do so, 19 | subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be 22 | included in all copies or substantial portions of the Software. 23 | 24 | Any person wishing to distribute modifications to the Software is 25 | asked to send the modifications to the original developer so that 26 | they can be incorporated into the canonical version. This is, 27 | however, not a binding provision of this license. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 30 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 31 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 32 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 33 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 34 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 35 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | */ 37 | /**********************************************************************/ 38 | 39 | /*! 40 | \file RtMidi.h 41 | */ 42 | 43 | #ifndef RTMIDI_H 44 | #define RTMIDI_H 45 | 46 | #define RTMIDI_VERSION "3.0.0" 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | /************************************************************************/ 54 | /*! \class RtMidiError 55 | \brief Exception handling class for RtMidi. 56 | 57 | The RtMidiError class is quite simple but it does allow errors to be 58 | "caught" by RtMidiError::Type. See the RtMidi documentation to know 59 | which methods can throw an RtMidiError. 60 | */ 61 | /************************************************************************/ 62 | 63 | class RtMidiError : public std::exception 64 | { 65 | public: 66 | //! Defined RtMidiError types. 67 | enum Type { 68 | WARNING, /*!< A non-critical error. */ 69 | DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ 70 | UNSPECIFIED, /*!< The default, unspecified error type. */ 71 | NO_DEVICES_FOUND, /*!< No devices found on system. */ 72 | INVALID_DEVICE, /*!< An invalid device ID was specified. */ 73 | MEMORY_ERROR, /*!< An error occured during memory allocation. */ 74 | INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ 75 | INVALID_USE, /*!< The function was called incorrectly. */ 76 | DRIVER_ERROR, /*!< A system driver error occured. */ 77 | SYSTEM_ERROR, /*!< A system error occured. */ 78 | THREAD_ERROR /*!< A thread error occured. */ 79 | }; 80 | 81 | //! The constructor. 82 | RtMidiError( const std::string& message, Type type = RtMidiError::UNSPECIFIED ) throw() : message_(message), type_(type) {} 83 | 84 | //! The destructor. 85 | virtual ~RtMidiError( void ) throw() {} 86 | 87 | //! Prints thrown error message to stderr. 88 | virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; } 89 | 90 | //! Returns the thrown error message type. 91 | virtual const Type& getType(void) const throw() { return type_; } 92 | 93 | //! Returns the thrown error message string. 94 | virtual const std::string& getMessage(void) const throw() { return message_; } 95 | 96 | //! Returns the thrown error message as a c-style string. 97 | virtual const char* what( void ) const throw() { return message_.c_str(); } 98 | 99 | protected: 100 | std::string message_; 101 | Type type_; 102 | }; 103 | 104 | //! RtMidi error callback function prototype. 105 | /*! 106 | \param type Type of error. 107 | \param errorText Error description. 108 | 109 | Note that class behaviour is undefined after a critical error (not 110 | a warning) is reported. 111 | */ 112 | typedef void (*RtMidiErrorCallback)( RtMidiError::Type type, const std::string &errorText, void *userData ); 113 | 114 | class MidiApi; 115 | 116 | class RtMidi 117 | { 118 | public: 119 | 120 | //! MIDI API specifier arguments. 121 | enum Api { 122 | UNSPECIFIED, /*!< Search for a working compiled API. */ 123 | MACOSX_CORE, /*!< Macintosh OS-X Core Midi API. */ 124 | LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ 125 | UNIX_JACK, /*!< The JACK Low-Latency MIDI Server API. */ 126 | WINDOWS_MM, /*!< The Microsoft Multimedia MIDI API. */ 127 | RTMIDI_DUMMY /*!< A compilable but non-functional API. */ 128 | }; 129 | 130 | //! A static function to determine the current RtMidi version. 131 | static std::string getVersion( void ) throw(); 132 | 133 | //! A static function to determine the available compiled MIDI APIs. 134 | /*! 135 | The values returned in the std::vector can be compared against 136 | the enumerated list values. Note that there can be more than one 137 | API compiled for certain operating systems. 138 | */ 139 | static void getCompiledApi( std::vector &apis ) throw(); 140 | 141 | //! Pure virtual openPort() function. 142 | virtual void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi" ) ) = 0; 143 | 144 | //! Pure virtual openVirtualPort() function. 145 | virtual void openVirtualPort( const std::string &portName = std::string( "RtMidi" ) ) = 0; 146 | 147 | //! Pure virtual getPortCount() function. 148 | virtual unsigned int getPortCount() = 0; 149 | 150 | //! Pure virtual getPortName() function. 151 | virtual std::string getPortName( unsigned int portNumber = 0 ) = 0; 152 | 153 | //! Pure virtual closePort() function. 154 | virtual void closePort( void ) = 0; 155 | 156 | //! Returns true if a port is open and false if not. 157 | /*! 158 | Note that this only applies to connections made with the openPort() 159 | function, not to virtual ports. 160 | */ 161 | virtual bool isPortOpen( void ) const = 0; 162 | 163 | //! Set an error callback function to be invoked when an error has occured. 164 | /*! 165 | The callback function will be called whenever an error has occured. It is best 166 | to set the error callback function before opening a port. 167 | */ 168 | virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ) = 0; 169 | 170 | protected: 171 | 172 | RtMidi(); 173 | virtual ~RtMidi(); 174 | 175 | MidiApi *rtapi_; 176 | }; 177 | 178 | /**********************************************************************/ 179 | /*! \class RtMidiIn 180 | \brief A realtime MIDI input class. 181 | 182 | This class provides a common, platform-independent API for 183 | realtime MIDI input. It allows access to a single MIDI input 184 | port. Incoming MIDI messages are either saved to a queue for 185 | retrieval using the getMessage() function or immediately passed to 186 | a user-specified callback function. Create multiple instances of 187 | this class to connect to more than one MIDI device at the same 188 | time. With the OS-X, Linux ALSA, and JACK MIDI APIs, it is also 189 | possible to open a virtual input port to which other MIDI software 190 | clients can connect. 191 | 192 | by Gary P. Scavone, 2003-2017. 193 | */ 194 | /**********************************************************************/ 195 | 196 | // **************************************************************** // 197 | // 198 | // RtMidiIn and RtMidiOut class declarations. 199 | // 200 | // RtMidiIn / RtMidiOut are "controllers" used to select an available 201 | // MIDI input or output interface. They present common APIs for the 202 | // user to call but all functionality is implemented by the classes 203 | // MidiInApi, MidiOutApi and their subclasses. RtMidiIn and RtMidiOut 204 | // each create an instance of a MidiInApi or MidiOutApi subclass based 205 | // on the user's API choice. If no choice is made, they attempt to 206 | // make a "logical" API selection. 207 | // 208 | // **************************************************************** // 209 | 210 | class RtMidiIn : public RtMidi 211 | { 212 | public: 213 | 214 | //! User callback function type definition. 215 | typedef void (*RtMidiCallback)( double timeStamp, std::vector *message, void *userData); 216 | 217 | //! Default constructor that allows an optional api, client name and queue size. 218 | /*! 219 | An exception will be thrown if a MIDI system initialization 220 | error occurs. The queue size defines the maximum number of 221 | messages that can be held in the MIDI queue (when not using a 222 | callback function). If the queue size limit is reached, 223 | incoming messages will be ignored. 224 | 225 | If no API argument is specified and multiple API support has been 226 | compiled, the default order of use is ALSA, JACK (Linux) and CORE, 227 | JACK (OS-X). 228 | 229 | \param api An optional API id can be specified. 230 | \param clientName An optional client name can be specified. This 231 | will be used to group the ports that are created 232 | by the application. 233 | \param queueSizeLimit An optional size of the MIDI input queue can be specified. 234 | */ 235 | RtMidiIn( RtMidi::Api api=UNSPECIFIED, 236 | const std::string& clientName = "RtMidi Input Client", 237 | unsigned int queueSizeLimit = 100 ); 238 | 239 | //! If a MIDI connection is still open, it will be closed by the destructor. 240 | ~RtMidiIn ( void ) throw(); 241 | 242 | //! Returns the MIDI API specifier for the current instance of RtMidiIn. 243 | RtMidi::Api getCurrentApi( void ) throw(); 244 | 245 | //! Open a MIDI input connection given by enumeration number. 246 | /*! 247 | \param portNumber An optional port number greater than 0 can be specified. 248 | Otherwise, the default or first port found is opened. 249 | \param portName An optional name for the application port that is used to connect to portId can be specified. 250 | */ 251 | void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Input" ) ); 252 | 253 | //! Create a virtual input port, with optional name, to allow software connections (OS X, JACK and ALSA only). 254 | /*! 255 | This function creates a virtual MIDI input port to which other 256 | software applications can connect. This type of functionality 257 | is currently only supported by the Macintosh OS-X, any JACK, 258 | and Linux ALSA APIs (the function returns an error for the other APIs). 259 | 260 | \param portName An optional name for the application port that is 261 | used to connect to portId can be specified. 262 | */ 263 | void openVirtualPort( const std::string &portName = std::string( "RtMidi Input" ) ); 264 | 265 | //! Set a callback function to be invoked for incoming MIDI messages. 266 | /*! 267 | The callback function will be called whenever an incoming MIDI 268 | message is received. While not absolutely necessary, it is best 269 | to set the callback function before opening a MIDI port to avoid 270 | leaving some messages in the queue. 271 | 272 | \param callback A callback function must be given. 273 | \param userData Optionally, a pointer to additional data can be 274 | passed to the callback function whenever it is called. 275 | */ 276 | void setCallback( RtMidiCallback callback, void *userData = 0 ); 277 | 278 | //! Cancel use of the current callback function (if one exists). 279 | /*! 280 | Subsequent incoming MIDI messages will be written to the queue 281 | and can be retrieved with the \e getMessage function. 282 | */ 283 | void cancelCallback(); 284 | 285 | //! Close an open MIDI connection (if one exists). 286 | void closePort( void ); 287 | 288 | //! Returns true if a port is open and false if not. 289 | /*! 290 | Note that this only applies to connections made with the openPort() 291 | function, not to virtual ports. 292 | */ 293 | virtual bool isPortOpen() const; 294 | 295 | //! Return the number of available MIDI input ports. 296 | /*! 297 | \return This function returns the number of MIDI ports of the selected API. 298 | */ 299 | unsigned int getPortCount(); 300 | 301 | //! Return a string identifier for the specified MIDI input port number. 302 | /*! 303 | \return The name of the port with the given Id is returned. 304 | \retval An empty string is returned if an invalid port specifier 305 | is provided. User code should assume a UTF-8 encoding. 306 | */ 307 | std::string getPortName( unsigned int portNumber = 0 ); 308 | 309 | //! Specify whether certain MIDI message types should be queued or ignored during input. 310 | /*! 311 | By default, MIDI timing and active sensing messages are ignored 312 | during message input because of their relative high data rates. 313 | MIDI sysex messages are ignored by default as well. Variable 314 | values of "true" imply that the respective message type will be 315 | ignored. 316 | */ 317 | void ignoreTypes( bool midiSysex = true, bool midiTime = true, bool midiSense = true ); 318 | 319 | //! Fill the user-provided vector with the data bytes for the next available MIDI message in the input queue and return the event delta-time in seconds. 320 | /*! 321 | This function returns immediately whether a new message is 322 | available or not. A valid message is indicated by a non-zero 323 | vector size. An exception is thrown if an error occurs during 324 | message retrieval or an input connection was not previously 325 | established. 326 | */ 327 | double getMessage( std::vector *message ); 328 | 329 | //! Set an error callback function to be invoked when an error has occured. 330 | /*! 331 | The callback function will be called whenever an error has occured. It is best 332 | to set the error callback function before opening a port. 333 | */ 334 | virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); 335 | 336 | protected: 337 | void openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ); 338 | 339 | }; 340 | 341 | /**********************************************************************/ 342 | /*! \class RtMidiOut 343 | \brief A realtime MIDI output class. 344 | 345 | This class provides a common, platform-independent API for MIDI 346 | output. It allows one to probe available MIDI output ports, to 347 | connect to one such port, and to send MIDI bytes immediately over 348 | the connection. Create multiple instances of this class to 349 | connect to more than one MIDI device at the same time. With the 350 | OS-X, Linux ALSA and JACK MIDI APIs, it is also possible to open a 351 | virtual port to which other MIDI software clients can connect. 352 | 353 | by Gary P. Scavone, 2003-2017. 354 | */ 355 | /**********************************************************************/ 356 | 357 | class RtMidiOut : public RtMidi 358 | { 359 | public: 360 | 361 | //! Default constructor that allows an optional client name. 362 | /*! 363 | An exception will be thrown if a MIDI system initialization error occurs. 364 | 365 | If no API argument is specified and multiple API support has been 366 | compiled, the default order of use is ALSA, JACK (Linux) and CORE, 367 | JACK (OS-X). 368 | */ 369 | RtMidiOut( RtMidi::Api api=UNSPECIFIED, 370 | const std::string& clientName = "RtMidi Output Client" ); 371 | 372 | //! The destructor closes any open MIDI connections. 373 | ~RtMidiOut( void ) throw(); 374 | 375 | //! Returns the MIDI API specifier for the current instance of RtMidiOut. 376 | RtMidi::Api getCurrentApi( void ) throw(); 377 | 378 | //! Open a MIDI output connection. 379 | /*! 380 | An optional port number greater than 0 can be specified. 381 | Otherwise, the default or first port found is opened. An 382 | exception is thrown if an error occurs while attempting to make 383 | the port connection. 384 | */ 385 | void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Output" ) ); 386 | 387 | //! Close an open MIDI connection (if one exists). 388 | void closePort( void ); 389 | 390 | //! Returns true if a port is open and false if not. 391 | /*! 392 | Note that this only applies to connections made with the openPort() 393 | function, not to virtual ports. 394 | */ 395 | virtual bool isPortOpen() const; 396 | 397 | //! Create a virtual output port, with optional name, to allow software connections (OS X, JACK and ALSA only). 398 | /*! 399 | This function creates a virtual MIDI output port to which other 400 | software applications can connect. This type of functionality 401 | is currently only supported by the Macintosh OS-X, Linux ALSA 402 | and JACK APIs (the function does nothing with the other APIs). 403 | An exception is thrown if an error occurs while attempting to 404 | create the virtual port. 405 | */ 406 | void openVirtualPort( const std::string &portName = std::string( "RtMidi Output" ) ); 407 | 408 | //! Return the number of available MIDI output ports. 409 | unsigned int getPortCount( void ); 410 | 411 | //! Return a string identifier for the specified MIDI port type and number. 412 | /*! 413 | \return The name of the port with the given Id is returned. 414 | \retval An empty string is returned if an invalid port specifier 415 | is provided. User code should assume a UTF-8 encoding. 416 | */ 417 | std::string getPortName( unsigned int portNumber = 0 ); 418 | 419 | //! Immediately send a single message out an open MIDI output port. 420 | /*! 421 | An exception is thrown if an error occurs during output or an 422 | output connection was not previously established. 423 | */ 424 | void sendMessage( const std::vector *message ); 425 | 426 | //! Immediately send a single message out an open MIDI output port. 427 | /*! 428 | An exception is thrown if an error occurs during output or an 429 | output connection was not previously established. 430 | 431 | \param message A pointer to the MIDI message as raw bytes 432 | \param size Length of the MIDI message in bytes 433 | */ 434 | void sendMessage( const unsigned char *message, size_t size ); 435 | 436 | //! Set an error callback function to be invoked when an error has occured. 437 | /*! 438 | The callback function will be called whenever an error has occured. It is best 439 | to set the error callback function before opening a port. 440 | */ 441 | virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); 442 | 443 | protected: 444 | void openMidiApi( RtMidi::Api api, const std::string &clientName ); 445 | }; 446 | 447 | 448 | // **************************************************************** // 449 | // 450 | // MidiInApi / MidiOutApi class declarations. 451 | // 452 | // Subclasses of MidiInApi and MidiOutApi contain all API- and 453 | // OS-specific code necessary to fully implement the RtMidi API. 454 | // 455 | // Note that MidiInApi and MidiOutApi are abstract base classes and 456 | // cannot be explicitly instantiated. RtMidiIn and RtMidiOut will 457 | // create instances of a MidiInApi or MidiOutApi subclass. 458 | // 459 | // **************************************************************** // 460 | 461 | class MidiApi 462 | { 463 | public: 464 | 465 | MidiApi(); 466 | virtual ~MidiApi(); 467 | virtual RtMidi::Api getCurrentApi( void ) = 0; 468 | virtual void openPort( unsigned int portNumber, const std::string &portName ) = 0; 469 | virtual void openVirtualPort( const std::string &portName ) = 0; 470 | virtual void closePort( void ) = 0; 471 | 472 | virtual unsigned int getPortCount( void ) = 0; 473 | virtual std::string getPortName( unsigned int portNumber ) = 0; 474 | 475 | inline bool isPortOpen() const { return connected_; } 476 | void setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ); 477 | 478 | //! A basic error reporting function for RtMidi classes. 479 | void error( RtMidiError::Type type, std::string errorString ); 480 | 481 | protected: 482 | virtual void initialize( const std::string& clientName ) = 0; 483 | 484 | void *apiData_; 485 | bool connected_; 486 | std::string errorString_; 487 | RtMidiErrorCallback errorCallback_; 488 | bool firstErrorOccurred_; 489 | void *errorCallbackUserData_; 490 | }; 491 | 492 | class MidiInApi : public MidiApi 493 | { 494 | public: 495 | 496 | MidiInApi( unsigned int queueSizeLimit ); 497 | virtual ~MidiInApi( void ); 498 | void setCallback( RtMidiIn::RtMidiCallback callback, void *userData ); 499 | void cancelCallback( void ); 500 | virtual void ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ); 501 | double getMessage( std::vector *message ); 502 | 503 | // A MIDI structure used internally by the class to store incoming 504 | // messages. Each message represents one and only one MIDI message. 505 | struct MidiMessage { 506 | std::vector bytes; 507 | 508 | //! Time in seconds elapsed since the previous message 509 | double timeStamp; 510 | 511 | // Default constructor. 512 | MidiMessage() 513 | :bytes(0), timeStamp(0.0) {} 514 | }; 515 | 516 | struct MidiQueue { 517 | unsigned int front; 518 | unsigned int back; 519 | unsigned int ringSize; 520 | MidiMessage *ring; 521 | 522 | // Default constructor. 523 | MidiQueue() 524 | :front(0), back(0), ringSize(0), ring(0) {} 525 | bool push(const MidiMessage&); 526 | bool pop(std::vector*, double*); 527 | unsigned int size(unsigned int *back=0, 528 | unsigned int *front=0); 529 | }; 530 | 531 | // The RtMidiInData structure is used to pass private class data to 532 | // the MIDI input handling function or thread. 533 | struct RtMidiInData { 534 | MidiQueue queue; 535 | MidiMessage message; 536 | unsigned char ignoreFlags; 537 | bool doInput; 538 | bool firstMessage; 539 | void *apiData; 540 | bool usingCallback; 541 | RtMidiIn::RtMidiCallback userCallback; 542 | void *userData; 543 | bool continueSysex; 544 | 545 | // Default constructor. 546 | RtMidiInData() 547 | : ignoreFlags(7), doInput(false), firstMessage(true), 548 | apiData(0), usingCallback(false), userCallback(0), userData(0), 549 | continueSysex(false) {} 550 | }; 551 | 552 | protected: 553 | RtMidiInData inputData_; 554 | }; 555 | 556 | class MidiOutApi : public MidiApi 557 | { 558 | public: 559 | 560 | MidiOutApi( void ); 561 | virtual ~MidiOutApi( void ); 562 | virtual void sendMessage( const unsigned char *message, size_t size ) = 0; 563 | }; 564 | 565 | // **************************************************************** // 566 | // 567 | // Inline RtMidiIn and RtMidiOut definitions. 568 | // 569 | // **************************************************************** // 570 | 571 | inline RtMidi::Api RtMidiIn :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } 572 | inline void RtMidiIn :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); } 573 | inline void RtMidiIn :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } 574 | inline void RtMidiIn :: closePort( void ) { rtapi_->closePort(); } 575 | inline bool RtMidiIn :: isPortOpen() const { return rtapi_->isPortOpen(); } 576 | inline void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) { ((MidiInApi *)rtapi_)->setCallback( callback, userData ); } 577 | inline void RtMidiIn :: cancelCallback( void ) { ((MidiInApi *)rtapi_)->cancelCallback(); } 578 | inline unsigned int RtMidiIn :: getPortCount( void ) { return rtapi_->getPortCount(); } 579 | inline std::string RtMidiIn :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } 580 | inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { ((MidiInApi *)rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); } 581 | inline double RtMidiIn :: getMessage( std::vector *message ) { return ((MidiInApi *)rtapi_)->getMessage( message ); } 582 | inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } 583 | 584 | inline RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } 585 | inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); } 586 | inline void RtMidiOut :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } 587 | inline void RtMidiOut :: closePort( void ) { rtapi_->closePort(); } 588 | inline bool RtMidiOut :: isPortOpen() const { return rtapi_->isPortOpen(); } 589 | inline unsigned int RtMidiOut :: getPortCount( void ) { return rtapi_->getPortCount(); } 590 | inline std::string RtMidiOut :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } 591 | inline void RtMidiOut :: sendMessage( const std::vector *message ) { ((MidiOutApi *)rtapi_)->sendMessage( &message->at(0), message->size() ); } 592 | inline void RtMidiOut :: sendMessage( const unsigned char *message, size_t size ) { ((MidiOutApi *)rtapi_)->sendMessage( message, size ); } 593 | inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } 594 | 595 | // **************************************************************** // 596 | // 597 | // MidiInApi and MidiOutApi subclass prototypes. 598 | // 599 | // **************************************************************** // 600 | 601 | #if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__) 602 | #define __RTMIDI_DUMMY__ 603 | #endif 604 | 605 | #if defined(__MACOSX_CORE__) 606 | 607 | class MidiInCore: public MidiInApi 608 | { 609 | public: 610 | MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ); 611 | ~MidiInCore( void ); 612 | RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; 613 | void openPort( unsigned int portNumber, const std::string &portName ); 614 | void openVirtualPort( const std::string &portName ); 615 | void closePort( void ); 616 | unsigned int getPortCount( void ); 617 | std::string getPortName( unsigned int portNumber ); 618 | 619 | protected: 620 | void initialize( const std::string& clientName ); 621 | }; 622 | 623 | class MidiOutCore: public MidiOutApi 624 | { 625 | public: 626 | MidiOutCore( const std::string &clientName ); 627 | ~MidiOutCore( void ); 628 | RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; 629 | void openPort( unsigned int portNumber, const std::string &portName ); 630 | void openVirtualPort( const std::string &portName ); 631 | void closePort( void ); 632 | unsigned int getPortCount( void ); 633 | std::string getPortName( unsigned int portNumber ); 634 | void sendMessage( const unsigned char *message, size_t size ); 635 | 636 | protected: 637 | void initialize( const std::string& clientName ); 638 | }; 639 | 640 | #endif 641 | 642 | #if defined(__UNIX_JACK__) 643 | 644 | class MidiInJack: public MidiInApi 645 | { 646 | public: 647 | MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ); 648 | ~MidiInJack( void ); 649 | RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; 650 | void openPort( unsigned int portNumber, const std::string &portName ); 651 | void openVirtualPort( const std::string &portName ); 652 | void closePort( void ); 653 | unsigned int getPortCount( void ); 654 | std::string getPortName( unsigned int portNumber ); 655 | 656 | protected: 657 | std::string clientName; 658 | 659 | void connect( void ); 660 | void initialize( const std::string& clientName ); 661 | }; 662 | 663 | class MidiOutJack: public MidiOutApi 664 | { 665 | public: 666 | MidiOutJack( const std::string &clientName ); 667 | ~MidiOutJack( void ); 668 | RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; 669 | void openPort( unsigned int portNumber, const std::string &portName ); 670 | void openVirtualPort( const std::string &portName ); 671 | void closePort( void ); 672 | unsigned int getPortCount( void ); 673 | std::string getPortName( unsigned int portNumber ); 674 | void sendMessage( const unsigned char *message, size_t size ); 675 | 676 | protected: 677 | std::string clientName; 678 | 679 | void connect( void ); 680 | void initialize( const std::string& clientName ); 681 | }; 682 | 683 | #endif 684 | 685 | #if defined(__LINUX_ALSA__) 686 | 687 | class MidiInAlsa: public MidiInApi 688 | { 689 | public: 690 | MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ); 691 | ~MidiInAlsa( void ); 692 | RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; 693 | void openPort( unsigned int portNumber, const std::string &portName ); 694 | void openVirtualPort( const std::string &portName ); 695 | void closePort( void ); 696 | unsigned int getPortCount( void ); 697 | std::string getPortName( unsigned int portNumber ); 698 | 699 | protected: 700 | void initialize( const std::string& clientName ); 701 | }; 702 | 703 | class MidiOutAlsa: public MidiOutApi 704 | { 705 | public: 706 | MidiOutAlsa( const std::string &clientName ); 707 | ~MidiOutAlsa( void ); 708 | RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; 709 | void openPort( unsigned int portNumber, const std::string &portName ); 710 | void openVirtualPort( const std::string &portName ); 711 | void closePort( void ); 712 | unsigned int getPortCount( void ); 713 | std::string getPortName( unsigned int portNumber ); 714 | void sendMessage( const unsigned char *message, size_t size ); 715 | 716 | protected: 717 | void initialize( const std::string& clientName ); 718 | }; 719 | 720 | #endif 721 | 722 | #if defined(__WINDOWS_MM__) 723 | 724 | class MidiInWinMM: public MidiInApi 725 | { 726 | public: 727 | MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ); 728 | ~MidiInWinMM( void ); 729 | RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; 730 | void openPort( unsigned int portNumber, const std::string &portName ); 731 | void openVirtualPort( const std::string &portName ); 732 | void closePort( void ); 733 | unsigned int getPortCount( void ); 734 | std::string getPortName( unsigned int portNumber ); 735 | 736 | protected: 737 | void initialize( const std::string& clientName ); 738 | }; 739 | 740 | class MidiOutWinMM: public MidiOutApi 741 | { 742 | public: 743 | MidiOutWinMM( const std::string &clientName ); 744 | ~MidiOutWinMM( void ); 745 | RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; 746 | void openPort( unsigned int portNumber, const std::string &portName ); 747 | void openVirtualPort( const std::string &portName ); 748 | void closePort( void ); 749 | unsigned int getPortCount( void ); 750 | std::string getPortName( unsigned int portNumber ); 751 | void sendMessage( const unsigned char *message, size_t size ); 752 | 753 | protected: 754 | void initialize( const std::string& clientName ); 755 | }; 756 | 757 | #endif 758 | 759 | #if defined(__RTMIDI_DUMMY__) 760 | 761 | class MidiInDummy: public MidiInApi 762 | { 763 | public: 764 | MidiInDummy( const std::string &/*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } 765 | RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } 766 | void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} 767 | void openVirtualPort( const std::string &/*portName*/ ) {} 768 | void closePort( void ) {} 769 | unsigned int getPortCount( void ) { return 0; } 770 | std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } 771 | 772 | protected: 773 | void initialize( const std::string& /*clientName*/ ) {} 774 | }; 775 | 776 | class MidiOutDummy: public MidiOutApi 777 | { 778 | public: 779 | MidiOutDummy( const std::string &/*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } 780 | RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } 781 | void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} 782 | void openVirtualPort( const std::string &/*portName*/ ) {} 783 | void closePort( void ) {} 784 | unsigned int getPortCount( void ) { return 0; } 785 | std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } 786 | void sendMessage( const unsigned char * /*message*/, size_t /*size*/ ) {} 787 | 788 | protected: 789 | void initialize( const std::string& /*clientName*/ ) {} 790 | }; 791 | 792 | #endif 793 | 794 | #endif 795 | -------------------------------------------------------------------------------- /rtmidi/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/naivesound/audio/rtmidi" 7 | ) 8 | 9 | func main() { 10 | log.Println(rtmidi.CompiledAPI()) 11 | 12 | in, err := rtmidi.NewMIDIInDefault() 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | defer in.Close() 17 | 18 | n, err := in.PortCount() 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | for i := 0; i < n; i++ { 23 | name, err := in.PortName(i) 24 | if err == nil { 25 | log.Println(name) 26 | if err := in.OpenPort(i, "RtMidi"); err != nil { 27 | log.Println(err) 28 | } 29 | } else { 30 | log.Println(err) 31 | } 32 | } 33 | 34 | //in.SetCallback(func(m rtmidi.MIDIIn, msg []byte, t float64) { 35 | //log.Println(msg, t) 36 | //}) 37 | //c := make(chan struct{}) 38 | //<-c 39 | 40 | for { 41 | m, t, err := in.Message() 42 | if len(m) > 0 { 43 | log.Println(m, t, err) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rtmidi/rtmidi.go: -------------------------------------------------------------------------------- 1 | package rtmidi 2 | 3 | /* 4 | #cgo CXXFLAGS: -g 5 | #cgo LDFLAGS: -g 6 | 7 | #cgo linux CXXFLAGS: -D__LINUX_ALSA__ 8 | #cgo linux LDFLAGS: -lasound -pthread 9 | #cgo windows CXXFLAGS: -D__WINDOWS_MM__ 10 | #cgo windows LDFLAGS: -luuid -lksuser -lwinmm -lole32 11 | #cgo darwin CXXFLAGS: -D__MACOSX_CORE__ 12 | #cgo darwin LDFLAGS: -framework CoreServices -framework CoreAudio -framework CoreMIDI -framework CoreFoundation 13 | 14 | #include 15 | #include "rtmidi_c.h" 16 | 17 | extern void goMIDIInCallback(double ts, unsigned char *msg, size_t msgsz, void *arg); 18 | 19 | static inline void midiInCallback(double ts, const unsigned char *msg, size_t msgsz, void *arg) { 20 | goMIDIInCallback(ts, (unsigned char*) msg, msgsz, arg); 21 | } 22 | 23 | static inline void cgoSetCallback(RtMidiPtr in, void *arg) { 24 | rtmidi_in_set_callback(in, midiInCallback, arg); 25 | } 26 | */ 27 | import "C" 28 | import ( 29 | "errors" 30 | "sync" 31 | "unsafe" 32 | ) 33 | 34 | type API C.enum_RtMidiApi 35 | 36 | const ( 37 | APIUnspecified API = C.RT_MIDI_API_UNSPECIFIED 38 | APIMacOSXCore = C.RT_MIDI_API_MACOSX_CORE 39 | APILinuxALSA = C.RT_MIDI_API_LINUX_ALSA 40 | APIUnixJack = C.RT_MIDI_API_UNIX_JACK 41 | APIWindowsMM = C.RT_MIDI_API_WINDOWS_MM 42 | APIDummy = C.RT_MIDI_API_RTMIDI_DUMMY 43 | ) 44 | 45 | func (api API) String() string { 46 | switch api { 47 | case APIUnspecified: 48 | return "unspecified" 49 | case APILinuxALSA: 50 | return "alsa" 51 | case APIUnixJack: 52 | return "jack" 53 | case APIMacOSXCore: 54 | return "coreaudio" 55 | case APIWindowsMM: 56 | return "winmm" 57 | case APIDummy: 58 | return "dummy" 59 | } 60 | return "?" 61 | } 62 | 63 | func CompiledAPI() (apis []API) { 64 | n := C.rtmidi_get_compiled_api(nil) 65 | capis := make([]C.enum_RtMidiApi, n, n) 66 | C.rtmidi_get_compiled_api(&capis[0]) 67 | for _, capi := range capis { 68 | apis = append(apis, API(capi)) 69 | } 70 | return apis 71 | } 72 | 73 | type MIDI interface { 74 | OpenPort(port int, name string) error 75 | OpenVirtualPort(name string) error 76 | Close() error 77 | PortCount() (int, error) 78 | PortName(port int) (string, error) 79 | } 80 | 81 | type MIDIIn interface { 82 | MIDI 83 | API() (API, error) 84 | IgnoreTypes(midiSysex bool, midiTime bool, midiSense bool) error 85 | SetCallback(func(MIDIIn, []byte, float64)) error 86 | CancelCallback() error 87 | Message() ([]byte, float64, error) 88 | } 89 | 90 | type MIDIOut interface { 91 | MIDI 92 | API() (API, error) 93 | SendMessage([]byte) error 94 | } 95 | 96 | type midi struct { 97 | midi C.RtMidiPtr 98 | } 99 | 100 | func (m *midi) OpenPort(port int, name string) error { 101 | p := C.CString(name) 102 | defer C.free(unsafe.Pointer(p)) 103 | C.rtmidi_open_port(m.midi, C.uint(port), p) 104 | if !m.midi.ok { 105 | return errors.New(C.GoString(m.midi.msg)) 106 | } 107 | return nil 108 | } 109 | 110 | func (m *midi) OpenVirtualPort(name string) error { 111 | p := C.CString(name) 112 | defer C.free(unsafe.Pointer(p)) 113 | C.rtmidi_open_virtual_port(m.midi, p) 114 | if !m.midi.ok { 115 | return errors.New(C.GoString(m.midi.msg)) 116 | } 117 | return nil 118 | } 119 | 120 | func (m *midi) PortName(port int) (string, error) { 121 | p := C.rtmidi_get_port_name(m.midi, C.uint(port)) 122 | if !m.midi.ok { 123 | return "", errors.New(C.GoString(m.midi.msg)) 124 | } 125 | defer C.free(unsafe.Pointer(p)) 126 | return C.GoString(p), nil 127 | } 128 | 129 | func (m *midi) PortCount() (int, error) { 130 | n := C.rtmidi_get_port_count(m.midi) 131 | if !m.midi.ok { 132 | return 0, errors.New(C.GoString(m.midi.msg)) 133 | } 134 | return int(n), nil 135 | } 136 | 137 | func (m *midi) Close() error { 138 | C.rtmidi_close_port(C.RtMidiPtr(m.midi)) 139 | if !m.midi.ok { 140 | return errors.New(C.GoString(m.midi.msg)) 141 | } 142 | return nil 143 | } 144 | 145 | type midiIn struct { 146 | midi 147 | in C.RtMidiInPtr 148 | cb func(MIDIIn, []byte, float64) 149 | } 150 | 151 | type midiOut struct { 152 | midi 153 | out C.RtMidiOutPtr 154 | } 155 | 156 | func NewMIDIInDefault() (MIDIIn, error) { 157 | in := C.rtmidi_in_create_default() 158 | if !in.ok { 159 | defer C.rtmidi_in_free(in) 160 | return nil, errors.New(C.GoString(in.msg)) 161 | } 162 | return &midiIn{in: in, midi: midi{midi: C.RtMidiPtr(in)}}, nil 163 | } 164 | 165 | func NewMIDIIn(api API, name string, queueSize int) (MIDIIn, error) { 166 | p := C.CString(name) 167 | defer C.free(unsafe.Pointer(p)) 168 | in := C.rtmidi_in_create(C.enum_RtMidiApi(api), p, C.uint(queueSize)) 169 | if !in.ok { 170 | defer C.rtmidi_in_free(in) 171 | return nil, errors.New(C.GoString(in.msg)) 172 | } 173 | return &midiIn{in: in, midi: midi{midi: C.RtMidiPtr(in)}}, nil 174 | } 175 | 176 | func (m *midiIn) API() (API, error) { 177 | api := C.rtmidi_in_get_current_api(m.in) 178 | if !m.in.ok { 179 | return APIUnspecified, errors.New(C.GoString(m.in.msg)) 180 | } 181 | return API(api), nil 182 | } 183 | 184 | func (m *midiIn) Close() error { 185 | unregisterMIDIIn(m) 186 | if err := m.midi.Close(); err != nil { 187 | return err 188 | } 189 | C.rtmidi_in_free(m.in) 190 | return nil 191 | } 192 | 193 | func (m *midiIn) IgnoreTypes(midiSysex bool, midiTime bool, midiSense bool) error { 194 | C.rtmidi_in_ignore_types(m.in, C._Bool(midiSysex), C._Bool(midiTime), C._Bool(midiSense)) 195 | if !m.in.ok { 196 | return errors.New(C.GoString(m.in.msg)) 197 | } 198 | return nil 199 | } 200 | 201 | var ( 202 | mu sync.Mutex 203 | inputs = map[int]*midiIn{} 204 | ) 205 | 206 | func registerMIDIIn(m *midiIn) int { 207 | mu.Lock() 208 | defer mu.Unlock() 209 | for i := 0; ; i++ { 210 | if _, ok := inputs[i]; !ok { 211 | inputs[i] = m 212 | return i 213 | } 214 | } 215 | } 216 | 217 | func unregisterMIDIIn(m *midiIn) { 218 | mu.Lock() 219 | defer mu.Unlock() 220 | for i := 0; i < len(inputs); i++ { 221 | if inputs[i] == m { 222 | delete(inputs, i) 223 | return 224 | } 225 | } 226 | } 227 | 228 | func findMIDIIn(k int) *midiIn { 229 | mu.Lock() 230 | defer mu.Unlock() 231 | return inputs[k] 232 | } 233 | 234 | //export goMIDIInCallback 235 | func goMIDIInCallback(ts C.double, msg *C.uchar, msgsz C.size_t, arg unsafe.Pointer) { 236 | k := int(uintptr(arg)) 237 | m := findMIDIIn(k) 238 | m.cb(m, C.GoBytes(unsafe.Pointer(msg), C.int(msgsz)), float64(ts)) 239 | } 240 | 241 | func (m *midiIn) SetCallback(cb func(MIDIIn, []byte, float64)) error { 242 | k := registerMIDIIn(m) 243 | m.cb = cb 244 | C.cgoSetCallback(m.in, unsafe.Pointer(uintptr(k))) 245 | if !m.in.ok { 246 | return errors.New(C.GoString(m.in.msg)) 247 | } 248 | return nil 249 | } 250 | 251 | func (m *midiIn) CancelCallback() error { 252 | unregisterMIDIIn(m) 253 | C.rtmidi_in_cancel_callback(m.in) 254 | if !m.in.ok { 255 | return errors.New(C.GoString(m.in.msg)) 256 | } 257 | return nil 258 | } 259 | 260 | func (m *midiIn) Message() ([]byte, float64, error) { 261 | msg := make([]C.uchar, 64*1024, 64*1024) 262 | sz := C.size_t(len(msg)) 263 | r := C.rtmidi_in_get_message(m.in, &msg[0], &sz) 264 | if !m.in.ok { 265 | return nil, 0, errors.New(C.GoString(m.in.msg)) 266 | } 267 | b := make([]byte, int(sz), int(sz)) 268 | for i, c := range msg[:sz] { 269 | b[i] = byte(c) 270 | } 271 | return b, float64(r), nil 272 | } 273 | 274 | func NewMIDIOutDefault() (MIDIOut, error) { 275 | out := C.rtmidi_out_create_default() 276 | if !out.ok { 277 | defer C.rtmidi_out_free(out) 278 | return nil, errors.New(C.GoString(out.msg)) 279 | } 280 | return &midiOut{out: out, midi: midi{midi: C.RtMidiPtr(out)}}, nil 281 | } 282 | 283 | func NewMIDIOut(api API, name string) (MIDIOut, error) { 284 | p := C.CString(name) 285 | defer C.free(unsafe.Pointer(p)) 286 | out := C.rtmidi_out_create(C.enum_RtMidiApi(api), p) 287 | if !out.ok { 288 | defer C.rtmidi_out_free(out) 289 | return nil, errors.New(C.GoString(out.msg)) 290 | } 291 | return &midiOut{out: out, midi: midi{midi: C.RtMidiPtr(out)}}, nil 292 | } 293 | 294 | func (m *midiOut) API() (API, error) { 295 | api := C.rtmidi_out_get_current_api(m.out) 296 | if !m.out.ok { 297 | return APIUnspecified, errors.New(C.GoString(m.out.msg)) 298 | } 299 | return API(api), nil 300 | } 301 | 302 | func (m *midiOut) Close() error { 303 | if err := m.midi.Close(); err != nil { 304 | return err 305 | } 306 | C.rtmidi_out_free(m.out) 307 | return nil 308 | } 309 | 310 | func (m *midiOut) SendMessage(b []byte) error { 311 | p := C.CBytes(b) 312 | defer C.free(unsafe.Pointer(p)) 313 | C.rtmidi_out_send_message(m.out, (*C.uchar)(p), C.int(len(b))) 314 | if !m.out.ok { 315 | return errors.New(C.GoString(m.out.msg)) 316 | } 317 | return nil 318 | } 319 | -------------------------------------------------------------------------------- /rtmidi/rtmidi_c.cpp: -------------------------------------------------------------------------------- 1 | #include "rtmidi_c.h" 2 | #include "RtMidi.h" 3 | #include 4 | #include 5 | 6 | class CallbackProxyUserData { 7 | public: 8 | CallbackProxyUserData(RtMidiCCallback cCallback, void *userData) 9 | : c_callback(cCallback), user_data(userData) {} 10 | RtMidiCCallback c_callback; 11 | void *user_data; 12 | }; 13 | 14 | /* RtMidi API */ 15 | int rtmidi_get_compiled_api( 16 | enum RtMidiApi *apis) // return length for NULL argument. 17 | { 18 | if (!apis) { 19 | std::vector *v = new std::vector(); 20 | try { 21 | RtMidi::getCompiledApi(*v); 22 | int size = v->size(); 23 | delete v; 24 | return size; 25 | } catch (...) { 26 | return -1; 27 | } 28 | } else { 29 | try { 30 | std::vector *v = new std::vector(); 31 | RtMidi::getCompiledApi(*v); 32 | for (unsigned int i = 0; i < v->size(); i++) 33 | apis[i] = (RtMidiApi)v->at(i); 34 | delete v; 35 | return 0; 36 | } catch (...) { 37 | return -1; 38 | } 39 | } 40 | } 41 | 42 | void rtmidi_error(MidiApi *api, enum RtMidiErrorType type, 43 | const char *errorString) { 44 | std::string msg = errorString; 45 | api->error((RtMidiError::Type)type, msg); 46 | } 47 | 48 | void rtmidi_open_port(RtMidiPtr device, unsigned int portNumber, 49 | const char *portName) { 50 | std::string name = portName; 51 | try { 52 | ((RtMidi *)device->ptr)->openPort(portNumber, name); 53 | 54 | } catch (const RtMidiError &err) { 55 | device->ok = false; 56 | device->msg = err.what(); 57 | } 58 | } 59 | 60 | void rtmidi_open_virtual_port(RtMidiPtr device, const char *portName) { 61 | std::string name = portName; 62 | try { 63 | ((RtMidi *)device->ptr)->openVirtualPort(name); 64 | 65 | } catch (const RtMidiError &err) { 66 | device->ok = false; 67 | device->msg = err.what(); 68 | } 69 | } 70 | 71 | void rtmidi_close_port(RtMidiPtr device) { 72 | try { 73 | ((RtMidi *)device->ptr)->closePort(); 74 | 75 | } catch (const RtMidiError &err) { 76 | device->ok = false; 77 | device->msg = err.what(); 78 | } 79 | } 80 | 81 | unsigned int rtmidi_get_port_count(RtMidiPtr device) { 82 | try { 83 | return ((RtMidi *)device->ptr)->getPortCount(); 84 | 85 | } catch (const RtMidiError &err) { 86 | device->ok = false; 87 | device->msg = err.what(); 88 | return -1; 89 | } 90 | } 91 | 92 | const char *rtmidi_get_port_name(RtMidiPtr device, unsigned int portNumber) { 93 | try { 94 | std::string name = ((RtMidi *)device->ptr)->getPortName(portNumber); 95 | return strdup(name.c_str()); 96 | 97 | } catch (const RtMidiError &err) { 98 | device->ok = false; 99 | device->msg = err.what(); 100 | return ""; 101 | } 102 | } 103 | 104 | /* RtMidiIn API */ 105 | RtMidiInPtr rtmidi_in_create_default() { 106 | RtMidiWrapper *wrp = new RtMidiWrapper; 107 | 108 | try { 109 | RtMidiIn *rIn = new RtMidiIn(); 110 | 111 | wrp->ptr = (void *)rIn; 112 | wrp->ok = true; 113 | wrp->msg = ""; 114 | 115 | } catch (const RtMidiError &err) { 116 | wrp->ptr = 0; 117 | wrp->ok = false; 118 | wrp->msg = err.what(); 119 | } 120 | 121 | return wrp; 122 | } 123 | 124 | RtMidiInPtr rtmidi_in_create(enum RtMidiApi api, const char *clientName, 125 | unsigned int queueSizeLimit) { 126 | std::string name = clientName; 127 | RtMidiWrapper *wrp = new RtMidiWrapper; 128 | 129 | try { 130 | RtMidiIn *rIn = new RtMidiIn((RtMidi::Api)api, name, queueSizeLimit); 131 | 132 | wrp->ptr = (void *)rIn; 133 | wrp->data = 0; 134 | wrp->ok = true; 135 | wrp->msg = ""; 136 | 137 | } catch (const RtMidiError &err) { 138 | wrp->ptr = 0; 139 | wrp->data = 0; 140 | wrp->ok = false; 141 | wrp->msg = err.what(); 142 | } 143 | 144 | return wrp; 145 | } 146 | 147 | void rtmidi_in_free(RtMidiInPtr device) { 148 | if (device->data) 149 | delete (CallbackProxyUserData *)device->data; 150 | delete (RtMidiIn *)device->ptr; 151 | delete device; 152 | } 153 | 154 | enum RtMidiApi rtmidi_in_get_current_api(RtMidiPtr device) { 155 | try { 156 | return (RtMidiApi)((RtMidiIn *)device->ptr)->getCurrentApi(); 157 | 158 | } catch (const RtMidiError &err) { 159 | device->ok = false; 160 | device->msg = err.what(); 161 | 162 | return RT_MIDI_API_UNSPECIFIED; 163 | } 164 | } 165 | 166 | static void callback_proxy(double timeStamp, 167 | std::vector *message, 168 | void *userData) { 169 | CallbackProxyUserData *data = 170 | reinterpret_cast(userData); 171 | data->c_callback(timeStamp, message->data(), message->size(), 172 | data->user_data); 173 | } 174 | 175 | void rtmidi_in_set_callback(RtMidiInPtr device, RtMidiCCallback callback, 176 | void *userData) { 177 | device->data = (void *)new CallbackProxyUserData(callback, userData); 178 | try { 179 | ((RtMidiIn *)device->ptr)->setCallback(callback_proxy, device->data); 180 | } catch (const RtMidiError &err) { 181 | device->ok = false; 182 | device->msg = err.what(); 183 | delete (CallbackProxyUserData *)device->data; 184 | device->data = 0; 185 | } 186 | } 187 | 188 | void rtmidi_in_cancel_callback(RtMidiInPtr device) { 189 | try { 190 | ((RtMidiIn *)device->ptr)->cancelCallback(); 191 | delete (CallbackProxyUserData *)device->data; 192 | device->data = 0; 193 | } catch (const RtMidiError &err) { 194 | device->ok = false; 195 | device->msg = err.what(); 196 | } 197 | } 198 | 199 | void rtmidi_in_ignore_types(RtMidiInPtr device, bool midiSysex, bool midiTime, 200 | bool midiSense) { 201 | ((RtMidiIn *)device->ptr)->ignoreTypes(midiSysex, midiTime, midiSense); 202 | } 203 | 204 | double rtmidi_in_get_message(RtMidiInPtr device, unsigned char *message, 205 | size_t *size) { 206 | try { 207 | // FIXME: use allocator to achieve efficient buffering 208 | std::vector v; 209 | double ret = ((RtMidiIn *)device->ptr)->getMessage(&v); 210 | 211 | if (v.size() > 0 && v.size() <= *size) { 212 | memcpy(message, v.data(), (int)v.size()); 213 | } 214 | 215 | *size = v.size(); 216 | return ret; 217 | } catch (const RtMidiError &err) { 218 | device->ok = false; 219 | device->msg = err.what(); 220 | return -1; 221 | } catch (...) { 222 | device->ok = false; 223 | device->msg = "Unknown error"; 224 | return -1; 225 | } 226 | } 227 | 228 | /* RtMidiOut API */ 229 | RtMidiOutPtr rtmidi_out_create_default() { 230 | RtMidiWrapper *wrp = new RtMidiWrapper; 231 | 232 | try { 233 | RtMidiOut *rOut = new RtMidiOut(); 234 | 235 | wrp->ptr = (void *)rOut; 236 | wrp->ok = true; 237 | wrp->msg = ""; 238 | 239 | } catch (const RtMidiError &err) { 240 | wrp->ptr = 0; 241 | wrp->ok = false; 242 | wrp->msg = err.what(); 243 | } 244 | 245 | return wrp; 246 | } 247 | 248 | RtMidiOutPtr rtmidi_out_create(enum RtMidiApi api, const char *clientName) { 249 | RtMidiWrapper *wrp = new RtMidiWrapper; 250 | std::string name = clientName; 251 | 252 | try { 253 | RtMidiOut *rOut = new RtMidiOut((RtMidi::Api)api, name); 254 | 255 | wrp->ptr = (void *)rOut; 256 | wrp->ok = true; 257 | wrp->msg = ""; 258 | 259 | } catch (const RtMidiError &err) { 260 | wrp->ptr = 0; 261 | wrp->ok = false; 262 | wrp->msg = err.what(); 263 | } 264 | 265 | return wrp; 266 | } 267 | 268 | void rtmidi_out_free(RtMidiOutPtr device) { 269 | delete (RtMidiOut *)device->ptr; 270 | delete device; 271 | } 272 | 273 | enum RtMidiApi rtmidi_out_get_current_api(RtMidiPtr device) { 274 | try { 275 | return (RtMidiApi)((RtMidiOut *)device->ptr)->getCurrentApi(); 276 | 277 | } catch (const RtMidiError &err) { 278 | device->ok = false; 279 | device->msg = err.what(); 280 | 281 | return RT_MIDI_API_UNSPECIFIED; 282 | } 283 | } 284 | 285 | int rtmidi_out_send_message(RtMidiOutPtr device, const unsigned char *message, 286 | int length) { 287 | try { 288 | ((RtMidiOut *)device->ptr)->sendMessage(message, length); 289 | return 0; 290 | } catch (const RtMidiError &err) { 291 | device->ok = false; 292 | device->msg = err.what(); 293 | return -1; 294 | } catch (...) { 295 | device->ok = false; 296 | device->msg = "Unknown error"; 297 | return -1; 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /rtmidi/rtmidi_c.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #ifndef RTMIDI_C_H 5 | #define RTMIDI_C_H 6 | 7 | #if defined(RTMIDI_EXPORT) 8 | #define RTMIDIAPI __declspec(dllexport) 9 | #else 10 | #define RTMIDIAPI //__declspec(dllimport) 11 | #endif 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | //! Wraps an RtMidi object for C function return statuses. 18 | struct RtMidiWrapper { 19 | //! The wrapped RtMidi object. 20 | void *ptr; 21 | void *data; 22 | 23 | //! True when the last function call was OK. 24 | bool ok; 25 | 26 | //! If an error occured (ok != true), set to an error message. 27 | const char *msg; 28 | }; 29 | 30 | //! Typedef for a generic RtMidi pointer. 31 | typedef struct RtMidiWrapper *RtMidiPtr; 32 | 33 | //! Typedef for a generic RtMidiIn pointer. 34 | typedef struct RtMidiWrapper *RtMidiInPtr; 35 | 36 | //! Typedef for a generic RtMidiOut pointer. 37 | typedef struct RtMidiWrapper *RtMidiOutPtr; 38 | 39 | enum RtMidiApi { 40 | RT_MIDI_API_UNSPECIFIED, /*!< Search for a working compiled API. */ 41 | RT_MIDI_API_MACOSX_CORE, /*!< Macintosh OS-X Core Midi API. */ 42 | RT_MIDI_API_LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ 43 | RT_MIDI_API_UNIX_JACK, /*!< The Jack Low-Latency MIDI Server API. */ 44 | RT_MIDI_API_WINDOWS_MM, /*!< The Microsoft Multimedia MIDI API. */ 45 | RT_MIDI_API_RTMIDI_DUMMY /*!< A compilable but non-functional API. */ 46 | }; 47 | 48 | enum RtMidiErrorType { 49 | RT_ERROR_WARNING, 50 | RT_ERROR_DEBUG_WARNING, 51 | RT_ERROR_UNSPECIFIED, 52 | RT_ERROR_NO_DEVICES_FOUND, 53 | RT_ERROR_INVALID_DEVICE, 54 | RT_ERROR_MEMORY_ERROR, 55 | RT_ERROR_INVALID_PARAMETER, 56 | RT_ERROR_INVALID_USE, 57 | RT_ERROR_DRIVER_ERROR, 58 | RT_ERROR_SYSTEM_ERROR, 59 | RT_ERROR_THREAD_ERROR 60 | }; 61 | 62 | /*! The type of a RtMidi callback function. 63 | * \param timeStamp The time at which the message has been received. 64 | * \param message The midi message. 65 | * \param userData Additional user data for the callback. 66 | */ 67 | typedef void (*RtMidiCCallback)(double timeStamp, const unsigned char *message, 68 | size_t message_size, void *userData); 69 | 70 | /* RtMidi API */ 71 | 72 | /*! Determine the available compiled MIDI APIs. 73 | * If the given `apis` parameter is null, returns the number of available APIs. 74 | * Otherwise, fill the given apis array with the RtMidi::Api values. 75 | * 76 | * \param apis An array or a null value. 77 | */ 78 | RTMIDIAPI int rtmidi_get_compiled_api( 79 | enum RtMidiApi *apis); // return length for NULL argument. 80 | 81 | //! Report an error. 82 | RTMIDIAPI void rtmidi_error(enum RtMidiErrorType type, const char *errorString); 83 | 84 | /*! Open a MIDI port. 85 | * 86 | * \param port Must be greater than 0 87 | * \param portName Name for the application port. 88 | */ 89 | RTMIDIAPI void rtmidi_open_port(RtMidiPtr device, unsigned int portNumber, 90 | const char *portName); 91 | 92 | /*! Creates a virtual MIDI port to which other software applications can 93 | * connect. 94 | * 95 | * \param portName Name for the application port. 96 | */ 97 | RTMIDIAPI void rtmidi_open_virtual_port(RtMidiPtr device, const char *portName); 98 | 99 | /*! Close a MIDI connection. 100 | */ 101 | RTMIDIAPI void rtmidi_close_port(RtMidiPtr device); 102 | 103 | /*! Return the number of available MIDI ports. 104 | */ 105 | RTMIDIAPI unsigned int rtmidi_get_port_count(RtMidiPtr device); 106 | 107 | /*! Return a string identifier for the specified MIDI input port number. 108 | */ 109 | RTMIDIAPI const char *rtmidi_get_port_name(RtMidiPtr device, 110 | unsigned int portNumber); 111 | 112 | /* RtMidiIn API */ 113 | 114 | //! Create a default RtMidiInPtr value, with no initialization. 115 | RTMIDIAPI RtMidiInPtr rtmidi_in_create_default(); 116 | 117 | /*! Create a RtMidiInPtr value, with given api, clientName and queueSizeLimit. 118 | * 119 | * \param api An optional API id can be specified. 120 | * \param clientName An optional client name can be specified. This 121 | * will be used to group the ports that are created 122 | * by the application. 123 | * \param queueSizeLimit An optional size of the MIDI input queue can be 124 | * specified. 125 | */ 126 | RTMIDIAPI RtMidiInPtr rtmidi_in_create(enum RtMidiApi api, 127 | const char *clientName, 128 | unsigned int queueSizeLimit); 129 | 130 | //! Deallocate the given pointer. 131 | RTMIDIAPI void rtmidi_in_free(RtMidiInPtr device); 132 | 133 | //! Returns the MIDI API specifier for the given instance of RtMidiIn. 134 | RTMIDIAPI enum RtMidiApi rtmidi_in_get_current_api(RtMidiPtr device); 135 | 136 | //! Set a callback function to be invoked for incoming MIDI messages. 137 | RTMIDIAPI void rtmidi_in_set_callback(RtMidiInPtr device, 138 | RtMidiCCallback callback, void *userData); 139 | 140 | //! Cancel use of the current callback function (if one exists). 141 | RTMIDIAPI void rtmidi_in_cancel_callback(RtMidiInPtr device); 142 | 143 | //! Specify whether certain MIDI message types should be queued or ignored 144 | //! during input. 145 | RTMIDIAPI void rtmidi_in_ignore_types(RtMidiInPtr device, bool midiSysex, 146 | bool midiTime, bool midiSense); 147 | 148 | /*! Fill the user-provided array with the data bytes for the next available 149 | * MIDI message in the input queue and return the event delta-time in seconds. 150 | * 151 | * \param message Must point to a char* that is already allocated. 152 | * SYSEX messages maximum size being 1024, a statically 153 | * allocated array could 154 | * be sufficient. 155 | * \param size Is used to return the size of the message obtained. 156 | */ 157 | RTMIDIAPI double rtmidi_in_get_message(RtMidiInPtr device, 158 | unsigned char *message, size_t *size); 159 | 160 | /* RtMidiOut API */ 161 | 162 | //! Create a default RtMidiInPtr value, with no initialization. 163 | RTMIDIAPI RtMidiOutPtr rtmidi_out_create_default(); 164 | 165 | /*! Create a RtMidiOutPtr value, with given and clientName. 166 | * 167 | * \param api An optional API id can be specified. 168 | * \param clientName An optional client name can be specified. This 169 | * will be used to group the ports that are created 170 | * by the application. 171 | */ 172 | RTMIDIAPI RtMidiOutPtr rtmidi_out_create(enum RtMidiApi api, 173 | const char *clientName); 174 | 175 | //! Deallocate the given pointer. 176 | RTMIDIAPI void rtmidi_out_free(RtMidiOutPtr device); 177 | 178 | //! Returns the MIDI API specifier for the given instance of RtMidiOut. 179 | RTMIDIAPI enum RtMidiApi rtmidi_out_get_current_api(RtMidiPtr device); 180 | 181 | //! Immediately send a single message out an open MIDI output port. 182 | RTMIDIAPI int rtmidi_out_send_message(RtMidiOutPtr device, 183 | const unsigned char *message, int length); 184 | 185 | #ifdef __cplusplus 186 | } 187 | #endif 188 | #endif 189 | -------------------------------------------------------------------------------- /soundio/_example-c/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(jack),1) 2 | CFLAGS += -DSOUNDIO_HAVE_JACK=1 3 | LDFLAGS += -lm -ljack -pthread 4 | endif 5 | ifeq ($(linux),1) 6 | CFLAGS += -DSOUNDIO_HAVE_ALSA=1 7 | LDFLAGS += -lm -lasound -pthread 8 | endif 9 | ifeq ($(macos),1) 10 | CFLAGS += -DSOUNDIO_HAVE_COREAUDIO=1 11 | LDFLAGS += -lm -framework CoreAudio -framework CoreMIDI -framework CoreFoundation 12 | endif 13 | ifeq ($(windows),1) 14 | CFLAGS += -DSOUNDIO_HAVE_WASAPI=1 15 | LDFLAGS += -lm -lole32 -lksuser -lwinmm -lws2_32 -mwindows -static 16 | endif 17 | 18 | example: example.o ../soundio_amalg.o 19 | $(CC) $^ $(LDFLAGS) -o $@ 20 | clean: 21 | rm -f example example.exe example.o ../soundio_amalg.o 22 | -------------------------------------------------------------------------------- /soundio/_example-c/example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../soundio.h" 5 | 6 | #define SAMPLE_RATE 44100 7 | #define SINE_HZ 440 8 | 9 | static const float PI = 3.1415926535f; 10 | static float seconds_offset = 0.0f; 11 | 12 | static void write_cb(struct SoundIoOutStream *out, int frame_count_min, 13 | int frame_count_max) { 14 | const struct SoundIoChannelLayout *layout = &out->layout; 15 | float float_sample_rate = out->sample_rate; 16 | float seconds_per_frame = 1.0f / float_sample_rate; 17 | struct SoundIoChannelArea *areas; 18 | int frames_left = frame_count_max; 19 | int err; 20 | 21 | while (frames_left > 0) { 22 | int frame_count = frames_left; 23 | 24 | if ((err = soundio_outstream_begin_write(out, &areas, &frame_count))) { 25 | fprintf(stderr, "%s\n", soundio_strerror(err)); 26 | return; 27 | } 28 | 29 | if (frame_count == 0) { 30 | break; 31 | } 32 | 33 | float radians_per_second = SINE_HZ * 2.0f * PI; 34 | for (int frame = 0; frame < frame_count; frame += 1) { 35 | float sample = sinf((seconds_offset + frame * seconds_per_frame) * 36 | radians_per_second); 37 | for (int channel = 0; channel < layout->channel_count; 38 | channel += 1) { 39 | float *ptr = 40 | (float *)(areas[channel].ptr + areas[channel].step * frame); 41 | *ptr = sample; 42 | } 43 | } 44 | seconds_offset = 45 | fmodf(seconds_offset + seconds_per_frame * frame_count, 1.0f); 46 | 47 | if ((err = soundio_outstream_end_write(out))) { 48 | fprintf(stderr, "%s\n", soundio_strerror(err)); 49 | return; 50 | } 51 | 52 | frames_left -= frame_count; 53 | } 54 | } 55 | 56 | static void underflow_cb(struct SoundIoOutStream *out) { 57 | fprintf(stderr, "underflow\n"); 58 | } 59 | 60 | int main() { 61 | struct SoundIo *soundio = soundio_create(); 62 | if (soundio == NULL) { 63 | fprintf(stderr, "soundio_create(): error\n"); 64 | return 1; 65 | } 66 | 67 | int err = soundio_connect(soundio); 68 | if (err) { 69 | fprintf(stderr, "soundio_connect(): %s\n", soundio_strerror(err)); 70 | return 1; 71 | } 72 | 73 | soundio_flush_events(soundio); 74 | 75 | int index = soundio_default_output_device_index(soundio); 76 | if (index < 0) { 77 | fprintf(stderr, "soundio_default_output_device_index(): not found\n"); 78 | return 1; 79 | } 80 | 81 | struct SoundIoDevice *dev = soundio_get_output_device(soundio, index); 82 | if (dev == NULL) { 83 | fprintf(stderr, "soundio_get_output_device(): error\n"); 84 | return 1; 85 | } 86 | 87 | struct SoundIoOutStream *out = soundio_outstream_create(dev); 88 | if (out == NULL) { 89 | fprintf(stderr, "soundio_outstream_create(): error\n"); 90 | return 1; 91 | } 92 | 93 | out->write_callback = write_cb; 94 | out->underflow_callback = underflow_cb; 95 | out->name = "example"; 96 | out->sample_rate = SAMPLE_RATE; 97 | out->format = SoundIoFormatFloat32NE; 98 | 99 | if ((err = soundio_outstream_open(out))) { 100 | fprintf(stderr, "soundio_outstream_open(): %s\n", 101 | soundio_strerror(err)); 102 | return 1; 103 | } 104 | 105 | if ((err = soundio_outstream_start(out))) { 106 | fprintf(stderr, "soundio_outstream_start(): %s\n", 107 | soundio_strerror(err)); 108 | return 1; 109 | } 110 | 111 | for (;;) { 112 | soundio_wait_events(soundio); 113 | } 114 | 115 | soundio_outstream_destroy(out); 116 | soundio_device_unref(dev); 117 | soundio_destroy(soundio); 118 | 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /soundio/amalg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | AMALG_H=soundio.h 4 | AMALG_C=soundio_amalg.c 5 | 6 | echo "Downloading libsoundio sources..." 7 | git clone https://github.com/andrewrk/libsoundio.git 8 | 9 | cd libsoundio 10 | 11 | cat soundio/endian.h soundio/soundio.h | sed 's!\(#include "endian.h"\)!// (amalg) \1!' > $AMALG_H 12 | 13 | echo "#include \"$AMALG_H\"" > $AMALG_C 14 | echo '#define SOUNDIO_VERSION_STRING ""' >> $AMALG_C 15 | echo '#define SOUNDIO_VERSION_MAJOR 0' >> $AMALG_C 16 | echo '#define SOUNDIO_VERSION_MINOR 0' >> $AMALG_C 17 | echo '#define SOUNDIO_VERSION_PATCH 0' >> $AMALG_C 18 | 19 | sed 's!\(#include ".*.h"\)!// (amalg) \1!' >> $AMALG_C << EOF 20 | $(cat src/soundio_internal.h) 21 | $(cat src/os.h) 22 | $(cat src/atomics.h) 23 | $(cat src/list.h) 24 | $(cat src/ring_buffer.h) 25 | $(cat src/util.h) 26 | $(cat src/dummy.h) 27 | 28 | #if SOUNDIO_HAVE_JACK 29 | $(cat src/jack.h) 30 | #endif 31 | #if SOUNDIO_HAVE_ALSA 32 | $(cat src/alsa.h) 33 | #endif 34 | #if SOUNDIO_HAVE_PULSEAUDIO 35 | $(cat src/pulseaudio.h) 36 | #endif 37 | #if SOUNDIO_HAVE_COREAUDIO 38 | $(cat src/coreaudio.h) 39 | #endif 40 | #if SOUNDIO_HAVE_WASAPI 41 | $(cat src/wasapi.h) 42 | #endif 43 | 44 | $(cat src/soundio_private.h) 45 | 46 | $(cat src/os.c) 47 | $(cat src/ring_buffer.c) 48 | $(cat src/util.c) 49 | $(cat src/dummy.c) 50 | $(cat src/soundio.c) 51 | $(cat src/channel_layout.c) 52 | 53 | #if SOUNDIO_HAVE_JACK 54 | $(sed \ 55 | -e 's!refresh_devices(!jack_refresh_devices(!' \ 56 | -e 's!my_flush_events(!jack_my_flush_events(!' \ 57 | src/jack.c) 58 | #endif 59 | #if SOUNDIO_HAVE_ALSA 60 | $(sed \ 61 | -e 's!refresh_devices(!alsa_refresh_devices(!' \ 62 | -e 's!my_flush_events(!alsa_my_flush_events(!' \ 63 | src/alsa.c) 64 | #endif 65 | #if SOUNDIO_HAVE_PULSEAUDIO 66 | $(sed \ 67 | -e 's!set_all_device_channel_layouts(!pa_set_all_device_channel_layouts(!' \ 68 | -e 's!set_all_device_formats(!pa_set_all_device_formats(!' \ 69 | -e 's!refresh_devices(!pa_refresh_devices(!' \ 70 | -e 's!my_flush_events(!pa_my_flush_events(!' \ 71 | src/pulseaudio.c) 72 | #endif 73 | #if SOUNDIO_HAVE_COREAUDIO 74 | $(cat src/coreaudio.c) 75 | #endif 76 | #if SOUNDIO_HAVE_WASAPI 77 | $(cat src/wasapi.c) 78 | #endif 79 | EOF 80 | 81 | mv $AMALG_C $AMALG_H .. 82 | cd .. 83 | 84 | rm -rf libsoundio 85 | 86 | echo "Done." 87 | 88 | --------------------------------------------------------------------------------