📄 sound_oss.cxx
字号:
// So collect devices by looking for dsp(N) and mixer(N). // (or /dev/audio(N) and mixer(N) on NetBSD // Notes. FreeBSD supports audio stream mixing. A single sound card // may have multiple /dev entries in the form /dev/dspN.M // eg /dev/dsp0.0 /dev/dsp0.1 /dev/dsp0.2 and /dev/dsp0.3 // When adding these to the 'dsp' string array, only the first one // found is used.#ifndef P_NETBSD // Look for dsp if (filename == "dsp") { dsp.SetAt(0, devname); } // Look for dspN entries. Insert at position N + 1 // and look for dspN.M entries. Insert at position N + 1 (ignoring M) if ((filename.GetLength() > 3) && (filename.Left(3) == "dsp")) { PString numbers = filename.Mid(3); // get everything after 'dsp' if (IsNumericString(numbers)) { PINDEX cardnum = numbers.AsInteger(); //dspN.M is truncated to dspN. // If we have not yet inserted something for this cardnum, insert it if (dsp.GetAt(cardnum+1) == NULL) { dsp.SetAt(cardnum+1, devname); } } }#else // Look for audio on NetBSD if (filename == "audio") { dsp.SetAt(0, devname); } // Look for audioN. Insert at position cardnum + 1 if ((filename.GetLength() > 5) && (filename.Left(5) == "audio")) { PString numbers = filename.Mid(5); // get everything after 'audio' if (IsNumericString(numbers)) { PINDEX cardnum = numbers.AsInteger(); dsp.SetAt(cardnum+1, devname); } }#endif // Look for mixer if (filename == "mixer") { mixer.SetAt(0, devname); } // Look for mixerN. Insert at position cardnum + 1 if ((filename.GetLength() > 5) && (filename.Left(5) == "mixer")) { PString numbers = filename.Mid(5); // get everything after 'mixer' if (IsNumericString(numbers)) { PINDEX cardnum = numbers.AsInteger(); mixer.SetAt(cardnum+1, devname); } } } } } while (devdir.Next());}PStringArray PSoundChannelOSS::GetDeviceNames(Directions /*dir*/){ // First locate sound cards. On Linux with devfs and on the other platforms // (eg FreeBSD), we search for filenames with dspN or mixerN. // On linux without devfs we scan all of the devices and look for ones // with major device numbers corresponding to OSS compatible drivers. POrdinalToString dsp, mixer;#ifdef P_LINUX PDirectory devdir = "/dev/sound"; if (devdir.Open()) { CollectSoundDevices("/dev/sound", dsp, mixer, TRUE); // use names (devfs) } else { CollectSoundDevices("/dev", dsp, mixer, FALSE); // use major numbers }#else CollectSoundDevices("/dev", dsp, mixer, TRUE); // use names#endif // Now we go through the collected devices and see if any have a phyisical reality PStringList devices; for (PINDEX i = 0; i < dsp.GetSize(); i++) { PINDEX cardnum = dsp.GetKeyAt(i); // Try and open mixer if have one as this is unlikely to fail for any // reason other than there not being a physical device if (mixer.Contains(cardnum)) { int fd = ::open(mixer[cardnum], O_RDONLY); if (fd >= 0) { // Do something with the mixer to be sure it is there int dummy; if (::ioctl(fd, SOUND_MIXER_READ_DEVMASK, &dummy) >= 0) devices.AppendString(dsp[cardnum]); ::close(fd); } else { // mixer failed but this could still be a valid dsp... // warning this is just a hack to make this work on strange mixer and dsp configurations // on my machine the first sound card registers 1 mixer and 2 dsp, so when my webcam // registers itself as dsp2 this test would fail... int fd = ::open(dsp[cardnum], O_RDONLY | O_NONBLOCK); if (fd >= 0 || errno == EBUSY) { devices.AppendString(dsp[cardnum]); ::close(fd); } } } else { // No mixer available, try and open it directly, this could fail if // the device happens to be open already int fd = ::open(dsp[cardnum], O_RDONLY | O_NONBLOCK); if (fd >= 0 || errno == EBUSY) { devices.AppendString(dsp[cardnum]); ::close(fd); } } } return devices;}PString PSoundChannelOSS::GetDefaultDevice(Directions dir){ // Normally /dev/dsp points to the default sound device. If this is not // present, probe /dev for sound devices and return the first detected device. // return the first dsp device detected PStringArray devicenames; devicenames = PSoundChannelOSS::GetDeviceNames(dir); return devicenames[0];}BOOL PSoundChannelOSS::Open(const PString & _device, Directions _dir, unsigned _numChannels, unsigned _sampleRate, unsigned _bitsPerSample){ Close(); // lock the dictionary PWaitAndSignal mutex(dictMutex); // make the direction value 1 or 2 int dir = _dir + 1; // if this device is in the dictionary if (handleDict().Contains(_device)) { SoundHandleEntry & entry = handleDict()[_device]; // see if the sound channel is already open in this direction if ((entry.direction & dir) != 0) { return FALSE; } // flag this entry as open in this direction entry.direction |= dir; os_handle = entry.handle; } else { // this is the first time this device has been used // open the device in read/write mode always // open the device in non-blocking mode to avoid hang if already open os_handle = ::open((const char *)_device, O_RDWR | O_NONBLOCK); if ((os_handle < 0) && (errno != EWOULDBLOCK)) return ConvertOSError(os_handle); // switch to blocking mode DWORD cmd = 0; ::ioctl(os_handle, FIONBIO, &cmd); // add the device to the dictionary SoundHandleEntry * entry = PNEW SoundHandleEntry; handleDict().SetAt(_device, entry); // save the information into the dictionary entry entry->handle = os_handle; entry->direction = dir; entry->numChannels = mNumChannels = _numChannels; entry->sampleRate = actualSampleRate = mSampleRate = _sampleRate; entry->bitsPerSample = mBitsPerSample = _bitsPerSample; entry->isInitialised = FALSE; entry->fragmentValue = 0x7fff0008; } // save the direction and device direction = _dir; device = _device; isInitialised = FALSE; return TRUE;}BOOL PSoundChannelOSS::Setup(){ PWaitAndSignal mutex(dictMutex); if (os_handle < 0) { PTRACE(6, "OSS\tSkipping setup of " << device << " as not open"); return FALSE; } if (isInitialised) { PTRACE(6, "OSS\tSkipping setup of " << device << " as instance already initialised"); return TRUE; } // the device must always be in the dictionary PAssertOS(handleDict().Contains(device)); // get record for the device SoundHandleEntry & entry = handleDict()[device]; // set default return status BOOL stat = TRUE; // do not re-initialise initialised devices if (entry.isInitialised) { PTRACE(6, "OSS\tSkipping setup for " << device << " as already initialised"); } else { PTRACE(6, "OSS\tInitialising " << device << "(" << (void *)(&entry) << ")");#if defined(P_LINUX) // enable full duplex (maybe). ::ioctl(os_handle, SNDCTL_DSP_SETDUPLEX, 0);#endif stat = FALSE; // must always set paramaters in the following order: // buffer paramaters // sample format (number of bits) // number of channels (mon/stereo) // speed (sampling rate) int arg, val; // reset the device first so it will accept the new parms if (ConvertOSError(::ioctl(os_handle, SNDCTL_DSP_RESET, &arg))) { // set the write fragment size (applies to sound output only) arg = val = entry.fragmentValue; ::ioctl(os_handle, SNDCTL_DSP_SETFRAGMENT, &arg); mBitsPerSample = entry.bitsPerSample;#if PBYTE_ORDER == PLITTLE_ENDIAN arg = val = (entry.bitsPerSample == 16) ? AFMT_S16_LE : AFMT_S8;#else arg = val = (entry.bitsPerSample == 16) ? AFMT_S16_BE : AFMT_S8;#endif if (ConvertOSError(::ioctl(os_handle, SNDCTL_DSP_SETFMT, &arg)) || (arg != val)) { mNumChannels = entry.numChannels; arg = val = (entry.numChannels == 2) ? 1 : 0; if (ConvertOSError(::ioctl(os_handle, SNDCTL_DSP_STEREO, &arg)) || (arg != val)) { mSampleRate = entry.sampleRate; arg = val = entry.sampleRate; if (ConvertOSError(::ioctl(os_handle, SNDCTL_DSP_SPEED, &arg))) { stat = TRUE; actualSampleRate = arg; } } }#if PTRACING audio_buf_info info; ::ioctl(os_handle, SNDCTL_DSP_GETOSPACE, &info); PTRACE(4, "OSS\tOutput: fragments = " << info.fragments << ", total frags = " << info.fragstotal << ", frag size = " << info.fragsize << ", bytes = " << info.bytes); ::ioctl(os_handle, SNDCTL_DSP_GETISPACE, &info); PTRACE(4, "OSS\tInput: fragments = " << info.fragments << ", total frags = " << info.fragstotal << ", frag size = " << info.fragsize << ", bytes = " << info.bytes);#endif } } // ensure device is marked as initialised isInitialised = TRUE; entry.isInitialised = TRUE; return stat;}BOOL PSoundChannelOSS::Close(){ // if the channel isn't open, do nothing if (os_handle < 0) return TRUE; // the device must be in the dictionary dictMutex.Wait(); SoundHandleEntry * entry; PAssert((entry = handleDict().GetAt(device)) != NULL, "Unknown sound device \"" + device + "\" found"); // modify the directions bit mask in the dictionary entry->direction ^= (direction+1); // if this is the last usage of this entry, then remove it if (entry->direction == 0) { handleDict().RemoveAt(device); dictMutex.Signal(); return PChannel::Close(); } // flag this channel as closed dictMutex.Signal(); os_handle = -1; return TRUE;}BOOL PSoundChannelOSS::IsOpen() const{ return os_handle >= 0;}BOOL PSoundChannelOSS::Write(const void * buf, PINDEX len){ lastWriteCount = 0; if (!Setup() || os_handle < 0) return FALSE; while (!ConvertOSError(::write(os_handle, (void *)buf, len))) if (GetErrorCode() != Interrupted) return FALSE; lastWriteCount += len; return TRUE;}BOOL PSoundChannelOSS::Read(void * buf, PINDEX len){ lastReadCount = 0; if (!Setup() || os_handle < 0) return FALSE; PTRACE(6, "OSS\tRead start");#if defined(P_FREEBSD)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -