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