📄 oss.cxx
字号:
/* * sound.cxx * * Sound driver implementation. * * Portable Windows Library * * Copyright (c) 1993-1998 Equivalence Pty. Ltd. * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Portable Windows Library. * * The Initial Developer of the Original Code is Equivalence Pty. Ltd. * * Portions are Copyright (C) 1993 Free Software Foundation, Inc. * All Rights Reserved. * * Contributor(s): Loopback feature: Philip Edelbrock <phil@netroedge.com>. * * $Log: oss.cxx,v $ * Revision 1.22 2000/07/04 20:34:16 rogerh * Only use ioctl SNDCTL_DSP_SETDUPLEX is Linux. It is not defined in FreeBSD * In NetBSD and OpenBSD (using liboss), the ioctl returns EINVAL. * * Revision 1.21 2000/07/02 14:18:27 craigs * Fixed various problems with buffer handling * * Revision 1.20 2000/07/02 05:49:43 craigs * Really fixed race condition in OSS open * * Revision 1.19 2000/07/02 04:55:18 craigs * Fixed stupid mistake with fix for OSS race condition * * Revision 1.18 2000/07/02 04:50:44 craigs * Fixed potential race condition in OSS initialise * * Revision 1.17 2000/05/11 02:05:54 craigs * Fixed problem with PLayFile not recognizing wait flag * * Revision 1.16 2000/05/10 02:10:44 craigs * Added implementation for PlayFile command * * Revision 1.15 2000/05/02 08:30:26 craigs * Removed "memory leaks" caused by brain-dead GNU linker * * Revision 1.14 2000/04/09 18:19:23 rogerh * Add my changes for NetBSD support. * * Revision 1.13 2000/03/08 12:17:09 rogerh * Add OpenBSD support * * Revision 1.12 2000/03/04 13:02:28 robertj * Added simple play functions for sound files. * * Revision 1.11 2000/02/15 23:11:34 robertj * Audio support for FreeBSD, thanks Roger Hardiman. * * Revision 1.10 2000/01/08 06:41:08 craigs * Fixed problem whereby failure to open sound device returns TRUE * * Revision 1.9 1999/08/24 13:40:26 craigs * Fixed problem with EINTR causing sound channel reads and write to fail * Thanks to phil@netroedge.com! * * Revision 1.8 1999/08/17 09:42:22 robertj * Fixed close of sound channel in loopback mode closing stdin! * * Revision 1.7 1999/08/17 09:28:47 robertj * Added audio loopback psuedo-device (thanks Philip Edelbrock) * * Revision 1.6 1999/07/19 01:31:49 craigs * Major rewrite to assure ioctls are all done in the correct order as OSS seems * to be incredibly sensitive to this. * * Revision 1.5 1999/07/11 13:42:13 craigs * pthreads support for Linux * * Revision 1.4 1999/06/30 13:49:26 craigs * Added code to allow full duplex audio * * Revision 1.3 1999/05/28 14:14:29 robertj * Added function to get default audio device. * * Revision 1.2 1999/05/22 12:49:05 craigs * Finished implementation for Linux OSS interface * * Revision 1.1 1999/02/25 03:45:00 robertj * Sound driver implementation changes for various unix platforms. * * Revision 1.1 1999/02/22 13:24:47 robertj * Added first cut sound implmentation. * */#pragma implementation "sound.h"#include <ptlib.h>#ifdef P_LINUX#include <sys/soundcard.h>#include <sys/time.h>#endif#ifdef P_FREEBSD#include <machine/soundcard.h>#endif#if defined(P_OPENBSD) || defined(P_NETBSD)#include <soundcard.h>#endif///////////////////////////////////////////////////////////////////////////////// declare type for sound handle dictionaryclass SoundHandleEntry : public PObject { PCLASSINFO(SoundHandleEntry, PObject) public: SoundHandleEntry(); int handle; int direction; unsigned numChannels; unsigned sampleRate; unsigned bitsPerSample; unsigned fragmentValue; BOOL isInitialised;};PDICTIONARY(SoundHandleDict, PString, SoundHandleEntry);#define LOOPBACK_BUFFER_SIZE 5000#define BYTESINBUF ((startptr<endptr)?(endptr-startptr):(LOOPBACK_BUFFER_SIZE+endptr-startptr))static char buffer[LOOPBACK_BUFFER_SIZE];static int startptr, endptr;PMutex PSoundChannel::dictMutex;static SoundHandleDict & handleDict(){ static SoundHandleDict dict; return dict;}PSound::PSound(unsigned channels, unsigned samplesPerSecond, unsigned bitsPerSample, PINDEX bufferSize, const BYTE * buffer){ encoding = 0; numChannels = channels; sampleRate = samplesPerSecond; sampleSize = bitsPerSample; SetSize(bufferSize); if (buffer != NULL) memcpy(GetPointer(), buffer, bufferSize);}PSound::PSound(const PFilePath & filename){ encoding = 0; numChannels = 1; sampleRate = 8000; sampleSize = 16; Load(filename);}PSound & PSound::operator=(const PBYTEArray & data){ PBYTEArray::operator=(data); return *this;}void PSound::SetFormat(unsigned channels, unsigned samplesPerSecond, unsigned bitsPerSample){ encoding = 0; numChannels = channels; sampleRate = samplesPerSecond; sampleSize = bitsPerSample; formatInfo.SetSize(0);}BOOL PSound::Load(const PFilePath & /*filename*/){ return FALSE;}BOOL PSound::Save(const PFilePath & /*filename*/){ return FALSE;}BOOL PSound::Play(){ PSoundChannel channel(PSoundChannel::GetDefaultDevice(PSoundChannel::Player), PSoundChannel::Player); if (!channel.IsOpen()) return FALSE; return channel.PlaySound(*this, TRUE);}BOOL PSound::PlayFile(const PFilePath & file, BOOL wait){ PSoundChannel channel(PSoundChannel::GetDefaultDevice(PSoundChannel::Player), PSoundChannel::Player); if (!channel.IsOpen()) return FALSE; return channel.PlayFile(file, wait);}///////////////////////////////////////////////////////////////////////////////SoundHandleEntry::SoundHandleEntry(){ handle = -1; direction = 0;}///////////////////////////////////////////////////////////////////////////////PSoundChannel::PSoundChannel(){ Construct();}PSoundChannel::PSoundChannel(const PString & device, Directions dir, unsigned numChannels, unsigned sampleRate, unsigned bitsPerSample){ Construct(); Open(device, dir, numChannels, sampleRate, bitsPerSample);}void PSoundChannel::Construct(){ os_handle = -1;}PSoundChannel::~PSoundChannel(){ Close();}PStringArray PSoundChannel::GetDeviceNames(Directions /*dir*/){ static const char * const devices[] = { "/dev/audio", "/dev/dsp", "/dev/dspW", "loopback" }; return PStringArray(PARRAYSIZE(devices), devices);}PString PSoundChannel::GetDefaultDevice(Directions /*dir*/){ return "/dev/dsp";}BOOL PSoundChannel::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 in in the dictionary if (handleDict().Contains(_device)) { PTRACE(6, "OSS\tOpen occured for existing entry"); 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 { PTRACE(6, "OSS\tOpen occured for new entry"); // this is the first time this device has been used // open the device in read/write mode always if (_device == "loopback") { startptr = endptr = 0; os_handle = 0; // Use os_handle value 0 to indicate loopback, cannot ever be stdin! } else if (!ConvertOSError(os_handle = ::open((const char *)_device, O_RDWR))) return FALSE; // 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 = _numChannels; entry->sampleRate = _sampleRate; entry->bitsPerSample = _bitsPerSample; entry->isInitialised = FALSE; entry->fragmentValue = 0x7fff0008; } // save the direction and device direction = _dir; device = _device; isInitialised = FALSE; return TRUE;}BOOL PSoundChannel::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 || (device == "loopback")) { 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); audio_buf_info info; ::ioctl(os_handle, SNDCTL_DSP_GETOSPACE, &info); PTRACE(6, "OSS\tOutput: fragments = " << info.fragments << ", total frags = " << info.fragstotal << ", frag size = " << info.fragsize << ", bytes = " << info.bytes); ::ioctl(os_handle, SNDCTL_DSP_GETISPACE, &info); PTRACE(6, "OSS\tInput: fragments = " << info.fragments << ", total frags = " << info.fragstotal << ", frag size = " << info.fragsize << ", bytes = " << info.bytes); arg = val = (entry.bitsPerSample == 16) ? AFMT_S16_LE : AFMT_S8; if (ConvertOSError(::ioctl(os_handle, SNDCTL_DSP_SETFMT, &arg)) || (arg != val)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -