[GnomeMeeting-devel-list] ALSA plugin
- From: Damien Sandras <dsandras seconix com>
- To: gnomemeeting-devel-list gnome org
- Subject: [GnomeMeeting-devel-list] ALSA plugin
- Date: 19 Aug 2003 00:43:40 +0200
Here are the latest versions of the ALSA plugin. Please test too.
Thanks for those of you who have tested it until now.
--
Damien Sandras <dsandras seconix com>
/*
* sound_alsa.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 ALSA Code is
* Damien Sandras <dsandras seconix com>
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): /
*
* $Log: sound_alsa.cxx,v $
*/
#include "sound_alsa.h"
#if !P_USE_INLINES
#include <ptlib/contain.inl>
#endif
#include <ptlib.h>
DECLARE_PLUGIN("ALSA", PDeviceManager::SoundIn | PDeviceManager::SoundOut);
DECLARE_PLUGIN_SOUNDINPUT(PSoundChannelALSA);
DECLARE_PLUGIN_SOUNDOUTPUT(PSoundChannelALSA);
///////////////////////////////////////////////////////////////////////////////
PAudioDelay::PAudioDelay()
{
firstTime = TRUE;
error = 0;
}
void PAudioDelay::Restart()
{
firstTime = TRUE;
}
BOOL PAudioDelay::Delay(int frameTime)
{
if (firstTime) {
firstTime = FALSE;
previousTime = PTime();
return TRUE;
}
error += frameTime;
PTime now;
PTimeInterval delay = now - previousTime;
error -= (int)delay.GetMilliSeconds();
previousTime = now;
if (error > 0)
#ifdef P_LINUX
usleep(error * 1000);
#else
PThread::Current()->Sleep(error);
#endif
return error <= -frameTime;
//if (headRoom > MAX_HEADROOM)
// PThread::Current()->Sleep(headRoom - MIN_HEADROOM);
}
///////////////////////////////////////////////////////////////////////////////
// declare type for sound handle dictionary
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(PSoundChannelALSA::GetDefaultDevice(PSoundChannelALSA::Player),
PSoundChannelALSA::Player);
if (!channel.IsOpen())
return FALSE;
return channel.PlaySound(*this, TRUE);
}
BOOL PSound::PlayFile(const PFilePath & file, BOOL wait)
{
PSoundChannel channel(PSoundChannelALSA::GetDefaultDevice(PSoundChannelALSA::Player),
PSoundChannelALSA::Player);
if (!channel.IsOpen())
return FALSE;
return channel.PlayFile(file, wait);
}
///////////////////////////////////////////////////////////////////////////////
SoundHandleEntry::SoundHandleEntry()
{
handle = -1;
direction = 0;
}
static PStringArray playback_devices;
static PStringArray capture_devices;
///////////////////////////////////////////////////////////////////////////////
PSoundChannelALSA::PSoundChannelALSA()
{
PSoundChannelALSA::Construct();
}
PSoundChannelALSA::PSoundChannelALSA (const PString &device,
Directions dir,
unsigned numChannels,
unsigned sampleRate,
unsigned bitsPerSample)
{
Construct();
Open (device, dir, numChannels, sampleRate, bitsPerSample);
}
void PSoundChannelALSA::Construct()
{
frame_bytes = 0;
period_size = 0;
periods = 0;
os_handle = NULL;
}
PSoundChannelALSA::~PSoundChannelALSA()
{
Close();
}
PStringArray PSoundChannelALSA::GetDeviceNames (Directions dir)
{
int card = -1, dev = -1;
snd_ctl_t *handle = NULL;
snd_ctl_card_info_t *info = NULL;
snd_pcm_info_t *pcminfo = NULL;
snd_pcm_stream_t stream;
char *name = NULL;
char card_id [32];
if (dir == Recorder) {
stream = SND_PCM_STREAM_CAPTURE;
capture_devices = PStringArray ();
}
else {
stream = SND_PCM_STREAM_PLAYBACK;
playback_devices = PStringArray ();
}
snd_ctl_card_info_alloca (&info);
snd_pcm_info_alloca (&pcminfo);
/* No sound card found */
if (snd_card_next (&card) < 0 || card < 0) {
return PStringArray ();
}
while (card >= 0) {
snprintf (card_id, 32, "hw:%d", card);
snd_ctl_open (&handle, card_id, 0);
snd_ctl_card_info (handle, info);
while (1) {
snd_ctl_pcm_next_device (handle, &dev);
if (dev < 0)
break;
snd_pcm_info_set_device (pcminfo, dev);
snd_pcm_info_set_subdevice (pcminfo, 0);
snd_pcm_info_set_stream (pcminfo, stream);
if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
snd_card_get_name (card, &name);
if (dir == Recorder) {
if (capture_devices.GetStringsIndex (name) == P_MAX_INDEX)
capture_devices.AppendString (name);
}
else {
if (playback_devices.GetStringsIndex (name) == P_MAX_INDEX)
playback_devices.AppendString (name);
}
free (name);
}
}
snd_ctl_close(handle);
snd_card_next (&card);
}
snd_pcm_info_free (pcminfo);
if (dir == Recorder)
return capture_devices;
else
return playback_devices;
}
PString PSoundChannelALSA::GetDefaultDevice(Directions dir)
{
PStringArray devicenames;
devicenames = PSoundChannelALSA::GetDeviceNames (dir);
return devicenames[0];
}
BOOL PSoundChannelALSA::Open (const PString & _device,
Directions _dir,
unsigned _numChannels,
unsigned _sampleRate,
unsigned _bitsPerSample)
{
PString real_device_name;
PINDEX i = 0;
snd_pcm_stream_t stream;
Close();
os_handle = NULL;
if (_dir == Recorder)
stream = SND_PCM_STREAM_CAPTURE;
else
stream = SND_PCM_STREAM_PLAYBACK;
/* Open in NONBLOCK mode */
if ((i = (_dir == Recorder) ? capture_devices.GetStringsIndex (_device) : playback_devices.GetStringsIndex (_device)) != P_MAX_INDEX)
real_device_name = "plughw:" + PString (i);
else {
PTRACE (1, "ALSA\tDevice unavailable");
return FALSE;
}
if (snd_pcm_open (&os_handle, real_device_name, stream, 0) < 0) {
PTRACE (1, "ALSA\tOpen Failed");
return FALSE;
}
else
snd_pcm_nonblock (os_handle, 0);
/* save internal parameters */
direction = _dir;
device = real_device_name;
mNumChannels = _numChannels;
mSampleRate = _sampleRate;
mBitsPerSample = _bitsPerSample;
isInitialised = FALSE;
PTRACE (1, "ALSA\tDevice " << real_device_name << " Opened");
return TRUE;
}
BOOL PSoundChannelALSA::Setup()
{
snd_pcm_hw_params_t *hw_params = NULL;
int err = 0;
enum _snd_pcm_format val = SND_PCM_FORMAT_UNKNOWN;
BOOL no_error = TRUE;
if (os_handle == NULL) {
PTRACE(6, "ALSA\tSkipping setup of " << device << " as not open");
return FALSE;
}
if (isInitialised) {
PTRACE(6, "ALSA\tSkipping setup of " << device << " as instance already initialised");
return TRUE;
}
#if PBYTE_ORDER == PLITTLE_ENDIAN
val = (mBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8;
#else
val = (mbitsPerSample == 16) ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_U8;
#endif
frame_bytes = (mNumChannels * (snd_pcm_format_width (val) / 8));
snd_pcm_hw_params_alloca (&hw_params);
if ((err = snd_pcm_hw_params_any (os_handle, hw_params)) < 0) {
PTRACE (1, "ALSA\tCannot initialize hardware parameter structure " <<
snd_strerror (err));
no_error = FALSE;
}
if ((err = snd_pcm_hw_params_set_access (os_handle, hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
PTRACE (1, "ALSA\tCannot set access type " <<
snd_strerror (err));
no_error = FALSE;
}
if ((err = snd_pcm_hw_params_set_format (os_handle, hw_params, val)) < 0) {
PTRACE (1, "ALSA\tCannot set sample format " <<
snd_strerror (err));
no_error = FALSE;
}
if ((err = snd_pcm_hw_params_set_rate (os_handle, hw_params,
mSampleRate, 0)) < 0) {
PTRACE (1, "ALSA\tCannot set sample rate " <<
snd_strerror (err));
no_error = FALSE;
}
if ((err = snd_pcm_hw_params_set_channels (os_handle, hw_params,
mNumChannels)) < 0) {
PTRACE (1, "ALSA\tCannot set channel count " <<
snd_strerror (err));
no_error = FALSE;
}
// Ignore errors here
if (periods && period_size) {
if ((err = snd_pcm_hw_params_set_period_size_near (os_handle,
hw_params,
period_size/frame_bytes,
0)) < 0)
PTRACE (1, "ALSA\tCannot set period size " <<
snd_strerror (err));
if ((err = snd_pcm_hw_params_set_periods (os_handle,
hw_params,
periods, 0)) < 0)
PTRACE (1, "ALSA\tCannot set number of periods " <<
snd_strerror (err));
if ((err = snd_pcm_hw_params_set_buffer_size_near (os_handle,
hw_params,
periods*period_size/frame_bytes)) < 0)
PTRACE (1, "ALSA\tCannot set buffer size " <<
snd_strerror (err));
}
if ((err = snd_pcm_hw_params (os_handle, hw_params)) < 0) {
PTRACE (1, "ALSA\tCannot set parameters " <<
snd_strerror (err));
no_error = FALSE;
}
isInitialised = TRUE;
return no_error;
}
BOOL PSoundChannelALSA::Close()
{
PWaitAndSignal m(device_mutex);
/* if the channel isn't open, do nothing */
if (!os_handle)
return FALSE;
if (isInitialised)
Abort ();
snd_pcm_close (os_handle);
os_handle = NULL;
return TRUE;
}
BOOL PSoundChannelALSA::Write (const void *buf, PINDEX len)
{
long r = 0;
char *buf2 = (char *) buf;
int pos = 0, max_try = 0;
PWaitAndSignal m(device_mutex);
if (!isInitialised && !Setup() || !len || !os_handle)
return FALSE;
do {
/* the number of frames to read is the buffer length
divided by the size of one frame */
r = snd_pcm_writei (os_handle, (char *) &buf2 [pos], len / frame_bytes);
if (r > 0) {
pos += r * frame_bytes;
len -= r * frame_bytes;
}
else {
if (r == -EPIPE) { /* under-run */
snd_pcm_prepare (os_handle);
} else if (r == -ESTRPIPE) {
while ((r = snd_pcm_resume (os_handle)) == -EAGAIN)
sleep(1); /* wait until the suspend flag is released */
if (r < 0)
snd_pcm_prepare (os_handle);
}
PTRACE (1, "ALSA\tCould not write");
max_try++;
}
} while (len > 0 && max_try < 5);
if (len != 0) {
memset ((char *) &buf2 [pos], 0, len);
PTRACE (1, "ALSA\tWrite Error, filling with zeros");
}
return TRUE;
}
BOOL PSoundChannelALSA::Read (void * buf, PINDEX len)
{
long r = 0;
char *buf2 = (char *) buf;
int pos = 0, max_try = 0;
lastReadCount = 0;
PWaitAndSignal m(device_mutex);
if (!isInitialised && !Setup() || !len || !os_handle)
return FALSE;
do {
/* the number of frames to read is the buffer length
divided by the size of one frame */
r = snd_pcm_readi (os_handle, (char *) &buf2 [pos], len / frame_bytes);
if (r > 0) {
pos += r * frame_bytes;
len -= r * frame_bytes;
lastReadCount += r * frame_bytes;
}
else {
if (r == -EPIPE) { /* under-run */
snd_pcm_prepare (os_handle);
} else if (r == -ESTRPIPE) {
while ((r = snd_pcm_resume (os_handle)) == -EAGAIN)
sleep(1); /* wait until the suspend flag is released */
if (r < 0)
snd_pcm_prepare (os_handle);
}
PTRACE (1, "ALSA\tCould not read");
max_try++;
}
} while (len > 0 && max_try < 5);
if (len != 0) {
memset ((char *) &buf2 [pos], 0, len);
lastReadCount += len;
PTRACE (1, "ALSA\tRead Error, filling with zeros");
}
return TRUE;
}
BOOL PSoundChannelALSA::SetFormat (unsigned numChannels,
unsigned sampleRate,
unsigned bitsPerSample)
{
if (!os_handle)
return SetErrorValues(NotOpen, EBADF);
/* check parameters */
PAssert((bitsPerSample == 8) || (bitsPerSample == 16), PInvalidParameter);
PAssert(numChannels >= 1 && numChannels <= 2, PInvalidParameter);
mNumChannels = numChannels;
mSampleRate = sampleRate;
mBitsPerSample = bitsPerSample;
/* mark this channel as uninitialised */
isInitialised = FALSE;
return TRUE;
}
unsigned PSoundChannelALSA::GetChannels() const
{
return mNumChannels;
}
unsigned PSoundChannelALSA::GetSampleRate() const
{
return mSampleRate;
}
unsigned PSoundChannelALSA::GetSampleSize() const
{
return mBitsPerSample;
}
BOOL PSoundChannelALSA::SetBuffers (PINDEX size, PINDEX count)
{
periods = count;
period_size = size;
return TRUE;
}
BOOL PSoundChannelALSA::GetBuffers(PINDEX & size, PINDEX & count)
{
return FALSE;
}
BOOL PSoundChannelALSA::PlaySound(const PSound & sound, BOOL wait)
{
return FALSE;
}
BOOL PSoundChannelALSA::PlayFile(const PFilePath & filename, BOOL wait)
{
return FALSE;
}
BOOL PSoundChannelALSA::HasPlayCompleted()
{
return TRUE;
}
BOOL PSoundChannelALSA::WaitForPlayCompletion()
{
return TRUE;
}
BOOL PSoundChannelALSA::RecordSound(PSound & sound)
{
return FALSE;
}
BOOL PSoundChannelALSA::RecordFile(const PFilePath & filename)
{
return FALSE;
}
BOOL PSoundChannelALSA::StartRecording()
{
return FALSE;
}
BOOL PSoundChannelALSA::IsRecordBufferFull()
{
return TRUE;
}
BOOL PSoundChannelALSA::AreAllRecordBuffersFull()
{
return TRUE;
}
BOOL PSoundChannelALSA::WaitForRecordBufferFull()
{
return TRUE;
}
BOOL PSoundChannelALSA::WaitForAllRecordBuffersFull()
{
return FALSE;
}
BOOL PSoundChannelALSA::Abort()
{
int r = 0;
if (!os_handle)
return FALSE;
if ((r = snd_pcm_drop (os_handle)) < 0) {
PTRACE (1, "ALSA\tCannot abort" <<
snd_strerror (r));
return FALSE;
}
else
return TRUE;
}
BOOL PSoundChannelALSA::SetVolume(unsigned newVal)
{
return FALSE;
}
BOOL PSoundChannelALSA::GetVolume(unsigned &devVol)
{
return FALSE;
}
BOOL PSoundChannelALSA::IsOpen () const
{
return (os_handle != NULL);
}
#include <ptlib.h>
#include <ptlib/plugins.h>
#include <alsa/asoundlib.h>
class PAudioDelay : public PObject
{
PCLASSINFO(PAudioDelay, PObject);
public:
PAudioDelay();
BOOL Delay(int time);
void Restart();
int GetError();
protected:
PTime previousTime;
BOOL firstTime;
int error;
};
#define MIN_HEADROOM 30
#define MAX_HEADROOM 60
class SoundHandleEntry : public PObject {
PCLASSINFO(SoundHandleEntry, PObject)
public:
SoundHandleEntry();
int handle;
int direction;
unsigned numChannels;
unsigned sampleRate;
unsigned bitsPerSample;
unsigned fragmentValue;
BOOL isInitialised;
};
#define LOOPBACK_BUFFER_SIZE 5000
#define BYTESINBUF ((startptr<endptr)?(endptr-startptr):(LOOPBACK_BUFFER_SIZE+endptr-startptr))
class PSoundChannelALSA: public PSoundChannel
{
public:
PSoundChannelALSA();
void Construct();
PSoundChannelALSA(const PString &device,
PSoundChannel::Directions dir,
unsigned numChannels,
unsigned sampleRate,
unsigned bitsPerSample);
~PSoundChannelALSA();
static PStringArray GetDeviceNames(PSoundChannel::Directions);
static PString GetDefaultDevice(PSoundChannel::Directions);
BOOL Open(const PString & _device,
Directions _dir,
unsigned _numChannels,
unsigned _sampleRate,
unsigned _bitsPerSample);
BOOL Setup();
BOOL Close();
BOOL Write(const void * buf, PINDEX len);
BOOL Read(void * buf, PINDEX len);
BOOL SetFormat(unsigned numChannels,
unsigned sampleRate,
unsigned bitsPerSample);
unsigned GetChannels() const;
unsigned GetSampleRate() const;
unsigned GetSampleSize() const;
BOOL SetBuffers(PINDEX size, PINDEX count);
BOOL GetBuffers(PINDEX & size, PINDEX & count);
BOOL PlaySound(const PSound & sound, BOOL wait);
BOOL PlayFile(const PFilePath & filename, BOOL wait);
BOOL HasPlayCompleted();
BOOL WaitForPlayCompletion();
BOOL RecordSound(PSound & sound);
BOOL RecordFile(const PFilePath & filename);
BOOL StartRecording();
BOOL IsRecordBufferFull();
BOOL AreAllRecordBuffersFull();
BOOL WaitForRecordBufferFull();
BOOL WaitForAllRecordBuffersFull();
BOOL Abort();
BOOL SetVolume(unsigned newVal);
BOOL GetVolume(unsigned &devVol);
virtual BOOL IsOpen() const;
private:
snd_pcm_t *os_handle; /* Handle, different fromt the PChannel handle */
int frame_bytes; /* Number of bytes in a frame */
int period_size;
int periods;
PMutex device_mutex;
};
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]