- Remove files at shouldn't be in the tree.
This commit is contained in:
parent
a414d6056c
commit
17c85b4825
@ -1,40 +0,0 @@
|
||||
# Makefile for the sound documentation
|
||||
|
||||
ARTICLE=sound
|
||||
INPUTS=
|
||||
STYLES=
|
||||
.SUFFIXES: .fig .ps
|
||||
OTHERSTUFF=
|
||||
FIGURES=
|
||||
PSS=
|
||||
ALLFIG= $(FIGURES)
|
||||
COMPAT= psfigps.tex
|
||||
|
||||
ALLPS= $(ALLFIG:.fig=.ps)
|
||||
|
||||
ALLSRCS= $(ARTICLE).tex $(STYLES) $(ALLFIG) $(PSS) $(INPUTS) \
|
||||
$(COMPAT) $(OTHERSTUFF) Makefile
|
||||
|
||||
.fig.ps:
|
||||
fig2dev -L ps -m 0.5 $*.fig > $*.ps
|
||||
|
||||
all: $(ARTICLE).dvi pnp.dvi ioctl.dvi Makefile
|
||||
|
||||
$(ARTICLE).dvi: allfig $(ARTICLE).tex $(INPUTS) $(STYLES) $(PSS)
|
||||
latex $(ARTICLE).tex
|
||||
|
||||
pnp.dvi: allfig pnp.tex $(INPUTS) $(STYLES) $(PSS)
|
||||
latex pnp.tex
|
||||
|
||||
ioctl.dvi: allfig pnp.tex $(INPUTS) $(STYLES) $(PSS)
|
||||
latex ioctl.tex
|
||||
|
||||
allfig: $(ALLPS)
|
||||
|
||||
clean:
|
||||
rm -f $(ALLPS) a c *.dvi *.aux *.log rl
|
||||
|
||||
tgz: $(ALLSRCS)
|
||||
tar cvf - $(ALLSRCS) | gzip -9 > $(ARTICLE).tgz
|
||||
|
||||
|
Binary file not shown.
@ -1,917 +0,0 @@
|
||||
\documentclass[11pt]{article}
|
||||
|
||||
\textwidth 6in
|
||||
\textheight 9in
|
||||
\oddsidemargin 0in
|
||||
\evensidemargin 0in
|
||||
\topmargin 0in
|
||||
|
||||
% \input{psfigps.tex}
|
||||
|
||||
\begin{document}
|
||||
\author{Luigi Rizzo\\
|
||||
Dipartimento di Ingegneria dell'Informazione -- Universit\`a di Pisa\\
|
||||
via Diotisalvi 2 -- 56126 Pisa (Italy)\\
|
||||
email: {\tt l.rizzo@iet.unipi.it}}
|
||||
\title{The FreeBSD audio driver}
|
||||
\maketitle
|
||||
|
||||
\begin{abstract}
|
||||
In this paper we describe the architecture of the new FreeBSD audio
|
||||
driver. We also give detailed information on the internal data
|
||||
structures used by the driver, and examples on how to add support
|
||||
for new devices.
|
||||
|
||||
{\bf WARNING:} since the code is rapidly evolving, there might be some
|
||||
minor differences between this paper and the source code. In doubt,
|
||||
the source code is the ultimate reference.
|
||||
|
||||
\end{abstract}
|
||||
|
||||
\section{Introduction}
|
||||
|
||||
The audio driver is the part of the kernel which handles audio-related
|
||||
peripherals. In FreeBSD, historically, there has been not much support
|
||||
for these devices. The original FreeBSD audio driver has been based on
|
||||
the Voxware 3.0 release, originally developed for Linux and ported to
|
||||
FreeBSD at 1.1 times. The driver in the source tree has been
|
||||
essentially unsupported since then.
|
||||
|
||||
In parallel, some volunteers have been using a driver derived from
|
||||
Voxware 3.5, which had support for full-duplex audio cards which have
|
||||
become popular in more recent times.
|
||||
|
||||
Both versions of the Voxware code are extremely complex and not very
|
||||
much in line with BSD device drivers. There are several motivations
|
||||
for that, the main one (we believe) was that the code has evolved
|
||||
through the years to support a number of boards which are sufficiently
|
||||
similar to suggest not to write a completely different driver, and
|
||||
sufficiently different to require the addition of state variables, new
|
||||
support routines, and feature enahncements, to the existing driver.
|
||||
The unfortunate result of this evolution was that the code in our hand
|
||||
was extremely difficult to configure, follow, and in many cases unable to
|
||||
support multiple devices. Furthermore, the complexity of the code,
|
||||
prevented proper support for it.
|
||||
|
||||
The desire for a functional audio driver for FreeBSD, together with
|
||||
the lack of support for the code currently in the source tree, and
|
||||
its complexity of the original driver, were for us sufficient
|
||||
motivations to start a large rewrite of the code.
|
||||
|
||||
From a user's point of view, we were especially concerned with the
|
||||
complexity of the configuration process, which, to be done properly,
|
||||
required a number of different devices to be compiled into the kernel,
|
||||
with (at times) subtle dependencies among the modules and the options
|
||||
to be used. So, one of our goal was to simplify the configuration
|
||||
process, a goal which we believe to have achieved.
|
||||
|
||||
From the kernel developer's point of view, the Voxware driver was hard
|
||||
to understand for two reasons. First, its structure was not much in
|
||||
line with BSD driver. This obviously derives from the original
|
||||
development on a different system. The second reason is the evolution
|
||||
of the driver during time, which, we believe, has degraded the quality of
|
||||
the code from a carefully designed original version.
|
||||
|
||||
We have started our work basing on Voxware 3.5. The Voxware code
|
||||
served mainly as a reference on what the features of the various
|
||||
boards were, and how problems had been dealt with. Our code
|
||||
then evolved into a fairly complete rewrite of the main modules,
|
||||
including the main configuration mechanisms, the DMA support
|
||||
routines, and, in many cases, also the board-specific drivers which
|
||||
could make use of many of the simplifications we have introduced.
|
||||
|
||||
\section{What the audio driver does}
|
||||
|
||||
The audio driver is intended to control all peripherals related to
|
||||
audio handling. This includes digital audio sampling and generation
|
||||
({\tt pcm}), communication with MIDI devices ({\tt midi}) and the
|
||||
synthesizers ({\tt synth}) which several audio cards provide. In
|
||||
addition to these devices, which have explicit entries in the kernel
|
||||
configuration file, audio cards generally provide a {\em mixer}
|
||||
device, which controls audio paths within the board.
|
||||
Our devices currently has only support for {\tt pcm} and mixer
|
||||
devices.
|
||||
|
||||
\subsection{The {\tt pcm} device}
|
||||
|
||||
The {\tt pcm} device is in charge of the audio sampling and
|
||||
generation. Such device is generally implemented by a CODEC, a piece
|
||||
of hardware containing one or two A/D and D/A converters. Depending on
|
||||
the board, the minimum and maximum sampling rate, the number of bits
|
||||
per sample, and the data format (e.g. the availability of compressed
|
||||
audio formats) varies. At a minimum, all boards seem to be capable of
|
||||
sampling mono audio with 8-bit unsigned values and 8 KHz resolutions.
|
||||
Most boards (especially the Windows-Sound-System ones, known as WSS or
|
||||
MSS) are also capable of full duplex stereo with 16-bit and 44.1KHz
|
||||
sampling rate (corresponding to CD-quality).
|
||||
|
||||
The {\tt pcm} device implements the {\tt ioctl()} calls to select the
|
||||
desired data format, and {\tt read()} and {\tt write()} functions to
|
||||
allow a smooth exchange of data with the codec, and {\tt select()} to
|
||||
notify the application when I/O is possible.
|
||||
|
||||
|
||||
\subsection{The mixer}
|
||||
|
||||
The mixer controls audio data paths, i.e. which sources are directed
|
||||
to the ADC input, which ones are directed to the audio output, and the
|
||||
attenuation on the various channels. There is a wide variety of
|
||||
features in this subsystem, as some boards do provide only on-off
|
||||
control on a limited number of sources, whereas some boards provide
|
||||
many sources and fine-grained volume controls.
|
||||
|
||||
We believe that a sufficiently general model of the {\tt mixer}
|
||||
is one actually using {\em two} mixers, one to connect sources to the
|
||||
ADC part of the codec, and one to connect sources to the physical
|
||||
output (usually the speakers). Some early boards often implement the
|
||||
mixer leading to the ADC only as a multiplexer with one active line,
|
||||
but that can be modeled by thinking of a very coarse (1 bit) volume
|
||||
control, and a restriction on the volumes on the various sources.
|
||||
|
||||
|
||||
\section{Kernel Configuration}
|
||||
|
||||
The configuration of the driver is done through the following
|
||||
statements included in the system configuration file:
|
||||
\begin{verbatim}
|
||||
device pcm0 at isa? port? tty irq N drq D flags F vector pcmintr
|
||||
\end{verbatim}
|
||||
The {\tt device pcm0} statement is in charge of bringing into the
|
||||
kernel all modules related to audio support.
|
||||
|
||||
The main audio subsystem is configured by using the {\tt device
|
||||
pcm0} statement. This line supplies the main parameters related to the
|
||||
configuration of a single sound card, which are:
|
||||
\begin{itemize}
|
||||
\item {\bf port} the I/O address for the codec (and generally mixer)
|
||||
subsystem. If no address is supplied, the driver looks at
|
||||
different addresses for the most common boards.
|
||||
\item {\bf irq} the interrupt line used by the board.
|
||||
\item {\bf drq} the first DMA channel used by the board. Modern
|
||||
sound boards supporting full-duplex can use two DMA channels,
|
||||
so we use the {\tt flags} field to specify the second channel.
|
||||
\item {\bf flags} used to specify the second DMA channel, the device
|
||||
type (overriding the autoconfiguration mechanism), and
|
||||
other information.
|
||||
In particular, the flags have the following use:\\
|
||||
\begin{center}
|
||||
\begin{tabular}{|c|l|}
|
||||
\hline Bits & Meaning \\
|
||||
\hline \hline
|
||||
2..0 & secondary (read) DMA channel for dual-dma boards. \\
|
||||
\hline
|
||||
4 & Enable secondary dma channel \\
|
||||
\hline
|
||||
15..8 & Specific device type/class. 0 means autoconfigure\\
|
||||
\hline
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
\end{itemize}
|
||||
A sound board often has more than the codec, and uses more than one
|
||||
I/O address block. Since the {\tt device} line in the config file
|
||||
does not permit to
|
||||
specify more than one address (except, perhaps, by abusing the {\tt
|
||||
flags} field, which we have already used though), we will
|
||||
use two additional {\tt device} entries to specify the i/o address
|
||||
for the MIDI ({\tt midi}) and Synthesizer ({\tt synth}) devices,
|
||||
respectively. These entries do not correspond to actual devices
|
||||
recognized by the kernel (in the sense that their probe routine will
|
||||
always fail, and their attach routine will do nothing and will never
|
||||
be called); rather, the parameters specified in these lines are used
|
||||
to extend the description of the board provided by the {\tt device
|
||||
pcmX} line.
|
||||
There is no support at the moment for {\tt midi} and {\tt synth}
|
||||
devices.
|
||||
|
||||
\subsection{Plug and Play support}
|
||||
|
||||
As a side effect of our work on the audio driver, we have implemented
|
||||
support for Plug and Play (PnP) device configuration. This was a
|
||||
necessity since many modern audio boards are configured through
|
||||
PnP. PnP support is described in a separate document.
|
||||
|
||||
In order to support PnP boards, one device entry is still necessary
|
||||
(i.e. {\tt pcm0}), to let the
|
||||
configure program include all the necessary parameters. The entry must
|
||||
include all fields, e.g.
|
||||
\begin{verbatim}
|
||||
device pcm0 at isa? port? tty irq 7 drq 1 vector pcmintr
|
||||
\end{verbatim}
|
||||
although
|
||||
the actual configuration of PnP devices (i.e. port addresses, IRQ,
|
||||
DMA, etc. will be auto detected, and unit numbers will be assigned
|
||||
starting from the next free one (e.g. {\tt pcm1, pcm2, ...}).
|
||||
|
||||
\section{Code structure}
|
||||
|
||||
The following table details the various file which make up the code.
|
||||
All files reside in the directory {\tt /sys/i386/isa/snd} .
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{|l|p{4in}|}
|
||||
\hline
|
||||
File Name & Description \\
|
||||
\hline \hline
|
||||
{\tt sound.c} & main device routines and autoconfiguration.\\
|
||||
\hline
|
||||
{\tt sound.h} & generic header for all kernel files.\\
|
||||
\hline
|
||||
{\tt soundcard.h} & generic header for all userland sound apps.\\
|
||||
\hline
|
||||
{\tt dmabuf.c} & DMA support. \\
|
||||
\hline
|
||||
{\tt sb\_dsp.c} & SoundBlaster driver \\
|
||||
\hline
|
||||
{\tt ad1848.c} & MSS-compatible driver \\
|
||||
\hline
|
||||
{\tt clones.c} & Miscellaneous code to enable SB/MSS emulation in
|
||||
various boards. \\
|
||||
\hline
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
|
||||
\subsection{The {\tt snddev\_info} structure}
|
||||
|
||||
Each known board is fully described by a {\tt snddev\_info}
|
||||
structure. The structure contains information of use at initialization
|
||||
time (such as board type, name, probe and attach routines), and at
|
||||
runtime. The latter include all board-specific functions, and the
|
||||
generic status information about the board when in use.
|
||||
|
||||
{\tt snddev\_info} is made of two parts, the first one which is
|
||||
generally initialized from a template which univoquely identifies the
|
||||
board, the second one which contains run-time parameters. The first part
|
||||
of the structure is:
|
||||
|
||||
\begin{verbatim}
|
||||
typedef struct _snddev_info snddev_info ;
|
||||
struct _snddev_info {
|
||||
char name[64];
|
||||
int type ; /* board type/class, search key during probes */
|
||||
int (*probe)(struct isa_device * dev);
|
||||
int (*attach)(struct isa_device * dev) ;
|
||||
d_open_t *open ;
|
||||
d_close_t *close ;
|
||||
d_read_t *read ;
|
||||
d_write_t *write ;
|
||||
d_ioctl_t *ioctl ;
|
||||
d_select_t *select ;
|
||||
irq_proc_t *isr ;
|
||||
snd_callback_t *callback;
|
||||
|
||||
int bufsize; /* DMA buffer size */
|
||||
u_long audio_fmt ; /* bitmap of supported audio formats */
|
||||
\end{verbatim}
|
||||
where the names of the fields clearly identify the functions.
|
||||
Board-specific code generally supplies, in the template, initializers
|
||||
for all the above fields. At attach time, and before calling the
|
||||
board-specific attach routines, the descriptor associated with the
|
||||
device, {\tt pcm\_info[dev->id\_unit]} is bzeroed, initialized with
|
||||
the template, then the fields in the second part of the structure
|
||||
(shown next) are initialized with default values (partly taken from
|
||||
the device config line, as in the case of i/o, irq and dma addresses),
|
||||
and the required buffers are allocated.
|
||||
|
||||
\begin{verbatim}
|
||||
int io_base, alt_base, conf_base,
|
||||
mix_base, midi_base, synth_base;
|
||||
|
||||
int bd_id; /* board-specific id */
|
||||
snd_dbuf dbuf_out, dbuf_in;
|
||||
int status_len, status_ptr ; /* to implement sndstat */
|
||||
|
||||
volatile u_long flags; /* generic flags */
|
||||
|
||||
u_long bd_flags; /* board-specific flags */
|
||||
int play_speed, rec_speed;
|
||||
|
||||
int play_blocksize, rec_blocksize;
|
||||
u_long play_fmt, rec_fmt ;
|
||||
u_long mix_devs, mix_rec_devs, mix_recsrc;
|
||||
u_short mix_levels[32];
|
||||
|
||||
u_long interrupts;
|
||||
void *device_data;
|
||||
\end{verbatim}
|
||||
The structure provides all generic fields for audio support, plus a
|
||||
couple of board-specific fields, {\tt bd\_id} and {\tt bd\_flags}. The
|
||||
former should be used to distinguish different versions of the same
|
||||
device (e.g. major/minor id in case of the SoundBlaster, codec type
|
||||
for MSS-compatible boards, etc.), the latter can be used to hold
|
||||
board-specific flags. Should these info not be sufficient, room is
|
||||
provided for a pointer to device-specific data so that the structure
|
||||
can be extended at will.
|
||||
|
||||
Note that the descriptor has room for several base addresses. One of
|
||||
this ({\tt io\_base}) is derived from the the kernel config line, and
|
||||
corresponds to the base address for codec access. The others can be
|
||||
derived using various mechanisms, e.g. hardwired info (some boards are
|
||||
known to have a given subsystem at a given address), additional lines
|
||||
in the config file, or PnP autoconfiguration info. These addresses are
|
||||
used to access the subsystems, and to check for conflicts during the
|
||||
configuration process.
|
||||
|
||||
Of particular importance is the {\tt flags} fields, which holds many
|
||||
flags used to determine the status of the device. Among them:
|
||||
\begin{verbatim}
|
||||
#define SND_F_BUSY 0x0001 /* has been opened (for write) */
|
||||
#define SND_F_BUSY_AUDIO 0x10000000
|
||||
#define SND_F_BUSY_DSP 0x20000000
|
||||
#define SND_F_BUSY_DSP16 0x40000000
|
||||
\end{verbatim}
|
||||
These flags indicate the busy status of the board. They are of
|
||||
exclusive use of the device-specific {\tt open()} and {\tt close()}
|
||||
routines. The intended purpose is to allow only one or more opens
|
||||
on the device. For the way the kernel operates, when multiple
|
||||
descriptors are open on the same device (e.g. because the process
|
||||
{\tt fork()}'ed after opening the device), only the last {\tt
|
||||
close()} will result in the invokation of the device-specific close.
|
||||
But, since in the Voxware mode, a device is accessed using three
|
||||
minor numbers (corresponding to {\tt /dev/dsp, /dev/audio, /dev/dspW}),
|
||||
we have to keep track of opens issued on the different devices and
|
||||
only call the close routines on the very last close.
|
||||
|
||||
\begin{verbatim}
|
||||
#define SND_F_READING 0x0004 /* have a pending read */
|
||||
#define SND_F_WRITING 0x0008 /* have a pending write */
|
||||
\end{verbatim}
|
||||
Allowing a single open does not suffice to exclude the possibility
|
||||
that a process forks and then multiple calls are issued concurrently.
|
||||
These two flags are used in the generic read/write routines to
|
||||
prevent concurrent calls of the same type, and serialize calls of
|
||||
different types for half-duplex devices. These two flags are
|
||||
generally used in the generic read/write code, and there should be
|
||||
no need to use them in the device-specific code, unless the read/write
|
||||
routines are reimplemented.
|
||||
|
||||
\begin{verbatim}
|
||||
#define SND_F_CLOSING 0x0040 /* a pending close */
|
||||
#define SND_F_ABORTING 0x1000 /* a pending abort on write */
|
||||
\end{verbatim}
|
||||
These flags mark special conditions, which should be known to the
|
||||
DMA routines to shut down the device properly. The {\tt CLOSING} flags
|
||||
are generally set by a call to {\tt close()} so that DMA transfers are
|
||||
shut down at the end of the current operation. The {\tt ABORTING}
|
||||
flags are set in response to user breaks or special {\tt ioctl}'s
|
||||
such as {\tt PAUSE}, and cause immediate suspension of the output.
|
||||
|
||||
\begin{verbatim}
|
||||
#define SND_F_STEREO 0x0100
|
||||
#define SND_F_XLAT8 0x0400 /* u-law <--> 8-bit unsigned */
|
||||
#define SND_F_XLAT16 0x0800 /* u-law <--> 16-bit signed */
|
||||
\end{verbatim}
|
||||
These are generic flags, marking the use of stereo audio and the
|
||||
need for special translations in order to support {\tt /dev/audio}
|
||||
on boards which do not have native $\mu$LAW support. In practice,
|
||||
the latter flags are only used for the SoundBlaster; furthermore,
|
||||
conversion between $\mu$LAW and 16-bit signed is currently not
|
||||
implemented (but required to implement full-duplex through {\tt
|
||||
/dev/audio} on the SB16).
|
||||
|
||||
\begin{verbatim}
|
||||
#define SND_F_INIT 0x4000 /* changed parameters. need init */
|
||||
\end{verbatim}
|
||||
Finally, this flag marks the need for board reinitialization at the
|
||||
next convenient time. This is because some parameters (e.g. speed,
|
||||
channels, etc.) might be changed while an I/O operation is in progress
|
||||
(e.g. a previous write is being flushed), and the board can only be
|
||||
reprogrammed at the end of a transfer. This flag helps in supporting
|
||||
the functionality. In the generic {\tt ioctl} handler, as an example,
|
||||
all calls which set a parameter update the descriptor, set the flag,
|
||||
and then invoke the callback function to apply the change. If successful,
|
||||
the flag will be cleared, otherwise it will stay set for the update to
|
||||
take place at the next convenient time.
|
||||
|
||||
\subsection{Autoconfiguration}
|
||||
|
||||
The main component of the driver is the file {\tt sound.c} which
|
||||
contains the autoconfiguration stuff and the main device routines.
|
||||
|
||||
The configuration process occurs as follows (see the following
|
||||
diagram).
|
||||
|
||||
\begin{verbatim}
|
||||
pcmprobe(dev) {
|
||||
foreach (p in supported devices)
|
||||
if (p->probe(dev) is successful) {
|
||||
snddev_last_probed = p ;
|
||||
return 1 ;
|
||||
}
|
||||
return 0 ; /* fail */
|
||||
}
|
||||
|
||||
pcmattach(dev) {
|
||||
initialize pcm_info[dev->id_unit] with data from the template,
|
||||
from snddev_last_probed, and from dev;
|
||||
call snddev_last_probed->attach(dev);
|
||||
}
|
||||
|
||||
xxx_probe(dev) {
|
||||
try to identify the board, possibly updating dev; can use
|
||||
pcm_info[dev->id_unit] as temporary storage (reset on exit);
|
||||
return 1 on success, 0 on failure.
|
||||
}
|
||||
|
||||
xxx_attach(dev) {
|
||||
initialize board and do the necessary allocation, complete the
|
||||
info in pcm_info[dev->id_unit] (initialized from the template
|
||||
and dev)
|
||||
}
|
||||
\end{verbatim}
|
||||
The kernel invokes {\tt pcmprobe()} to detect a board, passing it
|
||||
the parameters from the configuration line. The routine then scans
|
||||
the set of supported devices to see if the device is compatible
|
||||
with the configured unit, and in case invokes the probe routine,
|
||||
which is passed a pointer to the {\tt isa\_device} structure
|
||||
describing the device.
|
||||
|
||||
Compatibility is checked using the following rules. If a device
|
||||
type is specified, only that device is checked for. If the ``device
|
||||
type'' refers to a class of devices (e.g. all SoundBlaster-like
|
||||
boards, all MSS-like boards), then all known devices in that class
|
||||
are probed. Finally, if no device type is specified, then all
|
||||
devices are probed. Internally, a {\tt snddev\_info} structure is
|
||||
defined for each known board. For each board class, an array holds
|
||||
pointers to the descriptors of each board. Finally, an array holds
|
||||
pointers to the arrays describing each board class.
|
||||
|
||||
The probe routine is not supposed to do any resource allocation,
|
||||
just to check for the presence of the board. The only allowed side
|
||||
effect of the probe routine is to modify the content of the {\tt
|
||||
isa\_device} structure, in case a generic parameter was passed, to
|
||||
instantiate it to the correct value. If the board-specific probe
|
||||
routine is successful, so is {\tt pcmprobe()}. This will result in
|
||||
the invokation of {\tt pcmattach()}, with a pointer to the same
|
||||
{\tt isa\_device} structure that was used at probe time.
|
||||
|
||||
{\tt pcmattach()} will now copy the {\tt snddev\_info} structure
|
||||
describing the successfully-probed device into a permanent device
|
||||
descriptor, fill it up with run-time information, and call the
|
||||
board-specific attach routine. All the above is only possible
|
||||
because {\tt pcmprobe()} saves a pointer ({\tt last\_probed}) to
|
||||
the {\tt snddev\_info} corresponding to the successfully probed
|
||||
device before returning.
|
||||
|
||||
The board-specific attach routines has the task of configuring the
|
||||
boards using the supplied parameters. The only parameter passed
|
||||
to the attach routine is the {\tt isa\_device} pointer. This
|
||||
structure includes the unit number, so the board-specific can
|
||||
identify the permanent device descriptor as {\tt pcm\_info[unit]}
|
||||
(use {\tt midi\_info}, {\tt synth\_info} for other devices).
|
||||
|
||||
Although the return type is {\tt int}, the return value from the
|
||||
board-specific attach routine is not checked, similarly to what is
|
||||
done for the generic ISA attach routines. The only action that the
|
||||
kernel does on a successful attach is to register an interrupt for
|
||||
the device. As a consequence, a failed attach can be marked by
|
||||
setting {\tt dev->id\_irq = 0 ;} which has the consequence of not
|
||||
registering an interrupt for the device.
|
||||
|
||||
\subsection{Main device routines}
|
||||
|
||||
Upon a successful attach, {\tt pcmattach()} registers a {\tt cdevsw}
|
||||
entry for the audio device. All audio devices share the same routines,
|
||||
{\tt sndopen, sndclose, sndread, sndwrite, sndioctl, sndselect},
|
||||
which perform some generic actions and then invoke the board-specific
|
||||
routines, if available. The board-specific routines, as well as
|
||||
the probe and attach routines, have the same interface of a BSD
|
||||
device driver. This was a design choice meant to simplify development:
|
||||
one can, in principle, develop a driver outside the audio driver,
|
||||
and then move it under the audio driver with minimal effort.
|
||||
Additionally, this approach simplifies life to a kernel developer
|
||||
since there are no new interfaces to be learned.
|
||||
|
||||
\subsubsection{{\tt sndopen()} and {\tt sndclose()} }
|
||||
|
||||
The generic open and close routines do very little, they basically
|
||||
check that the unit and device number correspond to a supported
|
||||
device, and then invoke the (required) open/close routines for
|
||||
the specific board. There is little more the generic routines can
|
||||
do, since board vary widely and the open and close actions are very
|
||||
board-specific.
|
||||
|
||||
\subsubsection{{\tt sndread()} and {\tt sndwrite()} }
|
||||
|
||||
On the contrary, the generic read and write actions are very similar
|
||||
across all boards. In all cases, checks must be done for valid unit
|
||||
and device numbers. Then, concurrent operations of the same type
|
||||
must be denied. Finally, operations of different types on half-duplex
|
||||
devices must be properly sequenced. After these checks have been
|
||||
done, the generic read/write code requires to move data between the
|
||||
user buffer and the DMA buffer, in blocks of appropriate size.
|
||||
|
||||
\subsection{{\tt sndioctl()}}
|
||||
|
||||
The audio driver uses a very large number of {\tt ioctl}'s. Many
|
||||
of these simply require to read, or set, parameters in the device
|
||||
descriptor, so they can be implemented by the generic routine for
|
||||
all boards. Other ioctl calls, instead, require board-specific
|
||||
actions. We have devised a mechanism to allow board-specific ioctl
|
||||
to handle the desired set of ioctl calls, leaving the generic
|
||||
routine the task to deal with other, unhandled, calls.
|
||||
|
||||
The generic routine, after checking device and unit numbers, passes
|
||||
control to the board-specific routine, if existing. This can do its
|
||||
own processing and return the error status. If the return value is
|
||||
{\tt ENOSYS} (error number 78, ``Function not implemented''), then
|
||||
the generic ioctl {\tt switch} statement is invoked which performs
|
||||
the generic actions.
|
||||
|
||||
The generic routine tries to implement as much as possible of the
|
||||
sound calls, leaving little processing to the user code. In
|
||||
particular, it is assumed that all calls which just read the status
|
||||
of the device can fetch it directly from the audio descriptor.
|
||||
Updates to the parameters are done in the generic ioctl by setting
|
||||
the parameter in the descriptor (when possible, by checking that
|
||||
values are acceptable), setting the {\tt SND\_F\_INIT} flag and
|
||||
calling the device-specific {\tt callback()} function, if available.
|
||||
This in turn will try to perform the necessary action or, if not
|
||||
possible, leave the flag set for later operation.
|
||||
|
||||
|
||||
\subsection{{\tt sndselect()}}
|
||||
|
||||
A properly working select routine is fundamental for a audio device,
|
||||
because often an application has to handle multiple data streams.
|
||||
While it is true that an application can determine how long
|
||||
a read or write call will take to complete, knowing buffer sizes and
|
||||
sample rates, it is certainly more convenient and efficient to have a
|
||||
working {\tt select()}. The generic select routine should cover all
|
||||
needs for devices using DMA. The body of the routine is very
|
||||
standard. The only noticeable thing is that, when the user specified a
|
||||
preferred block size through one of the available {\tt ioctl()} calls,
|
||||
then {\tt select()} will return only when at least one whole block
|
||||
can be transferred. In other cases (default) one byte will suffice to
|
||||
make {\tt select()} return (although the system will still choose a blocksize
|
||||
for DMA transfers, corresponding to 0.25~s of data).
|
||||
|
||||
\subsection{{\tt /dev/sndstat}}
|
||||
|
||||
In the Voxware driver, a special, readonly, status device ({\tt
|
||||
/dev/sndstat}) was defined (minor number 6) which returned information
|
||||
on the audio system, including supported devices and configured
|
||||
devices. In our code, {\tt /dev/sndstat} is supported directly in
|
||||
the generic routines (in file {\tt sound.c}) and is defined for
|
||||
every unit returning the same information. In particular, the
|
||||
status device returns data from a statically allocated 4~KB buffer
|
||||
({\tt status\_buf}) which is filled-up at the first open with the
|
||||
function {\tt init\_status()}.
|
||||
|
||||
\section{DMA support}
|
||||
|
||||
DMA handling routines are an important module of the audio code.
|
||||
All the DMA-related code is in file {\tt dmabuf.c} and is completely
|
||||
new for this release.
|
||||
|
||||
At the time of this writing, DMA support does not use AUTOMODE.
|
||||
|
||||
Using DMA mode, the sound card requests the transfer of data by
|
||||
issuing DMA requests to the ISA DMA controller, which in turn
|
||||
satisfies the request. The ISA DMA controller can be programmed with a
|
||||
count, so that a signal (TC) is generated after the required amount of data
|
||||
has been transferred. In ``AUTO'' mode, the ISA DMA controller
|
||||
reinitializes itself at the end of the transfer. Otherwise, requests
|
||||
are not served anymore until the controller is reprogrammed.
|
||||
|
||||
In principle, the codec could just forward the TC signal to the
|
||||
appropriate interrupt line when enabled for DMA transfers. However,
|
||||
many codec usually have a DMA count register themselves, and generate
|
||||
an interrupt after the programmed number of bytes is transferred,
|
||||
in many cases without looking at the value of TC. This makes it
|
||||
possible to program the ISA DMA controller to use a large buffer,
|
||||
while letting the codec generate interrupts on smaller (and perhaps
|
||||
variable size) blocks.
|
||||
|
||||
There are several problems to be dealt with by the DMA code in the
|
||||
audio driver. First, and most important, we need to avoid overruns
|
||||
or underruns in transferring data from/to the audio board. This is
|
||||
more important for (old) boards which do not have a properly sized
|
||||
on-board FIFO. The second problem is to minimize the latency in
|
||||
all i/o functions, something which is especially important for
|
||||
full-duplex applications.
|
||||
|
||||
In the Voxware code, the DMA routines used a buffer partitioned into
|
||||
a number of fixed-size fragments. The programmer should use ioctls to
|
||||
select the fragment size which was best suited to his needs,
|
||||
generally in terms of latency.
|
||||
|
||||
In our code, we use a completely different approach. The DMA buffer
|
||||
is a single memory block divided into two, variable-size, areas:
|
||||
{\em READY} and {\em FREE}. Each area is identified by an
|
||||
offset into the buffer and a length. The data structure describing a
|
||||
DMA buffer is the following:
|
||||
\begin{verbatim}
|
||||
typedef struct _snd_dbuf {
|
||||
char *buf; /* pointer to the data buffer */
|
||||
int bufsize ; /* total buffer size */
|
||||
volatile int rp, fp; /* pointers to the ready and free area */
|
||||
volatile int dl; /* transfer size */
|
||||
volatile int rl, fl; /* length of ready and free areas. */
|
||||
int int_count; /* how many interrupts on this channel */
|
||||
int chan; /* dma channel */
|
||||
int sample_size ; /* 1, 2, 4 */
|
||||
struct selinfo sel; /* support for select */
|
||||
u_long total; /* total bytes processed */
|
||||
u_long prev_total; /* copy of the above when GETxPTR called */
|
||||
} snd_dbuf ;
|
||||
\end{verbatim}
|
||||
The READY area contains
|
||||
data ready to be transferred to the codec or to the user, depending
|
||||
on the direction of the transfer. The FREE area is empty and
|
||||
available for use. Both READY and FREE can wrap through the end of
|
||||
the buffer. When the dma engine is in use, it transfers {\tt dl} bytes
|
||||
from the beginning of the READY area (play) or to the beginning of the
|
||||
FREE area (record).
|
||||
|
||||
The status of a dma transfer can in many cases be detected from the
|
||||
value of the length components of the structures. If {\tt dl == 0},
|
||||
then the DMA engine is not active. If {\em fl == 0}, a user write will
|
||||
be blocking or a new DMA read cannot be started.
|
||||
If {\em rl == 0}, a user read will be blocking, or a new DMA write
|
||||
cannot be started. A idle dma descriptor has {\tt dl = rl = 0, fl
|
||||
= bufsize}.
|
||||
|
||||
\subsection{Handling block sizes}
|
||||
|
||||
The purpose of the subdivision of buffers in three areas is that we
|
||||
can have one pending DMA operation using the DMA area, some space for
|
||||
the next DMA operation (the READY area for a write, the FREE area for
|
||||
a READ), and a pending read or write on the third area.
|
||||
|
||||
Obviously we should avoid each of these areas to become too large and
|
||||
eat all the available buffer space, or too small and make operations
|
||||
inefficient. We will analize the details in the following two
|
||||
sections.
|
||||
|
||||
\subsection{DMA write}
|
||||
|
||||
In write operations, the boundary between READY and FREE is advanced
|
||||
to make room for user data. When possible, a DMA operation is
|
||||
started by advancing the boundary between DMA and READY by the
|
||||
amount of data which must be transferred. At the end of the DMA
|
||||
transfer, the DMA area is shrunk to zero length by extending the
|
||||
FREE area. These actions occur in the user write routine {\tt
|
||||
dsp\_write\_body()}, and in the write interrupt service routine
|
||||
{\tt dsp\_wrintr()}.
|
||||
|
||||
By using a straightforward implementation, the time required for the
|
||||
user write routine to make data available to the DMA engine would be
|
||||
proportional to the block size used in the write operation. This is
|
||||
mainly because of the use of the {\tt uiomove} function in the
|
||||
routine, possibly followed by a format conversion for translating
|
||||
between $\mu$-LAW and the native format supported by the codec.
|
||||
In order to minimize this latency, we do the transfer in blocks of
|
||||
increasing size, doubling it at each pass. This gives us very low
|
||||
latency, while at the same time enables the use of large blocks when
|
||||
needed, without requesting the user to specify a block size.
|
||||
|
||||
|
||||
\subsection{DMA read}
|
||||
|
||||
In read operations, a DMA operation needs to be started first, by
|
||||
advancing the boundary between DMA and FREE by the amount of required
|
||||
data. When the DMA transfer is complete, the boundary between READY
|
||||
and DMA is advanced. User reads can be served by removing data from
|
||||
the READY area and advancing the boundary between FREE and READY
|
||||
accordingly. These actions occur in the user read routine {\tt
|
||||
dsp\_read\_body()} and in the read interrupt service routine {\tt
|
||||
dsp\_rdintr()}.
|
||||
|
||||
Even for DMA read, we move data to user space in blocks of increasing
|
||||
size, so as to minimize latency in freeing space in the buffer.
|
||||
However, implementing the read poses another difficulty in deciding
|
||||
when to return data to a requesting application. Typically,
|
||||
a read request should return when the requested number of bytes is
|
||||
available. If data are already available, we can simply copy them
|
||||
and return. If no data is available and the DMA is inactive, we
|
||||
can start it with the requested amount of data, and then wait for
|
||||
the request to complete. However, we are in trouble if the read
|
||||
request arrives when a DMA operation is already scheduled (a normal
|
||||
situation) and the transfer length is (possibly much) larger than
|
||||
the requested amount of data. In this case, the interrupt will only
|
||||
arrive at the end of the operation, which might take a long time.
|
||||
This is not a problem with writes, since data are already buffered
|
||||
for the whole operation.
|
||||
|
||||
\subsection{Why this does not always work...}
|
||||
|
||||
Using single DMA mode avoids that, because of slow interrupt
|
||||
response, samples are played beyond the end of the buffer, or
|
||||
capture overwrites some old, still unread, data at the other end
|
||||
of the buffer.
|
||||
Single DMA mode has the problem of requiring fast interrupt
|
||||
response to ensure a smooth data transfer. If the system is not quick
|
||||
to reinitialize the DMA controller, some samples might be missed. To
|
||||
mitigate the problem, some controllers provide an internal FIFO which
|
||||
increases the allowable interrupt response time. As an example, a
|
||||
codec with 16-samples FIFO at 8~KHz gives 2~ms to reinitialize the
|
||||
codec itself, which is a reasonable time.
|
||||
This of course assumes that
|
||||
the codec makes good use of the FIFO e.g. in the play queue generates
|
||||
an interrupt when the count goes to 0 even when the FIFO is full. Some
|
||||
don't.
|
||||
|
||||
Another problem of using single DMA is that the same count is
|
||||
programmed into the two devices. It appears that some codecs when
|
||||
working in full duplex have the bad habit of forgetting to count some
|
||||
cycles, resulting in interrupts not being generated.
|
||||
|
||||
So, to sum up, single DMA mode works very well with well-behaved
|
||||
codecs and fast systems. When the system becomes slow, late interrupt
|
||||
response might cause ``clicks'' in the output, or missing samples in
|
||||
the input. When the codec is buggy, there might be deadlocks because
|
||||
of missing interrupts (we have only seen these in full duplex on some
|
||||
cards).
|
||||
|
||||
\subsection{Auto-mode DMA}
|
||||
|
||||
Auto-mode DMA refers to a special operating mode of the DMA engine
|
||||
which does not require the CPU to reinitialize the channel at the
|
||||
end of a transfer. Auto DMA can be enabled independently in the
|
||||
ISA DMA controller and in the sound card. The advantage of auto
|
||||
mode is that data can be transferred continuously and do not require
|
||||
the interrupt service routine to have a low latency (if AUTO DMA
|
||||
is used on both the ISA DMA controller and in the sound card).
|
||||
|
||||
Enabling AUTO DMA in the ISA DMA controller only saves the small time
|
||||
needed to reinitialize the DMA controller, and can help preventing the
|
||||
deadlock with broken codecs, since the ISA DMA controller will always
|
||||
be available to serve DMA requests. A drawback of this approach is
|
||||
that the codec might try to transfer data beyond the allowed space.
|
||||
|
||||
Enabling AUTO DMA in the codec only does not make any sense.
|
||||
|
||||
To overcome the problem of DMA transfers going beyond the allowed
|
||||
space, the following strategy can be used. For playback, we must
|
||||
insure that the region beyond the end of valid data contains data
|
||||
which do not produce audible effects. As an example, it can be
|
||||
initialized with replicas of the last sample (1, 2 or 4 bytes
|
||||
depending on the operating mode). The amount of space to be
|
||||
initialized in this way should correspond to the expected maximum
|
||||
interrupt latency.
|
||||
|
||||
For record, the whole buffer should not be used to acquire data, so
|
||||
that any overrun will just fill a guard region in the buffer, and not
|
||||
overwrite old data. Again the size of the guard region should be
|
||||
computed depending on the expected maximum interrupt latency.
|
||||
|
||||
\section{Board-specific routines}
|
||||
|
||||
All board-specific routines are contained in one or more board-specific
|
||||
source files, and possibly some include files. The board-specific
|
||||
file(s) should provide as a minimum a template {\tt snddev\_info}
|
||||
describing the board, and the various functions referenced in the
|
||||
template and not defined elsewhere. This generally includes the
|
||||
probe, attach, open, close, the callback, and the interrupt
|
||||
dispatcher.
|
||||
|
||||
In order to make board-specific files easily readable, we suggest
|
||||
to use a standard structure for them. As a reference, one can
|
||||
look at file {\tt sb\_dsp.c} which contains the driver for the
|
||||
SoundBlaster board.
|
||||
|
||||
The standard structure begins with the copyright. Then the body of the
|
||||
file is enclosed in the following lines:
|
||||
\begin{verbatim}
|
||||
#include <i386/isa/snd/sound.h>
|
||||
#if NPCM > 0
|
||||
... body of the file
|
||||
#endif /* NPCM > 0 */
|
||||
\end{verbatim}
|
||||
where the {\tt "sound.h"} contains all the generic include files and
|
||||
definitions for kernel audio modules. It also includes the
|
||||
config-generated file {\tt "snd.h"} which defines the number of
|
||||
statically-configured audio devices, NPCM (note that one is required
|
||||
but often suffices, since PnP devices will use additional unit numbers
|
||||
up to the maximum of {\tt NPCM\_MAX} which is currently defined as 8.
|
||||
The total number of audio devices, which can be used and should be
|
||||
incremented when PnP devices are detected, is held in the global
|
||||
variable {\tt u\_long nsnd} which is initialized with NPCM.
|
||||
|
||||
Next come the {\tt \#include} for all board-specific include files,
|
||||
followed by the prototype declaration for all static functions which go
|
||||
into the {\tt snddev\_info}, and then prototypes for all static
|
||||
functions in this module. We encourage to make functions static if not
|
||||
used by other modules, to reduce pollution of the name space.
|
||||
|
||||
After function prototypes, put the (initialized) descriptors for the
|
||||
board(s) supported by this file, e.g.
|
||||
\begin{verbatim}
|
||||
snddev_info sb_op_desc = {
|
||||
"SoundBlaster", SNDCARD_SB,
|
||||
sb_probe, sb_attach,
|
||||
sb_open, sb_close, NULL /* rd */, NULL /* wr */,
|
||||
sb_dsp_ioctl, sndselect, sbintr, sb_callback,
|
||||
DSP_BUFFSIZE,
|
||||
AFMT_U8,
|
||||
}
|
||||
\end{verbatim}
|
||||
|
||||
\subsection{{\tt xxx\_probe()}}
|
||||
|
||||
The board-specific probe routine is passed a pointer to a {\tt struct
|
||||
isa\_device} which contains all information collected from the {\tt
|
||||
device pcmX} line in the kernel configuration file. The probe code
|
||||
should do the minimum action to detect the board type and see if
|
||||
parameters are compatible with the board's features. It returns 1 in
|
||||
case of success, 0 in case of failure.
|
||||
|
||||
The probe code is
|
||||
not supposed to do any allocation. If some temporary storage is
|
||||
necessary, the probe code can use {\tt pcm\_info[dev->id\_unit]},
|
||||
which is available, although not initialized at this stage. This
|
||||
is convenient since, often, the probe and attach routine share the
|
||||
same code for determining the board type, and can store the detected
|
||||
information right there. Note however that {\tt
|
||||
pcm\_info[dev->id\_unit]} will be reinitialized at attach time, so that
|
||||
data structure should not be regarded as persistent information, but
|
||||
only as a convenient storage area.
|
||||
|
||||
\subsection{{\tt xxx\_attach()}}
|
||||
|
||||
Same as the probe routine, the attach routine is passed a pointer
|
||||
to a {\tt struct isa\_device} with info from the configuration
|
||||
file. This time, however, the generic attach routine will also
|
||||
initialize {\tt pcm\_info[dev->id\_unit]} copying the template
|
||||
corresponding to the device identified by the probe routine;
|
||||
moreover, values from the kernel config line are also copied in
|
||||
the correct places (in particular, this involves the various fields
|
||||
which depend on the value of {\tt flags}.
|
||||
|
||||
\subsection{Interrupt dispatcher}
|
||||
|
||||
All audio devices register the same interrupt service routine ({\em
|
||||
ISR}), {\tt pcmintr}. This merely calls the ISR which is specified
|
||||
in the device descriptor, passing it the unit number for which the
|
||||
interrupt has arrived (in practice, one is not aware of the presence
|
||||
of {\tt pcmintr}, which could also disappear in the future, and
|
||||
should write the ISR routine as a standard ISR for BSD systems.
|
||||
|
||||
The ISR mainly has to find out the interrupt reason (accessing {\tt
|
||||
pcm\_info[unit]} to collect board specific parameters) and perform
|
||||
whatever action is necessary. Usually, it is only necessary to call
|
||||
{\tt dsp\_wrintr()} or {\tt dsp\_rdintr()}, and possibly clear the
|
||||
interrupt flag in the codec.
|
||||
|
||||
\subsection{Callback routine}
|
||||
|
||||
The main interface between the generic audio code and the device
|
||||
specific code is the callback routine which is specified in the device
|
||||
descriptor. The callback routine is passed an {\tt int} argument which
|
||||
describes the reason of the callback. The argument is made of a mode
|
||||
(read and/or write) and a reason. Typically the callback is invoked
|
||||
from the dma code, upon user writes or interrupt routines. The code in
|
||||
the callback routine should typically perform codec-specific actions
|
||||
to start/stop a dma transfer in the desired direction.
|
||||
As an extension, the callback function can also be called by the
|
||||
generic routines at open and close times. This only happens if
|
||||
device-specific open and close are not implemented, and the
|
||||
functionalities are implemented here.
|
||||
|
||||
Again this is done for convenience, since in many cases the open and
|
||||
close routines share a common part (e.g. to check for device busy
|
||||
conditions, etc.) which this way need not to be replicated in
|
||||
different drivers.
|
||||
|
||||
\subsection{Mixer}
|
||||
|
||||
Sound cards generally have a mixer device, which is in charge of
|
||||
controlling signal flow, attenuation and filtering on the various
|
||||
paths. We mutuate the Voxware scheme in this driver, both because it
|
||||
is reasonably flexible, and for compatibility with existing
|
||||
applications. In this scheme, each of the possible mixer channels is
|
||||
associated to a bit in a bitmask.
|
||||
|
||||
Setting levels for a mixer channel generally just requires to write a
|
||||
value (associated to the level) in some location. Since the number of
|
||||
bits used by various mixer devices largely differs from board to
|
||||
board, the Voxware driver uses a description table to map channels
|
||||
into the appropriate addresses. A table is made of an array of {\tt
|
||||
mixer\_ent} entries, which are set with the {\tt PMIX\_ENT} and
|
||||
{\tt MIX\_ENT} macros (all defined in {\tt sound.h}):
|
||||
entries:
|
||||
|
||||
\begin{verbatim}
|
||||
struct mixer_def {
|
||||
u_int regno:7;
|
||||
u_int polarity:1;
|
||||
u_int bitoffs:4;
|
||||
u_int nbits:4;
|
||||
};
|
||||
typedef struct mixer_def mixer_ent;
|
||||
typedef struct mixer_def mixer_tab[32][2];
|
||||
|
||||
#define MIX_ENT(name, reg_l, pol_l, pos_l, len_l, reg_r, pol_r, pos_r, len_r) \
|
||||
{{reg_l, pol_l, pos_l, len_l}, {reg_r, pol_r, pos_r, len_r}}
|
||||
#define PMIX_ENT(name, reg_l, pos_l, len_l, reg_r, pos_r, len_r) \
|
||||
{{reg_l, 0, pos_l, len_l}, {reg_r, 0, pos_r, len_r}}
|
||||
|
||||
void
|
||||
change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval);
|
||||
\end{verbatim}
|
||||
The table is used by {\tt change\_bits()}, which accepts a pointer
|
||||
to a table, a pointer to the old value of the register, device
|
||||
(0..31), channel (left or right), and the new value to be set, in
|
||||
the range 0..100.
|
||||
|
||||
\end{document}
|
@ -1,82 +0,0 @@
|
||||
Various support files for the new sound driver for FreeBSD.
|
||||
|
||||
audio-voxware.cc
|
||||
|
||||
this is the driver for "vat" (tested with vat 4.0b2 in the
|
||||
ports distribution) to make things work in full duplex. It
|
||||
does _not_ use non-blocking i/o, otherwise vat will happily
|
||||
eat most of your CPU time since select will often return true
|
||||
even if less than one frame is ready.
|
||||
|
||||
The environment variable AUDIODEV will let you use different
|
||||
sound cards if you have more than one. It can contain the unit
|
||||
number (0, 1, 2, ...) or the full pathname.
|
||||
|
||||
As an additional bonus, you can select a fourth input which is
|
||||
a ulaw file which is written to the network instead of the
|
||||
input audio data.
|
||||
|
||||
CPU usage on my p5/133 in full duplex, local feedback and
|
||||
silence suppression (most demanding configuration I believe):
|
||||
PCM 3.5%
|
||||
DVI 4.8%
|
||||
LPC 11 %
|
||||
GSM 23 %
|
||||
|
||||
auvoxware.c
|
||||
|
||||
this is the driver for "nas" (tested with nas 1.2b5). Works
|
||||
fine in full duplex.
|
||||
|
||||
linux_a.c
|
||||
|
||||
driver for timidity, a midi-to-pcm converter. Using this program
|
||||
(and if you have spare cpu cycles) you can play midi files to
|
||||
/dev/audio without the need for a synthesis device.
|
||||
|
||||
linux.patch
|
||||
|
||||
this is a patch for 2.2.X users for the linux emulator. The
|
||||
files to correct are in /sys/i386/linux, and this patch
|
||||
implements a few ioctl which are used by the realvideo player
|
||||
for linux (rvplayer). In order to use these patches you have
|
||||
to recompile the linux_mod.o and install it in place as follows
|
||||
|
||||
cd /usr/src/lkm/linux
|
||||
make
|
||||
mv linux_mod.o /lkm/linux_mod.o
|
||||
|
||||
pcmio.c
|
||||
|
||||
a simple program to do i/o with the audio device. You can set
|
||||
on the command line the device, the speed, data format, stereo
|
||||
or mono... And if you use a bidirectional device, you can play
|
||||
and record at the same time.
|
||||
|
||||
E.g.
|
||||
|
||||
pcmio -f 1 +rec,stereo,44100,s16 song
|
||||
|
||||
will record from /dev/audio1 stereo,16-bit data to the file "song"
|
||||
|
||||
pcmio +play,stereo,44100,s16,loop 50s:20s song
|
||||
|
||||
will continuosly play 50s from "song" (skipping the first 20s)
|
||||
to /dev/audio
|
||||
|
||||
pcmio +rec,ulaw - | tee tapefile > /dev/audio
|
||||
|
||||
will record using ulaw (mono at 8KHz, default) to stdout, and
|
||||
the pipe will both dump things to "tapefile" and to /dev/audio
|
||||
to listen what you are recording (you could do the same with
|
||||
"cat...").
|
||||
|
||||
soundbyte.c
|
||||
|
||||
the audio module for speak_freely
|
||||
|
||||
test.c
|
||||
|
||||
lets you monitor the status of the device (/dev/audio1) by
|
||||
mmapping the descriptor and dumping to screen the interesting
|
||||
fields.
|
@ -1,435 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1991-1993 Regents of the University of California.
|
||||
* 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the Computer Systems
|
||||
* Engineering Group at Lawrence Berkeley Laboratory.
|
||||
* 4. Neither the name of the University nor of the Laboratory may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Full Duplex audio module for the new sound driver and full duplex
|
||||
* cards. Luigi Rizzo, from original sources supplied by Amancio Hasty.
|
||||
*
|
||||
* This includes some enhancements:
|
||||
* - the audio device to use can be in the AUDIODEV env. variable.
|
||||
* It can be either a unit number or a full pathname;
|
||||
* - use whatever format is available from the card (included split
|
||||
* format e.g. for the sb16);
|
||||
* - limit the maximum size of the playout queue to approx 4 frames;
|
||||
* this is necessary if the write channel is slower than expected;
|
||||
* the fix is based on two new ioctls, AIOGCAP and AIONWRITE,
|
||||
* but the code should compile with the old driver as well.
|
||||
*/
|
||||
|
||||
#include <osfcn.h>
|
||||
#include <machine/soundcard.h>
|
||||
#include "audio.h"
|
||||
#include "mulaw.h"
|
||||
#include "Tcl.h"
|
||||
|
||||
#define ULAW_ZERO 0x7f
|
||||
|
||||
extern const u_char lintomulawX[];
|
||||
|
||||
class VoxWare : public Audio {
|
||||
public:
|
||||
VoxWare();
|
||||
virtual int FrameReady();
|
||||
virtual u_char* Read();
|
||||
virtual void Write(u_char *);
|
||||
virtual void SetRGain(int);
|
||||
virtual void SetPGain(int);
|
||||
virtual void OutputPort(int);
|
||||
virtual void InputPort(int);
|
||||
virtual void Obtain();
|
||||
virtual void Release();
|
||||
virtual void RMute();
|
||||
virtual void RUnmute();
|
||||
virtual int HalfDuplex() const;
|
||||
protected:
|
||||
int ext_fd; /* source for external file */
|
||||
|
||||
u_char* readbuf;
|
||||
u_short *s16_buf;
|
||||
|
||||
int play_fmt ;
|
||||
#if defined(AIOGCAP) /* new sound driver */
|
||||
int rec_fmt ; /* the sb16 has split format... */
|
||||
snd_capabilities soundcaps;
|
||||
#endif
|
||||
};
|
||||
|
||||
static class VoxWareMatcher : public Matcher {
|
||||
public:
|
||||
VoxWareMatcher() : Matcher("audio") {}
|
||||
TclObject* match(const char* fmt) {
|
||||
if (strcmp(fmt, "voxware") == 0)
|
||||
return (new VoxWare);
|
||||
return (0);
|
||||
}
|
||||
} linux_audio_matcher;
|
||||
|
||||
VoxWare::VoxWare()
|
||||
{
|
||||
readbuf = new u_char[blksize];
|
||||
s16_buf = new u_short[blksize];
|
||||
|
||||
memset(readbuf, ULAW_ZERO, blksize);
|
||||
|
||||
ext_fd = -1 ; /* no external audio */
|
||||
iports = 4; /* number of input ports */
|
||||
}
|
||||
|
||||
void
|
||||
VoxWare::Obtain()
|
||||
{
|
||||
char *thedev;
|
||||
char buf[64];
|
||||
int d = -1;
|
||||
|
||||
if (HaveAudio())
|
||||
abort();
|
||||
thedev=getenv("AUDIODEV");
|
||||
if (thedev==NULL)
|
||||
thedev="/dev/audio";
|
||||
else if ( thedev[0] >= '0' && thedev[0] <= '9' ) {
|
||||
d = atoi(thedev);
|
||||
sprintf(buf,"/dev/audio%d", d);
|
||||
thedev = buf ;
|
||||
}
|
||||
fd = open(thedev, O_RDWR );
|
||||
thedev=getenv("MIXERDEV");
|
||||
if (thedev == NULL)
|
||||
if (d < 0)
|
||||
thedev = "/dev/mixer";
|
||||
else {
|
||||
sprintf(buf,"/dev/mixer%d", d);
|
||||
thedev = buf ;
|
||||
}
|
||||
|
||||
if (fd >= 0) {
|
||||
int i = -1 ;
|
||||
#if defined(AIOGCAP) /* new sound driver */
|
||||
i = ioctl(fd, AIOGCAP, &soundcaps);
|
||||
if (i == 0) {
|
||||
snd_chan_param pa;
|
||||
struct snd_size sz;
|
||||
|
||||
pa.play_rate = pa.rec_rate = 8000 ;
|
||||
pa.play_format = pa.rec_format = AFMT_MU_LAW ;
|
||||
switch (soundcaps.formats & (AFMT_FULLDUPLEX | AFMT_WEIRD)) {
|
||||
case AFMT_FULLDUPLEX :
|
||||
/*
|
||||
* this entry for cards with decent full duplex. Use s16
|
||||
* preferably (some are broken in ulaw) or ulaw or u8 otherwise.
|
||||
*/
|
||||
if (soundcaps.formats & AFMT_S16_LE)
|
||||
pa.play_format = pa.rec_format = AFMT_S16_LE ;
|
||||
else if (soundcaps.formats & AFMT_MU_LAW)
|
||||
pa.play_format = pa.rec_format = AFMT_MU_LAW ;
|
||||
else if (soundcaps.formats & AFMT_U8)
|
||||
pa.play_format = pa.rec_format = AFMT_U8 ;
|
||||
else {
|
||||
printf("sorry, no supported formats\n");
|
||||
close(fd);
|
||||
fd = -1 ;
|
||||
return;
|
||||
}
|
||||
break ;
|
||||
case AFMT_FULLDUPLEX | AFMT_WEIRD :
|
||||
/* this is the sb16... */
|
||||
if (soundcaps.formats & AFMT_S16_LE) {
|
||||
pa.play_format = AFMT_U8 ;
|
||||
pa.rec_format = AFMT_S16_LE;
|
||||
} else {
|
||||
printf("sorry, no supported formats\n");
|
||||
close(fd);
|
||||
fd = -1 ;
|
||||
return;
|
||||
}
|
||||
break ;
|
||||
default :
|
||||
printf("sorry don't know how to deal with this card\n");
|
||||
close (fd);
|
||||
fd = -1;
|
||||
break;
|
||||
}
|
||||
ioctl(fd, AIOSFMT, &pa);
|
||||
play_fmt = pa.play_format ;
|
||||
rec_fmt = pa.rec_format ;
|
||||
sz.play_size = (play_fmt == AFMT_S16_LE) ? 2*blksize : blksize;
|
||||
sz.rec_size = (rec_fmt == AFMT_S16_LE) ? 2*blksize : blksize;
|
||||
ioctl(fd, AIOSSIZE, &sz);
|
||||
} else
|
||||
#endif
|
||||
{ /* setup code for old voxware driver */
|
||||
}
|
||||
Audio::Obtain();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* note: HalfDuplex() uses a modified function of the new driver,
|
||||
* which will return AFMT_FULLDUPLEX set in SNDCTL_DSP_GETFMTS
|
||||
* for full-duplex devices. In the old driver this was 0 so
|
||||
* the default is to use half-duplex for them. Note also that I have
|
||||
* not tested half-duplex operation.
|
||||
*/
|
||||
int
|
||||
VoxWare::HalfDuplex() const
|
||||
{
|
||||
int i;
|
||||
ioctl(fd, SNDCTL_DSP_GETFMTS, &i);
|
||||
#if 0
|
||||
printf("SNDCTL_DSP_GETFMTS returns 0x%08x %s duplex\n",
|
||||
i, i & AFMT_FULLDUPLEX ? "full":"half");
|
||||
#endif
|
||||
return (i & AFMT_FULLDUPLEX) ? 0 : 1 ;
|
||||
}
|
||||
|
||||
void VoxWare::Release()
|
||||
{
|
||||
if (HaveAudio()) {
|
||||
Audio::Release();
|
||||
}
|
||||
}
|
||||
|
||||
void VoxWare::Write(u_char *cp)
|
||||
{
|
||||
int i = blksize, l;
|
||||
static int peak = 0;
|
||||
|
||||
if (play_fmt == AFMT_S16_LE) {
|
||||
for (i=0; i< blksize; i++)
|
||||
s16_buf[i] = mulawtolin[cp[i]] ;
|
||||
cp = (u_char *)s16_buf;
|
||||
i = 2 *blksize ;
|
||||
}
|
||||
else if (play_fmt == AFMT_S8) {
|
||||
for (i=0; i< blksize; i++) {
|
||||
int x = mulawtolin[cp[i]] ;
|
||||
x = (x >> 8 ) & 0xff;
|
||||
cp[i] = (u_char)x ;
|
||||
}
|
||||
i = blksize ;
|
||||
} else if (play_fmt == AFMT_U8) {
|
||||
/*
|
||||
* when translating to 8-bit formats, need to implement AGC
|
||||
* to avoid loss of resolution in the conversion.
|
||||
* The peak is multiplied by 2^13
|
||||
*/
|
||||
for (i=0; i< blksize; i++) {
|
||||
int x = mulawtolin[cp[i]] ;
|
||||
#if 0 /* AGC -- still not complete... */
|
||||
if (x < 0) x = -x ;
|
||||
if (x > peak) peak = ( peak*16 + x - peak ) / 16 ;
|
||||
else peak = ( peak*8192 + x - peak ) / 8192 ;
|
||||
if (peak < 128) peak = 128 ;
|
||||
/* at this point peak is in the range 128..32k
|
||||
* samples can be scaled and clipped consequently.
|
||||
*/
|
||||
x = x * 32768/peak ;
|
||||
if (x > 32767) x = 32767;
|
||||
else if (x < -32768) x = -32768;
|
||||
#endif
|
||||
x = (x >> 8 ) & 0xff;
|
||||
x = (x ^ 0x80) & 0xff ;
|
||||
cp[i] = (u_char)x ;
|
||||
}
|
||||
i = blksize ;
|
||||
}
|
||||
#if 0 && defined(AIOGCAP)
|
||||
int queued;
|
||||
ioctl(fd, AIONWRITE, &queued);
|
||||
queued = soundcaps.bufsize - queued ;
|
||||
if (play_fmt == AFMT_S16_LE) {
|
||||
if (queued > 8*blksize)
|
||||
i -= 8 ;
|
||||
} else {
|
||||
if (queued > 4*blksize)
|
||||
i -= 4 ;
|
||||
}
|
||||
#endif
|
||||
for ( ; i > 0 ; i -= l) {
|
||||
l = write(fd, cp, i);
|
||||
cp += l;
|
||||
}
|
||||
}
|
||||
|
||||
u_char* VoxWare::Read()
|
||||
{
|
||||
u_char* cp;
|
||||
int l=0, l0 = blksize, i = blksize;
|
||||
static int smean = 0 ; /* smoothed mean to remove DC */
|
||||
static int loops = 20 ;
|
||||
|
||||
cp = readbuf;
|
||||
|
||||
if (rec_fmt == AFMT_S16_LE) {
|
||||
cp = (u_char *)s16_buf;
|
||||
l0 = i = 2 *blksize ;
|
||||
}
|
||||
for ( ; i > 0 ; i -= l ) {
|
||||
l = read(fd, cp, i);
|
||||
cp += l ;
|
||||
}
|
||||
if (rec_fmt == AFMT_S16_LE) {
|
||||
for (i=0; i< blksize; i++) {
|
||||
#if 1 /* remove DC component... */
|
||||
int mean = smean >> 13;
|
||||
int dif = ((short) s16_buf[i]) - mean;
|
||||
smean += dif ;
|
||||
readbuf[i] = lintomulawX[ dif & 0x1ffff ] ;
|
||||
#else
|
||||
readbuf[i] = lintomulaw[ s16_buf[i] ] ;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (rec_fmt == AFMT_S8) {
|
||||
for (i=0; i< blksize; i++)
|
||||
readbuf[i] = lintomulaw[ readbuf[i]<<8 ] ;
|
||||
}
|
||||
else if (rec_fmt == AFMT_U8) {
|
||||
for (i=0; i< blksize; i++)
|
||||
readbuf[i] = lintomulaw[ (readbuf[i]<<8) ^ 0x8000 ] ;
|
||||
}
|
||||
if (iport == 3) {
|
||||
l = read(ext_fd, readbuf, blksize);
|
||||
if (l < blksize) {
|
||||
lseek(ext_fd, (off_t) 0, 0);
|
||||
read(ext_fd, readbuf+l, blksize - l);
|
||||
}
|
||||
}
|
||||
return readbuf;
|
||||
}
|
||||
|
||||
/*
|
||||
* should check that I HaveAudio() before trying to set gain.
|
||||
*
|
||||
* In most mixer devices, there is only a master volume control on
|
||||
* the capture channel, so the following code does not really work
|
||||
* as expected. The only (partial) exception is the MIC line, where
|
||||
* there is generally a 20dB boost which can be enabled or not
|
||||
* depending on the type of device.
|
||||
*/
|
||||
void VoxWare::SetRGain(int level)
|
||||
{
|
||||
double x = level;
|
||||
level = (int) (x/2.56);
|
||||
int foo = (level<<8) | level;
|
||||
if (!HaveAudio())
|
||||
Obtain();
|
||||
switch (iport) {
|
||||
case 2:
|
||||
case 1:
|
||||
break;
|
||||
case 0:
|
||||
if (ioctl(fd, MIXER_WRITE(SOUND_MIXER_MIC), &foo) == -1)
|
||||
printf("failed to set mic volume \n");
|
||||
break;
|
||||
}
|
||||
if (ioctl(fd, MIXER_WRITE(SOUND_MIXER_IGAIN), &foo) == -1)
|
||||
printf("failed set input line volume \n");
|
||||
rgain = level;
|
||||
}
|
||||
|
||||
void VoxWare::SetPGain(int level)
|
||||
{
|
||||
float x = level;
|
||||
level = (int) (x/2.56);
|
||||
int foo = (level<<8) | level;
|
||||
if (ioctl(fd, MIXER_WRITE(SOUND_MIXER_PCM), &foo) == -1) {
|
||||
printf("failed to output level %d \n", level);
|
||||
}
|
||||
pgain = level;
|
||||
}
|
||||
|
||||
void VoxWare::OutputPort(int p)
|
||||
{
|
||||
oport = p;
|
||||
}
|
||||
|
||||
void VoxWare::InputPort(int p)
|
||||
{
|
||||
int src = 0;
|
||||
|
||||
if (ext_fd >=0 && p != 3) {
|
||||
close(ext_fd);
|
||||
ext_fd = -1 ;
|
||||
}
|
||||
|
||||
switch(p) {
|
||||
case 3:
|
||||
fprintf(stderr,"input from file %s\n", ext_fname);
|
||||
if (ext_fd == -1)
|
||||
ext_fd = open(ext_fname, 0);
|
||||
if (ext_fd != -1)
|
||||
lseek(ext_fd, (off_t) 0, 0);
|
||||
break;
|
||||
case 2:
|
||||
src = 1 << SOUND_MIXER_LINE;
|
||||
break;
|
||||
case 1: /* cd ... */
|
||||
src = 1 << SOUND_MIXER_CD;
|
||||
break;
|
||||
case 0 :
|
||||
src = 1 << SOUND_MIXER_MIC;
|
||||
break;
|
||||
}
|
||||
if ( ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &src) == -1 ) {
|
||||
printf("failed to select input \n");
|
||||
p = 0;
|
||||
}
|
||||
iport = p;
|
||||
}
|
||||
|
||||
void VoxWare::RMute()
|
||||
{
|
||||
rmute |= 1;
|
||||
}
|
||||
|
||||
void VoxWare::RUnmute()
|
||||
{
|
||||
rmute &=~ 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* FrameReady must return 0 every so often, or the system will keep
|
||||
* processing mike data and not other events.
|
||||
*/
|
||||
int VoxWare::FrameReady()
|
||||
{
|
||||
int i = 0;
|
||||
int lim = blksize;
|
||||
|
||||
ioctl(fd, FIONREAD, &i );
|
||||
if (rec_fmt == AFMT_S16_LE) lim = 2*blksize;
|
||||
return (i >= lim) ? 1 : 0 ;
|
||||
}
|
||||
/*** end of file ***/
|
File diff suppressed because it is too large
Load Diff
@ -1,240 +0,0 @@
|
||||
# This file tells config what files go into building a kernel,
|
||||
# files marked standard are always included.
|
||||
#
|
||||
# $Id: files.i386,v 1.141.2.11 1997/04/04 04:35:46 gibbs Exp $
|
||||
#
|
||||
aic7xxx_asm optional ahc device-driver \
|
||||
dependency "$S/dev/aic7xxx/*.[chyl]" \
|
||||
compile-with "cd $S/dev/aic7xxx; make obj; make BINDIR=${.CURDIR} all install" \
|
||||
no-obj no-implicit-rule \
|
||||
clean "aic7xxx_asm"
|
||||
#
|
||||
aic7xxx_{seq,reg}.h optional ahc device-driver \
|
||||
compile-with "./aic7xxx_asm ${INCLUDES} -o aic7xxx_seq.h -r aic7xxx_reg.h $S/dev/aic7xxx/aic7xxx.seq" \
|
||||
no-obj no-implicit-rule before-depend \
|
||||
clean "aic7xxx_seq.h aic7xxx_reg.h" \
|
||||
dependency "$S/dev/aic7xxx/aic7xxx.{reg,seq} aic7xxx_asm"
|
||||
#
|
||||
linux_genassym optional compat_linux \
|
||||
dependency "$S/i386/linux/linux_genassym.c $S/i386/linux/linux.h" \
|
||||
compile-with "${CC} ${CFLAGS} ${PARAM} -UKERNEL -o $@ $<" \
|
||||
no-obj no-implicit-rule \
|
||||
clean "linux_genassym"
|
||||
#
|
||||
linux_assym.h optional compat_linux \
|
||||
dependency "linux_genassym" \
|
||||
compile-with "./linux_genassym > $@" \
|
||||
no-obj no-implicit-rule before-depend \
|
||||
clean "linux_assym.h"
|
||||
#
|
||||
i386/scsi/93cx6.c optional ahc device-driver
|
||||
i386/apm/apm.c optional apm device-driver
|
||||
i386/apm/apm_setup.s optional apm
|
||||
i386/eisa/3c5x9.c optional ep device-driver
|
||||
i386/eisa/aic7770.c optional ahc device-driver \
|
||||
dependency "aic7xxx_reg.h $S/i386/eisa/aic7770.c"
|
||||
i386/eisa/aha1742.c optional ahb device-driver
|
||||
i386/eisa/bt74x.c optional bt device-driver
|
||||
i386/eisa/eisaconf.c optional eisa
|
||||
i386/eisa/if_vx_eisa.c optional vx device-driver
|
||||
i386/eisa/if_fea.c optional fea device-driver
|
||||
i386/i386/autoconf.c standard device-driver
|
||||
i386/i386/cons.c standard
|
||||
i386/i386/db_disasm.c optional ddb
|
||||
i386/i386/db_interface.c optional ddb
|
||||
i386/i386/db_trace.c optional ddb
|
||||
i386/i386/i386-gdbstub.c optional ddb
|
||||
i386/i386/exception.s standard
|
||||
i386/i386/identcpu.c standard
|
||||
i386/i386/in_cksum.c optional inet
|
||||
# locore.s needs to be handled in Makefile to put it first. Otherwise it's
|
||||
# now normal.
|
||||
# i386/i386/locore.s standard
|
||||
i386/i386/machdep.c standard
|
||||
i386/i386/math_emulate.c optional math_emulate
|
||||
i386/i386/mem.c standard
|
||||
i386/i386/microtime.s standard
|
||||
i386/i386/perfmon.c optional perfmon profiling-routine
|
||||
i386/i386/perfmon.c optional perfmon
|
||||
i386/i386/pmap.c standard
|
||||
i386/i386/procfs_machdep.c standard
|
||||
i386/i386/support.s standard
|
||||
i386/i386/swtch.s standard
|
||||
i386/i386/sys_machdep.c standard
|
||||
i386/i386/trap.c standard
|
||||
i386/i386/userconfig.c optional userconfig
|
||||
i386/i386/vm_machdep.c standard
|
||||
i386/ibcs2/ibcs2_fcntl.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_stat.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_ipc.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_msg.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_misc.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_other.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_signal.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_ioctl.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_socksys.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_sysi86.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_util.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_isc.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_isc_sysent.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_xenix.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_xenix_sysent.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_errno.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_sysent.c optional ibcs2
|
||||
i386/ibcs2/ibcs2_sysvec.c optional ibcs2
|
||||
i386/ibcs2/imgact_coff.c optional ibcs2
|
||||
i386/isa/aha1542.c optional aha device-driver
|
||||
i386/isa/aic6360.c optional aic device-driver
|
||||
i386/isa/b004.c optional bqu device-driver
|
||||
i386/isa/bt5xx-445.c optional bt device-driver
|
||||
i386/isa/clock.c standard
|
||||
i386/isa/cronyx.c optional cx device-driver
|
||||
i386/isa/ctx.c optional ctx device-driver
|
||||
i386/isa/cx.c optional cx device-driver
|
||||
i386/isa/cy.c optional cy device-driver
|
||||
i386/isa/diskslice_machdep.c standard
|
||||
i386/isa/elink.c optional ep device-driver
|
||||
i386/isa/elink.c optional ie device-driver
|
||||
i386/isa/fd.c optional fd device-driver
|
||||
i386/isa/ft.c optional ft device-driver
|
||||
i386/isa/gpib.c optional gp device-driver
|
||||
i386/isa/asc.c optional asc device-driver
|
||||
i386/isa/gsc.c optional gsc device-driver
|
||||
i386/isa/if_ar.c optional ar device-driver
|
||||
i386/isa/if_cx.c optional cx device-driver
|
||||
i386/isa/if_ed.c optional ed device-driver
|
||||
i386/isa/if_eg.c optional eg device-driver
|
||||
i386/isa/if_el.c optional el device-driver
|
||||
i386/isa/if_ep.c optional ep device-driver
|
||||
i386/isa/if_ex.c optional ex device-driver
|
||||
i386/isa/if_fe.c optional fe device-driver
|
||||
i386/isa/if_ie.c optional ie device-driver
|
||||
i386/isa/if_ix.c optional ix device-driver
|
||||
i386/isa/if_le.c optional le device-driver
|
||||
i386/isa/if_lnc.c optional lnc device-driver
|
||||
i386/isa/if_sr.c optional sr device-driver
|
||||
i386/isa/if_ze.c optional ze device-driver
|
||||
i386/isa/if_zp.c optional zp device-driver
|
||||
i386/isa/isa.c optional isa device-driver
|
||||
i386/isa/istallion.c optional stli device-driver
|
||||
i386/isa/joy.c optional joy device-driver
|
||||
i386/isa/kbdio.c optional psm device-driver
|
||||
i386/isa/kbdio.c optional sc device-driver
|
||||
i386/isa/kbdio.c optional vt device-driver
|
||||
i386/isa/lpt.c optional lpt device-driver
|
||||
i386/isa/labpc.c optional labpc device-driver
|
||||
i386/isa/mcd.c optional mcd device-driver
|
||||
i386/isa/mse.c optional mse device-driver
|
||||
i386/isa/ncr5380.c optional nca device-driver
|
||||
i386/isa/npx.c optional npx device-driver
|
||||
i386/isa/pcaudio.c optional pca device-driver
|
||||
i386/isa/matcd/matcd.c optional matcd device-driver
|
||||
i386/isa/pcibus.c optional pci device-driver
|
||||
i386/isa/pcicx.c optional ze device-driver
|
||||
i386/isa/pcicx.c optional zp device-driver
|
||||
i386/isa/pcvt/pcvt_drv.c optional vt device-driver
|
||||
i386/isa/pcvt/pcvt_ext.c optional vt device-driver
|
||||
i386/isa/pcvt/pcvt_kbd.c optional vt device-driver
|
||||
i386/isa/pcvt/pcvt_out.c optional vt device-driver
|
||||
i386/isa/pcvt/pcvt_sup.c optional vt device-driver
|
||||
i386/isa/pcvt/pcvt_vtf.c optional vt device-driver
|
||||
i386/isa/pnp.c optional pnp device-driver
|
||||
i386/isa/prof_machdep.c optional profiling-routine
|
||||
i386/isa/psm.c optional psm device-driver
|
||||
i386/isa/qcam.c optional qcam device-driver
|
||||
i386/isa/qcamio.c optional qcam device-driver
|
||||
i386/isa/random_machdep.c standard
|
||||
i386/isa/rc.c optional rc device-driver
|
||||
i386/isa/scd.c optional scd device-driver
|
||||
i386/isa/seagate.c optional sea device-driver
|
||||
i386/isa/si.c optional si device-driver
|
||||
i386/isa/si_code.c optional si device-driver
|
||||
i386/isa/sio.c optional sio device-driver
|
||||
i386/isa/snd/sound.c optional pcm device-driver
|
||||
i386/isa/snd/dmabuf.c optional pcm device-driver
|
||||
i386/isa/snd/ad1848.c optional pcm device-driver
|
||||
i386/isa/snd/sb_dsp.c optional pcm device-driver
|
||||
i386/isa/snd/clones.c optional pcm device-driver
|
||||
i386/isa/spigot.c optional spigot device-driver
|
||||
i386/isa/spkr.c optional speaker device-driver
|
||||
i386/isa/stallion.c optional stl device-driver
|
||||
i386/isa/syscons.c optional sc device-driver
|
||||
i386/isa/tw.c optional tw device-driver
|
||||
i386/isa/ultra14f.c optional uha device-driver
|
||||
i386/isa/wd.c optional wdc device-driver
|
||||
i386/isa/wd.c optional wd device-driver
|
||||
i386/isa/atapi.c optional atapi device-driver
|
||||
i386/isa/wcd.c optional wcd device-driver
|
||||
i386/isa/wd7000.c optional wds device-driver
|
||||
i386/isa/wt.c optional wt device-driver
|
||||
i386/linux/imgact_linux.c optional compat_linux
|
||||
i386/linux/linux_dummy.c optional compat_linux
|
||||
i386/linux/linux_file.c optional compat_linux
|
||||
i386/linux/linux_ioctl.c optional compat_linux
|
||||
i386/linux/linux_ipc.c optional compat_linux
|
||||
i386/linux/linux_locore.s optional compat_linux \
|
||||
dependency "linux_assym.h"
|
||||
i386/linux/linux_misc.c optional compat_linux
|
||||
i386/linux/linux_signal.c optional compat_linux
|
||||
i386/linux/linux_socket.c optional compat_linux
|
||||
i386/linux/linux_stats.c optional compat_linux
|
||||
i386/linux/linux_sysent.c optional compat_linux
|
||||
i386/linux/linux_sysvec.c optional compat_linux
|
||||
i386/linux/linux_util.c optional compat_linux
|
||||
i386/scsi/aic7xxx.c optional ahc device-driver \
|
||||
dependency "aic7xxx_{reg,seq}.h"
|
||||
i386/scsi/bt.c optional bt device-driver
|
||||
libkern/bcd.c standard
|
||||
libkern/divdi3.c standard
|
||||
libkern/inet_ntoa.c standard
|
||||
libkern/index.c standard
|
||||
libkern/mcount.c optional profiling-routine
|
||||
libkern/moddi3.c standard
|
||||
libkern/qdivrem.c standard
|
||||
libkern/qsort.c standard
|
||||
libkern/random.c standard
|
||||
libkern/scanc.c standard
|
||||
libkern/skpc.c standard
|
||||
libkern/strcat.c standard
|
||||
libkern/strcmp.c standard
|
||||
libkern/strcpy.c standard
|
||||
libkern/strlen.c standard
|
||||
libkern/strncmp.c standard
|
||||
libkern/strncpy.c standard
|
||||
libkern/udivdi3.c standard
|
||||
libkern/umoddi3.c standard
|
||||
gnu/i386/fpemul/div_small.s optional gpl_math_emulate
|
||||
gnu/i386/fpemul/errors.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/fpu_arith.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/fpu_aux.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/fpu_entry.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/fpu_etc.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/fpu_trig.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/get_address.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/load_store.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/poly_2xm1.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/poly_atan.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/poly_div.s optional gpl_math_emulate
|
||||
gnu/i386/fpemul/poly_l2.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/poly_mul64.s optional gpl_math_emulate
|
||||
gnu/i386/fpemul/poly_sin.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/poly_tan.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/polynomial.s optional gpl_math_emulate
|
||||
gnu/i386/fpemul/reg_add_sub.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/reg_compare.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/reg_constant.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/reg_div.s optional gpl_math_emulate
|
||||
gnu/i386/fpemul/reg_ld_str.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/reg_mul.c optional gpl_math_emulate
|
||||
gnu/i386/fpemul/reg_norm.s optional gpl_math_emulate
|
||||
gnu/i386/fpemul/reg_round.s optional gpl_math_emulate
|
||||
gnu/i386/fpemul/reg_u_add.s optional gpl_math_emulate
|
||||
gnu/i386/fpemul/reg_u_div.s optional gpl_math_emulate
|
||||
gnu/i386/fpemul/reg_u_mul.s optional gpl_math_emulate
|
||||
gnu/i386/fpemul/reg_u_sub.s optional gpl_math_emulate
|
||||
gnu/i386/fpemul/wm_shrx.s optional gpl_math_emulate
|
||||
gnu/i386/fpemul/wm_sqrt.s optional gpl_math_emulate
|
||||
gnu/i386/isa/dgb.c optional dgb device-driver
|
||||
gnu/i386/isa/nic3008.c optional nic device-driver
|
||||
gnu/i386/isa/nic3009.c optional nnic device-driver
|
||||
pci/wd82371.c optional wd device-driver
|
@ -1,41 +0,0 @@
|
||||
--- linux.h.orig Tue Dec 3 16:47:28 1996
|
||||
+++ linux.h Mon Nov 17 00:05:29 1997
|
||||
@@ -491,6 +491,9 @@
|
||||
#define LINUX_SNDCTL_DSP_GETOSPACE 0x500C
|
||||
#define LINUX_SNDCTL_DSP_GETISPACE 0x500D
|
||||
#define LINUX_SNDCTL_DSP_NONBLOCK 0x500E
|
||||
+#define LINUX_SNDCTL_DSP_GETCAPS 0x500F
|
||||
+#define LINUX_SNDCTL_DSP_GETIPTR 0x5011
|
||||
+#define LINUX_SNDCTL_DSP_GETOPTR 0x5012
|
||||
#define LINUX_SOUND_MIXER_WRITE_VOLUME 0x4d00
|
||||
#define LINUX_SOUND_MIXER_WRITE_BASS 0x4d01
|
||||
#define LINUX_SOUND_MIXER_WRITE_TREBLE 0x4d02
|
||||
--- linux_ioctl.c.orig Sat Nov 9 22:10:15 1996
|
||||
+++ linux_ioctl.c Mon Nov 17 10:20:14 1997
|
||||
@@ -691,6 +691,26 @@
|
||||
args->cmd = SNDCTL_DSP_NONBLOCK;
|
||||
return ioctl(p, (struct ioctl_args *)args, retval);
|
||||
|
||||
+ case LINUX_SNDCTL_DSP_GETCAPS:
|
||||
+ args->cmd = SNDCTL_DSP_GETCAPS;
|
||||
+ return ioctl(p, (struct ioctl_args *)args, retval);
|
||||
+
|
||||
+ case LINUX_SNDCTL_DSP_GETIPTR:
|
||||
+ args->cmd = SNDCTL_DSP_GETIPTR;
|
||||
+ return ioctl(p, (struct ioctl_args *)args, retval);
|
||||
+
|
||||
+ case LINUX_SNDCTL_DSP_GETOPTR:
|
||||
+ args->cmd = SNDCTL_DSP_GETOPTR;
|
||||
+ { int a= ioctl(p, (struct ioctl_args *)args, retval);
|
||||
+ struct count_info *p = (struct count_info *)(args->arg);
|
||||
+#if 0
|
||||
+ p->bytes += 128 ;
|
||||
+ uprintf("GETOPTR bytes %d blk %d ptr %d\n",
|
||||
+ p->bytes, p->blocks, p->ptr);
|
||||
+#endif
|
||||
+ return a;
|
||||
+ }
|
||||
+
|
||||
case LINUX_SOUND_MIXER_WRITE_VOLUME:
|
||||
args->cmd = SOUND_MIXER_WRITE_VOLUME;
|
||||
return ioctl(p, (struct ioctl_args *)args, retval);
|
@ -1,238 +0,0 @@
|
||||
/*
|
||||
* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka
|
||||
* Toivonen <toivonen@clinet.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 675
|
||||
* Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* linux_audio.c
|
||||
*
|
||||
* Functions to play sound on the VoxWare audio driver (Linux or FreeBSD)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef linux
|
||||
#include <sys/ioctl.h> /* new with 1.2.0? Didn't need this under
|
||||
* 1.1.64 */
|
||||
#include <linux/soundcard.h>
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <stdio.h>
|
||||
#include <machine/soundcard.h>
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "output.h"
|
||||
#include "controls.h"
|
||||
|
||||
static int open_output(void); /* 0=success, 1=warning, -1=fatal
|
||||
* error */
|
||||
static void close_output(void);
|
||||
static void output_data(int32 * buf, int32 count);
|
||||
static void flush_output(void);
|
||||
static void purge_output(void);
|
||||
|
||||
/* export the playback mode */
|
||||
|
||||
#define dpm linux_play_mode
|
||||
|
||||
PlayMode dpm = {
|
||||
DEFAULT_RATE, PE_16BIT | PE_SIGNED,
|
||||
-1,
|
||||
{0}, /* default: get all the buffer fragments you
|
||||
* can */
|
||||
"Linux dsp device", 'd',
|
||||
"/dev/dsp",
|
||||
open_output,
|
||||
close_output,
|
||||
output_data,
|
||||
flush_output,
|
||||
purge_output
|
||||
};
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/*
|
||||
* We currently only honor the PE_MONO bit, the sample rate, and the number
|
||||
* of buffer fragments. We try 16-bit signed data first, and then 8-bit
|
||||
* unsigned if it fails. If you have a sound device that can't handle either,
|
||||
* let me know.
|
||||
*/
|
||||
|
||||
static int
|
||||
open_output(void)
|
||||
{
|
||||
int fd, tmp, i, warnings = 0;
|
||||
|
||||
/* Open the audio device */
|
||||
fd = open(dpm.name, O_RDWR /* | O_NDELAY */);
|
||||
if (fd < 0) {
|
||||
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
|
||||
dpm.name, sys_errlist[errno]);
|
||||
return -1;
|
||||
}
|
||||
/* They can't mean these */
|
||||
dpm.encoding &= ~(PE_ULAW | PE_BYTESWAP);
|
||||
|
||||
|
||||
/*
|
||||
* Set sample width to whichever the user wants. If it fails, try the
|
||||
* other one.
|
||||
*/
|
||||
|
||||
i = tmp = (dpm.encoding & PE_16BIT) ? 16 : 8;
|
||||
if (dpm.encoding & PE_16BIT) {
|
||||
int fmt = AFMT_S16_LE ;
|
||||
|
||||
if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0 || fmt != AFMT_S16_LE) {
|
||||
fmt = AFMT_U8 ;
|
||||
if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0 || fmt != AFMT_U8) {
|
||||
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
|
||||
"%s doesn't support 16- or 8-bit sample width",
|
||||
dpm.name);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
|
||||
"Sample width adjusted to %d bits", tmp);
|
||||
dpm.encoding ^= PE_16BIT;
|
||||
warnings = 1;
|
||||
}
|
||||
}
|
||||
if (dpm.encoding & PE_16BIT)
|
||||
dpm.encoding |= PE_SIGNED;
|
||||
else
|
||||
dpm.encoding &= ~PE_SIGNED;
|
||||
|
||||
|
||||
/*
|
||||
* Try stereo or mono, whichever the user wants. If it fails, try the
|
||||
* other.
|
||||
*/
|
||||
|
||||
i = tmp = (dpm.encoding & PE_MONO) ? 0 : 1;
|
||||
if ((ioctl(fd, SNDCTL_DSP_STEREO, &tmp) < 0) || tmp != i) {
|
||||
i = tmp = (dpm.encoding & PE_MONO) ? 1 : 0;
|
||||
|
||||
if ((ioctl(fd, SNDCTL_DSP_STEREO, &tmp) < 0) || tmp != i) {
|
||||
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
|
||||
"%s doesn't support mono or stereo samples",
|
||||
dpm.name);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (tmp == 0)
|
||||
dpm.encoding |= PE_MONO;
|
||||
else
|
||||
dpm.encoding &= ~PE_MONO;
|
||||
ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "Sound adjusted to %sphonic",
|
||||
(tmp == 0) ? "mono" : "stereo");
|
||||
warnings = 1;
|
||||
}
|
||||
/* Set the sample rate */
|
||||
|
||||
tmp = dpm.rate;
|
||||
if (ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0) {
|
||||
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
|
||||
"%s doesn't support a %d Hz sample rate",
|
||||
dpm.name, dpm.rate);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (tmp != dpm.rate) {
|
||||
dpm.rate = tmp;
|
||||
ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
|
||||
"Output rate adjusted to %d Hz", dpm.rate);
|
||||
warnings = 1;
|
||||
}
|
||||
/* Older VoxWare drivers don't have buffer fragment capabilities */
|
||||
#ifdef SNDCTL_DSP_SETFRAGMENT
|
||||
/* Set buffer fragments (in extra_param[0]) */
|
||||
|
||||
tmp = 2+ AUDIO_BUFFER_BITS ;
|
||||
if (!(dpm.encoding & PE_MONO))
|
||||
tmp++;
|
||||
if (dpm.encoding & PE_16BIT)
|
||||
tmp++;
|
||||
tmp |= (dpm.extra_param[0] << 16);
|
||||
i = tmp;
|
||||
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &tmp) < 0) {
|
||||
ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
|
||||
"%s doesn't support %d-byte buffer fragments", dpm.name, (1 << i));
|
||||
/*
|
||||
* It should still work in some fashion. We should use a secondary
|
||||
* buffer anyway -- 64k isn't enough.
|
||||
*/
|
||||
warnings = 1;
|
||||
}
|
||||
#else
|
||||
if (dpm.extra_param[0]) {
|
||||
ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
|
||||
"%s doesn't support buffer fragments", dpm.name);
|
||||
warnings = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
dpm.fd = fd;
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
static void
|
||||
output_data(int32 * buf, int32 count)
|
||||
{
|
||||
char *p;
|
||||
int res, l;
|
||||
|
||||
if (!(dpm.encoding & PE_MONO))
|
||||
count *= 2; /* Stereo samples */
|
||||
|
||||
if (dpm.encoding & PE_16BIT) {
|
||||
/* Convert data to signed 16-bit PCM */
|
||||
s32tos16(buf, count);
|
||||
res = count*2;
|
||||
} else {
|
||||
/* Convert to 8-bit unsigned and write out. */
|
||||
s32tou8(buf, count);
|
||||
res = count ;
|
||||
}
|
||||
for (p = buf ; res > 0 ; res -= l ) {
|
||||
l = write(dpm.fd, p, res);
|
||||
if (l < 0) return ;
|
||||
p += l ;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
close_output(void)
|
||||
{
|
||||
close(dpm.fd);
|
||||
}
|
||||
|
||||
static void
|
||||
flush_output(void)
|
||||
{
|
||||
ioctl(dpm.fd, SNDCTL_DSP_SYNC);
|
||||
}
|
||||
|
||||
static void
|
||||
purge_output(void)
|
||||
{
|
||||
ioctl(dpm.fd, SNDCTL_DSP_RESET);
|
||||
}
|
@ -1,278 +0,0 @@
|
||||
/*
|
||||
* This is a simple program which demonstrates use of mmapped DMA buffer
|
||||
* of the sound driver directly from application program.
|
||||
*
|
||||
* This sample program works (currently) only with Linux, FreeBSD and BSD/OS
|
||||
* (FreeBSD and BSD/OS require OSS version 3.8-beta16 or later.
|
||||
*
|
||||
* Note! Don't use mmapped DMA buffers (direct audio) unless you have
|
||||
* very good reasons to do it. Programs using this feature will not
|
||||
* work with all soundcards. GUS (GF1) is one of them (GUS MAX works).
|
||||
*
|
||||
* This program requires version 3.5-beta7 or later of OSS
|
||||
* (3.8-beta16 or later in FreeBSD and BSD/OS).
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/soundcard.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
main()
|
||||
{
|
||||
int fd, sz, fsz, i, tmp, n, l, have_data=0, nfrag;
|
||||
int caps;
|
||||
|
||||
int sd, sl=0, sp;
|
||||
|
||||
unsigned char data[500000], *dp = data;
|
||||
|
||||
struct buffmem_desc imemd, omemd;
|
||||
caddr_t buf;
|
||||
struct timeval tim;
|
||||
|
||||
unsigned char *op;
|
||||
|
||||
struct audio_buf_info info;
|
||||
|
||||
int frag = 0xffff000c; /* Max # fragments of 2^13=8k bytes */
|
||||
|
||||
fd_set writeset;
|
||||
|
||||
close(0);
|
||||
if ((fd=open("/dev/dsp", O_RDWR, 0))==-1)
|
||||
{
|
||||
perror("/dev/dsp");
|
||||
exit(-1);
|
||||
}
|
||||
/*
|
||||
* Then setup sampling parameters. Just sampling rate in this case.
|
||||
*/
|
||||
|
||||
tmp = 48000;
|
||||
ioctl(fd, SNDCTL_DSP_SPEED, &tmp);
|
||||
printf("Speed set to %d\n", tmp);
|
||||
|
||||
/*
|
||||
* Load some test data.
|
||||
*/
|
||||
|
||||
sl = sp = 0;
|
||||
if ((sd=open("smpl", O_RDONLY, 0))!=-1)
|
||||
{
|
||||
sl = read(sd, data, sizeof(data));
|
||||
printf("%d bytes read from file.\n", sl);
|
||||
close(sd);
|
||||
}
|
||||
else perror("smpl");
|
||||
|
||||
if (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps)==-1)
|
||||
{
|
||||
perror("/dev/dsp");
|
||||
fprintf(stderr, "Sorry but your sound driver is too old\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the device has capability to do this. Currently just
|
||||
* CS4231 based cards will work.
|
||||
*
|
||||
* The application should also check for DSP_CAP_MMAP bit but this
|
||||
* version of driver doesn't have it yet.
|
||||
*/
|
||||
/* ioctl(fd, SNDCTL_DSP_SETSYNCRO, 0); */
|
||||
|
||||
/*
|
||||
* You need version 3.5-beta7 or later of the sound driver before next
|
||||
* two lines compile. There is no point to modify this program to
|
||||
* compile with older driver versions since they don't have working
|
||||
* mmap() support.
|
||||
*/
|
||||
if (!(caps & DSP_CAP_TRIGGER) ||
|
||||
!(caps & DSP_CAP_MMAP))
|
||||
{
|
||||
fprintf(stderr, "Sorry but your soundcard can't do this\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Select the fragment size. This is propably important only when
|
||||
* the program uses select(). Fragment size defines how often
|
||||
* select call returns.
|
||||
*/
|
||||
|
||||
ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag);
|
||||
|
||||
/*
|
||||
* Compute total size of the buffer. It's important to use this value
|
||||
* in mmap() call.
|
||||
*/
|
||||
|
||||
if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info)==-1)
|
||||
{
|
||||
perror("GETOSPACE");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
sz = info.fragstotal * info.fragsize;
|
||||
fsz = info.fragsize;
|
||||
|
||||
/*
|
||||
* Call mmap().
|
||||
*
|
||||
* IMPORTANT NOTE!!!!!!!!!!!
|
||||
*
|
||||
* Full duplex audio devices have separate input and output buffers.
|
||||
* It is not possible to map both of them at the same mmap() call. The buffer
|
||||
* is selected based on the prot argument in the following way:
|
||||
*
|
||||
* - PROT_READ (alone) selects the input buffer.
|
||||
* - PROT_WRITE (alone) selects the output buffer.
|
||||
* - PROT_WRITE|PROT_READ together select the output buffer. This combination
|
||||
* is required in BSD to make the buffer accessible. With just PROT_WRITE
|
||||
* every attempt to access the returned buffer will result in segmentation/bus
|
||||
* error. PROT_READ|PROT_WRITE is also permitted in Linux with OSS version
|
||||
* 3.8-beta16 and later (earlier versions don't accept it).
|
||||
*
|
||||
* Non duplex devices have just one buffer. When an application wants to do both
|
||||
* input and output it's recommended that the device is closed and re-opened when
|
||||
* switching between modes. PROT_READ|PROT_WRITE can be used to open the buffer
|
||||
* for both input and output (with OSS 3.8-beta16 and later) but the result may be
|
||||
* unpredictable.
|
||||
*/
|
||||
|
||||
if ((buf=mmap(NULL, sz, PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0))==(caddr_t)-1)
|
||||
{
|
||||
perror("mmap (write)");
|
||||
exit(-1);
|
||||
}
|
||||
printf("mmap (out) returned %08x\n", buf);
|
||||
op=buf;
|
||||
|
||||
/*
|
||||
* op contains now a pointer to the DMA buffer
|
||||
*/
|
||||
|
||||
/*
|
||||
* Then it's time to start the engine. The driver doesn't allow read() and/or
|
||||
* write() when the buffer is mapped. So the only way to start operation is
|
||||
* to togle device's enable bits. First set them off. Setting them on enables
|
||||
* recording and/or playback.
|
||||
*/
|
||||
|
||||
tmp = 0;
|
||||
ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp);
|
||||
printf("Trigger set to %08x\n", tmp);
|
||||
|
||||
/*
|
||||
* It might be usefull to write some data to the buffer before starting.
|
||||
*/
|
||||
|
||||
tmp = PCM_ENABLE_OUTPUT;
|
||||
ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp);
|
||||
printf("Trigger set to %08x\n", tmp);
|
||||
|
||||
/*
|
||||
* The machine is up and running now. Use SNDCTL_DSP_GETOPTR to get the
|
||||
* buffer status.
|
||||
*
|
||||
* NOTE! The driver empties each buffer fragmen after they have been
|
||||
* played. This prevents looping sound if there are some performance problems
|
||||
* in the application side. For similar reasons it recommended that the
|
||||
* application uses some amout of play ahead. It can rewrite the unplayed
|
||||
* data later if necessary.
|
||||
*/
|
||||
|
||||
nfrag = 0;
|
||||
while (1)
|
||||
{
|
||||
struct count_info count;
|
||||
int p, l, extra;
|
||||
|
||||
FD_ZERO(&writeset);
|
||||
FD_SET(fd, &writeset);
|
||||
|
||||
tim.tv_sec = 10;
|
||||
tim.tv_usec= 0;
|
||||
|
||||
select(fd+1, &writeset, &writeset, NULL, NULL);
|
||||
/*
|
||||
* SNDCTL_DSP_GETOPTR (and GETIPTR as well) return three items. The
|
||||
* bytes field returns number of bytes played since start. It can be used
|
||||
* as a real time clock.
|
||||
*
|
||||
* The blocks field returns number of fragment transitions (interrupts) since
|
||||
* previous GETOPTR call. It can be used as a method to detect underrun
|
||||
* situations.
|
||||
*
|
||||
* The ptr field is the DMA pointer inside the buffer area (in bytes from
|
||||
* the beginning of total buffer area).
|
||||
*/
|
||||
|
||||
if (ioctl(fd, SNDCTL_DSP_GETOPTR, &count)==-1)
|
||||
{
|
||||
perror("GETOPTR");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
nfrag += count.blocks;
|
||||
|
||||
#ifdef VERBOSE
|
||||
|
||||
printf("\rTotal: %09d, Fragment: %03d, Ptr: %06d",
|
||||
count.bytes, nfrag, count.ptr);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Caution! This version doesn't check for bounds of the DMA
|
||||
* memory area. It's possible that the returned pointer value is not aligned
|
||||
* to fragment boundaries. It may be several samples behind the boundary
|
||||
* in case there was extra delay between the actual hardware interrupt and
|
||||
* the time when DSP_GETOPTR was called.
|
||||
*
|
||||
* Don't just call memcpy() with length set to 'fragment_size' without
|
||||
* first checking that the transfer really fits to the buffer area.
|
||||
* A mistake of just one byte causes seg fault. It may be easiest just
|
||||
* to align the returned pointer value to fragment boundary before using it.
|
||||
*
|
||||
* It would be very good idea to write few extra samples to next fragment
|
||||
* too. Otherwise several (uninitialized) samples from next fragment
|
||||
* will get played before your program gets chance to initialize them.
|
||||
* Take in count the fact thaat there are other processes batling about
|
||||
* the same CPU. This effect is likely to be very annoying if fragment
|
||||
* size is decreased too much.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Just a minor clarification to the above. The following line alings
|
||||
* the pointer to fragment boundaries. Note! Don't trust that fragment
|
||||
* size is always a power of 2. It may not be so in future.
|
||||
*/
|
||||
count.ptr = (count.ptr/fsz)*fsz;
|
||||
|
||||
#ifdef VERBOSE
|
||||
printf(" memcpy(%6d, %4d)", (dp-data), fsz);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set few bytes in the beginning of next fragment too.
|
||||
*/
|
||||
if ((count.ptr+fsz+16) < sz) /* Last fragment? */
|
||||
extra = 16;
|
||||
else
|
||||
extra = 0;
|
||||
|
||||
memcpy(op+count.ptr, dp, fsz+extra);
|
||||
|
||||
dp += fsz;
|
||||
if (dp > (data+sl-fsz))
|
||||
dp = data;
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
@ -1,236 +0,0 @@
|
||||
/*
|
||||
* pcmio.c -- a simple utility for controlling audio I/O
|
||||
* (rate, channels, resolution...)
|
||||
*
|
||||
* (C) Luigi Rizzo 1998
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <machine/soundcard.h>
|
||||
|
||||
char * usage_string =
|
||||
"Usage: %s [-f device] [+parameters] [len:skip] file { file }\n"
|
||||
"where device is the device to be used (default /dev/audio)\n"
|
||||
"[len:skip] is the subsection of the file to be played\n"
|
||||
"with a '-' indicating the default\n"
|
||||
"and parameters is a comma-separated list containing one or more of\n"
|
||||
" N the sampling speed\n"
|
||||
" stereo|mono \n"
|
||||
" cd|u8|alaw|ulaw|s16 data format\n"
|
||||
" loop loop (play cyclically)\n"
|
||||
;
|
||||
|
||||
#define BLKSZ 32768
|
||||
int format=AFMT_MU_LAW;
|
||||
int rate=8000 ;
|
||||
int stereo = 0 ;
|
||||
char dev[128];
|
||||
int audiodev;
|
||||
int play = 1 ; /* default */
|
||||
int loop = 0 ;
|
||||
int skip = 0;
|
||||
int size = -1 ;
|
||||
|
||||
extern char *optarg;
|
||||
extern int optind, optopt, opterr, optreset;
|
||||
|
||||
int
|
||||
usage(char *s)
|
||||
{
|
||||
printf(usage_string, s);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int
|
||||
parse_len(char *s)
|
||||
{
|
||||
int par = -1;
|
||||
int mul = 1;
|
||||
int p = strlen(s);
|
||||
|
||||
if (p == 0)
|
||||
return -1 ;
|
||||
|
||||
if (*s != '-')
|
||||
par = atoi(s);
|
||||
else
|
||||
return -1 ;
|
||||
switch(s[p-1]) {
|
||||
case 'k':
|
||||
case 'K':
|
||||
mul = 1024;
|
||||
break;
|
||||
case 'm':
|
||||
case 'M':
|
||||
mul = 1024*1024;
|
||||
break;
|
||||
case 's':
|
||||
mul = rate * (stereo+1);
|
||||
if (format == AFMT_S16_LE)
|
||||
mul += mul;
|
||||
break;
|
||||
}
|
||||
return par*mul;
|
||||
}
|
||||
|
||||
void
|
||||
parse_fmt(char *fmt)
|
||||
{
|
||||
char *s;
|
||||
int v, last = 0 ;
|
||||
|
||||
again:
|
||||
while (*fmt && (*fmt == ' ' || *fmt == '\t')) fmt++;
|
||||
s = fmt;
|
||||
while (*s && ! (*s == ',' || *s == ':' || *s == ';') ) s++;
|
||||
if (*s)
|
||||
*s='\0';
|
||||
else
|
||||
last = 1 ;
|
||||
v = atoi(fmt) ;
|
||||
if (v > 0 && v < 1000000)
|
||||
rate = v ;
|
||||
else {
|
||||
if (!strcmp(fmt, "ulaw")) format = AFMT_MU_LAW;
|
||||
else if (!strcmp(fmt, "alaw")) format = AFMT_A_LAW;
|
||||
else if (!strcmp(fmt, "u8")) format = AFMT_U8 ;
|
||||
else if (!strcmp(fmt, "s16")) format = AFMT_S16_LE ;
|
||||
else if (!strcmp(fmt, "mono")) stereo = 0;
|
||||
else if (!strcmp(fmt, "stereo")) stereo = 1;
|
||||
else if (!strcmp(fmt, "loop")) loop = 1;
|
||||
else if (!strcmp(fmt, "rec")) play = 0;
|
||||
else if (!strcmp(fmt, "play")) play = 1;
|
||||
else if (!strcmp(fmt, "cd")) {
|
||||
stereo = 1 ;
|
||||
format = AFMT_S16_LE ;
|
||||
rate = 44100;
|
||||
}
|
||||
}
|
||||
if (last == 0) {
|
||||
fmt = s+1;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
char buf[BLKSZ];
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i,c;
|
||||
int ac = argc;
|
||||
char *p;
|
||||
|
||||
strcpy(dev, "/dev/audio");
|
||||
|
||||
while ( (c= getopt(argc, argv, "f:") ) != EOF ) {
|
||||
switch (c) {
|
||||
case 'f' :
|
||||
if (optarg[0] >='0' && optarg[0] <='9')
|
||||
sprintf(dev, "/dev/audio%s", optarg);
|
||||
else
|
||||
strcpy(dev, optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*argv[optind] == '+') {
|
||||
parse_fmt(argv[optind]+1);
|
||||
optind++;
|
||||
}
|
||||
/*
|
||||
* assume a string with a "," and no "/" as a command
|
||||
*/
|
||||
if (strstr(argv[optind],",") && !strstr(argv[optind],"/") ) {
|
||||
parse_fmt(argv[optind]);
|
||||
optind++;
|
||||
}
|
||||
/*
|
||||
* assume a string with a ":" and no "/" as a time limit
|
||||
*/
|
||||
if ( (p = strstr(argv[optind] , ":")) && !strstr(argv[optind],"/") ) {
|
||||
*p = '\0';
|
||||
size = parse_len(argv[optind]);
|
||||
skip = parse_len(p+1);
|
||||
optind++;
|
||||
}
|
||||
printf("Using device %s, speed %d, mode 0x%08x, %s\n",
|
||||
dev, rate, format, stereo ? "stereo":"mono");
|
||||
printf("using files: ");
|
||||
for (i=optind; i< argc ; i++)
|
||||
printf("[%d] %s, ",i, argv[i]);
|
||||
printf("\n");
|
||||
|
||||
audiodev = open(dev, play ? 1 : 0);
|
||||
if (audiodev < 0) {
|
||||
printf("failed to open %d\n", dev);
|
||||
exit(2);
|
||||
}
|
||||
ioctl(audiodev, SNDCTL_DSP_SETFMT, &format);
|
||||
ioctl(audiodev, SNDCTL_DSP_STEREO, &stereo);
|
||||
ioctl(audiodev, SNDCTL_DSP_SPEED, &rate);
|
||||
printf("-- format %d,%s,0x%08x, len %d skip %d\n",
|
||||
rate, stereo? "stereo":"mono",format, size, skip);
|
||||
if (play) {
|
||||
off_t ofs;
|
||||
int limit;
|
||||
again:
|
||||
for (i=optind; i< argc ; i++) {
|
||||
int l = -2;
|
||||
int f = open(argv[i], O_RDONLY);
|
||||
int sz ;
|
||||
|
||||
printf("opened %s returns %d\n", argv[i], f);
|
||||
if (f < 0)
|
||||
continue;
|
||||
limit = size;
|
||||
if (skip > 0) {
|
||||
ofs = skip;
|
||||
lseek(f, ofs, 0 /* begin */ );
|
||||
}
|
||||
sz = BLKSZ;
|
||||
if (limit > 0 && limit < sz)
|
||||
sz = limit ;
|
||||
while ( (l = read(f, buf, sz) ) > 0 ) {
|
||||
write(audiodev, buf, l);
|
||||
if (limit > 0) {
|
||||
limit -= l ;
|
||||
if (limit > 0 && limit < sz)
|
||||
sz = limit ;
|
||||
if (limit <= 0 )
|
||||
break;
|
||||
if (limit < sz)
|
||||
sz = limit ;
|
||||
}
|
||||
}
|
||||
close(f);
|
||||
}
|
||||
if (loop)
|
||||
goto again;
|
||||
} else { /* record */
|
||||
int l = -2;
|
||||
int f ;
|
||||
if (!strcmp(argv[optind], "-") )
|
||||
f = 1;
|
||||
else
|
||||
f = open(argv[optind], O_WRONLY | O_CREAT | O_TRUNC, 0664);
|
||||
fprintf(stderr,"open %s returns %d\n", argv[optind], f);
|
||||
|
||||
while ( size > 0 && (l = read(audiodev, buf, BLKSZ) ) > 0 ) {
|
||||
if (l <= skip) {
|
||||
skip -= l ; /* at most skip = 0 */
|
||||
continue;
|
||||
} else { /* l > skip */
|
||||
l -= skip ;
|
||||
if (l > size)
|
||||
l = size ;
|
||||
write(f, buf+skip, l);
|
||||
skip = 0 ;
|
||||
size -= l ;
|
||||
}
|
||||
}
|
||||
close(f);
|
||||
}
|
||||
}
|
@ -1,275 +0,0 @@
|
||||
/*
|
||||
* Sound interface for Speak Freely for Unix
|
||||
*
|
||||
* Designed and implemented in July of 1990 by John Walker
|
||||
*
|
||||
* FreeBSD / voxware version
|
||||
*/
|
||||
|
||||
#define BUFL 8000
|
||||
|
||||
#include "speakfree.h"
|
||||
|
||||
#include <sys/dir.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
/* #include <math.h> */
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#ifdef LINUX
|
||||
#include <linux/soundcard.h>
|
||||
#else
|
||||
#include <machine/soundcard.h>
|
||||
#endif
|
||||
#define AUDIO_MIN_GAIN 0
|
||||
#define AUDIO_MAX_GAIN 255
|
||||
static int abuf_size;
|
||||
|
||||
#define SoundFile "/dev/audio"
|
||||
#define AUDIO_CTLDEV "/dev/mixer"
|
||||
|
||||
#define MAX_GAIN 100
|
||||
|
||||
struct sound_buf {
|
||||
struct sound_buf *snext; /* Next sound buffer */
|
||||
int sblen; /* Length of this sound buffer */
|
||||
unsigned char sbtext[2]; /* Actual sampled sound */
|
||||
};
|
||||
|
||||
/* Local variables */
|
||||
|
||||
static int audiof = -1; /* Audio device file descriptor */
|
||||
static int Audio_fd; /* Audio control port */
|
||||
struct sound_buf *sbchain = NULL, /* Sound buffer chain links */
|
||||
*sbtail = NULL;
|
||||
static int sbtotal = 0; /* Total sample bytes in memory */
|
||||
static int playing = FALSE;/* Replay in progress ? */
|
||||
/* static int playqsize; *//* Output queue size */
|
||||
static int playlen = 0; /* Length left to play */
|
||||
static unsigned char *playbuf = NULL; /* Current play pointer */
|
||||
static int squelch = 0; /* Squelch value */
|
||||
|
||||
/* Convert local gain into device parameters */
|
||||
|
||||
static unsigned
|
||||
scale_gain(unsigned g)
|
||||
{
|
||||
return (AUDIO_MIN_GAIN + (unsigned)
|
||||
((int) ((((double) (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN)) *
|
||||
((double) g / (double) MAX_GAIN)) + 0.5)));
|
||||
}
|
||||
|
||||
#ifdef HALF_DUPLEX
|
||||
static int oldvol = -1;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SOUNDINIT -- Open the sound peripheral and initialise for access. Return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
|
||||
int
|
||||
soundinit(int iomode)
|
||||
{
|
||||
int attempts = 3;
|
||||
|
||||
assert(audiof == -1);
|
||||
while (attempts-- > 0) {
|
||||
if ((audiof = open(SoundFile, iomode)) >= 0) {
|
||||
|
||||
if ((Audio_fd = open(AUDIO_CTLDEV, O_RDWR)) < 0) {
|
||||
perror(AUDIO_CTLDEV);
|
||||
return FALSE;
|
||||
}
|
||||
/* fcntl(audiof, F_SETFL, O_NDELAY); */
|
||||
#ifndef AUDIO_BLOCKING
|
||||
if (ioctl(audiof, SNDCTL_DSP_NONBLOCK, NULL) < 0) {
|
||||
perror("SNDCTL_DSP_NONBLOCK");
|
||||
return FALSE;
|
||||
}
|
||||
if (ioctl(audiof, SNDCTL_DSP_GETBLKSIZE, &abuf_size) < 0) {
|
||||
perror("SNDCTL_DSP_GETBLKSIZE");
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
#ifdef HALF_DUPLEX
|
||||
if (iomode == O_RDONLY) {
|
||||
if (oldvol == -1)
|
||||
oldvol = soundgetvol();
|
||||
soundplayvol(0);
|
||||
} else if (iomode == O_WRONLY && oldvol != -1 ) {
|
||||
if (soundgetvol() == 0)
|
||||
soundplayvol(oldvol);
|
||||
oldvol = -1;
|
||||
}
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
if (errno != EINTR)
|
||||
break;
|
||||
fprintf(stderr, "Audio open: retrying EINTR attempt %d\n", attempts);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* SOUNDTERM -- Close the sound device. chan=1 for play, 2:capture */
|
||||
|
||||
void
|
||||
soundterm(int chan)
|
||||
{
|
||||
if (audiof >= 0) {
|
||||
int arg;
|
||||
#ifdef AIOSTOP /* FreeBSD */
|
||||
if (chan == 2) {
|
||||
arg = AIOSYNC_CAPTURE;
|
||||
ioctl(audiof, AIOSTOP, &arg);
|
||||
}
|
||||
#endif
|
||||
#ifdef SNDCTL_DSP_SYNC
|
||||
if (chan == 1)
|
||||
ioctl(audiof, SNDCTL_DSP_SYNC);
|
||||
#endif
|
||||
#ifdef HALF_DUPLEX
|
||||
if (oldvol != -1) {
|
||||
if (soundgetvol() == 0)
|
||||
soundplayvol(oldvol);
|
||||
oldvol = -1;
|
||||
}
|
||||
#endif
|
||||
if (close(audiof) < 0)
|
||||
perror("closing audio device");
|
||||
if (close(Audio_fd) < 0)
|
||||
perror("closing audio control device");
|
||||
audiof = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* SOUNDPLAY -- Begin playing a sound. */
|
||||
|
||||
void
|
||||
soundplay(int len, unsigned char *buf)
|
||||
{
|
||||
int ios;
|
||||
|
||||
assert(audiof != -1);
|
||||
while (TRUE) {
|
||||
ios = write(audiof, buf, len);
|
||||
if (ios == -1)
|
||||
sf_usleep(100000);
|
||||
else {
|
||||
if (ios < len) {
|
||||
buf += ios;
|
||||
len -= ios;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* SOUNDPLAYVOL -- Set playback volume from 0 (silence) to 100 (full on). */
|
||||
|
||||
void
|
||||
soundplayvol(int value)
|
||||
{
|
||||
int arg;
|
||||
|
||||
arg = (value << 8) | value;
|
||||
|
||||
if (ioctl(Audio_fd, SOUND_MIXER_WRITE_PCM, &arg) < 0)
|
||||
perror("SOUND_MIXER_WRITE_PCM");
|
||||
}
|
||||
|
||||
#ifdef HALF_DUPLEX
|
||||
|
||||
/* SOUNDGETVOL -- Get current playback volume. */
|
||||
|
||||
int
|
||||
soundgetvol()
|
||||
{
|
||||
int arg, v1, v2;
|
||||
|
||||
if (ioctl(Audio_fd, SOUND_MIXER_READ_PCM, &arg) < 0) {
|
||||
perror("SOUND_MIXER_READ_PCM");
|
||||
return -1;
|
||||
}
|
||||
v1 = arg & 0xFF;
|
||||
v2 = (arg >> 8) & 0xFF;
|
||||
return (v1 > v2) ? v1 : v2;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* SOUNDRECGAIN -- Set recording gain from 0 (minimum) to 100 (maximum). */
|
||||
|
||||
void
|
||||
soundrecgain(int value)
|
||||
{
|
||||
int arg;
|
||||
|
||||
arg = (value << 8) | value;
|
||||
|
||||
if (ioctl(Audio_fd, SOUND_MIXER_WRITE_RECLEV, &arg) < 0)
|
||||
perror("SOUND_MIXER_WRITE_RECLEV");
|
||||
}
|
||||
|
||||
/*
|
||||
* SOUNDDEST -- Set destination for generated sound. If "where" is 0,
|
||||
* sound goes to the built-in speaker; if 1, to the audio output jack.
|
||||
*/
|
||||
|
||||
void
|
||||
sounddest(int where)
|
||||
{
|
||||
}
|
||||
|
||||
/* SOUNDGRAB -- Return audio information in the record queue. */
|
||||
|
||||
int
|
||||
soundgrab(char *buf, int len)
|
||||
{
|
||||
long read_size;
|
||||
int c;
|
||||
|
||||
read_size = len;
|
||||
#ifndef AUDIO_BLOCKING
|
||||
if (read_size > abuf_size) {
|
||||
read_size = abuf_size;
|
||||
}
|
||||
#endif
|
||||
while (TRUE) {
|
||||
c = read(audiof, buf, read_size);
|
||||
if (c < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
} else if (errno == EAGAIN) {
|
||||
c = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (c < 0) {
|
||||
perror("soundgrab");
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* SOUNDFLUSH -- Flush any queued sound. */
|
||||
|
||||
void
|
||||
soundflush()
|
||||
{
|
||||
char sb[BUFL];
|
||||
int c;
|
||||
|
||||
#ifndef AUDIO_BLOCKING
|
||||
while (TRUE) {
|
||||
c = read(audiof, sb, BUFL < abuf_size ? BUFL : abuf_size);
|
||||
if (c < 0 && errno == EAGAIN)
|
||||
c = 0;
|
||||
if (c < 0)
|
||||
perror("soundflush");
|
||||
if (c <= 0)
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* test.c -- a simple utility for testing audio I/O
|
||||
*
|
||||
* (C) Luigi Rizzo 1997
|
||||
*
|
||||
* This code mmaps the io descriptor, then every second dumps the
|
||||
* relevant data structures.
|
||||
*
|
||||
* call it as "test unit" where unit is the unit number you want
|
||||
* to see displayed.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
caddr_t r, w, d;
|
||||
|
||||
#include </sys/i386/isa/snd/sound.h>
|
||||
|
||||
void
|
||||
print_d(u_long *p, int unit)
|
||||
{
|
||||
snddev_info *d;
|
||||
int i;
|
||||
for (i=0; i<2000; i++) {
|
||||
d = (snddev_info *)&(p[i]);
|
||||
if ( d->magic == MAGIC(unit) )
|
||||
break;
|
||||
}
|
||||
if (i == 2000) {
|
||||
printf("desc not found\n");
|
||||
return;
|
||||
}
|
||||
printf("device type %d name %s\n", d->type, d->name);
|
||||
for (i=0;;i++) {
|
||||
if (i%20 == 0)
|
||||
printf("flags... fmt speed .bsz. c in-rl:in-dl:in-fl.ints "
|
||||
" c ou-fl:ou_dl:ou-rl.ints |\n");
|
||||
printf("%08x %3x %5d %5d %d %5d %5d %5d %4d %d %5d %5d %5d %4d |\n",
|
||||
d->flags, d->play_fmt, d->play_speed, d->play_blocksize,
|
||||
d->dbuf_in.chan,
|
||||
d->dbuf_in.rl,
|
||||
d->dbuf_in.dl,
|
||||
d->dbuf_in.fl,
|
||||
d->dbuf_in.int_count,
|
||||
|
||||
d->dbuf_out.chan,
|
||||
d->dbuf_out.fl,
|
||||
d->dbuf_out.dl,
|
||||
d->dbuf_out.rl,
|
||||
d->dbuf_out.int_count);
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int fd ;
|
||||
int unit = 0;
|
||||
char devn[64];
|
||||
|
||||
if (argc>1) unit=atoi(argv[1]);
|
||||
sprintf(devn,"/dev/mixer%d", unit);
|
||||
fd = open (devn, O_RDWR);
|
||||
printf("open returns %d\n", fd);
|
||||
|
||||
w = mmap(NULL, 0x10000, PROT_READ, 0, fd, 0); /* play */
|
||||
r = mmap(NULL, 0x10000, PROT_READ, 0, fd, 1<<24); /* rec */
|
||||
d = mmap(NULL, 0x2000, PROT_READ, 0, fd, 2<<24); /* desc */
|
||||
|
||||
printf("mmap: w 0x%08lx, r 0x%08lx, d 0x%08lx\n", w, r, d);
|
||||
if (d && (int)d != -1 ) {
|
||||
print_d((u_long *)d, unit);
|
||||
}
|
||||
if (w && (int)w != -1) munmap(w, 0x10000);
|
||||
if (r && (int)r != -1) munmap(r, 0x10000);
|
||||
if (d && (int)d != -1) munmap(d, 0x2000);
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user