// Code generated by cmd/cgo; DO NOT EDIT.

//line /home/stu/go/pkg/mod/github.com/hajimehoshi/oto@v0.7.1/driver_linux.go:1:1
// Copyright 2017 The Oto Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !js
// +build !android
// +build !ios

package oto

/*
#cgo pkg-config: alsa

#include <alsa/asoundlib.h>

static void check(int *err, int newErr) {
  if (*err) {
    return;
  }
  *err = newErr;
}

static int ALSA_hw_params(
    snd_pcm_t          *pcm,
    unsigned           sampleRate,
    unsigned           numChans,
    snd_pcm_format_t   format,
    snd_pcm_uframes_t* buffer_size,
    snd_pcm_uframes_t* period_size) {
  snd_pcm_hw_params_t* params = NULL;
  int err = 0;
  snd_pcm_hw_params_alloca(&params);
  check(&err, snd_pcm_hw_params_any(pcm, params));

  check(&err, snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED));
  check(&err, snd_pcm_hw_params_set_format(pcm, params, format));
  check(&err, snd_pcm_hw_params_set_channels(pcm, params, numChans));
  check(&err, snd_pcm_hw_params_set_rate_resample(pcm, params, 1));
  check(&err, snd_pcm_hw_params_set_rate_near(pcm, params, &sampleRate, NULL));
  check(&err, snd_pcm_hw_params_set_buffer_size_near(pcm, params, buffer_size));
  check(&err, snd_pcm_hw_params_set_period_size_near(pcm, params, period_size, NULL));

  check(&err, snd_pcm_hw_params(pcm, params));

  return err;
}
*/
import _ "unsafe"

import (
	"fmt"
	"unsafe"
)

type driver struct {
	handle          * /*line :66:19*/_Ctype_snd_pcm_t /*line :66:30*/
	buf             []byte
	bufSamples      int
	numChans        int
	bitDepthInBytes int
}

func alsaError(err  /*line :73:20*/_Ctype_int /*line :73:25*/) error {
	return fmt.Errorf("oto: ALSA error: %s", ( /*line :74:43*/_Cfunc_GoString /*line :74:52*/)(( /*line :74:54*/_Cfunc_snd_strerror /*line :74:67*/)(err)))
}

func newDriver(sampleRate, numChans, bitDepthInBytes, bufferSizeInBytes int) (tryWriteCloser, error) {
	p := &driver{
		numChans:        numChans,
		bitDepthInBytes: bitDepthInBytes,
	}

	// open a default ALSA audio device for blocking stream playback
	cs := ( /*line :84:8*/_Cfunc_CString /*line :84:16*/)("default")
	defer func() func() { _cgo0 := /*line :85:15*/unsafe.Pointer(cs); return func() { _cgoCheckPointer(_cgo0, nil); /*line :85:34*/_Cfunc_free(_cgo0); }}()()
	if errCode := func() _Ctype_int{ _cgoBase0 := /*line :86:31*/&p.handle; _cgo0 := _cgoBase0; var _cgo1 *_Ctype_char = /*line :86:42*/cs; var _cgo2 _Ctype_snd_pcm_stream_t = /*line :86:46*/_Ciconst_SND_PCM_STREAM_PLAYBACK /*line :86:71*/; var _cgo3 _Ctype_int = /*line :86:73*/0; _cgoCheckPointer(_cgoBase0, 0 == 0); return /*line :86:75*/_Cfunc_snd_pcm_open(_cgo0, _cgo1, _cgo2, _cgo3); }(); errCode < 0 {
		return nil, alsaError(errCode)
	}

	// bufferSize is the total size of the main circular buffer fullness of this buffer
	// oscilates somewhere between bufferSize and bufferSize-periodSize
	bufferSize :=  /*line :92:16*/_Ctype_snd_pcm_uframes_t /*line :92:35*/(bufferSizeInBytes / (numChans * bitDepthInBytes))
	// periodSize is the number of samples that will be taken from the main circular
	// buffer at once, we leave this value to bufferSize, because ALSA will change that
	// to the maximum viable number, obviously lower than bufferSize
	periodSize := bufferSize

	// choose the correct sample format according to bitDepthInBytes
	var format  /*line :99:13*/_Ctype_snd_pcm_format_t /*line :99:31*/
	switch bitDepthInBytes {
	case 1:
		format = ( /*line :102:12*/_Ciconst_SND_PCM_FORMAT_U8 /*line :102:30*/)
	case 2:
		format = ( /*line :104:12*/_Ciconst_SND_PCM_FORMAT_S16_LE /*line :104:34*/)
	default:
		panic(fmt.Errorf("oto: bitDepthInBytes must be 1 or 2, got %d", bitDepthInBytes))
	}

	// set the device hardware parameters according to sampleRate, numChans, format, bufferSize
	// and periodSize
	//
	// bufferSize and periodSize are passed as pointers, because they may be changed according
	// to the wisdom of ALSA
	//
	// ALSA will try too keep them as close to what was requested as possible
	if errCode := func() _Ctype_int{ _cgo0 := /*line :116:33*/p.handle; var _cgo1 _Ctype_uint = _Ctype_uint /*line :116:49*/(sampleRate); var _cgo2 _Ctype_uint = _Ctype_uint /*line :116:69*/(numChans); var _cgo3 _Ctype_snd_pcm_format_t = /*line :116:81*/format; var _cgo4 *_Ctype_snd_pcm_uframes_t = /*line :116:89*/&bufferSize; var _cgo5 *_Ctype_snd_pcm_uframes_t = /*line :116:102*/&periodSize; _cgoCheckPointer(_cgo0, nil); return /*line :116:114*/_Cfunc_ALSA_hw_params(_cgo0, _cgo1, _cgo2, _cgo3, _cgo4, _cgo5); }(); errCode < 0 {
		p.Close()
		return nil, alsaError(errCode)
	}

	// Raspberry Pi 400 fix - for some reason the call to ALSA_hw_params can result in the bufferSize and periodSize
	// being the same (this could be becuase the rpi driver does not drop the periodSize below 1024, whereas desktop
	// Linux is happy to go lower). When the bufferSize and periodSize are the same it causes constant buffer underruns
	// that stutter the audio continuously.
	//
	// So in the case where an RPi (or any other driver) returns the same buffer and period size we will change the
	// buffer size to twice that of the period size and re-request hw params.
	if bufferSize == periodSize {
		bufferSize = periodSize * 2
		if errCode := func() _Ctype_int{ _cgo0 := /*line :130:34*/p.handle; var _cgo1 _Ctype_uint = _Ctype_uint /*line :130:50*/(sampleRate); var _cgo2 _Ctype_uint = _Ctype_uint /*line :130:70*/(numChans); var _cgo3 _Ctype_snd_pcm_format_t = /*line :130:82*/format; var _cgo4 *_Ctype_snd_pcm_uframes_t = /*line :130:90*/&bufferSize; var _cgo5 *_Ctype_snd_pcm_uframes_t = /*line :130:103*/&periodSize; _cgoCheckPointer(_cgo0, nil); return /*line :130:115*/_Cfunc_ALSA_hw_params(_cgo0, _cgo1, _cgo2, _cgo3, _cgo4, _cgo5); }(); errCode < 0 {
			p.Close()
			return nil, alsaError(errCode)
		}
	}

	// allocate the buffer of the size of the period, use the periodSize that we've got back
	// from ALSA after it's wise decision
	p.bufSamples = int(periodSize)
	p.buf = []byte{}

	return p, nil
}

func (p *driver) TryWrite(data []byte) (n int, err error) {
	bufSize := p.bufSamples * p.numChans * p.bitDepthInBytes
	for len(data) > 0 {
		toWrite := min(len(data), max(0, bufSize-len(p.buf)))
		p.buf = append(p.buf, data[:toWrite]...)
		data = data[toWrite:]
		n += toWrite

		// our buffer is not full and we've used up all the data, we'll keep them and finish
		if len(p.buf) < bufSize {
			break
		}

		// write samples to the main circular buffer
		wrote := func() _Ctype_snd_pcm_sframes_t{ _cgo0 := /*line :158:29*/p.handle; _cgoIndex1 := &/*line :158:55*/p.buf; _cgo1 := /*line :158:39*/unsafe.Pointer(&(*_cgoIndex1)[0]); var _cgo2 _Ctype_snd_pcm_uframes_t = _Ctype_snd_pcm_uframes_t /*line :158:85*/(p.bufSamples); _cgoCheckPointer(_cgo0, nil); _cgoCheckPointer(_cgo1, *_cgoIndex1); return /*line :158:100*/_Cfunc_snd_pcm_writei(_cgo0, _cgo1, _cgo2); }()
		if wrote == -( /*line :159:16*/_Ciconst_EPIPE /*line :159:22*/) {
			// Underrun!
			if errCode := func() _Ctype_int{ _cgo0 := /*line :161:36*/p.handle; _cgoCheckPointer(_cgo0, nil); return /*line :161:45*/_Cfunc_snd_pcm_prepare(_cgo0); }(); errCode < 0 {
				return 0, alsaError(errCode)
			}
			continue
		}
		if wrote < 0 {
			// an error occurred while writing samples
			return 0, alsaError( /*line :168:24*/_Ctype_int /*line :168:29*/(wrote))
		}
		p.buf = p.buf[int(wrote)*p.numChans*p.bitDepthInBytes:]
	}
	return n, nil
}

func (p *driver) Close() error {
	// drop the remaining unprocessed samples in the main circular buffer
	if errCode := func() _Ctype_int{ _cgo0 := /*line :177:31*/p.handle; _cgoCheckPointer(_cgo0, nil); return /*line :177:40*/_Cfunc_snd_pcm_drop(_cgo0); }(); errCode < 0 {
		return alsaError(errCode)
	}
	if errCode := func() _Ctype_int{ _cgo0 := /*line :180:32*/p.handle; _cgoCheckPointer(_cgo0, nil); return /*line :180:41*/_Cfunc_snd_pcm_close(_cgo0); }(); errCode < 0 {
		return alsaError(errCode)
	}
	return nil
}

func (d *driver) tryWriteCanReturnWithoutWaiting() bool {
	return true
}
