NetBSD 1.6 の /sys/dev/audio.c の解説コーナー。
$Id: audio.c.html,v 1.18 2002/08/18 06:27:36 isaki Exp $

ターゲットとなるソースはこちら。
/*	$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


1129行〜1317行: audio_read()

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);
}


1337行〜1372行: audio_calc_blksize()

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));
}


1453行〜1720行: audio_write()

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);
}


井崎のホームページへ戻る
isaki@NetBSD.org / isaki@x68k.net