mirror of https://github.com/stella-emu/stella.git
Added ALSA v0.9 API sound backend to the X11 and SDL versions.
This is BETA code and should be thoroughly tested. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@148 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
parent
22b69558ce
commit
9feac30d84
|
@ -0,0 +1,308 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SSSS tt lll lll
|
||||||
|
// SS SS tt ll ll
|
||||||
|
// SS tttttt eeee ll ll aaaa
|
||||||
|
// SSSS tt ee ee ll ll aa
|
||||||
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||||
|
// SS SS tt ee ll ll aa aa
|
||||||
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
|
//
|
||||||
|
// Copyright (c) 1995-2002 by Bradford W. Mott
|
||||||
|
//
|
||||||
|
// See the file "license" for information on usage and redistribution of
|
||||||
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
|
//
|
||||||
|
// $Id: SoundALSA.cxx,v 1.1 2002-12-01 02:12:26 stephena Exp $
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "SoundALSA.hxx"
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
SoundALSA::SoundALSA()
|
||||||
|
: myIsInitializedFlag(false),
|
||||||
|
myPcmHandle(0),
|
||||||
|
myMixerHandle(0),
|
||||||
|
myMixerElem(0),
|
||||||
|
myOriginalVolumeLeft(-1),
|
||||||
|
myOriginalVolumeRight(-1),
|
||||||
|
myBufferSize(0),
|
||||||
|
mySampleRate(0)
|
||||||
|
{
|
||||||
|
Int32 err;
|
||||||
|
char pcmName[] = "plughw:0,0";
|
||||||
|
char mixerName[] = "PCM";
|
||||||
|
char mixerCard[] = "default";
|
||||||
|
|
||||||
|
snd_pcm_hw_params_t* hwparams;
|
||||||
|
|
||||||
|
// Allocate the snd_pcm_hw_params_t structure on the stack
|
||||||
|
snd_pcm_hw_params_alloca(&hwparams);
|
||||||
|
|
||||||
|
// Open the PCM device for writing
|
||||||
|
if((err = snd_pcm_open(&myPcmHandle, pcmName, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init hwparams with full configuration space
|
||||||
|
if((err = snd_pcm_hw_params_any(myPcmHandle, hwparams)) < 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set interleaved access
|
||||||
|
if((err = snd_pcm_hw_params_set_access(myPcmHandle, hwparams,
|
||||||
|
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the audio data format
|
||||||
|
if((err = snd_pcm_hw_params_set_format(myPcmHandle, hwparams, SND_PCM_FORMAT_U8)) < 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the number of audio channels to 1 (mono mode)
|
||||||
|
if((err = snd_pcm_hw_params_set_channels(myPcmHandle, hwparams, 1)) < 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the audio sample rate. If the exact rate is not supported by
|
||||||
|
// the hardware, use nearest possible rate
|
||||||
|
mySampleRate = 31400;
|
||||||
|
if((err = snd_pcm_hw_params_set_rate_near(myPcmHandle, hwparams, mySampleRate, 0)) < 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
mySampleRate = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set number of fragments to 2
|
||||||
|
if((err = snd_pcm_hw_params_set_periods(myPcmHandle, hwparams, 2, 0)) < 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
mySampleRate = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set size of fragments to 512 bytes
|
||||||
|
myBufferSize = 512;
|
||||||
|
if((err = snd_pcm_hw_params_set_period_size(myPcmHandle, hwparams,
|
||||||
|
myBufferSize, 0)) < 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
mySampleRate = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply HW parameter settings to PCM device
|
||||||
|
if((err = snd_pcm_hw_params(myPcmHandle, hwparams)) < 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
mySampleRate = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// Now, open the mixer so we'll be able to change the volume
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
snd_mixer_selem_id_t* mixerID;
|
||||||
|
|
||||||
|
// Allocate simple mixer ID
|
||||||
|
snd_mixer_selem_id_alloca(&mixerID);
|
||||||
|
|
||||||
|
// Sets simple mixer ID and name
|
||||||
|
snd_mixer_selem_id_set_index(mixerID, 0);
|
||||||
|
snd_mixer_selem_id_set_name(mixerID, mixerName);
|
||||||
|
|
||||||
|
// Open the mixer device
|
||||||
|
if((err = snd_mixer_open(&myMixerHandle, 0)) < 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
mySampleRate = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach the mixer to the default sound card
|
||||||
|
if((err = snd_mixer_attach(myMixerHandle, mixerCard)) < 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
snd_mixer_close(myMixerHandle);
|
||||||
|
mySampleRate = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the mixer with the sound system
|
||||||
|
if((err = snd_mixer_selem_register(myMixerHandle, NULL, NULL)) < 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
snd_mixer_close(myMixerHandle);
|
||||||
|
mySampleRate = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((err = snd_mixer_load(myMixerHandle)) < 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
snd_mixer_close(myMixerHandle);
|
||||||
|
mySampleRate = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the mixer element that will be used to control volume
|
||||||
|
if((myMixerElem = snd_mixer_find_selem(myMixerHandle, mixerID)) == 0)
|
||||||
|
{
|
||||||
|
cerr << "SoundALSA: " << snd_strerror(err) << endl;
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
snd_mixer_close(myMixerHandle);
|
||||||
|
mySampleRate = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the original volume so we can restore it on exit
|
||||||
|
snd_mixer_selem_get_playback_volume(myMixerElem, (_snd_mixer_selem_channel_id) 0,
|
||||||
|
&myOriginalVolumeLeft);
|
||||||
|
snd_mixer_selem_get_playback_volume(myMixerElem, (_snd_mixer_selem_channel_id) 1,
|
||||||
|
&myOriginalVolumeRight);
|
||||||
|
|
||||||
|
// Prepare the audio device for playback
|
||||||
|
snd_pcm_prepare(myPcmHandle);
|
||||||
|
|
||||||
|
// Indicate that the sound system is fully initialized
|
||||||
|
myIsInitializedFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
SoundALSA::~SoundALSA()
|
||||||
|
{
|
||||||
|
closeDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundALSA::closeDevice()
|
||||||
|
{
|
||||||
|
if(myIsInitializedFlag)
|
||||||
|
{
|
||||||
|
// Restore original volume
|
||||||
|
if(myMixerHandle)
|
||||||
|
{
|
||||||
|
if((myOriginalVolumeLeft != -1) && (myOriginalVolumeRight != -1))
|
||||||
|
{
|
||||||
|
snd_mixer_selem_set_playback_volume(myMixerElem, (_snd_mixer_selem_channel_id) 0,
|
||||||
|
myOriginalVolumeLeft);
|
||||||
|
snd_mixer_selem_set_playback_volume(myMixerElem, (_snd_mixer_selem_channel_id) 1,
|
||||||
|
myOriginalVolumeRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_mixer_close(myMixerHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(myPcmHandle)
|
||||||
|
snd_pcm_close(myPcmHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
myIsInitializedFlag = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
uInt32 SoundALSA::getSampleRate() const
|
||||||
|
{
|
||||||
|
return myIsInitializedFlag ? mySampleRate : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool SoundALSA::isSuccessfullyInitialized() const
|
||||||
|
{
|
||||||
|
return myIsInitializedFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundALSA::setSoundVolume(uInt32 volume)
|
||||||
|
{
|
||||||
|
if(myIsInitializedFlag && myMixerElem)
|
||||||
|
{
|
||||||
|
if(volume < 0)
|
||||||
|
{
|
||||||
|
volume = 0;
|
||||||
|
}
|
||||||
|
if(volume > 100)
|
||||||
|
{
|
||||||
|
volume = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
long int lowerBound, upperBound, newVolume;
|
||||||
|
snd_mixer_selem_get_playback_volume_range(myMixerElem, &lowerBound, &upperBound);
|
||||||
|
|
||||||
|
newVolume = (long int) (((upperBound - lowerBound) * volume / 100.0) + lowerBound);
|
||||||
|
snd_mixer_selem_set_playback_volume(myMixerElem, (_snd_mixer_selem_channel_id) 0,
|
||||||
|
newVolume);
|
||||||
|
snd_mixer_selem_set_playback_volume(myMixerElem, (_snd_mixer_selem_channel_id) 1,
|
||||||
|
newVolume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundALSA::updateSound(MediaSource& mediaSource)
|
||||||
|
{
|
||||||
|
if(myIsInitializedFlag)
|
||||||
|
{
|
||||||
|
snd_pcm_sframes_t frames;
|
||||||
|
uInt8 periodCount = 0;
|
||||||
|
|
||||||
|
// Dequeue samples as long as full fragments are available
|
||||||
|
while(mediaSource.numberOfAudioSamples() >= myBufferSize)
|
||||||
|
{
|
||||||
|
uInt8 buffer[myBufferSize];
|
||||||
|
mediaSource.dequeueAudioSamples(buffer, myBufferSize);
|
||||||
|
|
||||||
|
if((frames = snd_pcm_writei(myPcmHandle, buffer, myBufferSize)) == -EPIPE)
|
||||||
|
{
|
||||||
|
snd_pcm_prepare(myPcmHandle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
periodCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill any unused fragments with silence so that we have a lower
|
||||||
|
// risk of having playback underruns
|
||||||
|
for(int i = 0; i < 1-periodCount; ++i)
|
||||||
|
{
|
||||||
|
frames = snd_pcm_avail_update(myPcmHandle);
|
||||||
|
if (frames > 0)
|
||||||
|
{
|
||||||
|
uInt8 buffer[frames];
|
||||||
|
memset((void*)buffer, 0, frames);
|
||||||
|
snd_pcm_writei(myPcmHandle, buffer, frames);
|
||||||
|
}
|
||||||
|
else if(frames == -EPIPE) // this should never happen
|
||||||
|
{
|
||||||
|
cerr << "EPIPE after write\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SSSS tt lll lll
|
||||||
|
// SS SS tt ll ll
|
||||||
|
// SS tttttt eeee ll ll aaaa
|
||||||
|
// SSSS tt ee ee ll ll aa
|
||||||
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||||
|
// SS SS tt ee ll ll aa aa
|
||||||
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
|
//
|
||||||
|
// Copyright (c) 1995-2002 by Bradford W. Mott
|
||||||
|
//
|
||||||
|
// See the file "license" for information on usage and redistribution of
|
||||||
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
|
//
|
||||||
|
// $Id: SoundALSA.hxx,v 1.1 2002-12-01 02:12:26 stephena Exp $
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#ifndef SOUNDALSA_HXX
|
||||||
|
#define SOUNDALSA_HXX
|
||||||
|
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
|
||||||
|
#include "Sound.hxx"
|
||||||
|
#include "bspf.hxx"
|
||||||
|
#include "MediaSrc.hxx"
|
||||||
|
|
||||||
|
/**
|
||||||
|
This class implements a sound class using the
|
||||||
|
Advanced Linux Sound Architecture (ALSA) version 0.9.x API.
|
||||||
|
|
||||||
|
@author Stephen Anthony
|
||||||
|
@version $Id: SoundALSA.hxx,v 1.1 2002-12-01 02:12:26 stephena Exp $
|
||||||
|
*/
|
||||||
|
class SoundALSA : public Sound
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
Create a new sound object
|
||||||
|
*/
|
||||||
|
SoundALSA();
|
||||||
|
|
||||||
|
/**
|
||||||
|
Destructor
|
||||||
|
*/
|
||||||
|
virtual ~SoundALSA();
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
Closes the sound device
|
||||||
|
*/
|
||||||
|
void closeDevice();
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return the playback sample rate for the sound device.
|
||||||
|
|
||||||
|
@return The playback sample rate
|
||||||
|
*/
|
||||||
|
uInt32 getSampleRate() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return true iff the sound device was successfully initlaized.
|
||||||
|
|
||||||
|
@return true iff the sound device was successfully initlaized.
|
||||||
|
*/
|
||||||
|
bool isSuccessfullyInitialized() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets the volume of the sound device to the specified level. The
|
||||||
|
volume is given as a precentage from 0 to 100.
|
||||||
|
|
||||||
|
@param volume The new volume for the sound device
|
||||||
|
*/
|
||||||
|
void setSoundVolume(uInt32 volume);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Update the sound device using the audio sample from the specified
|
||||||
|
media source.
|
||||||
|
|
||||||
|
@param mediaSource The media source to get audio samples from.
|
||||||
|
*/
|
||||||
|
void updateSound(MediaSource& mediaSource);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Indicates if the sound device was successfully initialized
|
||||||
|
bool myIsInitializedFlag;
|
||||||
|
|
||||||
|
// Handle for the PCM device
|
||||||
|
snd_pcm_t* myPcmHandle;
|
||||||
|
|
||||||
|
// Handle for the mixer device
|
||||||
|
snd_mixer_t* myMixerHandle;
|
||||||
|
|
||||||
|
// Mixer elem, used to set volume
|
||||||
|
snd_mixer_elem_t* myMixerElem;
|
||||||
|
|
||||||
|
// Original mixer volume when the sound device was opened
|
||||||
|
long int myOriginalVolumeLeft;
|
||||||
|
long int myOriginalVolumeRight;
|
||||||
|
|
||||||
|
// PCM buffer size
|
||||||
|
uInt32 myBufferSize;
|
||||||
|
|
||||||
|
// PCM sample rate
|
||||||
|
uInt32 mySampleRate;
|
||||||
|
};
|
||||||
|
#endif
|
Loading…
Reference in New Issue