// Write writes given PCM data. // Returns wrote value is total bytes was written. func (handle *Handle) Write(buf []byte) (wrote int, err error) { frames := len(buf) / handle.SampleSize() / handle.Channels w := C.snd_pcm_writei(handle.cHandle, unsafe.Pointer(&buf[0]), C.snd_pcm_uframes_t(frames)) // Underrun? Retry. if w == -C.EPIPE { C.snd_pcm_prepare(handle.cHandle) w = C.snd_pcm_writei(handle.cHandle, unsafe.Pointer(&buf[0]), C.snd_pcm_uframes_t(frames)) } if w < 0 { return 0, fmt.Errorf("Write failed. %s", strError(_Ctype_int(w))) } wrote = int(w) wrote *= handle.FrameSize() return wrote, nil }
// Read reads samples into a buffer and returns the amount read. func (c *CaptureDevice) Read(buffer interface{}) (samples int, err error) { bufferType := reflect.TypeOf(buffer) if !(bufferType.Kind() == reflect.Array || bufferType.Kind() == reflect.Slice) { return 0, errors.New("Read requires an array type") } sizeError := errors.New("Read requires a matching sample size") switch bufferType.Elem().Kind() { case reflect.Int8: if c.formatSampleSize() != 1 { return 0, sizeError } case reflect.Int16: if c.formatSampleSize() != 2 { return 0, sizeError } case reflect.Int32, reflect.Float32: if c.formatSampleSize() != 4 { return 0, sizeError } case reflect.Float64: if c.formatSampleSize() != 8 { return 0, sizeError } default: return 0, errors.New("Read does not support this format") } val := reflect.ValueOf(buffer) length := val.Len() sliceData := val.Slice(0, length) var frames = C.snd_pcm_uframes_t(length / c.Channels) bufPtr := unsafe.Pointer(sliceData.Index(0).Addr().Pointer()) ret := C.snd_pcm_readi(c.h, bufPtr, frames) if ret == -C.EPIPE { C.snd_pcm_prepare(c.h) return 0, ErrOverrun } else if ret < 0 { return 0, createError("read error", C.int(ret)) } samples = int(ret) * c.Channels return }
// Skip certain number of frames func (handle *Handle) SkipFrames(frames int) (int, error) { // Get safe count of frames which can be forwarded. var framesForwardable C.snd_pcm_sframes_t framesForwardable = C.snd_pcm_forwardable(handle.cHandle) if framesForwardable < 0 { return 0, errors.New(fmt.Sprintf("Retrieving forwardable frames failed. %s", strError(_Ctype_int(framesForwardable)))) } if int(_Ctype_int(framesForwardable)) < frames { frames = int(_Ctype_int(framesForwardable)) } // Move application frame position forward. var framesForwarded C.snd_pcm_sframes_t framesForwarded = C.snd_pcm_forward(handle.cHandle, C.snd_pcm_uframes_t(frames)) if framesForwarded < 0 { return 0, errors.New(fmt.Sprintf("Cannot forward frames. %s", strError(_Ctype_int(framesForwarded)))) } return int(_Ctype_int(framesForwarded)), nil }
// ApplyHwParams changes ALSA hardware parameters for the current stream. func (handle *Handle) ApplyHwParams() error { var cHwParams *C.snd_pcm_hw_params_t err := C.snd_pcm_hw_params_malloc(&cHwParams) if err < 0 { return errors.New(fmt.Sprintf("Cannot allocate hardware parameter structure. %s", strError(err))) } err = C.snd_pcm_hw_params_any(handle.cHandle, cHwParams) if err < 0 { return errors.New(fmt.Sprintf("Cannot initialize hardware parameter structure. %s", strError(err))) } err = C.snd_pcm_hw_params_set_access(handle.cHandle, cHwParams, C.SND_PCM_ACCESS_RW_INTERLEAVED) if err < 0 { return errors.New(fmt.Sprintf("Cannot set access type. %s", strError(err))) } err = C.snd_pcm_hw_params_set_format(handle.cHandle, cHwParams, C.snd_pcm_format_t(handle.SampleFormat)) if err < 0 { return errors.New(fmt.Sprintf("Cannot set sample format. %s", strError(err))) } var cSampleRate _Ctype_uint = _Ctype_uint(handle.SampleRate) err = C.snd_pcm_hw_params_set_rate_near(handle.cHandle, cHwParams, &cSampleRate, nil) if err < 0 { return errors.New(fmt.Sprintf("Cannot set sample rate. %s", strError(err))) } err = C.snd_pcm_hw_params_set_channels(handle.cHandle, cHwParams, _Ctype_uint(handle.Channels)) if err < 0 { return errors.New(fmt.Sprintf("Cannot set number of channels. %s", strError(err))) } if handle.Periods > 0 { // Set number of periods. Periods used to be called fragments. /*err = C.snd_pcm_hw_params_set_periods(handle.cHandle, cHwParams, _Ctype_uint(handle.Periods), 0) if err < 0 { return os.NewError(fmt.Sprintf("Cannot set number of periods. %s", strError(err))) }*/ var cPeriods _Ctype_uint = _Ctype_uint(handle.Periods) var cDir _Ctype_int = 0 // Exact value is <,=,> the returned one following dir (-1,0,1) err = C.snd_pcm_hw_params_set_periods_near(handle.cHandle, cHwParams, &cPeriods, &cDir) if err < 0 { return errors.New(fmt.Sprintf("Cannot set number of periods. %s", strError(err))) } } if handle.Buffersize > 0 { // Set buffer size (in frames). The resulting latency is given by // latency = periodsize * periods / (rate * bytes_per_frame) /*err = C.snd_pcm_hw_params_set_buffer_size(handle.cHandle, cHwParams, _Ctypedef_snd_pcm_uframes_t(handle.Buffersize)) if err < 0 { return os.NewError(fmt.Sprintf("Cannot set buffersize. %s", strError(err))) }*/ var cBuffersize C.snd_pcm_uframes_t = C.snd_pcm_uframes_t(handle.Buffersize) err = C.snd_pcm_hw_params_set_buffer_size_near(handle.cHandle, cHwParams, &cBuffersize) if err < 0 { return errors.New(fmt.Sprintf("Cannot set buffersize. %s", strError(err))) } } // Drain current data and make sure we aren't underrun. C.snd_pcm_drain(handle.cHandle) err = C.snd_pcm_hw_params(handle.cHandle, cHwParams) if err < 0 { return errors.New(fmt.Sprintf("Cannot set hardware parameters. %s", strError(err))) } C.snd_pcm_hw_params_free(cHwParams) return nil }
func (d *device) createDevice(deviceName string, channels int, format Format, rate int, playback bool, bufferParams BufferParams) (err error) { deviceCString := C.CString(deviceName) defer C.free(unsafe.Pointer(deviceCString)) var ret C.int if playback { ret = C.snd_pcm_open(&d.h, deviceCString, C.SND_PCM_STREAM_PLAYBACK, 0) } else { ret = C.snd_pcm_open(&d.h, deviceCString, C.SND_PCM_STREAM_CAPTURE, 0) } if ret < 0 { return fmt.Errorf("could not open ALSA device %s", deviceName) } runtime.SetFinalizer(d, (*device).Close) var hwParams *C.snd_pcm_hw_params_t ret = C.snd_pcm_hw_params_malloc(&hwParams) if ret < 0 { return createError("could not alloc hw params", ret) } defer C.snd_pcm_hw_params_free(hwParams) ret = C.snd_pcm_hw_params_any(d.h, hwParams) if ret < 0 { return createError("could not set default hw params", ret) } ret = C.snd_pcm_hw_params_set_access(d.h, hwParams, C.SND_PCM_ACCESS_RW_INTERLEAVED) if ret < 0 { return createError("could not set access params", ret) } ret = C.snd_pcm_hw_params_set_format(d.h, hwParams, C.snd_pcm_format_t(format)) if ret < 0 { return createError("could not set format params", ret) } ret = C.snd_pcm_hw_params_set_channels(d.h, hwParams, C.uint(channels)) if ret < 0 { return createError("could not set channels params", ret) } ret = C.snd_pcm_hw_params_set_rate(d.h, hwParams, C.uint(rate), 0) if ret < 0 { return createError("could not set rate params", ret) } var bufferSize = C.snd_pcm_uframes_t(bufferParams.BufferFrames) if bufferParams.BufferFrames == 0 { // Default buffer size: max buffer size ret = C.snd_pcm_hw_params_get_buffer_size_max(hwParams, &bufferSize) if ret < 0 { return createError("could not get buffer size", ret) } } ret = C.snd_pcm_hw_params_set_buffer_size_near(d.h, hwParams, &bufferSize) if ret < 0 { return createError("could not set buffer size", ret) } // Default period size: 1/8 of a second var periodFrames = C.snd_pcm_uframes_t(rate / 8) if bufferParams.PeriodFrames > 0 { periodFrames = C.snd_pcm_uframes_t(bufferParams.PeriodFrames) } else if bufferParams.Periods > 0 { periodFrames = C.snd_pcm_uframes_t(int(bufferSize) / bufferParams.Periods) } ret = C.snd_pcm_hw_params_set_period_size_near(d.h, hwParams, &periodFrames, nil) if ret < 0 { return createError("could not set period size", ret) } var periods = C.uint(0) ret = C.snd_pcm_hw_params_get_periods(hwParams, &periods, nil) if ret < 0 { return createError("could not get periods", ret) } ret = C.snd_pcm_hw_params(d.h, hwParams) if ret < 0 { return createError("could not set hw params", ret) } d.frames = int(periodFrames) d.Channels = channels d.Format = format d.Rate = rate d.BufferParams.BufferFrames = int(bufferSize) d.BufferParams.PeriodFrames = int(periodFrames) d.BufferParams.Periods = int(periods) return }