%----------------------------------------------------------------------------
\chapter{The Sound Library}
%----------------------------------------------------------------------------
If a SoundBlaster16 sound card is available, S.Ha.R.K. allows to sample and play
sounds by using the functions provided by the sound library
\footnote{Currently
only the sound blaster 16 is supported; the code of the library is directly
inherited from the Hartik 3.3.0 Kernel...}. The library currently supports
either program or DMA controlled sampling an playing, according to 4 possible
operating modes:
\begin{itemize}
\item PIO mode;
\item DMA-Raw mode;
\item DMA-Double-buffering mode;
\item DMA-Self-buffering mode.
\end{itemize}
Working under PIO mode, sounds can be sampled and played only with 8 bit PCM.
The frequence depends on the hardware speeds but cannot in any case overcome 10
Khz. This mode is reserved for the pure classical hard real-time approach which
refuses the usage of DMA controlled I/O.
The DMA-Raw mode uses DMA controller to sample and play directly on a memory
buffer. Owing to technical problems related to the structure of the PC DMA
controller, the buffer's size can be no bigger than 64K. This mode is the one
that minimizes the DMA operations' impact on CPU.
The DMA Double-Buffering mode uses an internal buffer in order to overcome the
64k limitation. The internal buffer is split into two parts: while the DMA
tranfers data to one half, an ad-hoc task moves data between the second half and
a user-provided external memory region. In this way, it is possible for a user
to work on samples much bigger than 64K, paying the fee of a higher CPU load
\footnote{This is possible only if the protected mode is used.}.
The DMA-Self-Buffering mode allows the user to directly handle the internal
buffer. The user specifies a function to be activated every time the DMA
controller has finished transferring data on one half of the internal buyffer.
In this mode, the user can obtain the data while they are being sampled; the
time lag between sampling and data delivery is thus reduced. Such a feature
makes this working mode interesting for real-time applications. Independently of
the chosen working mode, an operation can be either synchronous or asynchronous.
A synchronous operation provides the task invoking the operation with a
synchronizing point located at its ending. In order to use the sound library
functions, the files \texttt{drivers/sound.h} and \texttt{drivers/dma.h} must be
included. The former contains the prototypes of the declared functions, the
latter is necessary because the sound library uses DMA.
The first step to be performed is initializing the audio drivers by the
\texttt{sound\_init} function. Then, if one wishes to work in DMA-Raw mode, it
is necessary to allocate a memory buffer and align it by calling
\texttt{dma\_getpage()} (in the remaining modes no particular alignment is
required for the buffer). If the DMA-Self-buffering mode is chosen, the
programmer has to properly set the functions to be called every time the DMA
finishes working on one half of the internal buffer; this can be done by calling
the \texttt{sound\_setfun()} primitive. As soon as these operations have been
performed, sampling or playing can be made through \texttt{sound\_sample()} and
\texttt{sound\_play()}, respectively.
\vspace{7mm}
\begin{intest}
SOUND\_INIT\index{sound\_init()}
\end{intest}
\begin{description}
\item [\textbf{void sound\_init(WORD rawbufsize, WORD tick);}]
\item [\textbf{Description:}] It initializes the audio driver by allocating the
internal
buffer for the DMA-Double-buffering and DMA-Self-Buffering modes. The
\texttt{rawbufsize} parameter contains the dimension of this buffer. Higher
values reduce the CPU load and are thus advised when using the
DMA-Double-buffering mode. Lower values, on the contrary, can be used to shorten
the latency between sampling and data delivering (particularly when using
DMA-Self-buffering). The \texttt{tick} parameter contains the value of the
system tick; its correctness is fundamental for the PIO mode.
\end{description}
\begin{intest}
SOUND\_INFO\index{sound\_info()}
\end{intest}
\begin{description}
\item [\textbf{void sound\_info(void);}]
\item [\textbf{Description:}] It outputs on the screen some information concerning
the soundcard and the drivers.
\end{description}
\begin{intest}
SOUND\_SETFUN\index{sound\_setfun()}
\end{intest}
\begin{description}
\item [\textbf{void sound\_setfun(int ({*}infun)(BYTE {*}rawbuff),}\\
\texttt{int ({*}outfun)(BYTE {*}rawbuff));}]
\item [\textbf{Description:}] It specifies the functions to be called when the DMA
finishes working on one of the two internal buffer's halves when using
DMA-Self-Buffering mode. The function pointed by \texttt{infun} is used when
performing sampling operations, whereas \texttt{outfun} is used for playing
operations. Both functions receive a pointer to the half-buffer not currently
acted upon by the DMA (the half-buffer sizes are equal to one half of the
\texttt{sound\_init()} parameter) and have to return 0 if the operation has not
yet been finished, 1 if it is going to finish in the next DMA cycle, and 2 if it
finishes immediately. Attention should be paid to the fact that these functions
are periodically called with a frequency equal to the operation's frequency
divided by the half-buffer's size; thus, they should be very short in order not
to overload the system.
\end{description}
\begin{description}
\item [Example:]
\end{description}
\begin{tt}
\begin{verbatim}
int osc_fun(BYTE *b) {
int i;
int sum = 0;
BYTE *p;
/* Averages the values read from the buffer */
/* and writes the result on a CAB shared */
/* with a task */
for (i = 0; i < (BUFFDIM >> 1); i++)
sum += b[i];
sum = (BUFFDIM >> 1);
p = cab_reserve(cc);
*p = (BYTE) sum;
cab_putmes(cc, p);
return 0;
}
...
void *io_task(void *arg) {
int x, y;
BYTE *p;
BYTE page = 0;
char str[50];
short int talk, silencecount;
/* This task reads the value put on the CAB by */
/* the self-buffering function */
/* sets the self-buffering function */
sound_setfun(osc_infun, -1);
/* starts the sampling operation */
sound_sample(NULL, 20000, 0, DMA_OP | PCM8 | MYFUN);
cc = cab_create("osc_cab", sizeof(BYTE), 3);
for (;;) {
/* reads and proccesses */
/* the CAB's value */
...
task_endcycle();
}
return 0;
}
\end{verbatim}
\end{tt}
\begin{intest}
SOUND\_SAMPLE\index{sound\_sample()}
\end{intest}
\begin{description}
\item [\textbf{void sound\_sample(BYTE {*}buf, DWORD sps, DWORD len, BYTE t);}]
\item [\textbf{Description:}] It samples \texttt{len} bytes in the \texttt{buf}
buffer at the frequency of \texttt{sps} samples per second with the mode
expressed by \texttt{t}. The latter can be assigned one of the following
constants:
\begin{itemize}
\item \texttt{PIO\_OP} operates using PIO mode: as said earlier, in this mode
values for \texttt{sps} higher than 10000 make no sense. Moreover, for the
sampling and playing to happen with the correct timing, it is necessary that the
audio driver be initialized with the \texttt{tick} parameter set to the system
tick expressed in microseconds (see \texttt{sound\_init()} for more details).
\item \texttt{DMA\_OP} operates using one of the DMA modes (the default is
DMA-Double-Buffering). The internal buffer size is specified in
\texttt{sound\_init}.
\item \texttt{PCM8} operates using 8 bit PCM format (it is the default). It is
the only possible format in PIO mode.
\item \texttt{PCM16} operates using 16 bit PCM format. This choice is
meaningless in PIO mode.
\item \texttt{SYNCH} synchronous operation: it is necessary to call
\texttt{sound\_wait()} after \texttt{sound\_sample()}.
\item \texttt{ASYNCH} asynchronous operation.
\item \texttt{MYFUN} operates with DMA-Self-buffering mode; it makes sense only
if \texttt{DMA\_OP} has been set.
\item \texttt{NOBUFF} operates in DMA-Raw-Mode; it makes sense only if
\texttt{DMA\_OP} has been set.
\end{itemize}
\end{description}
\begin{description}
\item [Example:]
\end{description}
\begin{tt}
\begin{verbatim}
BYTE buff[0xFFFFF]; /* buffer for sampling */
void main() {
sys_init(\&s);
keyb_init(NULL);
clear();
sound_init(0x4000, TICK);
sound_info();
cprintf("Recording...");
sound_sample(buff, 44000, 0x8FFFF, DMA_OP | PCM8 | SYNCH);
...
}
\end{verbatim}
\end{tt}
\begin{intest}
SOUND\_PLAY\index{sound\_play()}
\end{intest}
\begin{description}
\item [\textbf{void sound\_play(BYTE {*}buff, DWORD sps, DWORD len, BYTE t);}]
\item [\textbf{Description:}] It plays \texttt{len} bytes taken from the \texttt{b}
buffer
at the frequency of \texttt{sps} samples per second with the mode expressed by
\texttt{t}. As far as the values of \texttt{t} are concerned, the reader can
refer to \texttt{sound\_sample}.
\end{description}
\begin{intest}
DMA\_GETPAGE\index{dma\_getpage()}
\end{intest}
\begin{description}
\item [\textbf{BYTE {*}dma\_getpage(DWORD {*}dim);}]
\item [\textbf{Description:}] It allocates a buffer having size \texttt{dim} fitting
for
use in DMA operations. Such a usage is possible only if the buffer does not
contain bytes whose address differs in the Most Significant Bits. The best way
to achieve this feature is to allocate buffers sized less than 64K starting from
addresses having the LSB equal to 0. This job is performed by
\texttt{dma\_getpage}. It should be noted that such a feature is necessary only
using DMA-Raw-Mode, since the buffer allocation is automatically performed by
\texttt{sound\_init} when using DMA-Double-Buffering and DMA-Self-Bufering
modes.
\end{description}
\begin{description}
\item [Example:]
\end{description}
\begin{tt} \begin{verbatim}
void main(void)
{
BYTE *p;
int i;
/* Monitors the time stolen by the DMA */
/* to the CPU during a 10 Khz sampling */
sys_init(
&s);
keyb_init(NULL);
...
clear();
p = dma_getpage(0xFFFF);
sound_init(0x200, TICK);
sound_info();
for (i = 0; i < 80; i++) cprintf("_");
cprintf("ref_time:
%f ", myrif);
cprintf("Unloaded system:
%f", load(&myrif));
cprintf("DMA Recording...");
sound_sample(p, 10000, 0xFFFF, DMA_OP | PCM8 | NOBUFF);
...
\end{verbatim}
\end{tt}
\begin{intest}
SOUND\_WAIT\index{sound\_wait()}
\end{intest}
\begin{description}
\item [\textbf{void sound\_wait(void);}]
\item [\textbf{Description:}] It is the synchronization primitive for synchronous
operations. The task calling \texttt{sound\_wait()} blocks itself until the
synchronous operation is finished. The call to this function is mandatory for
synchronous operations. On the other hand, using the function in conjunction
with an asynchronous operation is an error.
\end{description}
\begin{description}
\item [Example:]
\end{description}
\begin{tt}
\begin{verbatim}
sound_sample(buff, 44000, 0x8FFFF, DMA_OP | PCM8 | SYNCH);
...
/* waits until the sampling termination */
sound_wait();
...
\end{verbatim}
\end{tt}