00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
#include "wvdsp.h"
00012
#include "wvfork.h"
00013
#include <sys/ioctl.h>
00014
#include <sys/soundcard.h>
00015
#include <fcntl.h>
00016
#include <sched.h>
00017
#include <time.h>
00018
00019 #define DO_RATEADJ 0
00020
00021 static const char *
AUDIO_DEVICE =
"/dev/dsp";
00022
00023 static int msec_lat(
int frags,
int frag_bits,
int srate)
00024 {
00025
return frags * (1<<frag_bits) * 1000 / srate;
00026 }
00027
00028 WvDsp::WvDsp(
int msec_latency,
int srate,
int bits,
bool stereo,
00029
bool readable,
bool writable,
bool _realtime,
bool _oss)
00030 :
log("DSP",
WvLog::Debug2), rcircle(102400), wcircle(102400),
00031 inrate(bits/8 * (stereo ? 2 : 1), srate, srate),
00032 outrate(bits/8 * (stereo ? 2 : 1), srate, srate)
00033 {
00034 is_realtime = _realtime;
00035
00036
int mode = 0;
00037
00038 outrate.
orate_n = srate;
00039
00040 assert(msec_latency >= 0);
00041 assert(srate >= 8000);
00042 assert(srate <= 48000);
00043 assert(bits == 8 || bits == 16);
00044 assert(readable || writable);
00045
00046
#if DO_RATEADJ
00047
00048
00049
00050
if (readable)
00051 outrate.
match_rate = &inrate;
00052
#endif
00053
00054
if (readable && writable)
00055 mode = O_RDWR;
00056
else if (readable)
00057 mode = O_RDONLY;
00058
else if (writable)
00059 mode = O_WRONLY;
00060
00061
00062
00063
if ((fd = open(
AUDIO_DEVICE, mode | O_NONBLOCK)) < 0)
00064 {
00065 seterr(errno);
00066
return;
00067 }
00068
00069
00070
00071
00072 fcntl(fd, F_SETFL, mode);
00073
00074
00075 num_frags = 5;
00076
int frag_size_bits = 7;
00077
int lat;
00078
if (msec_latency > 1000)
00079 msec_latency = 1000;
00080
while (msec_latency > (lat =
msec_lat(num_frags, frag_size_bits, srate)))
00081 {
00082
if (frag_size_bits < 14 && msec_latency >= 2*lat)
00083 frag_size_bits++;
00084
else
00085 num_frags++;
00086 }
00087
00088
log(WvLog::Debug,
"With %s %s-bit frags, latency will be about %s ms.\n",
00089 num_frags, frag_size_bits, lat);
00090
00091 frag_size = (1 << frag_size_bits);
00092
if (!setioctl(SNDCTL_DSP_SETFRAGMENT, (num_frags << 16) | frag_size_bits))
00093 seterr(
"can't set frag size!");
00094
00095
if (bits == 16)
00096 {
00097
if (!setioctl(SNDCTL_DSP_SETFMT, AFMT_S16_NE))
00098 seterr(
"can't set sample size!");
00099 }
00100
else if (bits == 8)
00101 {
00102
if (!setioctl(SNDCTL_DSP_SETFMT, AFMT_S8))
00103 seterr(
"can't set sample size!");
00104 }
00105
00106
if (!setioctl(SNDCTL_DSP_CHANNELS, stereo ? 2 : 1))
00107 seterr(
"can't set number of channels!");
00108
00109
if (!setioctl(SNDCTL_DSP_SPEED, srate))
00110 seterr(
"can't set sampling rate!");
00111
00112
00113
00114
00115
00116
if (_oss)
00117 {
00118
00119 subproc(readable, writable);
00120 }
00121
else
00122 {
00123
00124
if (readable) subproc(
true,
false);
00125
00126
00127
if (writable) subproc(
false,
true);
00128 }
00129
00130 rloop.
nowrite();
00131 wloop.
noread();
00132
realtime();
00133 }
00134
00135
00136 WvDsp::~WvDsp()
00137 {
00138
close();
00139 }
00140
00141
00142 bool WvDsp::pre_select(SelectInfo &si)
00143 {
00144
bool ret =
false;
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
if (si.wants.readable)
00157 {
00158 rloop.
drain();
00159
if (rcircle.
used())
00160
return true;
00161
else
00162 ret |= rloop.
pre_select(si);
00163 }
00164
00165
if (si.wants.writable)
00166
return true;
00167
00168
return ret;
00169 }
00170
00171
00172 bool WvDsp::post_select(SelectInfo &si)
00173 {
00174
bool ret =
false;
00175
00176
if (si.wants.readable)
00177 {
00178
if (rcircle.
used())
00179
return true;
00180
else
00181 ret |= rloop.
post_select(si);
00182 }
00183
00184
return ret;
00185 }
00186
00187
00188 size_t
WvDsp::uread(
void *buf, size_t len)
00189 {
00190
if (len == 0)
00191
return 0;
00192 size_t avail = rcircle.
used();
00193
00194
00195 {
00196
WvDynBuf silly;
00197
void *data = silly.
alloc(avail);
00198 size_t got = rcircle.
get(data, avail);
00199 silly.
unalloc(avail - got);
00200
#if DO_RATEADJ
00201
inrate.
encode(silly, rbuf);
00202
#else
00203
rbuf.
merge(silly);
00204
#endif
00205
}
00206
00207 avail = rbuf.
used();
00208
if (avail < len)
00209 len = avail;
00210
00211 rbuf.
move(buf, len);
00212
return len;
00213 }
00214
00215
00216 size_t
WvDsp::uwrite(
const void *buf, size_t len)
00217 {
00218
static time_t last_dump;
00219
00220
if (len == 0)
00221
return 0;
00222
00223
if (last_dump < time(NULL) - 1)
00224 {
00225
log(WvLog::Debug,
"writer rates: %s/%s; reader rates: %s/%s\n",
00226 outrate.
getirate(), outrate.
getorate(),
00227 inrate.
getirate(), inrate.
getorate());
00228 last_dump = time(NULL);
00229 }
00230
00231
#if DO_RATEADJ
00232
outrate.
flushmembuf(buf, len, wbuf);
00233
#else
00234
wbuf.
put(buf, len);
00235
#endif
00236
00237 size_t howmuch = wcircle.
left();
00238
00239
if (howmuch > wbuf.
used())
00240 howmuch = wbuf.
used();
00241
00242 buf = wbuf.
get(howmuch);
00243 wcircle.
put(buf, howmuch);
00244 wloop.
write(
"", 1);
00245
00246
return len;
00247 }
00248
00249
00250 bool WvDsp::isok()
const
00251
{
00252
return (fd >= 0);
00253 }
00254
00255
00256 void WvDsp::close()
00257 {
00258
if (fd >= 0)
00259 ::close(fd);
00260 fd = -1;
00261
00262
00263 rloop.
close();
00264 wloop.
close();
00265 }
00266
00267
00268
bool WvDsp::setioctl(
int ctl,
int param)
00269 {
00270
return ioctl(fd, ctl, ¶m) >= 0;
00271 }
00272
00273
00274
00275 void WvDsp::realtime()
00276 {
00277
if (is_realtime)
00278 {
00279
struct sched_param sch;
00280 memset(&sch, 0,
sizeof(sch));
00281 sch.sched_priority = 1;
00282
if (sched_setscheduler(getpid(), SCHED_FIFO, &sch) < 0)
00283 seterr(
"can't set scheduler priority!");
00284 }
00285 }
00286
00287
00288
void WvDsp::subproc(
bool reading,
bool writing)
00289 {
00290 intTable fds(4);
00291 fds.add(
new int(rloop.
getrfd()),
true);
00292 fds.add(
new int(rloop.
getwfd()),
true);
00293 fds.add(
new int(wloop.
getrfd()),
true);
00294 fds.add(
new int(wloop.
getwfd()),
true);
00295
00296 pid_t pid =
wvfork(fds);
00297
if (pid < 0)
00298 {
00299 seterr(errno);
00300
return;
00301 }
00302
else if (pid > 0)
00303
return;
00304
00305
00306
00307
char buf[10240];
00308
00309
realtime();
00310
00311 rloop.
noread();
00312 wloop.
nowrite();
00313
00314
if (!reading)
00315 rloop.
close();
00316
if (!writing)
00317 wloop.
close();
00318
00319
while (
isok() && (rloop.
isok() || wloop.
isok()))
00320 {
00321
if (reading)
00322 {
00323 size_t len = do_uread(buf,
sizeof(buf));
00324
if (len)
00325 {
00326 rcircle.
put(buf, len);
00327 rloop.
write(
"", 1);
00328 }
00329 }
00330
00331
if (writing)
00332 {
00333 wloop.
drain();
00334 size_t avail;
00335
while ((avail = wcircle.
used()) >= frag_size)
00336 {
00337
if (avail > frag_size)
00338 avail = frag_size;
00339 size_t len = wcircle.
get(buf, avail);
00340 do_uwrite(buf, len);
00341 }
00342
if (!reading)
00343 wloop.
select(-1);
00344 }
00345 }
00346
00347 _exit(0);
00348 }
00349
00350
00351 size_t
WvDsp::ispace()
00352 {
00353 audio_buf_info info;
00354
00355
if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) < 0)
00356 {
00357
log(WvLog::Error,
"error in GETISPACE\n");
00358
return 0;
00359 }
00360
00361
return info.fragments;
00362 }
00363
00364
00365 size_t
WvDsp::ospace()
00366 {
00367 audio_buf_info info;
00368
00369
if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) < 0)
00370 {
00371
log(WvLog::Error,
"error in GETOSPACE\n");
00372
return 0;
00373 }
00374
00375
return num_frags - info.fragments;
00376 }
00377
00378
00379 size_t WvDsp::do_uread(
void *buf, size_t len)
00380 {
00381
if (!len)
return 0;
00382
00383
if (len < frag_size)
00384
log(WvLog::Warning,
"reading less than frag size: %s/%s\n", len, frag_size);
00385
00386 size_t i, i2;
00387
00388
if (len > frag_size)
00389 len = frag_size;
00390
00391
if ((i =
ispace()) > 1)
00392 {
00393
if (i > num_frags * 2)
00394 {
00395
log(
"resetting: frag count is broken! (%s)\n", i);
00396 ioctl(fd, SNDCTL_DSP_RESET, NULL);
00397 }
00398
else
00399 {
00400 i2 = i;
00401
while (i2-- > 1)
00402 {
00403
char buf2[frag_size];
00404 ::read(fd, buf2, frag_size);
00405 }
00406
00407 }
00408 }
00409
00410
00411
00412
int ret = ::read(fd, buf, len);
00413
if (ret < 0)
00414 {
00415
if (errno != EAGAIN)
00416
seterr(errno);
00417
return 0;
00418 }
00419
00420
if (ret && ret < (
int)len && ret < (
int)frag_size)
00421
log(
"inbuf underflow (%s/%s)!\n", ret, len);
00422
00423
return ret;
00424 }
00425
00426
00427 size_t WvDsp::do_uwrite(
const void *buf, size_t len)
00428 {
00429
if (!len)
return 0;
00430
00431
if (len < frag_size)
00432
log(WvLog::Warning,
"writing less than frag size: %s/%s\n",
00433 len, frag_size);
00434
00435
int o =
ospace(), o2;
00436
00437
if (o < 2)
00438 {
00439 o2 = o;
00440
while (o2++ < 2)
00441 {
00442
char buf2[frag_size];
00443 memset(buf2, 0,
sizeof(buf2));
00444 ::write(fd, buf2, frag_size);
00445 }
00446
00447 }
00448
00449
if (o >= (
int)num_frags-1)
00450 {
00451
00452
return len;
00453 }
00454
00455 size_t ret = ::write(fd, buf, len);
00456
if (ret < 0)
00457 {
00458
log(
"Error: %s\n", errno);
00459
if (errno != EAGAIN)
00460
seterr(errno);
00461
return len;
00462 }
00463
00464
00465
00466
00467
00468
00469
return len;
00470 }
00471
00472