/* $NetBSD: audio.c,v 1.155 2002/03/23 17:17:10 kent Exp $ */ |
追従履歴:
2002/03/21: audio.c,v 1.154
2002/03/24: audio.c,v 1.155
int
audio_read(struct audio_softc *sc, struct uio *uio, int ioflag)
{
struct audio_ringbuffer *cb = &sc->sc_rr;
u_char *outp;
int error, s, used, cc, n;
const struct audio_params *params;
int hw_bits_per_sample;
if (cb->mmapped)
return EINVAL;
DPRINTFN(1,("audio_read: cc=%lu mode=%d\n",
(unsigned long)uio->uio_resid, sc->sc_mode));
params = &sc->sc_rparams;
switch (params->hw_encoding) {
case AUDIO_ENCODING_SLINEAR_LE:
case AUDIO_ENCODING_SLINEAR_BE:
case AUDIO_ENCODING_ULINEAR_LE:
case AUDIO_ENCODING_ULINEAR_BE:
hw_bits_per_sample = params->hw_channels * params->precision
* params->factor;
break;
default:
hw_bits_per_sample = 8 * params->factor / params->factor_denom;
}
error = 0;
/*
* If hardware is half-duplex and currently playing, return
* silence blocks based on the number of blocks we have output.
* half duplex のハードウェアで、現在再生中なら
* 無音ブロックを返す(?)。
*/
if (!sc->sc_full_duplex &&
(sc->sc_mode & AUMODE_PLAY)) {
while (uio->uio_resid > 0 && !error) {
s = splaudio();
for(;;) {
cc = sc->sc_pr.stamp - sc->sc_wstamp;
if (cc > 0)
break;
DPRINTF(("audio_read: stamp=%lu, wstamp=%lu\n",
sc->sc_pr.stamp, sc->sc_wstamp));
if (ioflag & IO_NDELAY) {
splx(s);
return (EWOULDBLOCK);
}
error = audio_sleep(&sc->sc_rchan, "aud_hr");
if (sc->sc_dying)
error = EIO;
if (error) {
splx(s);
return (error);
}
}
splx(s);
if (uio->uio_resid < cc)
cc = uio->uio_resid;
DPRINTFN(1,("audio_read: reading in write mode, "
"cc=%d\n", cc));
error = audio_silence_copyout(sc, cc, uio);
sc->sc_wstamp += cc;
}
return (error);
}
/*
* uio_resid がなくなるまでループする。
* ユーザランドから指定された残り読み込み量。
*/
while (uio->uio_resid > 0 && !error) {
/*
* ユーザランドに渡すデータはリングバッファ cb から直接渡す
* のではなく、一旦 rconvbuffer に移ったものの中から渡される。
* そのため rconvbuffer が空っぽなら if 文の中に入って
* rconvbuffer を埋める。rconvbuffer_end と _begin はポインタ
* ではなく rconvbuffer からの位置を示す整数値。従って
* _end - _begin == 0 なら rconvbuffer が空であることを示して
* いる。_end - _begin が負になることはありえないはずだが
* 念のためかしら? DIAGNOSTIC で printf 入れたほうがよくない?
* #if DIAGNOSTIC
* if (rconvbuffer_end - rconvbuffer_begin < 0)
* printf("hoge");
* #endif
*/
if (sc->sc_rconvbuffer_end - sc->sc_rconvbuffer_begin <= 0) {
s = splaudio();
/*
* リングバッファ cb の使用量(水位)が 1サンプリング分
* もなければ、それ以上たまるまで while() の中で sleep
* とかする。
*/
while (cb->used * 8 < hw_bits_per_sample) {
if (!sc->sc_rbus) {
error = audiostartr(sc);
if (error) {
splx(s);
return (error);
}
}
if (ioflag & IO_NDELAY) {
splx(s);
return (EWOULDBLOCK);
}
DPRINTFN(1, ("audio_read: sleep used=%d\n",
cb->used));
error = audio_sleep(&sc->sc_rchan, "aud_rd");
if (sc->sc_dying)
error = EIO;
if (error) {
splx(s);
return error;
}
}
/*
* Move data in the ring buffer to sc_rconvbuffer as
* possible with/without rate conversion.
* 周波数変換する/しないに関わらずリングバッファ(cb)
* の中のデータを rconvbuffer に移動する。
*/
used = cb->used;
outp = cb->outp;
cb->copying = 1;
splx(s);
/*
* cc はこれから移動しようとするバイト数。
* とりあえず今リングバッファにあるデータの
* 下限水位を越えている分を対象にする。
*/
cc = used - cb->usedlow; /* maximum to read */
/*
* rconvbuffer より大きくても読み込めないので
* rconvbuffer_size で律速する。
*/
if (cc > sc->sc_rconvbuffer_size)
cc = sc->sc_rconvbuffer_size;
/*
* factor_denom > 1 の場合、後の sw_code を通した後で
* データサイズが factor_denom 倍になるので、
* その分を考慮して読み込むサイズを減らしておく。
*/
n = cc * params->factor / params->factor_denom;
if (n < cc)
cc = n;
/* よく分からんけど端数を切り捨てて align する */
/* cc must be aligned by the sample size */
cc = (cc * 8 / hw_bits_per_sample) * hw_bits_per_sample / 8;
#ifdef DIAGNOSTIC
if (cc == 0)
printf("audio_read: cc=0 hw_bits_per_sample=%d\n",
hw_bits_per_sample);
#endif
/*
* The format of data in the ring buffer is
* [hw_sample_rate, hw_encoding, hw_precision, hw_channels]
* この時点でリングバッファ(cb)の中のデータは
* o ハードウェア独自のサンプリングレート
* o ハードウェア独自のエンコード
* o ハードウェア独自の precision
* o ハードウェア独自のチャンネル数
*/
#if NAURATECONV > 0
/*
* NAURATECONV が指定されているドライバの場合の処理。
* 周波数変換が必要ないドライバの場合でも auconv_record()
* が処理してくれるらしい。
*
* auconv_record() は周波数とチャンネル数を変換するもの。
* outp から cc バイトのデータを変換して rconvbuffer に
* 格納する。出力されたデータのサイズは rconvbuffer_end
* に入っている。
*/
sc->sc_rconvbuffer_end =
auconv_record(&sc->sc_rconv, params,
sc->sc_rconvbuffer, outp, cc);
#else
/*
* NAURATECONV が指定されていない場合はこちら。
* auconv_record() は結果としてリングバッファ(cb)から
* rconvbuffer へのコピーを行なっていることになるので、
* こちらでも memcpy を使って rconvbuffer にコピーし
* 整合性をとる。
*/
n = cb->end - outp;
if (cc <= n) {
memcpy(sc->sc_rconvbuffer, outp, cc);
} else {
memcpy(sc->sc_rconvbuffer, outp, n);
memcpy(sc->sc_rconvbuffer + n, cb->start,
cc - n);
}
sc->sc_rconvbuffer_end = cc;
#endif /* !NAURATECONV */
/*
* The format of data in sc_rconvbuffer is
* [sample_rate, hw_encoding, hw_precision, channels]
* この時点で rconvbuffer から始まるデータは
* o ユーザランドが指定したサンプリングレート
* o ハードウェア独自のエンコード
* o ハードウェア独自の precision
* o ユーザランドが指定したチャンネル数
*/
/*
* リングバッファ(cb)のポインタを書き戻す。
* これで cb にはもう用はない。
*/
used -= cc;
outp += cc;
if (outp >= cb->end)
outp -= cb->end - cb->start;
s = splaudio();
cb->outp = outp;
cb->used = used;
cb->copying = 0;
splx(s);
/*
* sw_code が指定されている場合 sw_code でエンコードと
* precision の変換を行なう。
*/
if (params->sw_code) {
/*
* 録音時の sw_code に渡す cc は、sw_code で
* 変換した後のデータサイズという変態仕様らしい。
* とりあえず rconvbuffer の中身全部にする。
*/
cc = sc->sc_rconvbuffer_end;
#ifdef DIAGNOSTIC
if (cc % params->factor != 0)
printf("audio_read: cc is not aligned"
": cc=%d factor=%d\n", cc,
params->factor);
#endif
/*
* factor をかけて sw_code を通した後のデータ
* サイズを事前に用意する。
* factor の定義は再生時の sw_code の通過前後の
* データサイズの比率なので、録音時はふつう逆数
* になる。
*/
cc = cc * params->factor_denom / params->factor;
#ifdef DIAGNOSTIC
if (cc == 0)
printf("audio_read: cc=0 "
"factor=%d/%d\n",
params->factor,
params->factor_denom);
#endif
/*
* sw_code を実行して rconvbuffer からのデータを
* 変換する。変換後のデータは rconvbuffer に上書き
* されるというこちらも変態仕様。
* 変換後のサイズはすでに cc で求まっているので
* rconvbuffer_end には cc を代入するだけ。
*/
params->sw_code(sc->hw_hdl, sc->sc_rconvbuffer,
cc);
sc->sc_rconvbuffer_end = cc;
}
/*
* rconvbuffer は先頭から使用しているので _begin = 0。
*/
sc->sc_rconvbuffer_begin = 0;
/*
* The format of data in sc_rconvbuffer is
* [sample_rate, encoding, precision, channels]
* この時点で rconvbuffer のデータは
* o ユーザランドから指定されたサンプリングレート
* o ユーザランドから指定されたエンコード
* o ユーザランドから指定された precision
* o ユーザランドから指定されたチャンネル数
*/
/* ここが if 文の終り */
}
/*
* ここでめでたく、rconvbuffer に必ずデータが入っている状態
* になったのでユーザランドにデータを渡す。
* cc はユーザランドに渡すバイト数。とりあえず目一杯渡すことに
* する。
*/
cc = sc->sc_rconvbuffer_end - sc->sc_rconvbuffer_begin;
/*
* cc が uio_resid よりも大きければ、返しすぎ(?)ないように
* uio_resid で律速する。
*/
if (uio->uio_resid < cc)
cc = uio->uio_resid; /* and no more than we want */
DPRINTFN(0,("audio_read: buffer=%p[%d] (~ %d), cc=%d\n",
sc->sc_rconvbuffer, sc->sc_rconvbuffer_begin,
sc->sc_rconvbuffer_end, cc));
/*
* uiomove() はユーザランドにデータを返して uio_resid を
* 更新するので、uiomove() 前後の uio_resid を比較すると
* 実際に返したバイト数が分かる。それが cc。
*/
n = uio->uio_resid;
error = uiomove(sc->sc_rconvbuffer + sc->sc_rconvbuffer_begin,
cc, uio);
cc = n - uio->uio_resid; /* number of bytes actually moved */
/*
* 実際に返した分だけ rconvbuffer_begin を進める。
* rconvbuffer に溜っているやつを全部ユーザランドに返したら
* _begin == _end となるため、バッファは再び rconvbuffer の
* 先頭から使用されるという仕組み。
*/
sc->sc_rconvbuffer_begin += cc;
}
return (error);
}
|
void
audio_calc_blksize(struct audio_softc *sc, int mode)
{
struct audio_hw_if *hw = sc->hw_if;
struct audio_params *parm;
struct audio_ringbuffer *rb;
int bs;
if (sc->sc_blkset)
return;
if (mode == AUMODE_PLAY) {
parm = &sc->sc_pparams;
rb = &sc->sc_pr;
} else {
parm = &sc->sc_rparams;
rb = &sc->sc_rr;
}
bs = parm->hw_sample_rate * audio_blk_ms / 1000 *
parm->hw_channels * parm->precision / NBBY *
parm->factor;
ROUNDSIZE(bs);
if (hw->round_blocksize)
bs = hw->round_blocksize(sc->hw_hdl, bs);
/*
* The blocksize should never be 0, but a faulty
* driver might set it wrong. Just use something.
*/
if (bs <= 0)
bs = 512;
rb->blksize = bs;
DPRINTF(("audio_calc_blksize: %s blksize=%d\n",
mode == AUMODE_PLAY ? "play" : "record", bs));
}
|
int
audio_write(struct audio_softc *sc, struct uio *uio, int ioflag)
{
struct audio_ringbuffer *cb = &sc->sc_pr;
u_char *inp, *einp;
int saveerror, error, s, n, cc, used;
struct audio_params *params;
int samples, hw_bits_per_sample, user_bits_per_sample;
int input_remain, space;
DPRINTFN(2,("audio_write: sc=%p count=%lu used=%d(hi=%d)\n",
sc, (unsigned long)uio->uio_resid, sc->sc_pr.used,
sc->sc_pr.usedhigh));
if (cb->mmapped)
return EINVAL;
if (uio->uio_resid == 0) {
sc->sc_eof++;
return 0;
}
/*
* If half-duplex and currently recording, throw away data.
* half duplex で現在再生中ならデータ捨てる。
*/
if (!sc->sc_full_duplex &&
(sc->sc_mode & AUMODE_RECORD)) {
uio->uio_offset += uio->uio_resid;
uio->uio_resid = 0;
DPRINTF(("audio_write: half-dpx read busy\n"));
return (0);
}
if (!(sc->sc_mode & AUMODE_PLAY_ALL) && sc->sc_playdrop > 0) {
n = min(sc->sc_playdrop, uio->uio_resid);
DPRINTF(("audio_write: playdrop %d\n", n));
uio->uio_offset += n;
uio->uio_resid -= n;
sc->sc_playdrop -= n;
if (uio->uio_resid == 0)
return 0;
}
params = &sc->sc_pparams;
DPRINTFN(1, ("audio_write: sr=%ld, enc=%d, prec=%d, chan=%d, sw=%p, "
"fact=%d\n",
sc->sc_pparams.sample_rate, sc->sc_pparams.encoding,
sc->sc_pparams.precision, sc->sc_pparams.channels,
sc->sc_pparams.sw_code, sc->sc_pparams.factor));
/*
* For some encodings, handle data in sample unit.
*/
switch (params->hw_encoding) {
case AUDIO_ENCODING_SLINEAR_LE:
case AUDIO_ENCODING_SLINEAR_BE:
case AUDIO_ENCODING_ULINEAR_LE:
case AUDIO_ENCODING_ULINEAR_BE:
hw_bits_per_sample = params->hw_channels * params->precision
* params->factor;
user_bits_per_sample = params->channels * params->precision;
break;
default:
hw_bits_per_sample = 8 * params->factor / params->factor_denom;
user_bits_per_sample = 8;
}
#ifdef DIAGNOSTIC
if (hw_bits_per_sample > MAX_SAMPLE_SIZE * 8) {
printf("audio_write(): Invalid sample size: cur=%d max=%d\n",
hw_bits_per_sample / 8, MAX_SAMPLE_SIZE);
}
#endif
/*
* 何のスペース計算してんの?
*/
space = ((params->hw_sample_rate / params->sample_rate) + 1)
* hw_bits_per_sample / 8;
error = 0;
/*
* データがある間ループする?
* uio_resid はユーザランドから渡されたデータの残り量。
* sc_input_fagment_length は前回の書き込み時のあまり。
* つまり input_remain は過去と今回を含めユーザランドから渡されて
* いるデータのうち未処理の(= ハードウェアに渡していない)バイト数。
*/
while ((input_remain = uio->uio_resid + sc->sc_input_fragment_length) > 0
&& !error) {
s = splaudio();
/*
* バッファ内の残りデータ量がユーザレベルでの1サンプル分も
* ないなら、ユーザランドから読み込む。
*/
if (input_remain < user_bits_per_sample / 8) {
n = uio->uio_resid;
DPRINTF(("audio_write: fragment uiomove length=%d\n", n));
error = uiomove(sc->sc_input_fragment
+ sc->sc_input_fragment_length,
n, uio);
/*
* XXX: なんで n 足してるんだろう。
* sc->sc_input_fragment_length += n - uio->uio_resid;
* じゃなくていいんだっけ?
*/
if (!error)
sc->sc_input_fragment_length += n;
splx(s);
return (error);
}
/*
* 現在の水位と space の和が水位上限を越える場合、
* audio_sleep() して水位が減るまで待つ。
*/
while (cb->used + space >= cb->usedhigh) {
DPRINTFN(2, ("audio_write: sleep used=%d lowat=%d "
"hiwat=%d\n",
cb->used, cb->usedlow, cb->usedhigh));
if (ioflag & IO_NDELAY) {
splx(s);
return (EWOULDBLOCK);
}
error = audio_sleep(&sc->sc_wchan, "aud_wr");
if (sc->sc_dying)
error = EIO;
if (error) {
splx(s);
return (error);
}
}
used = cb->used;
inp = cb->inp;
cb->copying = 1;
splx(s);
/*
* cc は現在の水位から上限まで、つまりリングバッファに
* 書き込める最大値とする。
*/
cc = cb->usedhigh - used; /* maximum to write */
/*
* リングバッファのサイズより大きかったら律速する。
* XXX: リングバッファのサイズで律速していいんだっけ?
*/
/* cc may be greater than the size of the ring buffer */
if (cc > cb->end - cb->start)
cc = cb->end - cb->start;
/*
* この時点で、cc はリングバッファに書き込める最大値?
* samples をリングバッファに書き込めるハードウェアの
* 最大サンプル数にする。
*/
/* cc: # of bytes we can write to the ring buffer */
samples = cc * 8 / hw_bits_per_sample;
#ifdef DIAGNOSTIC
if (samples == 0)
printf("audio_write: samples (cc/hw_bps) == 0\n");
#endif
/*
* samples をリングバッファに書き込めるユーザデータの
* 最大サンプル数にする。
*/
/* samples: # of samples we can write to the ring buffer */
samples = samples * params->sample_rate / params->hw_sample_rate;
#ifdef DIAGNOSTIC
if (samples == 0)
printf("audio_write: samples (rate/hw_rate) == 0 "
"usedhigh-used=%d cc/hw_bps=%d/%d "
"rate/hw_rate=%ld/%ld space=%d\n",
cb->usedhigh - cb->used, cc,
hw_bits_per_sample / 8, params->sample_rate,
params->hw_sample_rate, space);
#endif
/*
* cc をリングバッファに書き込めるユーザデータの
* 最大バイト数にする。
*/
/* samples: # of samples in source data */
cc = samples * user_bits_per_sample / 8;
/* cc: # of bytes in source data */
/*
* 実際に処理するバイト数を求める。
* cc が処理できる最大バイト数なので、残りがそれ以上あれば
* cc のまま。残り (input_remain) が cc より少なければ
* input_remainを align して、そっちを使う。
*/
if (input_remain < cc) /* and no more than we have */
cc = (input_remain * 8 / user_bits_per_sample)
* user_bits_per_sample / 8;
#ifdef DIAGNOSTIC
if (cc == 0)
printf("audio_write: cc == 0\n");
#endif
/*
* 後の sw_code でデータサイズが増えることを考慮しておく。
* データは sw_code * factor / factor_denom 倍になって
* pconvbuffer に置かれるので、置けるかどうかチェックしている。
* 置けないようなら cc を置けるサイズに設定し直す。
*/
if (cc * params->factor / params->factor_denom
> sc->sc_pconvbuffer_size) {
/*
* cc = (pconv / factor / user_bps ) * user_bps
*/
cc = (sc->sc_pconvbuffer_size * params->factor_denom
* 8 / params->factor / user_bits_per_sample)
* user_bits_per_sample / 8;
}
#ifdef DIAGNOSTIC
/*
* This should never happen since the block size and and
* block pointers are always nicely aligned.
*/
if (cc == 0) {
printf("audio_write: cc == 0, swcode=%p, factor=%d "
"remain=%d u_bps=%d hw_bps=%d\n",
sc->sc_pparams.sw_code, sc->sc_pparams.factor,
input_remain, user_bits_per_sample,
hw_bits_per_sample);
cb->copying = 0;
return EINVAL;
}
#endif
DPRINTFN(1, ("audio_write: uiomove cc=%d inp=%p, left=%lu\n",
cc, inp, (unsigned long)uio->uio_resid));
/*
* input_fragment(残りものバッファ) にあるデータを pconvbuffer
* にコピーする。cc はその分引いておく。残りものバッファが空
* でも大丈夫。
*/
memcpy(sc->sc_pconvbuffer, sc->sc_input_fragment,
sc->sc_input_fragment_length);
cc -= sc->sc_input_fragment_length;
/*
* その続き以降にユーザランドから残りのデータを読み込む。
* 指定した分だけ読めなかったらメッセージを表示する。
* 指定した分が読めないってことはあり得ないはず?
*/
n = uio->uio_resid;
error = uiomove(sc->sc_pconvbuffer + sc->sc_input_fragment_length,
cc, uio);
if (cc != n - uio->uio_resid) {
printf("audio_write: uiomove didn't move requested "
"amount: requested=%d, actual=%ld\n",
cc, (long)n - uio->uio_resid);
}
/* number of bytes actually moved */
/*
* cc は実際に pconvbuffer に入っているデータのバイト数。
* input_fragment バッファは空になったので 0 にしておく。
*/
cc = sc->sc_input_fragment_length + n - uio->uio_resid;
sc->sc_input_fragment_length = 0;
#ifdef AUDIO_DEBUG
if (error)
printf("audio_write:(1) uiomove failed %d; cc=%d "
"inp=%p\n", error, cc, inp);
#endif
/*
* Continue even if uiomove() failed because we may have
* gotten a partial block.
* uiomove() がこけても、データの一部分は受け取っている
* かもしれないので、処理は続けるぜってことで。
*/
/*
* The format of data in sc_pconvbuffer is:
* [sample_rate, encoding, precision, channels]
* この時点で pconvbuffer の中のデータは
* o ユーザランドから渡されたサンプリングレート
* o ユーザランドから渡されたエンコード
* o ユーザランドから渡された precision
* o ユーザランドから渡されたチャンネル数
*/
/*
* sw_code があれば実行する。
* 再生時の sw_code に渡す cc は変換前のデータのバイト数
* らしい。pconvbuffer から cc バイトのデータを変換し
* 結果は pconvbuffer からに上書きされる。
* 処理後に別途 factor / factor_denom を用いて計算する。
*/
if (sc->sc_pparams.sw_code) {
sc->sc_pparams.sw_code(sc->hw_hdl,
sc->sc_pconvbuffer, cc);
/* Adjust count after the expansion. */
cc = cc * sc->sc_pparams.factor
/ sc->sc_pparams.factor_denom;
DPRINTFN(1, ("audio_write: expanded cc=%d\n", cc));
}
/*
* The format of data in sc_pconvbuffer is:
* [sample_rate, hw_encoding, hw_precision, channels]
*
* この時点で pconvbuffer にあるデータは
* o ユーザランドから渡されたサンプリングレート
* o ハードウェア独自のエンコード
* o ハードウェア独自の precision
* o ユーザランドから渡されたチャンネル
*/
#if NAURATECONV > 0
/*
* NAURATECONV が定義されているならこっちを実行。
* pconvbuffer から cc バイトのデータのサンプリング
* レートとチャンネル数を変換し結果を inp 以降に格納
* する。出力されたデータのバイト数は cc に代入される。
*/
cc = auconv_play(&sc->sc_pconv, params, inp,
sc->sc_pconvbuffer, cc);
#else
/*
* NAURATECONV が定義されていない場合こっちを実行。
* auconv_play() は結果的に pconvbuffer から inp
* (リングバッファ cb の中) へのコピーを行なっている
* ことになるので、それと同じことを memcpy で行なう。
*/
n = cb->end - inp;
if (cc <= n) {
memcpy(inp, sc->sc_pconvbuffer, cc);
} else {
memcpy(inp, sc->sc_pconvbuffer, n);
memcpy(cb->start, sc->sc_pconvbuffer + n, cc - n);
}
#endif /* !NAURATECONV */
/*
* The format of data in inp is:
* [hw_sample_rate, hw_encoding, hw_precision, hw_channels]
* cc is the size of data actually written to inp.
* この時点で inp からのデータは
* o ハードウェア独自のサンプリングレート
* o ハードウェア独自のエンコード
* o ハードウェア独自の precision
* o ハードウェア独自のチャンネル数
* cc は実際に inp に書いたデータサイズになっている。
*/
/*
* リングバッファ cb に書き戻すためのポインタを計算。
*/
einp = cb->inp + cc;
if (einp >= cb->end)
einp -= cb->end - cb->start; /* not cb->bufsize */
s = splaudio();
/*
* This is a very suboptimal way of keeping track of
* silence in the buffer, but it is simple.
*/
sc->sc_sil_count = 0;
cb->inp = einp;
cb->used += cc;
/*
* If the interrupt routine wants the last block filled AND
* the copy did not fill the last block completely it needs to
* be padded.
*/
if (cb->needfill &&
(inp - cb->start) / cb->blksize ==
(einp - cb->start) / cb->blksize) {
/* Figure out how many bytes to a block boundary. */
cc = cb->blksize - (einp - cb->start) % cb->blksize;
DPRINTF(("audio_write: partial fill %d\n", cc));
} else
cc = 0;
cb->needfill = 0;
cb->copying = 0;
if (!sc->sc_pbus && !cb->pause) {
saveerror = error;
error = audiostartp(sc);
if (saveerror != 0) {
/* Report the first error that occurred. */
error = saveerror;
}
}
splx(s);
if (cc != 0) {
DPRINTFN(1, ("audio_write: fill %d\n", cc));
audio_fill_silence(&sc->sc_pparams, einp, cc);
}
}
return (error);
}
|