⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sound_oss.cxx

📁 sloedgy open sip stack source code
💻 CXX
📖 第 1 页 / 共 3 页
字号:
          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;
    entry->resampleRate  = 0;
  }
   
  // 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");
    resampleRate = entry.resampleRate;

  } 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;

            // detect cases where the hardware can't do the actual rate we need, but can do a simple multiple
            if (arg != (int)entry.sampleRate) {
              if (((arg / entry.sampleRate) * entry.sampleRate) == (unsigned)arg) {
                PTRACE(3, "Resampling data at " << entry.sampleRate << " to match hardware rate of " << arg);
                resampleRate = entry.resampleRate = arg / entry.sampleRate;
              } else {
                PTRACE_IF(4, actualSampleRate != (unsigned)val, "Actual sample rate selected is " << actualSampleRate << ", not " << entry.sampleRate);
                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;

  if (resampleRate == 0) {
    while (!ConvertOSError(::write(os_handle, (void *)buf, len))) 
      if (GetErrorCode() != Interrupted)
        return FALSE;
    lastWriteCount += len;
  }

  else {
    // cut the data into 1K blocks and upsample it
    lastWriteCount = 0;
    BYTE resampleBuffer[1024];
    const BYTE * src    = (const BYTE *)buf;
    const BYTE * srcEnd = src + len;
    while (src < srcEnd) {

      // expand the data by the appropriate sample ratio
      BYTE * dst = resampleBuffer;
      const BYTE * srcStart = src;
      unsigned j;
       
      while ((src < srcEnd) && (dst < (resampleBuffer + sizeof(resampleBuffer) - resampleRate*2))) {
        for (j = 0; j < resampleRate; ++j) {
          memcpy(dst, src, 2);
          dst += 2 ;
        }
        src += 2;
      }
      lastWriteCount += src - srcStart;
      while (!ConvertOSError(::write(os_handle, resampleBuffer, dst - resampleBuffer))) {
        if (GetErrorCode() != Interrupted) 
          return FALSE;
      }
    }

  }

  return TRUE;
}

BOOL PSoundChannelOSS::Read(void * buf, PINDEX len)
{
  lastReadCount = 0;

  if (!Setup() || os_handle < 0)
    return FALSE;

  if (resampleRate == 0) {

    PINDEX total = 0;
    while (total < len) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -