/* $NetBSD: audiodef.h,v 1.7.2.4 2020/05/18 18:12:24 martin Exp $ */ /* * Copyright (C) 2017 Tetsuya Isaki. All rights reserved. * Copyright (C) 2017 Y.Sugahara (moveccr). All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _SYS_DEV_AUDIO_AUDIODEF_H_ #define _SYS_DEV_AUDIO_AUDIODEF_H_ #ifdef _KERNEL_OPT #include "opt_audio.h" #endif /* Number of HW buffer's blocks. */ #define NBLKHW (3) /* Number of track output buffer's blocks. Must be > NBLKHW */ #define NBLKOUT (4) /* Minimum number of usrbuf's blocks. */ #define AUMINNOBLK (3) /* * Whether the playback mixer use single buffer mode. * It reduces the latency one block but needs machine power. * In case of the double buffer (as default), it increases the latency * but can be expected to stabilize even on slower machines. */ /* #define AUDIO_HW_SINGLE_BUFFER */ /* * Whether supports per-track volume. * For now, there are no user interfaces to get/set it. */ /* #define AUDIO_SUPPORT_TRACK_VOLUME */ /* * AUDIO_SCALEDOWN() * This macro should be used for audio wave data only. * * The arithmetic shift right (ASR) (in other words, floor()) is good for * this purpose, and will be faster than division on the most platform. * The division (in other words, truncate()) is not so bad alternate for * this purpose, and will be fast enough. * (Using ASR is 1.9 times faster than division on my amd64, and 1.3 times * faster on my m68k. -- isaki 201801.) * * However, the right shift operator ('>>') for negative integer is * "implementation defined" behavior in C (note that it's not "undefined" * behavior). So only if implementation defines '>>' as ASR, we use it. */ #if defined(__GNUC__) /* gcc defines '>>' as ASR. */ #define AUDIO_SCALEDOWN(value, bits) ((value) >> (bits)) #else #define AUDIO_SCALEDOWN(value, bits) ((value) / (1 << (bits))) #endif /* conversion stage */ typedef struct { audio_ring_t srcbuf; audio_ring_t *dst; audio_filter_t filter; audio_filter_arg_t arg; } audio_stage_t; typedef enum { AUDIO_STATE_CLEAR, /* no data, no need to drain */ AUDIO_STATE_RUNNING, /* need to drain */ AUDIO_STATE_DRAINING, /* now draining */ } audio_state_t; typedef struct audio_track { /* * AUMODE_PLAY for playback track, or * AUMODE_RECORD for recoding track. * Note that AUMODE_PLAY_ALL is maintained by file->mode, not here. */ int mode; audio_ring_t usrbuf; /* user i/o buffer */ u_int usrbuf_blksize; /* usrbuf block size in bytes */ struct uvm_object *uobj; bool mmapped; /* device is mmap()-ed */ u_int usrbuf_stamp; /* transferred bytes from/to stage */ u_int usrbuf_stamp_last; /* last stamp */ u_int usrbuf_usedhigh;/* high water mark in bytes */ u_int usrbuf_usedlow; /* low water mark in bytes */ /* * Track input format. It means usrbuf.fmt for playback, or * mixer->trackfmt for recording. */ audio_format2_t inputfmt; /* * Pointer to track (conversion stage's) input buffer. * Must be protected by track lock (only for recording track). */ audio_ring_t *input; /* * Track (conversion stage's) output buffer. * Must be protected by track lock (only for playback track). */ audio_ring_t outbuf; audio_stage_t codec; /* encoding conversion stage */ audio_stage_t chvol; /* channel volume stage */ audio_stage_t chmix; /* channel mix stage */ audio_stage_t freq; /* frequency conversion stage */ /* Work area for frequency conversion. */ u_int freq_step; /* src/dst ratio */ u_int freq_current; /* counter */ u_int freq_leap; /* correction counter per block */ aint_t freq_prev[AUDIO_MAX_CHANNELS]; /* previous values */ aint_t freq_curr[AUDIO_MAX_CHANNELS]; /* current values */ /* Per-channel volumes (0..256) */ uint16_t ch_volume[AUDIO_MAX_CHANNELS]; #if defined(AUDIO_SUPPORT_TRACK_VOLUME) /* Track volume (0..256) */ u_int volume; #endif audio_trackmixer_t *mixer; /* connected track mixer */ /* Sequence number picked up by track mixer. */ uint64_t seq; audio_state_t pstate; /* playback state */ bool is_pause; /* Statistic counters. */ uint64_t inputcounter; /* # of frames input to track */ uint64_t outputcounter; /* # of frames output from track */ uint64_t useriobytes; /* # of bytes xfer to/from userland */ uint64_t dropframes; /* # of frames dropped */ int eofcounter; /* count of zero-sized write */ /* * Non-zero if the track is in use. * Must access atomically. */ volatile uint lock; int id; /* track id for debug */ } audio_track_t; struct audio_file { struct audio_softc *sc; dev_t dev; /* * Playback and recording track, or NULL if the track is unavailable. */ audio_track_t *ptrack; audio_track_t *rtrack; /* * Indicates the operation mode of this file. * AUMODE_PLAY means playback is requested. * AUMODE_RECORD means recording is requested. * AUMODE_PLAY_ALL affects nothing but can be get/set for backward * compatibility. */ int mode; /* process who wants audio SIGIO. */ pid_t async_audio; /* true when closing */ bool dying; SLIST_ENTRY(audio_file) entry; }; struct audio_trackmixer { struct audio_softc *sc; int mode; /* AUMODE_PLAY or AUMODE_RECORD */ audio_format2_t track_fmt; /* track <-> trackmixer format */ int frames_per_block; /* number of frames in a block */ /* * software master volume (0..256) * Must be protected by sc_intr_lock. */ u_int volume; /* * Volume recovery timer in auto gain control. * Must be protected by sc_intr_lock. */ int voltimer; audio_format2_t mixfmt; void *mixsample; /* mixing buf in double-sized int */ /* * true if trackmixer does LE<->BE conversion. * Generally an encoding conversion should be done by each hardware * driver but for most modern little endian drivers which support * only linear PCM it's troublesome issue to consider about big endian * arch. Therefore, we do this conversion here only if the hardware * format is SLINEAR_OE:16. */ bool swap_endian; audio_filter_t codec; /* hardware codec */ audio_filter_arg_t codecarg; /* and its argument */ audio_ring_t codecbuf; /* also used for wide->int conversion */ audio_ring_t hwbuf; /* HW I/O buf */ void *sih; /* softint cookie */ /* Must be protected by sc_lock. */ kcondvar_t outcv; uint64_t mixseq; /* seq# currently being mixed */ uint64_t hwseq; /* seq# HW output completed */ /* initial blktime n/d = AUDIO_BLK_MS / 1000 */ int blktime_n; /* blk time numerator */ int blktime_d; /* blk time denominator */ /* XXX */ uint64_t hw_complete_counter; }; /* * Audio Ring Buffer. */ #ifdef DIAGNOSTIC #define DIAGNOSTIC_ring(ring) audio_diagnostic_ring(__func__, (ring)) extern void audio_diagnostic_ring(const char *, const audio_ring_t *); #else #define DIAGNOSTIC_ring(ring) #endif /* * Convert number of frames to number of bytes. */ static __inline int frametobyte(const audio_format2_t *fmt, int frames) { return frames * fmt->channels * fmt->stride / NBBY; } /* * Return the number of frames per block. */ static __inline int frame_per_block(const audio_trackmixer_t *mixer, const audio_format2_t *fmt) { return (fmt->sample_rate * mixer->blktime_n + mixer->blktime_d - 1) / mixer->blktime_d; } /* * Round idx. idx must be non-negative and less than 2 * capacity. */ static __inline int auring_round(const audio_ring_t *ring, int idx) { DIAGNOSTIC_ring(ring); KASSERTMSG(idx >= 0, "idx=%d", idx); KASSERTMSG(idx < ring->capacity * 2, "idx=%d ring->capacity=%d", idx, ring->capacity); if (idx < ring->capacity) { return idx; } else { return idx - ring->capacity; } } /* * Return ring's tail (= head + used) position. * This position indicates next frame of the last valid frames. */ static __inline int auring_tail(const audio_ring_t *ring) { return auring_round(ring, ring->head + ring->used); } /* * Return ring's head pointer. * This function can be used only if the stride of the 'ring' is equal to * the internal stride. Don't use this for hw buffer. */ static __inline aint_t * auring_headptr_aint(const audio_ring_t *ring) { KASSERTMSG(ring->fmt.stride == sizeof(aint_t) * NBBY, "ring->fmt.stride=%d sizeof(aint_t)*NBBY=%zd", ring->fmt.stride, sizeof(aint_t) * NBBY); return (aint_t *)ring->mem + ring->head * ring->fmt.channels; } /* * Return ring's tail (= head + used) pointer. * This function can be used only if the stride of the 'ring' is equal to * the internal stride. Don't use this for hw buffer. */ static __inline aint_t * auring_tailptr_aint(const audio_ring_t *ring) { KASSERTMSG(ring->fmt.stride == sizeof(aint_t) * NBBY, "ring->fmt.stride=%d sizeof(aint_t)*NBBY=%zd", ring->fmt.stride, sizeof(aint_t) * NBBY); return (aint_t *)ring->mem + auring_tail(ring) * ring->fmt.channels; } /* * Return ring's head pointer. * This function can be used even if the stride of the 'ring' is equal to * or not equal to the internal stride. */ static __inline uint8_t * auring_headptr(const audio_ring_t *ring) { return (uint8_t *)ring->mem + ring->head * ring->fmt.channels * ring->fmt.stride / NBBY; } /* * Return ring's tail pointer. * It points the next position of the last valid frames. * This function can be used even if the stride of the 'ring' is equal to * or not equal to the internal stride. */ static __inline uint8_t * auring_tailptr(audio_ring_t *ring) { return (uint8_t *)ring->mem + auring_tail(ring) * ring->fmt.channels * ring->fmt.stride / NBBY; } /* * Return ring's capacity in bytes. */ static __inline int auring_bytelen(const audio_ring_t *ring) { return frametobyte(&ring->fmt, ring->capacity); } /* * Take out n frames from head of ring. * This function only manipurates counters. It doesn't manipurate any * actual buffer data. */ #define auring_take(ring, n) auring_take_(__func__, __LINE__, ring, n) static __inline void auring_take_(const char *func, int line, audio_ring_t *ring, int n) { DIAGNOSTIC_ring(ring); KASSERTMSG(n >= 0, "called from %s:%d: n=%d", func, line, n); KASSERTMSG(ring->used >= n, "called from %s:%d: ring->used=%d n=%d", func, line, ring->used, n); ring->head = auring_round(ring, ring->head + n); ring->used -= n; } /* * Append n frames into tail of ring. * This function only manipurates counters. It doesn't manipurate any * actual buffer data. */ #define auring_push(ring, n) auring_push_(__func__, __LINE__, ring, n) static __inline void auring_push_(const char *func, int line, audio_ring_t *ring, int n) { DIAGNOSTIC_ring(ring); KASSERT(n >= 0); KASSERTMSG(ring->used + n <= ring->capacity, "called from %s:%d: ring->used=%d n=%d ring->capacity=%d", func, line, ring->used, n, ring->capacity); ring->used += n; } /* * Return the number of contiguous frames in used. */ static __inline int auring_get_contig_used(const audio_ring_t *ring) { DIAGNOSTIC_ring(ring); if (ring->head + ring->used <= ring->capacity) { return ring->used; } else { return ring->capacity - ring->head; } } /* * Return the number of contiguous free frames. */ static __inline int auring_get_contig_free(const audio_ring_t *ring) { DIAGNOSTIC_ring(ring); if (ring->head + ring->used < ring->capacity) { return ring->capacity - (ring->head + ring->used); } else { return ring->capacity - ring->used; } } #endif /* !_SYS_DEV_AUDIO_AUDIODEF_H_ */