Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
BitchX
GitHub Repository: BitchX/BitchX1.3
Path: blob/master/dll/amp/buffer.c
1074 views
1
/* this file is a part of amp software
2
3
buffer.c: written by Andrew Richards <[email protected]>
4
(except printout())
5
6
Last modified by:
7
Karl Anders Oygard added flushing of audio buffers, 13 May 1997
8
9
*/
10
#include "amp.h"
11
#include "transform.h"
12
13
#include <sys/types.h>
14
#include <sys/signal.h>
15
#ifdef HAVE_MLOCK
16
#ifdef OS_Linux
17
#include <sys/mman.h>
18
#endif
19
#endif
20
#ifdef HAVE_SYS_SELECT_H
21
#include <sys/select.h>
22
#endif
23
#include <sys/time.h>
24
#include <sys/wait.h>
25
#include <unistd.h>
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <string.h>
29
30
#include "audioIO.h"
31
#include "audio.h"
32
33
#if !defined(OS_Linux) && !defined(AUSIZ)
34
#define AUSIZ 32768
35
#endif
36
37
struct ringBuffer { /* A ring buffer to store the data in */
38
char *bufferPtr; /* buffer pointer */
39
int inPos, outPos; /* positions for reading and writing */
40
};
41
42
43
static int buffer_fd;
44
static int control_fd;
45
46
/* little endian systems do not require any byte swapping whatsoever.
47
* big endian systems require byte swapping when writing .wav files,
48
* but not when playing directly
49
*/
50
51
/* This is a separate (but inlined) function to make it easier to implement */
52
/* a threaded buffer */
53
54
inline void audioBufferWrite(char *buf,int bytes)
55
{
56
write(buffer_fd, buf, bytes);
57
}
58
59
void printout(void)
60
{
61
int j;
62
63
if (nch==2)
64
j=32 * 18 * 2;
65
else
66
j=32 * 18;
67
68
if (AUDIO_BUFFER_SIZE==0)
69
audioWrite((char*)sample_buffer, j * sizeof(short));
70
else
71
audioBufferWrite((char*)sample_buffer, j * sizeof(short));
72
}
73
74
int AUDIO_BUFFER_SIZE;
75
76
#define bufferSize(A) (((A)->inPos+AUDIO_BUFFER_SIZE-(A)->outPos) % AUDIO_BUFFER_SIZE)
77
#define bufferFree(A) (AUDIO_BUFFER_SIZE-1-bufferSize(A))
78
79
void
80
initBuffer(struct ringBuffer *buffer)
81
{
82
buffer->bufferPtr=malloc(AUDIO_BUFFER_SIZE);
83
if (buffer->bufferPtr==NULL)
84
_exit(-1);
85
#ifdef HAVE_MLOCK
86
mlock(buffer->bufferPtr,AUDIO_BUFFER_SIZE);
87
#endif
88
buffer->inPos = 0;
89
buffer->outPos = 0;
90
}
91
92
void
93
freeBuffer(struct ringBuffer *buffer)
94
{
95
#ifdef HAVE_MLOCK
96
munlock(buffer->bufferPtr,AUDIO_BUFFER_SIZE);
97
#endif
98
free(buffer->bufferPtr);
99
}
100
101
/* This just sends some bogus data on the control pipe, which will cause */
102
/* the reader to flush its buffer. */
103
104
void
105
audioBufferFlush()
106
{
107
if (AUDIO_BUFFER_SIZE!=0)
108
{
109
int dummy;
110
111
/* We could use the control pipe for passing commands to the */
112
/* audio player process, but so far we haven't bothered. */
113
114
write(control_fd, &dummy, sizeof dummy);
115
} else
116
audioFlush();
117
}
118
119
120
/* The decoded data are stored in a the ring buffer until they can be sent */
121
/* to the audio device. Variables are named in relation to the buffer */
122
/* ie writes are writes from the codec to the buffer and reads are reads */
123
/* from the buffer to the audio device */
124
125
int
126
audioBufferOpen(int frequency, int stereo, int volume)
127
{
128
struct ringBuffer audioBuffer;
129
130
int inFd,outFd,ctlFd,cnt,pid;
131
int inputFinished=FALSE;
132
int percentFull;
133
fd_set inFdSet,outFdSet;
134
fd_set *outFdPtr;
135
struct timeval timeout;
136
int filedes[2];
137
int controldes[2];
138
139
140
if (pipe(filedes) || pipe(controldes))
141
{
142
perror("pipe");
143
exit(-1);
144
}
145
if ((pid=fork())!=0)
146
{
147
/* if we are the parent */
148
control_fd=controldes[1];
149
close(filedes[0]);
150
buffer_fd=filedes[1];
151
close(controldes[0]);
152
return(pid); /* return the pid */
153
}
154
155
156
/* we are the child */
157
close(filedes[1]);
158
inFd=filedes[0];
159
close(controldes[1]);
160
ctlFd=controldes[0];
161
audioOpen(frequency,stereo,volume);
162
outFd=getAudioFd();
163
initBuffer(&audioBuffer);
164
165
while(1)
166
{
167
timeout.tv_sec=0;
168
timeout.tv_usec=0;
169
FD_ZERO(&inFdSet);
170
FD_ZERO(&outFdSet);
171
FD_SET(ctlFd,&inFdSet);
172
FD_SET(outFd,&outFdSet);
173
174
if (bufferSize(&audioBuffer)<AUSIZ)
175
{ /* is the buffer too empty */
176
outFdPtr = NULL; /* yes, don't try to write */
177
if (inputFinished) /* no more input, buffer exhausted -> exit */
178
break;
179
} else
180
outFdPtr=&outFdSet; /* no, select on write */
181
182
/* check we have at least AUSIZ bytes left (don't want <1k bits) */
183
if ((bufferFree(&audioBuffer)>=AUSIZ) && !inputFinished)
184
FD_SET(inFd,&inFdSet);
185
186
/* The following selects() are basically all that is left of the system
187
dependent code outside the audioIO_*&c files. These selects really
188
need to be moved into the audioIO_*.c files and replaced with a
189
function like audioIOReady(inFd, &checkIn, &checkAudio, wait) where
190
it checks the status of the input or audio output if checkIn or
191
checkAudio are set and returns with checkIn or checkAudio set to TRUE
192
or FALSE depending on whether or not data is available. If wait is
193
FALSE the function should return immediately, if wait is TRUE the
194
process should BLOCK until the required condition is met. NB: The
195
process MUST relinquish the CPU during this check or it will gobble
196
up all the available CPU which sort of defeats the purpose of the
197
buffer.
198
199
This is tricky for people who don't have file descriptors (and
200
select) to do the job. In that case a buffer implemented using
201
threads should work. The way things are set up now a threaded version
202
shouldn't be to hard to implement. When I get some time... */
203
204
/* check if we can read or write */
205
if (select(MAX3(inFd,outFd,ctlFd)+1,&inFdSet,outFdPtr,NULL,NULL) > -1)
206
{
207
if (outFdPtr && FD_ISSET(outFd,outFdPtr))
208
{ /* need to write */
209
int bytesToEnd = AUDIO_BUFFER_SIZE - audioBuffer.outPos;
210
211
percentFull=100*bufferSize(&audioBuffer)/AUDIO_BUFFER_SIZE;
212
if (AUSIZ>bytesToEnd)
213
{
214
cnt = audioWrite(audioBuffer.bufferPtr + audioBuffer.outPos, bytesToEnd);
215
cnt += audioWrite(audioBuffer.bufferPtr, AUSIZ - bytesToEnd);
216
audioBuffer.outPos = AUSIZ - bytesToEnd;
217
}
218
else
219
{
220
cnt = audioWrite(audioBuffer.bufferPtr + audioBuffer.outPos, AUSIZ);
221
audioBuffer.outPos += AUSIZ;
222
}
223
224
}
225
if (FD_ISSET(inFd,&inFdSet))
226
{ /* need to read */
227
cnt = read(inFd, audioBuffer.bufferPtr + audioBuffer.inPos, MIN(AUSIZ, AUDIO_BUFFER_SIZE - audioBuffer.inPos));
228
if (cnt >= 0)
229
{
230
audioBuffer.inPos = (audioBuffer.inPos + cnt) % AUDIO_BUFFER_SIZE;
231
232
if (cnt==0)
233
inputFinished=TRUE;
234
}
235
else
236
_exit(-1);
237
}
238
if (FD_ISSET(ctlFd,&inFdSet))
239
{
240
int dummy;
241
242
cnt = read(ctlFd, &dummy, sizeof dummy);
243
if (cnt >= 0)
244
{
245
audioBuffer.inPos = audioBuffer.outPos = 0;
246
audioFlush();
247
}
248
else
249
_exit(-1);
250
}
251
}
252
else
253
_exit(-1);
254
}
255
close(inFd);
256
audioClose();
257
exit(0);
258
return 0; /* just to get rid of warnings */
259
}
260
261
void
262
audioBufferClose()
263
{
264
/* Close the buffer pipe and wait for the buffer process to close */
265
close(buffer_fd);
266
waitpid(0,0,0);
267
}
268
269