[GnomeMeeting-devel-list] ALSA module
- From: Damien Sandras <dsandras seconix com>
- To: gnomemeeting-devel-list gnome org
- Cc: julien puydt laposte net
- Subject: [GnomeMeeting-devel-list] ALSA module
- Date: 14 Aug 2003 18:08:50 +0200
Hello to all,
Here is the result of my late night hacking. This is an ALSA module for
native ALSA support and it works great, at least here.
The current features are :
- Supports real devices names
- Works as it should now that bugs in the plugins have been fixed (what
a night ;) )
The current things that do not work:
- In some cases, the audio will drop and the codec will be closed. I
know why, Ill fix it.
- There is still a weird behavior when the open of the device fails.
That will disappear once people will be using either OSS or ALSA but not
both at the same time. Julien is working on that.
- Too many devices are detected. For example, the microphone of the
webcam can not be used as a player (only recorder) but it is in the list
of possible players. That will be fixed.
Despite those problems, I urge you to test them with your config and to
try all weird cases possible (device busy and so on). I want at least 20
mails of people having tried on monday, or I stop coding, and Julien too
:D
Thanks,
--
Damien Sandras <dsandras seconix com>
#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 buffer_size;
};
/*
* 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: sound_alsa.cxx,v $
*/
#include "sound_alsa.h"
#include <ptlib.h>
DECLARE_PLUGIN("ALSA", PDeviceManager::SNDIN | PDeviceManager::SNDOUT);
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 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;
buffer_size = 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;
char *name = NULL;
char card_id [32];
snd_pcm_stream_t stream;
if (dir == Recorder)
stream = SND_PCM_STREAM_CAPTURE;
else
stream = SND_PCM_STREAM_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 devices;
}
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);
devices.AppendString (name);
free (name);
/* }
}
snd_ctl_close(handle);*/
snd_card_next (&card);
}
return 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 = devices.GetStringsIndex (_device)) != P_MAX_INDEX)
real_device_name = "plughw:" + PString (i);
else
real_device_name = PString ("plughw:0");
if (snd_pcm_open (&os_handle, real_device_name, stream, 0) < 0)
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 (buffer_size)
err = snd_pcm_hw_params_set_buffer_size_near (os_handle, hw_params,
buffer_size / frame_bytes);
PTRACE (1, "LALLALA " << err);
if ((err = snd_pcm_hw_params (os_handle, hw_params)) < 0) {
PTRACE (1, "ALSA\tCannot set parameters " <<
snd_strerror (err));
no_error = FALSE;
}
snd_pcm_hw_params_free (hw_params);
if ((err = snd_pcm_prepare (os_handle)) < 0) {
PTRACE (1, "ALSA\tCannot prepare audio interface for use" <<
snd_strerror (err));
no_error = FALSE;
}
snd_pcm_prepare (os_handle);
isInitialised = TRUE;
return no_error;
}
BOOL PSoundChannelALSA::Close()
{
/* if the channel isn't open, do nothing */
if (!os_handle)
return FALSE;
PTRACE (1, "Trying to close");
Abort ();
snd_pcm_close (os_handle);
os_handle = NULL;
return TRUE;
}
BOOL PSoundChannelALSA::Write (const void *buf, PINDEX len)
{
long r = 0;
if (!isInitialised && !Setup())
return FALSE;
/* the number of frames to write is the buffer length
divided by the size of one frame */
while ((r = snd_pcm_writei (os_handle, buf, len / frame_bytes)) < 0) {
PTRACE (1, "Could not write");
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, "Written " << r);
return TRUE;
}
BOOL PSoundChannelALSA::Read (void * buf, PINDEX len)
{
long r = 0;
char *buf2 = (char *) buf;
int pos = 0;
PTRACE (1, "Read with len " << len << " frame_bytes "
<< frame_bytes);
lastReadCount = 0;
if (!isInitialised && !Setup())
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);
PTRACE (1, "Read " << r << " at " << pos);
if (r > 0) {
pos += r * frame_bytes;
len -= r * frame_bytes;
lastReadCount += r * frame_bytes;
PTRACE (1, "Still len to read " << len << " at " << pos);
}
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);
}
} while (r >= 1 && len > 0);
PTRACE (1, "Read Done " << r);
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;
}
// Get the number of channels (mono/stereo) in the sound.
unsigned PSoundChannelALSA::GetChannels() const
{
return mNumChannels;
}
// Get the sample rate in samples per second.
unsigned PSoundChannelALSA::GetSampleRate() const
{
return mSampleRate;
}
// Get the sample size in bits per sample.
unsigned PSoundChannelALSA::GetSampleSize() const
{
return mBitsPerSample;
}
BOOL PSoundChannelALSA::SetBuffers (PINDEX size, PINDEX count)
{
PTRACE (1, "Asked size " << size << " and count " << count);
buffer_size = size * count;
return TRUE;
}
BOOL PSoundChannelALSA::GetBuffers(PINDEX & size, PINDEX & count)
{
return TRUE;
}
BOOL PSoundChannelALSA::PlaySound(const PSound & sound, BOOL wait)
{
return TRUE;
}
BOOL PSoundChannelALSA::PlayFile(const PFilePath & filename, BOOL wait)
{
return TRUE;
}
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 TRUE;
}
BOOL PSoundChannelALSA::IsRecordBufferFull()
{
return TRUE;
}
BOOL PSoundChannelALSA::AreAllRecordBuffersFull()
{
return TRUE;
}
BOOL PSoundChannelALSA::WaitForRecordBufferFull()
{
return TRUE;
}
BOOL PSoundChannelALSA::WaitForAllRecordBuffersFull()
{
return FALSE;
}
BOOL PSoundChannelALSA::Abort()
{
PTRACE (1, "ABORT");
if (os_handle)
snd_pcm_drop (os_handle);
return TRUE;
}
BOOL PSoundChannelALSA::SetVolume(unsigned newVal)
{
return TRUE;
}
BOOL PSoundChannelALSA::GetVolume(unsigned &devVol)
{
return TRUE;
}
BOOL PSoundChannelALSA::IsOpen () const
{
PTRACE (1, "IsOpen ");
return (os_handle != NULL);
}
include ../make/unix.mak
ifeq (1, $(HAS_AVC1394))
EXTERNALOBJS += lib/video/avc.so
endif
ifeq (1, $(HAS_V4L))
EXTERNALOBJS += lib/video/v4l.so
endif
ifeq (1, $(HAS_OSS))
EXTERNALOBJS += lib/sound/oss.so
endif
EXTERNALOBJS += lib/sound/alsa.so
TARGET = plugins
include ../make/common.mak
plugins: lib/video lib/sound $(EXTERNALOBJS)
lib/video:
mkdir -p lib/video
lib/sound:
mkdir -p lib/sound
lib/video/v4l.so: src/vidinput_v4l.cxx
$(CPLUS) $(CFLAGS) $(STDCCFLAGS) \
-I./include -shared $< -o $@
lib/video/avc.so: src/vidinput_avc.cxx
$(CPLUS) $(CFLAGS) $(STDCCFLAGS) \
-lraw1394 -ldv -lrom1394 \
-I./include -shared $< -o $@
lib/sound/oss.so: src/sound_oss.cxx
$(CPLUS) $(CFLAGS) $(STDCCFLAGS) \
-DPTRACING=1 -I./include -shared $< -o $@
lib/sound/alsa.so: src/sound_alsa.cxx
$(CPLUS) $(CFLAGS) $(STDCCFLAGS) \
-lasound \
-DPTRACING=1 -I./include -shared $< -o $@
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]