📄 buffer.c
字号:
/* this file is a part of amp software
buffer.c: written by Andrew Richards <A.Richards@phys.canterbury.ac.nz>
(except printout())
Last modified by:
Karl Anders Oygard added flushing of audio buffers, 13 May 1997
*/
#include "amp.h"
#include "transform.h"
#include <sys/types.h>
#ifdef HAVE_MLOCK
#ifdef OS_Linux
#include <sys/mman.h>
#endif
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "audioIO.h"
#include "audio.h"
struct ringBuffer { /* A ring buffer to store the data in */
char *bufferPtr; /* buffer pointer */
int inPos, outPos; /* positions for reading and writing */
};
static int buffer_fd;
static int control_fd;
/* little endian systems do not require any byte swapping whatsoever.
* big endian systems require byte swapping when writing .wav files,
* but not when playing directly
*/
/* This is a separate (but inlined) function to make it easier to implement */
/* a threaded buffer */
inline void
audioBufferWrite(char *buf,int bytes)
{
write(buffer_fd, buf, bytes);
}
void printout(void)
{
int j;
if (nch==2)
j=32 * 18 * 2;
else
j=32 * 18;
if (A_WRITE_TO_FILE) {
#ifndef NO_BYTE_SWAPPING
short *ptr = (short*) sample_buffer;
int i = j;
for (;i>=0;--i)
ptr[i] = ptr[i] << 8 | ptr[i] >> 8;
#endif
fwrite(sample_buffer, sizeof(short), j, out_file);
}
if (A_AUDIO_PLAY) {
#ifdef LINUX_REALTIME
rt_printout((short*) sample_buffer, j * sizeof(short));
#else /* LINUX_REALTIME */
if (AUDIO_BUFFER_SIZE==0)
audioWrite((char*)sample_buffer, j * sizeof(short));
else
audioBufferWrite((char*)sample_buffer, j * sizeof(short));
#endif /* LINUX_REALTIME */
}
}
int AUDIO_BUFFER_SIZE;
#define bufferSize(A) (((A)->inPos+AUDIO_BUFFER_SIZE-(A)->outPos) % AUDIO_BUFFER_SIZE)
#define bufferFree(A) (AUDIO_BUFFER_SIZE-1-bufferSize(A))
void
initBuffer(struct ringBuffer *buffer)
{
buffer->bufferPtr=malloc(AUDIO_BUFFER_SIZE);
if (buffer->bufferPtr==NULL) {
die("Unable to allocate write buffer\n");
}
#ifdef HAVE_MLOCK
mlock(buffer->bufferPtr,AUDIO_BUFFER_SIZE);
#endif
buffer->inPos = 0;
buffer->outPos = 0;
DB(buffer2, msg("Allocated %d bytes for audio buffer\n",AUDIO_BUFFER_SIZE) );
}
void
freeBuffer(struct ringBuffer *buffer)
{
#ifdef HAVE_MLOCK
munlock(buffer->bufferPtr,AUDIO_BUFFER_SIZE);
#endif
free(buffer->bufferPtr);
}
/* This just sends some bogus data on the control pipe, which will cause */
/* the reader to flush its buffer. */
void
audioBufferFlush()
{
if (AUDIO_BUFFER_SIZE!=0) {
int dummy;
/* We could use the control pipe for passing commands to the */
/* audio player process, but so far we haven't bothered. */
write(control_fd, &dummy, sizeof dummy);
} else
audioFlush();
}
/* The decoded data are stored in a the ring buffer until they can be sent */
/* to the audio device. Variables are named in relation to the buffer */
/* ie writes are writes from the codec to the buffer and reads are reads */
/* from the buffer to the audio device */
int
audioBufferOpen(int frequency, int stereo, int volume)
{
struct ringBuffer audioBuffer;
int inFd,outFd,ctlFd,cnt,cntr=0,pid;
int inputFinished=FALSE;
int percentFull;
fd_set inFdSet,outFdSet;
fd_set *outFdPtr;
struct timeval timeout;
int filedes[2];
int controldes[2];
DB(buffer, msg("Starting audio buffer\n") );
if (pipe(filedes) || pipe(controldes)) {
perror("pipe");
exit(-1);
}
DB(buffer, msg("readpipe=%d writepipe=%d\n",filedes[0],filedes[1]) );
if ((pid=fork())!=0) { /* if we are the parent */
control_fd=controldes[1];
close(filedes[0]);
buffer_fd=filedes[1];
close(controldes[0]);
return(pid); /* return the pid */
}
DB(buffer, msg("Audio buffer (pipe) starting\n") );
/* we are the child */
close(filedes[1]);
inFd=filedes[0];
close(controldes[1]);
ctlFd=controldes[0];
audioOpen(frequency,stereo,volume);
outFd=getAudioFd();
initBuffer(&audioBuffer);
while(1) {
timeout.tv_sec=0;
timeout.tv_usec=0;
FD_ZERO(&inFdSet);
FD_ZERO(&outFdSet);
FD_SET(ctlFd,&inFdSet);
FD_SET(outFd,&outFdSet);
DB(buffer,
msg("BS=%d inPos=%d outPos=%d bufferSize=%d bufferFree=%d\n",
AUDIO_BUFFER_SIZE,audioBuffer.inPos,audioBuffer.outPos,
bufferSize(&audioBuffer),bufferFree(&audioBuffer)) );
if (bufferSize(&audioBuffer)<AUSIZ) { /* is the buffer too empty */
outFdPtr=NULL; /* yes, don't try to write */
if (inputFinished) /* no more input, buffer exhausted -> exit */
break;
} else
outFdPtr=&outFdSet; /* no, select on write */
/* check we have at least AUSIZ bytes left (don't want <1k bits) */
if ((bufferFree(&audioBuffer)>=AUSIZ) && !inputFinished)
FD_SET(inFd,&inFdSet);
/* The following selects() are basically all that is left of the system
dependent code outside the audioIO_*.c files. These selects really
need to be moved into the audioIO_*.c files and replaced with a
function like audioIOReady(inFd, &checkIn, &checkAudio, wait) where
it checks the status of the input or audio output if checkIn or
checkAudio are set and returns with checkIn or checkAudio set to TRUE
or FALSE depending on whether or not data is available. If wait is
FALSE the function should return immediately, if wait is TRUE the
process should BLOCK until the required condition is met. NB: The
process MUST relinquish the CPU during this check or it will gobble
up all the available CPU which sort of defeats the purpose of the
buffer.
This is tricky for people who don't have file descriptors (and
select) to do the job. In that case a buffer implemented using
threads should work. The way things are set up now a threaded version
shouldn't be to hard to implement. When I get some time... */
/* check if we can read or write */
if (select(MAX3(inFd,outFd,ctlFd)+1,&inFdSet,outFdPtr,NULL,NULL) > -1) {
if (outFdPtr && FD_ISSET(outFd,outFdPtr)) { /* need to write */
int bytesToEnd = AUDIO_BUFFER_SIZE - audioBuffer.outPos;
percentFull=100*bufferSize(&audioBuffer)/AUDIO_BUFFER_SIZE;
#if defined(DEBUG)
if ((cntr++ % (16/(AUSIZ/4096)))==0) msg("\rBuffer (%2d%%) %6d",percentFull,bufferSize(&audioBuffer));
#endif
if (AUSIZ>bytesToEnd) {
cnt = audioWrite(audioBuffer.bufferPtr + audioBuffer.outPos, bytesToEnd);
cnt += audioWrite(audioBuffer.bufferPtr, AUSIZ - bytesToEnd);
audioBuffer.outPos = AUSIZ - bytesToEnd;
} else {
cnt = audioWrite(audioBuffer.bufferPtr + audioBuffer.outPos, AUSIZ);
audioBuffer.outPos += AUSIZ;
}
DB(buffer2, msg("buffer: wrote %d bytes (to audio)\n",cnt) );
}
if (FD_ISSET(inFd,&inFdSet)) { /* need to read */
cnt = read(inFd, audioBuffer.bufferPtr + audioBuffer.inPos, MIN(AUSIZ, AUDIO_BUFFER_SIZE - audioBuffer.inPos));
if (cnt >= 0) {
audioBuffer.inPos = (audioBuffer.inPos + cnt) % AUDIO_BUFFER_SIZE;
DB(buffer2, msg("buffer: read %d bytes\n",cnt) );
if (cnt==0) {
inputFinished=TRUE;
DB(buffer, msg("inputFinished\n") );
}
} else {
perror("read");
exit(-1);
}
}
if (FD_ISSET(ctlFd,&inFdSet)) {
int dummy;
cnt = read(ctlFd, &dummy, sizeof dummy);
if (cnt >= 0) {
audioBuffer.inPos = audioBuffer.outPos = 0;
audioFlush();
} else {
perror("read");
exit(-1);
}
}
} else {
perror("select");
exit(-1);
}
}
close(inFd);
DB(buffer, msg("Buffer pipe closed %d\n",inFd); )
audioClose();
exit(0);
return 0; /* just to get rid of warnings */
}
void
audioBufferClose()
{
/* Close the buffer pipe and wait for the buffer process to close */
close(buffer_fd);
waitpid(0,0,0);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -