Bug 852401 - Remove sydneyaudio. r=doublec

--HG--
rename : media/libsydneyaudio/src/gonk/AudioSystem.h => dom/system/gonk/android_audio/AudioSystem.h
rename : media/libsydneyaudio/src/gonk/AudioTrack.h => dom/system/gonk/android_audio/AudioTrack.h
rename : media/libsydneyaudio/src/gonk/EffectApi.h => dom/system/gonk/android_audio/EffectApi.h
rename : media/libsydneyaudio/src/gonk/IAudioFlinger.h => dom/system/gonk/android_audio/IAudioFlinger.h
rename : media/libsydneyaudio/src/gonk/IAudioFlingerClient.h => dom/system/gonk/android_audio/IAudioFlingerClient.h
rename : media/libsydneyaudio/src/gonk/IAudioRecord.h => dom/system/gonk/android_audio/IAudioRecord.h
rename : media/libsydneyaudio/src/gonk/IAudioTrack.h => dom/system/gonk/android_audio/IAudioTrack.h
rename : media/libsydneyaudio/src/gonk/IEffect.h => dom/system/gonk/android_audio/IEffect.h
rename : media/libsydneyaudio/src/gonk/IEffectClient.h => dom/system/gonk/android_audio/IEffectClient.h
This commit is contained in:
Matthew Gregan 2013-03-19 17:12:36 +13:00
parent c4c535f1ae
commit c7c49a4e9a
45 changed files with 16 additions and 7629 deletions

View File

@ -1107,7 +1107,6 @@ vpx/vpx_decoder.h
vpx/vpx_encoder.h
vpx/vp8cx.h
vpx/vp8dx.h
sydneyaudio/sydney_audio.h
vorbis/codec.h
theora/theoradec.h
tremor/ivorbiscodec.h

View File

@ -4211,7 +4211,6 @@ MOZ_JSDEBUGGER=1
MOZ_AUTH_EXTENSION=1
MOZ_OGG=1
MOZ_RAW=
MOZ_SYDNEYAUDIO=
MOZ_SPEEX_RESAMPLER=1
MOZ_SOUNDTOUCH=1
MOZ_CUBEB=
@ -5370,7 +5369,6 @@ MOZ_ARG_DISABLE_BOOL(ogg,
if test -n "$MOZ_OGG"; then
AC_DEFINE(MOZ_OGG)
MOZ_SYDNEYAUDIO=1
MOZ_CUBEB=1
MOZ_MEDIA=1
@ -5453,7 +5451,6 @@ MOZ_ARG_DISABLE_BOOL(wmf,
if test -n "$MOZ_WMF"; then
AC_DEFINE(MOZ_WMF)
MOZ_SYDNEYAUDIO=1
MOZ_CUBEB=1
MOZ_MEDIA=1
fi;
@ -5550,7 +5547,6 @@ AC_SUBST(MOZ_LIBVPX_CFLAGS)
AC_SUBST(MOZ_LIBVPX_LIBS)
if test "$MOZ_WEBM"; then
MOZ_SYDNEYAUDIO=1
MOZ_CUBEB=1
MOZ_MEDIA=1
if test "$MOZ_SAMPLE_TYPE_FLOAT32"; then
@ -5654,19 +5650,14 @@ MOZ_ARG_DISABLE_BOOL(wave,
if test -n "$MOZ_WAVE"; then
AC_DEFINE(MOZ_WAVE)
MOZ_SYDNEYAUDIO=1
MOZ_CUBEB=1
MOZ_MEDIA=1
fi
dnl ========================================================
dnl = Handle dependent SYDNEYAUDIO, CUBEB, and MEDIA defines
dnl = Handle dependent CUBEB and MEDIA defines
dnl ========================================================
if test -n "$MOZ_SYDNEYAUDIO"; then
AC_DEFINE(MOZ_SYDNEYAUDIO)
fi
if test -n "$MOZ_SPEEX_RESAMPLER"; then
AC_DEFINE(MOZ_SPEEX_RESAMPLER)
fi
@ -5676,26 +5667,7 @@ if test -n "$MOZ_SOUNDTOUCH"; then
fi
if test -n "$MOZ_CUBEB"; then
case "$target" in
*-android*|*-linuxandroid*)
AC_DEFINE(MOZ_CUBEB)
;;
*-linux*)
AC_DEFINE(MOZ_CUBEB)
;;
*-mingw*)
AC_DEFINE(MOZ_CUBEB)
;;
*-darwin*)
AC_DEFINE(MOZ_CUBEB)
;;
*-openbsd*)
AC_DEFINE(MOZ_CUBEB)
;;
*)
dnl Other targets will be enabled soon.
;;
esac
AC_DEFINE(MOZ_CUBEB)
fi
if test -n "$MOZ_MEDIA"; then
@ -5727,7 +5699,7 @@ dnl = Check alsa availability on Linux if using sydneyaudio
dnl ========================================================
dnl If using sydneyaudio with Linux, ensure that the alsa library is available
if test -n "$MOZ_SYDNEYAUDIO" -a "$OS_TARGET" = "Linux"; then
if test -n "$MOZ_CUBEB" -a "$OS_TARGET" = "Linux"; then
MOZ_ALSA=1
fi
@ -8851,7 +8823,6 @@ AC_SUBST(MOZ_APP_COMPONENT_LIBS)
AC_SUBST(MOZ_APP_EXTRA_LIBS)
AC_SUBST(MOZ_MEDIA)
AC_SUBST(MOZ_SYDNEYAUDIO)
AC_SUBST(MOZ_SPEEX_RESAMPLER)
AC_SUBST(MOZ_SOUNDTOUCH)
AC_SUBST(MOZ_CUBEB)

View File

@ -13,9 +13,6 @@
#include "mozilla/Monitor.h"
#include "mozilla/Mutex.h"
#include <algorithm>
extern "C" {
#include "sydneyaudio/sydney_audio.h"
}
#include "mozilla/Preferences.h"
#if defined(MOZ_CUBEB)
@ -33,63 +30,15 @@ public:
namespace mozilla {
#if defined(XP_MACOSX)
#define SA_PER_STREAM_VOLUME 1
#endif
#ifdef PR_LOGGING
PRLogModuleInfo* gAudioStreamLog = nullptr;
#endif
static const uint32_t FAKE_BUFFER_SIZE = 176400;
// Number of milliseconds per second.
static const int64_t MS_PER_S = 1000;
class NativeAudioStream : public AudioStream
{
public:
~NativeAudioStream();
NativeAudioStream();
nsresult Init(int32_t aNumChannels, int32_t aRate,
const dom::AudioChannelType aAudioChannelType);
void Shutdown();
nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames);
uint32_t Available();
void SetVolume(double aVolume);
void Drain();
void Start();
void Pause();
void Resume();
int64_t GetPosition();
int64_t GetPositionInFrames();
int64_t GetPositionInFramesInternal();
bool IsPaused();
int32_t GetMinWriteSize();
private:
int32_t WriteToBackend(const float* aBuffer, uint32_t aFrames);
int32_t WriteToBackend(const short* aBuffer, uint32_t aFrames);
double mVolume;
void* mAudioHandle;
// True if this audio stream is paused.
bool mPaused;
// True if this stream has encountered an error.
bool mInError;
};
#define PREF_VOLUME_SCALE "media.volume_scale"
#define PREF_USE_CUBEB "media.use_cubeb"
#define PREF_CUBEB_LATENCY "media.cubeb_latency_ms"
static Mutex* gAudioPrefsLock = nullptr;
static double gVolumeScale;
static bool gUseCubeb;
static uint32_t gCubebLatency;
static int PrefChanged(const char* aPref, void* aClosure)
@ -103,10 +52,6 @@ static int PrefChanged(const char* aPref, void* aClosure)
NS_ConvertUTF16toUTF8 utf8(value);
gVolumeScale = std::max<double>(0, PR_strtod(utf8.get(), nullptr));
}
} else if (strcmp(aPref, PREF_USE_CUBEB) == 0) {
bool value = Preferences::GetBool(aPref, true);
MutexAutoLock lock(*gAudioPrefsLock);
gUseCubeb = value;
} else if (strcmp(aPref, PREF_CUBEB_LATENCY) == 0) {
// Arbitrary default stream latency of 100ms. The higher this
// value, the longer stream volume changes will take to become
@ -125,12 +70,6 @@ static double GetVolumeScale()
}
#if defined(MOZ_CUBEB)
static bool GetUseCubeb()
{
MutexAutoLock lock(*gAudioPrefsLock);
return gUseCubeb;
}
static cubeb* gCubebContext;
static cubeb* GetCubebContext()
@ -151,29 +90,6 @@ static uint32_t GetCubebLatency()
}
#endif
static sa_stream_type_t ConvertChannelToSAType(dom::AudioChannelType aType)
{
switch(aType) {
case dom::AUDIO_CHANNEL_NORMAL:
return SA_STREAM_TYPE_SYSTEM;
case dom::AUDIO_CHANNEL_CONTENT:
return SA_STREAM_TYPE_MUSIC;
case dom::AUDIO_CHANNEL_NOTIFICATION:
return SA_STREAM_TYPE_NOTIFICATION;
case dom::AUDIO_CHANNEL_ALARM:
return SA_STREAM_TYPE_ALARM;
case dom::AUDIO_CHANNEL_TELEPHONY:
return SA_STREAM_TYPE_VOICE_CALL;
case dom::AUDIO_CHANNEL_RINGER:
return SA_STREAM_TYPE_RING;
case dom::AUDIO_CHANNEL_PUBLICNOTIFICATION:
return SA_STREAM_TYPE_ENFORCED_AUDIBLE;
default:
NS_ERROR("The value of AudioChannelType is invalid");
return SA_STREAM_TYPE_MAX;
}
}
#if defined(MOZ_CUBEB) && (__ANDROID__)
static cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannelType aType)
{
@ -216,8 +132,6 @@ void AudioStream::InitLibrary()
PrefChanged(PREF_VOLUME_SCALE, nullptr);
Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
#if defined(MOZ_CUBEB)
PrefChanged(PREF_USE_CUBEB, nullptr);
Preferences::RegisterCallback(PrefChanged, PREF_USE_CUBEB);
PrefChanged(PREF_CUBEB_LATENCY, nullptr);
Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
#endif
@ -227,7 +141,7 @@ void AudioStream::ShutdownLibrary()
{
Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
#if defined(MOZ_CUBEB)
Preferences::UnregisterCallback(PrefChanged, PREF_USE_CUBEB);
Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
#endif
delete gAudioPrefsLock;
gAudioPrefsLock = nullptr;
@ -315,258 +229,6 @@ int64_t AudioStream::GetWritten()
return mWritten;
}
NativeAudioStream::NativeAudioStream() :
mVolume(1.0),
mAudioHandle(0),
mPaused(false),
mInError(false)
{
}
NativeAudioStream::~NativeAudioStream()
{
Shutdown();
}
nsresult NativeAudioStream::Init(int32_t aNumChannels, int32_t aRate,
const dom::AudioChannelType aAudioChannelType)
{
mInRate = mOutRate = aRate;
mChannels = aNumChannels;
if (sa_stream_create_pcm(reinterpret_cast<sa_stream_t**>(&mAudioHandle),
NULL,
SA_MODE_WRONLY,
SA_PCM_FORMAT_S16_NE,
aRate,
aNumChannels) != SA_SUCCESS) {
mAudioHandle = nullptr;
mInError = true;
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("NativeAudioStream: sa_stream_create_pcm error"));
return NS_ERROR_FAILURE;
}
int saError = sa_stream_set_stream_type(static_cast<sa_stream_t*>(mAudioHandle),
ConvertChannelToSAType(aAudioChannelType));
if (saError != SA_SUCCESS && saError != SA_ERROR_NOT_SUPPORTED) {
mAudioHandle = nullptr;
mInError = true;
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("NativeAudioStream: sa_stream_set_stream_type error"));
return NS_ERROR_FAILURE;
}
if (sa_stream_open(static_cast<sa_stream_t*>(mAudioHandle)) != SA_SUCCESS) {
sa_stream_destroy(static_cast<sa_stream_t*>(mAudioHandle));
mAudioHandle = nullptr;
mInError = true;
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("NativeAudioStream: sa_stream_open error"));
return NS_ERROR_FAILURE;
}
mInError = false;
mAudioClock.Init();
return NS_OK;
}
void NativeAudioStream::Shutdown()
{
if (!mAudioHandle)
return;
sa_stream_destroy(static_cast<sa_stream_t*>(mAudioHandle));
mAudioHandle = nullptr;
mInError = true;
}
int32_t NativeAudioStream::WriteToBackend(const AudioDataValue* aBuffer, uint32_t aSamples)
{
double scaledVolume = GetVolumeScale() * mVolume;
nsAutoArrayPtr<short> outputBuffer(new short[aSamples]);
ConvertAudioSamplesWithScale(aBuffer, outputBuffer.get(), aSamples, scaledVolume);
if (sa_stream_write(static_cast<sa_stream_t*>(mAudioHandle),
outputBuffer,
aSamples * sizeof(short)) != SA_SUCCESS) {
return -1;
}
mAudioClock.UpdateWritePosition(aSamples / mChannels);
return aSamples;
}
nsresult NativeAudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames)
{
NS_ASSERTION(!mPaused, "Don't write audio when paused, you'll block");
if (mInError)
return NS_ERROR_FAILURE;
uint32_t samples = aFrames * mChannels;
int32_t written = -1;
if (mInRate != mOutRate) {
if (EnsureTimeStretcherInitialized() != NS_OK) {
return NS_ERROR_FAILURE;
}
mTimeStretcher->putSamples(aBuf, aFrames);
uint32_t numFrames = mTimeStretcher->numSamples();
uint32_t arraySize = numFrames * mChannels * sizeof(AudioDataValue);
nsAutoArrayPtr<AudioDataValue> data(new AudioDataValue[arraySize]);
uint32_t framesAvailable = mTimeStretcher->receiveSamples(data, numFrames);
NS_ASSERTION(mTimeStretcher->numSamples() == 0,
"We did not get all the data from the SoundTouch pipeline.");
// It is possible to have nothing to write: the data are in the processing
// pipeline, and will be written to the backend next time.
if (framesAvailable) {
written = WriteToBackend(data, framesAvailable * mChannels);
} else {
written = 0;
}
} else {
written = WriteToBackend(aBuf, samples);
}
mWritten += aFrames;
if (written == -1) {
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("NativeAudioStream: sa_stream_write error"));
mInError = true;
return NS_ERROR_FAILURE;
}
return NS_OK;
}
uint32_t NativeAudioStream::Available()
{
// If the audio backend failed to open, lie and say we'll accept some
// data.
if (mInError)
return FAKE_BUFFER_SIZE;
size_t s = 0;
if (sa_stream_get_write_size(static_cast<sa_stream_t*>(mAudioHandle), &s) != SA_SUCCESS)
return 0;
return s / mChannels / sizeof(short);
}
void NativeAudioStream::SetVolume(double aVolume)
{
NS_ASSERTION(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
#if defined(SA_PER_STREAM_VOLUME)
if (sa_stream_set_volume_abs(static_cast<sa_stream_t*>(mAudioHandle), aVolume) != SA_SUCCESS) {
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("NativeAudioStream: sa_stream_set_volume_abs error"));
mInError = true;
}
#else
mVolume = aVolume;
#endif
}
void NativeAudioStream::Drain()
{
NS_ASSERTION(!mPaused, "Don't drain audio when paused, it won't finish!");
// Write all the frames still in the time stretcher pipeline.
if (mTimeStretcher) {
uint32_t numFrames = mTimeStretcher->numSamples();
uint32_t arraySize = numFrames * mChannels * sizeof(AudioDataValue);
nsAutoArrayPtr<AudioDataValue> data(new AudioDataValue[arraySize]);
uint32_t framesAvailable = mTimeStretcher->receiveSamples(data, numFrames);
int32_t written = 0;
if (framesAvailable) {
written = WriteToBackend(data, framesAvailable * mChannels);
}
if (written == -1) {
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("NativeAudioStream: sa_stream_write error"));
mInError = true;
}
NS_ASSERTION(mTimeStretcher->numSamples() == 0,
"We did not get all the data from the SoundTouch pipeline.");
}
if (mInError)
return;
int r = sa_stream_drain(static_cast<sa_stream_t*>(mAudioHandle));
if (r != SA_SUCCESS && r != SA_ERROR_INVALID) {
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("NativeAudioStream: sa_stream_drain error"));
mInError = true;
}
}
void NativeAudioStream::Start()
{
// Since sydneyaudio is a push API, the playback is started when enough frames
// have been written. Hence, Start() is a noop.
}
void NativeAudioStream::Pause()
{
if (mInError)
return;
mPaused = true;
sa_stream_pause(static_cast<sa_stream_t*>(mAudioHandle));
}
void NativeAudioStream::Resume()
{
if (mInError)
return;
mPaused = false;
sa_stream_resume(static_cast<sa_stream_t*>(mAudioHandle));
}
int64_t NativeAudioStream::GetPosition()
{
return mAudioClock.GetPosition();
}
int64_t NativeAudioStream::GetPositionInFrames()
{
return mAudioClock.GetPositionInFrames();
}
int64_t NativeAudioStream::GetPositionInFramesInternal()
{
if (mInError) {
return -1;
}
sa_position_t positionType = SA_POSITION_WRITE_SOFTWARE;
#if defined(XP_WIN)
positionType = SA_POSITION_WRITE_HARDWARE;
#endif
int64_t position = 0;
if (sa_stream_get_position(static_cast<sa_stream_t*>(mAudioHandle),
positionType, &position) == SA_SUCCESS) {
return position / mChannels / sizeof(short);
}
return -1;
}
bool NativeAudioStream::IsPaused()
{
return mPaused;
}
int32_t NativeAudioStream::GetMinWriteSize()
{
size_t size;
int r = sa_stream_get_min_write(static_cast<sa_stream_t*>(mAudioHandle),
&size);
if (r == SA_ERROR_NOT_SUPPORTED)
return 1;
else if (r != SA_SUCCESS || size > INT32_MAX)
return -1;
return static_cast<int32_t>(size / mChannels / sizeof(short));
}
#if defined(MOZ_CUBEB)
class nsCircularByteBuffer
{
@ -653,7 +315,6 @@ class BufferedAudioStream : public AudioStream
int64_t GetPositionInFrames();
int64_t GetPositionInFramesInternal();
bool IsPaused();
int32_t GetMinWriteSize();
// This method acquires the monitor and forward the call to the base
// class, to prevent a race on |mTimeStretcher|, in
// |AudioStream::EnsureTimeStretcherInitialized|.
@ -739,11 +400,9 @@ private:
AudioStream* AudioStream::AllocateStream()
{
#if defined(MOZ_CUBEB)
if (GetUseCubeb()) {
return new BufferedAudioStream();
}
return new BufferedAudioStream();
#endif
return new NativeAudioStream();
return nullptr;
}
#if defined(MOZ_CUBEB)
@ -878,12 +537,6 @@ BufferedAudioStream::Available()
return BytesToFrames(mBuffer.Available());
}
int32_t
BufferedAudioStream::GetMinWriteSize()
{
return 1;
}
void
BufferedAudioStream::SetVolume(double aVolume)
{

View File

@ -161,10 +161,6 @@ public:
// Returns true when the audio stream is paused.
virtual bool IsPaused() = 0;
// Returns the minimum number of audio frames which must be written before
// you can be sure that something will be played.
virtual int32_t GetMinWriteSize() = 0;
int GetRate() { return mOutRate; }
int GetChannels() { return mChannels; }

View File

@ -23,6 +23,7 @@ EXPORTS = \
AudioNodeStream.h \
AudioSampleFormat.h \
AudioSegment.h \
AudioStream.h \
BufferMediaResource.h \
DecoderTraits.h \
DOMMediaStream.h \
@ -51,6 +52,7 @@ CPPSRCS = \
AudioNodeEngine.cpp \
AudioNodeStream.cpp \
AudioSegment.cpp \
AudioStream.cpp \
DecoderTraits.cpp \
DOMMediaStream.cpp \
FileBlockCache.cpp \
@ -67,15 +69,6 @@ CPPSRCS = \
VideoUtils.cpp \
$(NULL)
ifdef MOZ_SYDNEYAUDIO
EXPORTS += \
AudioStream.h \
$(NULL)
CPPSRCS += \
AudioStream.cpp \
$(NULL)
endif
FORCE_STATIC_LIB = 1
include $(topsrcdir)/config/config.mk

View File

@ -986,7 +986,6 @@ void MediaDecoderStateMachine::AudioLoop()
bool setPlaybackRate;
bool preservesPitch;
bool setPreservesPitch;
int32_t minWriteFrames = -1;
AudioChannelType audioChannelType;
{
@ -1089,9 +1088,6 @@ void MediaDecoderStateMachine::AudioLoop()
NS_WARNING("Setting the pitch preservation failed in AudioLoop.");
}
}
if (minWriteFrames == -1) {
minWriteFrames = mAudioStream->GetMinWriteSize();
}
NS_ASSERTION(mReader->AudioQueue().GetSize() > 0,
"Should have data to play");
// See if there's a gap in the audio. If there is, push silence into the
@ -1149,23 +1145,6 @@ void MediaDecoderStateMachine::AudioLoop()
// before the audio thread terminates.
bool seeking = false;
{
int64_t unplayedFrames = audioDuration % minWriteFrames;
if (minWriteFrames > 1 && unplayedFrames > 0) {
// Sound is written by libsydneyaudio to the hardware in blocks of
// frames of size minWriteFrames. So if the number of frames we've
// written isn't an exact multiple of minWriteFrames, we'll have
// left over audio data which hasn't yet been written to the hardware,
// and so that audio will not start playing. Write silence to ensure
// the last block gets pushed to hardware, so that playback starts.
int64_t framesToWrite = minWriteFrames - unplayedFrames;
if (framesToWrite < UINT32_MAX / channels) {
// Write silence manually rather than using PlaySilence(), so that
// the AudioAPI doesn't get a copy of the audio frames.
ReentrantMonitorAutoExit exit(mDecoder->GetReentrantMonitor());
WriteSilence(mAudioStream, framesToWrite);
}
}
int64_t oldPosition = -1;
int64_t position = GetMediaTime();
while (oldPosition != position &&

View File

@ -10,7 +10,7 @@ Each video element for a media file has two threads:
hardware. This is done in a separate thread to ensure that the
audio hardware gets a constant stream of data without
interruption due to decoding or display. At some point
libsydneyaudio will be refactored to have a callback interface
AudioStream will be refactored to have a callback interface
where it asks for data and an extra thread will no longer be
needed.
@ -70,7 +70,7 @@ to shut down the decode thread in order to conserve resources.
During playback the audio thread will be idle (via a Wait() on the
monitor) if the audio queue is empty. Otherwise it constantly pops
audio data off the queue and plays it with a blocking write to the audio
hardware (via AudioStream and libsydneyaudio).
hardware (via AudioStream).
*/
#if !defined(MediaDecoderStateMachine_h__)

View File

@ -12,9 +12,9 @@
* The decoder implementation is currently limited to Linear PCM encoded
* audio data with one or two channels of 8- or 16-bit samples at sample
* rates from 100 Hz to 96 kHz. The number of channels is limited by what
* the audio backend (sydneyaudio via AudioStream) currently supports. The
* supported sample rate is artificially limited to arbitrarily selected sane
* values. Support for additional channels (and other new features) would
* the audio backend (via AudioStream) currently supports. The supported
* sample rate is artificially limited to arbitrarily selected sane values.
* Support for additional channels (and other new features) would
* require extending WaveDecoder to support parsing the newer
* WAVE_FORMAT_EXTENSIBLE chunk format.
**/

View File

@ -17,7 +17,7 @@
#include "mozilla/Hal.h"
#include "AudioManager.h"
#include "gonk/AudioSystem.h"
#include "android_audio/AudioSystem.h"
#include "nsIObserverService.h"
#include "mozilla/Services.h"
#include "AudioChannelService.h"

View File

@ -59,8 +59,6 @@ CPPSRCS += \
VolumeServiceIOThread.cpp \
VolumeServiceTest.cpp \
$(NULL)
# for our local copy of AudioSystem.h
LOCAL_INCLUDES += -I$(topsrcdir)/media/libsydneyaudio/src
EXPORTS = \
GonkGPSGeolocationProvider.h \
nsVolume.h \

View File

@ -1107,7 +1107,6 @@ vpx/vpx_decoder.h
vpx/vpx_encoder.h
vpx/vp8cx.h
vpx/vp8dx.h
sydneyaudio/sydney_audio.h
vorbis/codec.h
theora/theoradec.h
tremor/ivorbiscodec.h

View File

@ -88,9 +88,7 @@
#include "GStreamerFormatHelper.h"
#endif
#ifdef MOZ_SYDNEYAUDIO
#include "AudioStream.h"
#endif
#ifdef MOZ_WIDGET_GONK
#include "nsVolumeService.h"
@ -246,9 +244,7 @@ nsLayoutStatics::Initialize()
return rv;
}
#ifdef MOZ_SYDNEYAUDIO
AudioStream::InitLibrary();
#endif
nsContentSink::InitializeStatics();
nsHtml5Module::InitializeStatics();
@ -354,9 +350,7 @@ nsLayoutStatics::Shutdown()
GStreamerFormatHelper::Shutdown();
#endif
#ifdef MOZ_SYDNEYAUDIO
AudioStream::ShutdownLibrary();
#endif
#ifdef MOZ_WMF
WMFDecoder::UnloadDLLs();

View File

@ -71,12 +71,6 @@ SHARED_LIBRARY_LIBS += \
endif
endif
ifdef MOZ_SYDNEYAUDIO
SHARED_LIBRARY_LIBS += \
$(DEPTH)/media/libsydneyaudio/src/$(LIB_PREFIX)sydneyaudio.$(LIB_SUFFIX) \
$(NULL)
endif
ifdef MOZ_SPEEX_RESAMPLER
SHARED_LIBRARY_LIBS += \
$(DEPTH)/media/libspeex_resampler/src/$(LIB_PREFIX)speex_resampler.$(LIB_SUFFIX) \

View File

@ -83,19 +83,6 @@ vorbis_synthesis_pcmout
vorbis_synthesis_read
vorbis_synthesis_restart
#endif
#ifdef MOZ_SYDNEYAUDIO
sa_stream_create_pcm
sa_stream_destroy
sa_stream_drain
sa_stream_get_min_write
sa_stream_get_position
sa_stream_get_write_size
sa_stream_open
sa_stream_pause
sa_stream_resume
sa_stream_write
sa_stream_set_stream_type
#endif
#ifdef MOZ_SPEEX_RESAMPLER
speex_resampler_init
speex_resampler_init_frac

View File

@ -1,34 +0,0 @@
Jean-Marc Valin (jmspeex) <jean-marc.valin@usherbrooke.ca>
- Design
Lennart Poettering <lennart@poettering.net>
- Design
Shane Stephens (shans) <shans@annodex.net>
- First Implementation
Chris Double (doublec) <chris.double@double.co.nz>
- ALSA support
- OSS support
Brian Lu <brian.lu@sun.com>
- Sun Audio support
Jeremy D. Lea (reg@openpave.org)
- OSS support
Marcin Lubonski <marcin@it.uts.edu.au>
- Port to windows
Michael Martin (tahn) <myk.martin@gmail.com>
- Port to Max OS X
- OSS support
- ALSA support
Jan Gerber (j^) <j@oil21.org>
- Library creation
Silvia Pfeiffer (ginger) <silvia@annodex.net>
- general maintenance

View File

@ -1,12 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk

View File

@ -1,4 +0,0 @@
This source was originally based on libsydneyaudio from
http://git.xiph.org/?p=libsydneyaudio.git commit 716c3c17. As this project
appears to be dead, substantial local changes have been made to this version.
Refer to the version control logs for details.

View File

@ -1,18 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
EXPORTS_NAMESPACES = sydneyaudio
EXPORTS_sydneyaudio = \
sydney_audio.h \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -1,5 +0,0 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

View File

@ -1,460 +0,0 @@
#ifndef foosydneyhfoo
#define foosydneyhfoo
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* Requirements:
- In sync mode, the device will automatically write data so that an initial read causes writes
of zeros to be issued to that one can do "while (1); {read(); write()}
- All functions are thread-safe and can be called in any thread context. None of the functions is
async-signal safe.
- It is assumed that duplex streams have a single clock (synchronised)
*/
#include <sys/types.h>
#if !defined (WIN32)
#include <sys/param.h>
#include <inttypes.h>
#if defined(__FreeBSD__) || defined(ANDROID)
#include <sys/endian.h>
#endif
#else
#include <stddef.h>
#endif
/* Detect byte order, based on sys/param.h */
#undef SA_LITTLE_ENDIAN
#undef SA_BIG_ENDIAN
#if defined(__BYTE_ORDER)
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define SA_LITTLE_ENDIAN 1
# elif __BYTE_ORDER == __BIG_ENDIAN
# define SA_BIG_ENDIAN 1
# endif
#elif defined(_BYTE_ORDER)
# if _BYTE_ORDER == _LITTLE_ENDIAN
# define SA_LITTLE_ENDIAN 1
# elif _BYTE_ORDER == _BIG_ENDIAN
# define SA_BIG_ENDIAN 1
# endif
#elif defined(WIN32)
# define SA_LITTLE_ENDIAN 1
#elif defined(__APPLE__)
# if defined(__BIG_ENDIAN__)
# define SA_BIG_ENDIAN 1
# else
# define SA_LITTLE_ENDIAN 1
# endif
#elif defined(SOLARIS)
# if defined(_BIG_ENDIAN)
# define SA_BIG_ENDIAN 1
# else
# define SA_LITTLE_ENDIAN 1
# endif
#elif defined(AIX)
# define SA_BIG_ENDIAN 1
#else
# error "Cannot determine byte order!"
#endif
#if defined(WIN32)
#if !defined(int32_t)
typedef __int32 int32_t;
#endif
#if !defined(int64_t)
typedef __int64 int64_t;
#endif
#endif
typedef struct sa_stream sa_stream_t;
#if defined(WIN32) || defined(OS2)
/* (left << 16 | right) (16 bits per channel) */
#define SA_VOLUME_MUTED ((int32_t) (0x00000000))
#else
/** Volume that corresponds to muted in/out */
#define SA_VOLUME_MUTED ((int32_t) (-0x80000000))
#endif
/** Ways to express seek offsets for pread/pwrite */
typedef enum {
SA_SEEK_RELATIVE,
SA_SEEK_ABSOLUTE,
SA_SEEK_RELATIVE_END,
_SA_SEEK_MAX
} sa_seek_t;
/** Supported formats */
typedef enum {
SA_PCM_FORMAT_U8,
SA_PCM_FORMAT_ULAW,
SA_PCM_FORMAT_ALAW,
SA_PCM_FORMAT_S16_LE,
SA_PCM_FORMAT_S16_BE,
SA_PCM_FORMAT_S24_LE,
SA_PCM_FORMAT_S24_BE,
SA_PCM_FORMAT_S32_LE,
SA_PCM_FORMAT_S32_BE,
SA_PCM_FORMAT_FLOAT32_LE,
SA_PCM_FORMAT_FLOAT32_BE,
_SA_PCM_FORMAT_MAX
} sa_pcm_format_t;
/* Native/reverse endianness definitions for PCM */
#ifdef SA_LITTLE_ENDIAN
#define SA_PCM_FORMAT_S16_NE SA_PCM_FORMAT_S16_LE
#define SA_PCM_FORMAT_S24_NE SA_PCM_FORMAT_S24_LE
#define SA_PCM_FORMAT_S32_NE SA_PCM_FORMAT_S32_LE
#define SA_PCM_FORMAT_FLOAT32_NE SA_PCM_FORMAT_FLOAT32_LE
#define SA_PCM_FORMAT_S16_RE SA_PCM_FORMAT_S16_BE
#define SA_PCM_FORMAT_S24_RE SA_PCM_FORMAT_S24_BE
#define SA_PCM_FORMAT_S32_RE SA_PCM_FORMAT_S32_BE
#define SA_PCM_FORMAT_FLOAT32_RE SA_PCM_FORMAT_FLOAT32_BE
#else
#define SA_PCM_FORMAT_S16_NE SA_PCM_FORMAT_S16_BE
#define SA_PCM_FORMAT_S24_NE SA_PCM_FORMAT_S24_BE
#define SA_PCM_FORMAT_S32_NE SA_PCM_FORMAT_S32_BE
#define SA_PCM_FORMAT_FLOAT32_NE SA_PCM_FORMAT_FLOAT32_BE
#define SA_PCM_FORMAT_S16_RE SA_PCM_FORMAT_S16_LE
#define SA_PCM_FORMAT_S24_RE SA_PCM_FORMAT_S24_LE
#define SA_PCM_FORMAT_S32_RE SA_PCM_FORMAT_S32_LE
#define SA_PCM_FORMAT_FLOAT32_RE SA_PCM_FORMAT_FLOAT32_LE
#endif
#define SA_CODEC_MPEG "mpeg"
#define SA_CODEC_AC3 "ac3"
#define SA_CODEC_GSM "gsm"
#define SA_CODEC_VORBIS "vorbis"
#define SA_CODEC_SPEEX "speex"
/** Device opening modes */
typedef enum {
SA_MODE_WRONLY = 1,
SA_MODE_RDONLY = 2,
SA_MODE_RDWR = 3,
_SA_MODE_MAX = 4
} sa_mode_t;
/** Error codes */
typedef enum {
SA_SUCCESS = 0,
SA_ERROR_NOT_SUPPORTED = -1,
SA_ERROR_INVALID = -2,
SA_ERROR_STATE = -3,
SA_ERROR_OOM = -4,
SA_ERROR_NO_DEVICE = -5,
SA_ERROR_NO_DRIVER = -6,
SA_ERROR_NO_CODEC = -7,
SA_ERROR_NO_PCM_FORMAT = -7,
SA_ERROR_SYSTEM = -8,
SA_ERROR_NO_INIT = -9,
SA_ERROR_NO_META = -10,
SA_ERROR_NO_DATA = -11,
SA_ERROR_NO_SPACE = -12,
_SA_ERROR_MAX = -13
} sa_error_t;
/** Possible events for notifications */
typedef enum {
SA_NOTIFY_REQUEST_START,
SA_NOTIFY_REQUEST_STOP,
SA_NOTIFY_CHANGED_READ_VOLUME,
SA_NOTIFY_CHANGED_WRITE_VOLUME,
SA_NOTIFY_CHANGED_DEVICE,
_SA_NOTIFY_MAX
} sa_notify_t;
/** Classes of events */
typedef enum {
SA_EVENT_REQUEST_IO,
SA_EVENT_INIT_THREAD,
SA_EVENT_NOTIFY,
SA_EVENT_ERROR,
_SA_EVENT_MAX
} sa_event_t;
/** List of sample position queries */
typedef enum {
SA_POSITION_WRITE_DELAY,
SA_POSITION_WRITE_HARDWARE,
SA_POSITION_WRITE_SOFTWARE,
SA_POSITION_READ_DELAY,
SA_POSITION_READ_HARDWARE,
SA_POSITION_READ_SOFTWARE,
SA_POSITION_DUPLEX_DELAY,
_SA_POSITION_MAX
} sa_position_t;
/* Channel positions */
typedef enum {
SA_CHANNEL_MONO,
SA_CHANNEL_LEFT,
SA_CHANNEL_RIGHT,
SA_CHANNEL_CENTER,
SA_CHANNEL_FRONT_LEFT,
SA_CHANNEL_FRONT_RIGHT,
SA_CHANNEL_FRONT_CENTER,
SA_CHANNEL_REAR_LEFT,
SA_CHANNEL_REAR_RIGHT,
SA_CHANNEL_REAR_CENTER,
SA_CHANNEL_LFE,
SA_CHANNEL_FRONT_LEFT_OF_CENTER,
SA_CHANNEL_FRONT_RIGHT_OF_CENTER,
SA_CHANNEL_SIDE_LEFT,
SA_CHANNEL_SIDE_RIGHT,
SA_CHANNEL_TOP_CENTER,
SA_CHANNEL_TOP_FRONT_LEFT,
SA_CHANNEL_TOP_FRONT_RIGHT,
SA_CHANNEL_TOP_FRONT_CENTER,
SA_CHANNEL_TOP_REAR_LEFT,
SA_CHANNEL_TOP_REAR_RIGHT,
SA_CHANNEL_TOP_REAR_CENTER,
SA_CHANNEL_AUX0,
SA_CHANNEL_AUX1,
SA_CHANNEL_AUX2,
SA_CHANNEL_AUX3,
SA_CHANNEL_AUX4,
SA_CHANNEL_AUX5,
SA_CHANNEL_AUX6,
SA_CHANNEL_AUX7,
SA_CHANNEL_AUX8,
SA_CHANNEL_AUX9,
SA_CHANNEL_AUX10,
SA_CHANNEL_AUX11,
SA_CHANNEL_AUX12,
SA_CHANNEL_AUX13,
SA_CHANNEL_AUX14,
SA_CHANNEL_AUX15,
SA_CHANNEL_AUX16,
SA_CHANNEL_AUX17,
SA_CHANNEL_AUX18,
SA_CHANNEL_AUX19,
SA_CHANNEL_AUX20,
SA_CHANNEL_AUX21,
SA_CHANNEL_AUX22,
SA_CHANNEL_AUX23,
SA_CHANNEL_AUX24,
SA_CHANNEL_AUX25,
SA_CHANNEL_AUX26,
SA_CHANNEL_AUX27,
SA_CHANNEL_AUX28,
SA_CHANNEL_AUX29,
SA_CHANNEL_AUX30,
SA_CHANNEL_AUX31,
_SA_CHANNEL_MAX
} sa_channel_t;
typedef enum {
SA_STATE_INIT,
SA_STATE_RUNNING,
SA_STATE_STOPPED,
/* put more stuff */
_SA_STATE_MAX
} sa_state_t;
typedef enum {
SA_XRUN_MODE_STOP,
SA_XRUN_MODE_SPIN,
_SA_XRUN_MODE_MAX
} sa_xrun_mode_t;
typedef enum {
SA_ADJUST_UP = 1,
SA_ADJUST_DOWN = -1,
SA_ADJUST_NONE = 0
} sa_adjust_t;
typedef enum {
SA_STREAM_TYPE_VOICE_CALL = 0,
SA_STREAM_TYPE_SYSTEM = 1,
SA_STREAM_TYPE_RING = 2,
SA_STREAM_TYPE_MUSIC = 3,
SA_STREAM_TYPE_ALARM = 4,
SA_STREAM_TYPE_NOTIFICATION = 5,
SA_STREAM_TYPE_BLUETOOTH_SCO = 6,
SA_STREAM_TYPE_ENFORCED_AUDIBLE = 7,
SA_STREAM_TYPE_DTMF = 8,
SA_STREAM_TYPE_TTS = 9,
SA_STREAM_TYPE_FM = 10,
SA_STREAM_TYPE_MAX
} sa_stream_type_t;
/* Some kind of meta information. */
#define SA_META_CLIENT_NAME "sydney.client-name" /* utf-8 */
#define SA_META_PROCESS_ID "sydney.process-id" /* getpid() */
#define SA_META_LANGUAGE "sydney.language" /* de_DE and similar */
/* Some kind of meta information. Not filled in */
#define SA_META_STREAM_NAME "sydney.stream-name" /* utf-8 */
#define SA_META_ICON_NAME "sydney.icon-name" /* file name (no slashes) */
#define SA_META_ICON_PNG "sydney.icon-png" /* PNG blob */
#define SA_META_ROLE "sydney.role" /* one of: "music", "phone", "game", "event" */
#define SA_META_X11_DISPLAY "sydney.x11-display" /* X11 display */
#define SA_META_X11_WINDOW "sydney.x11-window" /* X11 window id */
/** Main callback function */
typedef int (*sa_event_callback_t)(sa_stream_t *s, sa_event_t event);
/** Create an opaque (e.g. AC3) codec stream */
int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec);
/** Normal way to open a PCM device */
int sa_stream_create_pcm(sa_stream_t **s, const char *client_name, sa_mode_t mode, sa_pcm_format_t format, unsigned int rate, unsigned int nchannels);
/** Assign audio stream type.
This function should be called after sa_stream_create_pcm(...) and before
sa_stream_open(...) so the stream type can be assigned into lower layer */
int sa_stream_set_stream_type(sa_stream_t *s, const sa_stream_type_t stream_type);
/** Initialise the device */
int sa_stream_open(sa_stream_t *s);
/** Close/destroy everything */
int sa_stream_destroy(sa_stream_t *s);
/* "Soft" params */
int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size);
int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size);
int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size);
int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size);
/** Set the mapping between channels and the loudspeakers */
int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n);
/** Whether xruns cause the card to reset */
int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode);
/** Set the device to non-interleaved mode */
int sa_stream_set_non_interleaved(sa_stream_t *s, int enable);
/** Require dynamic sample rate */
int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable);
/** Select driver */
int sa_stream_set_driver(sa_stream_t *s, const char *driver);
/** Start callback */
int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback);
/** Start callback */
int sa_stream_stop_thread(sa_stream_t *s);
/** Change the device connected to the stream */
int sa_stream_change_device(sa_stream_t *s, const char *device_name);
/** volume in hundreths of dB*/
int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n);
/** volume in hundreths of dB*/
int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n);
/** Change the sampling rate */
int sa_stream_change_rate(sa_stream_t *s, unsigned int rate);
/** Change some meta data that is attached to the stream */
int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size);
/** Associate opaque user data */
int sa_stream_change_user_data(sa_stream_t *s, const void *value);
/* Hardware-related. This is implementation-specific and hardware specific. */
int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction);
int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction);
int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction);
int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction);
/* Query functions */
int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode);
int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size);
int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format);
int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate);
int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels);
int sa_stream_get_user_data(sa_stream_t *s, void **value);
int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size);
int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size);
int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size);
int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size);
int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n);
int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode);
int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled);
int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled);
int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size);
int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size);
int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n);
int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n);
int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size);
int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction);
int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction);
int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction);
int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction);
/** Get current state of the audio device */
int sa_stream_get_state(sa_stream_t *s, sa_state_t *state);
/** Obtain the error code */
int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error);
/** Obtain the notification code */
int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify);
/** sync/timing */
int sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos);
/* Blocking IO calls */
/** Interleaved capture function */
int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes);
/** Non-interleaved capture function */
int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes);
/** Interleaved playback function */
int sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes);
/** Non-interleaved playback function */
int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes);
/** Interleaved playback function with seek offset */
int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
/** Non-interleaved playback function with seek offset */
int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
/** Query how much can be read without blocking */
int sa_stream_get_read_size(sa_stream_t *s, size_t *size);
/** Query how much can be written without blocking */
int sa_stream_get_write_size(sa_stream_t *s, size_t *size);
/* Control/xrun */
/** Resume playing after a pause */
int sa_stream_resume(sa_stream_t *s);
/** Pause audio playback (do not empty the buffer) */
int sa_stream_pause(sa_stream_t *s);
/** Block until all audio has been played */
int sa_stream_drain(sa_stream_t *s);
/** Returns the minimum number of bytes which must be written before any
audio is played by the hardware. */
int sa_stream_get_min_write(sa_stream_t *s, size_t *size);
/** Return a human readable error */
const char *sa_strerror(int code);
/* Extensions */
int
sa_stream_set_volume_abs(sa_stream_t *s, float vol);
int
sa_stream_get_volume_abs(sa_stream_t *s, float *vol);
#endif

View File

@ -1,9 +0,0 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += ['include', 'src']
MODULE = 'sydneyaudio'

View File

@ -1,80 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
LIBRARY_NAME = sydneyaudio
FORCE_STATIC_LIB= 1
ifeq (WINNT,$(OS_TARGET))
VISIBILITY_FLAGS =
endif
ifneq (,$(filter DragonFly FreeBSD GNU GNU_% NetBSD OpenBSD,$(OS_ARCH)))
CSRCS = \
sydney_audio_oss.c \
$(NULL)
endif
ifeq ($(MOZ_WIDGET_TOOLKIT),gonk)
CPPSRCS = \
sydney_audio_gonk.cpp \
$(NULL)
else ifeq ($(MOZ_WIDGET_TOOLKIT),android)
CSRCS = \
sydney_audio_android.c \
$(NULL)
endif
ifeq ($(OS_ARCH),WINNT)
CSRCS = \
sydney_audio_waveapi.c \
$(NULL)
endif
ifeq ($(OS_ARCH),OS2)
CSRCS = \
sydney_audio_os2.c \
$(NULL)
endif
ifeq ($(OS_ARCH),Darwin)
CSRCS = \
sydney_audio_mac.c \
$(NULL)
OS_LIBS += -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon
endif
ifeq ($(OS_ARCH),SunOS)
CSRCS = \
sydney_audio_sunaudio.c \
$(NULL)
endif
ifeq ($(OS_ARCH),AIX)
CSRCS = \
sydney_audio_aix.c \
$(NULL)
endif
ifdef MOZ_ALSA
CSRCS = \
sydney_audio_alsa.c \
$(NULL)
endif
ifeq ($(OS_ARCH),WINNT)
OS_LIBS += winmm.lib
endif
include $(topsrcdir)/config/rules.mk
LOCAL_INCLUDES += -I$(srcdir)/../include
CFLAGS += $(MOZ_ALSA_CFLAGS)

View File

@ -1,7 +0,0 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
MODULE = 'sydneyaudio'

View File

@ -1,458 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stropts.h>
#include <sys/audio.h>
#include <errno.h>
#include <stdio.h>
#include <pthread.h>
#include "sydney_audio.h"
#ifndef DEFAULT_AUDIO_DEVICE
#define DEFAULT_AUDIO_DEVICE "/dev/paud0/1"
#endif
#define LOOP_WHILE_EINTR(v,func) do { (v) = (func); } \
while ((v) == -1 && errno == EINTR);
typedef struct sa_buf sa_buf;
struct sa_buf {
unsigned int size; /* the size of data */
sa_buf *next;
unsigned char data[]; /* sound data */
};
struct sa_stream
{
int audio_fd;
pthread_mutex_t mutex;
pthread_t thread_id;
int playing;
int64_t bytes_played;
/* audio format info */
/* default setting */
unsigned int default_n_channels;
unsigned int default_rate;
unsigned int default_precision;
/* used settings */
unsigned int rate;
unsigned int n_channels;
unsigned int precision;
/* buffer list */
sa_buf *bl_head;
sa_buf *bl_tail;
};
/* Use a default buffer size with enough room for one second of audio,
* assuming stereo data at 44.1kHz with 32 bits per channel, and impose
* a generous limit on the number of buffers.
*/
#define BUF_SIZE (2 * 44100 * 4)
static void* audio_callback(void* s);
static sa_buf *new_buffer(int size);
/*
* -----------------------------------------------------------------------------
* Startup and shutdown functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_create_pcm(
sa_stream_t ** _s,
const char * client_name,
sa_mode_t mode,
sa_pcm_format_t format,
unsigned int rate,
unsigned int n_channels
)
{
sa_stream_t * s = 0;
/* Make sure we return a NULL stream pointer on failure. */
if (_s == NULL)
return SA_ERROR_INVALID;
*_s = NULL;
if (mode != SA_MODE_WRONLY)
return SA_ERROR_NOT_SUPPORTED;
if (format != SA_PCM_FORMAT_S16_LE)
return SA_ERROR_NOT_SUPPORTED;
/*
* Allocate the instance and required resources.
*/
if ((s = malloc(sizeof(sa_stream_t))) == NULL)
return SA_ERROR_OOM;
if (pthread_mutex_init(&s->mutex, NULL) != 0) {
free(s);
return SA_ERROR_SYSTEM;
}
s->audio_fd = NULL;
s->rate = rate;
s->n_channels = n_channels;
s->precision = 16;
s->playing = 0;
s->bytes_played = 0;
s->bl_tail = s->bl_head = NULL;
*_s = s;
return SA_SUCCESS;
}
int
sa_stream_open(sa_stream_t *s)
{
int fd,err;
char *device_name;
audio_init init;
audio_control control;
audio_change change;
device_name = DEFAULT_AUDIO_DEVICE;
if (s == NULL)
return SA_ERROR_NO_INIT;
if (s->audio_fd != NULL)
return SA_ERROR_INVALID;
fd = open(device_name,O_WRONLY | O_NONBLOCK);
if (fd >= 0)
{
close (fd);
fd = open (device_name, O_WRONLY);
}
if ( fd < 0 )
{
printf("Open %s failed:%s ",device_name,strerror(errno));
return SA_ERROR_NO_DEVICE;
}
init.srate = s->rate;
init.channels = s->n_channels;
init.mode = AUDIO_PCM;
init.flags = AUDIO_BIG_ENDIAN | AUDIO_TWOS_COMPLEMENT;
init.operation = AUDIO_PLAY;
if (ioctl(s->audio_fd, AUDIO_INIT, &init) < 0) {
close(s->audio_fd);
return 0;
}
change.balance = 0x3fff0000;
change.volume = 0x3fff0000;
change.monitor = AUDIO_IGNORE;
change.input = AUDIO_IGNORE;
change.output = AUDIO_OUTPUT_1;
control.ioctl_request = AUDIO_CHANGE;
control.position = 0;
control.request_info = &change;
if (ioctl(s->audio_fd, AUDIO_CONTROL, &control) < 0) {
close(s->audio_fd);
return 0;
}
control.ioctl_request = AUDIO_START;
control.request_info = NULL;
if (ioctl(s->audio_fd, AUDIO_CONTROL, &control) < 0) {
close(s->audio_fd);
return 0;
}
return SA_SUCCESS;
}
int
sa_stream_destroy(sa_stream_t *s)
{
int result;
if (s == NULL)
return SA_SUCCESS;
pthread_mutex_lock(&s->mutex);
result = SA_SUCCESS;
/*
* Shut down the audio output device.
* and release resources
*/
if (s->audio_fd != NULL)
{
if (close(s->audio_fd) < 0)
{
perror("Close aix audio fd failed");
result = SA_ERROR_SYSTEM;
}
}
s->thread_id = 0;
while (s->bl_head != NULL) {
sa_buf * next = s->bl_head->next;
free(s->bl_head);
s->bl_head = next;
}
pthread_mutex_unlock(&s->mutex);
if (pthread_mutex_destroy(&s->mutex) != 0) {
result = SA_ERROR_SYSTEM;
}
free(s);
return result;
}
/*
* -----------------------------------------------------------------------------
* Data read and write functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes)
{
int result;
sa_buf *buf;
if (s == NULL || s->audio_fd == NULL)
return SA_ERROR_NO_INIT;
if (nbytes == 0)
return SA_SUCCESS;
/*
* Append the new data to the end of our buffer list.
*/
result = SA_SUCCESS;
buf = new_buffer(nbytes);
if (buf == NULL)
return SA_ERROR_OOM;
memcpy(buf->data,data, nbytes);
pthread_mutex_lock(&s->mutex);
if (!s->bl_head)
s->bl_head = buf;
else
s->bl_tail->next = buf;
s->bl_tail = buf;
pthread_mutex_unlock(&s->mutex);
/*
* Once we have our first block of audio data, enable the audio callback
* function. This doesn't need to be protected by the mutex, because
* s->playing is not used in the audio callback thread, and it's probably
* better not to be inside the lock when we enable the audio callback.
*/
if (!s->playing) {
s->playing = 1;
if (pthread_create(&s->thread_id, NULL, audio_callback, s) != 0) {
result = SA_ERROR_SYSTEM;
}
}
return result;
}
static void*
audio_callback(void* data)
{
sa_stream_t* s = (sa_stream_t*)data;
sa_buf *buf;
int fd,nbytes_written,bytes,nbytes;
fd = s->audio_fd;
while (1)
{
if (s->thread_id == 0)
break;
pthread_mutex_lock(&s->mutex);
while (s->bl_head)
{
buf = s->bl_head;
s->bl_head = s->bl_head->next;
nbytes_written = 0;
nbytes = buf->size;
while (nbytes_written < nbytes)
{
LOOP_WHILE_EINTR(bytes,(write(fd, (void *)((buf->data)+nbytes_written), nbytes-nbytes_written)));
nbytes_written += bytes;
if (nbytes_written != nbytes)
printf("AixAudio\tWrite completed short - %d vs %d. Write more data\n",nbytes_written,nbytes);
}
free(buf);
s->bytes_played += nbytes;
}
pthread_mutex_unlock(&s->mutex);
}
return NULL;
}
/*
* -----------------------------------------------------------------------------
* General query and support functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_get_write_size(sa_stream_t *s, size_t *size)
{
sa_buf * b;
size_t used = 0;
if (s == NULL )
return SA_ERROR_NO_INIT;
/* there is no interface to get the avaiable writing buffer size
* in aix audio, we return max size here to force sa_stream_write() to
* be called when there is data to be played
*/
*size = BUF_SIZE;
return SA_SUCCESS;
}
/* ---------------------------------------------------------------------------
* General query and support functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos)
{
if (s == NULL) {
return SA_ERROR_NO_INIT;
}
if (position != SA_POSITION_WRITE_SOFTWARE) {
return SA_ERROR_NOT_SUPPORTED;
}
pthread_mutex_lock(&s->mutex);
*pos = s->bytes_played;
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
static sa_buf *
new_buffer(int size)
{
sa_buf * b = malloc(sizeof(sa_buf) + size);
if (b != NULL) {
b->size = size;
b->next = NULL;
}
return b;
}
/*
* -----------------------------------------------------------------------------
* Unsupported functions
* -----------------------------------------------------------------------------
*/
#define UNSUPPORTED(func) func { return SA_ERROR_NOT_SUPPORTED; }
UNSUPPORTED(int sa_stream_pause(sa_stream_t *s))
UNSUPPORTED(int sa_stream_resume(sa_stream_t *s))
UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec))
UNSUPPORTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n))
UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode))
UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver))
UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback))
UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s))
UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name))
UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate))
UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size))
UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value))
UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode))
UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size))
UNSUPPORTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format))
UNSUPPORTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate))
UNSUPPORTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels))
UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value))
UNSUPPORTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode))
UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size))
UNSUPPORTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size))
UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size))
UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state))
UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error))
UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify))
UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_drain(sa_stream_t *s))
UNSUPPORTED(int sa_stream_get_min_write(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_set_stream_type(sa_stream_t *s, const sa_stream_type_t stream_type))
const char *sa_strerror(int code) { return NULL; }

View File

@ -1,549 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <pthread.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include "sydney_audio.h"
#define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin"
/* ALSA implementation based heavily on sydney_audio_mac.c */
pthread_mutex_t sa_alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
struct sa_stream {
snd_pcm_t* output_unit;
int64_t bytes_written;
int64_t last_position;
/* audio format info */
unsigned int rate;
unsigned int n_channels;
/* work around bug 573924 */
int pulseaudio;
int resumed;
};
/*
* -----------------------------------------------------------------------------
* Error Handler to prevent output to stderr
* ----------------------------------------------------------------------------
*/
static void
quiet_error_handler(const char* file,
int line,
const char* function,
int err,
const char* format,
...)
{
}
/*
* -----------------------------------------------------------------------------
* Startup and shutdown functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_create_pcm(
sa_stream_t ** _s,
const char * client_name,
sa_mode_t mode,
sa_pcm_format_t format,
unsigned int rate,
unsigned int n_channels
) {
sa_stream_t * s = 0;
/*
* Make sure we return a NULL stream pointer on failure.
*/
if (_s == NULL) {
return SA_ERROR_INVALID;
}
*_s = NULL;
if (mode != SA_MODE_WRONLY) {
return SA_ERROR_NOT_SUPPORTED;
}
if (format != SA_PCM_FORMAT_S16_NE) {
return SA_ERROR_NOT_SUPPORTED;
}
/*
* Allocate the instance and required resources.
*/
if ((s = malloc(sizeof(sa_stream_t))) == NULL) {
return SA_ERROR_OOM;
}
s->output_unit = NULL;
s->bytes_written = 0;
s->last_position = 0;
s->rate = rate;
s->n_channels = n_channels;
s->pulseaudio = 0;
s->resumed = 0;
*_s = s;
return SA_SUCCESS;
}
int
sa_stream_open(sa_stream_t *s) {
snd_output_t* out;
char* buf;
size_t bufsz;
snd_pcm_hw_params_t* hwparams;
snd_pcm_sw_params_t* swparams;
int dir;
snd_pcm_uframes_t period;
if (s == NULL) {
return SA_ERROR_NO_INIT;
}
if (s->output_unit != NULL) {
return SA_ERROR_INVALID;
}
pthread_mutex_lock(&sa_alsa_mutex);
/* Turn off debug output to stderr */
snd_lib_error_set_handler(quiet_error_handler);
if (snd_pcm_open(&s->output_unit,
"default",
SND_PCM_STREAM_PLAYBACK,
0) < 0) {
pthread_mutex_unlock(&sa_alsa_mutex);
return SA_ERROR_NO_DEVICE;
}
if (snd_pcm_set_params(s->output_unit,
#ifdef SA_LITTLE_ENDIAN
SND_PCM_FORMAT_S16_LE,
#else
SND_PCM_FORMAT_S16_BE,
#endif
SND_PCM_ACCESS_RW_INTERLEAVED,
s->n_channels,
s->rate,
1,
500000) < 0) {
snd_pcm_close(s->output_unit);
s->output_unit = NULL;
pthread_mutex_unlock(&sa_alsa_mutex);
return SA_ERROR_NOT_SUPPORTED;
}
/* ugly alsa-pulse plugin detection */
snd_output_buffer_open(&out);
snd_pcm_dump(s->output_unit, out);
bufsz = snd_output_buffer_string(out, &buf);
s->pulseaudio = bufsz >= strlen(ALSA_PA_PLUGIN) &&
strncmp(buf, ALSA_PA_PLUGIN, strlen(ALSA_PA_PLUGIN)) == 0;
snd_output_close(out);
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_hw_params_current(s->output_unit, hwparams);
snd_pcm_hw_params_get_period_size(hwparams, &period, &dir);
pthread_mutex_unlock(&sa_alsa_mutex);
return SA_SUCCESS;
}
int
sa_stream_get_min_write(sa_stream_t *s, size_t *size) {
int r;
snd_pcm_uframes_t threshold;
snd_pcm_sw_params_t* swparams;
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
snd_pcm_sw_params_alloca(&swparams);
snd_pcm_sw_params_current(s->output_unit, swparams);
r = snd_pcm_sw_params_get_start_threshold(swparams, &threshold);
if (r < 0) {
return SA_ERROR_NO_INIT;
}
*size = snd_pcm_frames_to_bytes(s->output_unit, threshold);
return SA_SUCCESS;
}
int
sa_stream_destroy(sa_stream_t *s) {
int result = SA_SUCCESS;
if (s == NULL) {
return result;
}
/*
* Shut down the audio output device.
*/
if (s->output_unit != NULL) {
pthread_mutex_lock(&sa_alsa_mutex);
if (snd_pcm_close(s->output_unit) < 0) {
result = SA_ERROR_SYSTEM;
}
pthread_mutex_unlock(&sa_alsa_mutex);
}
free(s);
return result;
}
/*
* -----------------------------------------------------------------------------
* Data read and write functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
snd_pcm_sframes_t frames, nframes, avail;
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (nbytes == 0) {
return SA_SUCCESS;
}
nframes = snd_pcm_bytes_to_frames(s->output_unit, nbytes);
while(nframes>0) {
if (s->resumed) {
avail = snd_pcm_avail_update(s->output_unit);
frames = snd_pcm_writei(s->output_unit, data, nframes > avail ? avail : nframes);
avail = snd_pcm_avail_update(s->output_unit);
s->resumed = avail != 0;
} else {
avail = snd_pcm_avail_update(s->output_unit);
avail = avail < 64 ? 64 : avail;
frames = snd_pcm_writei(s->output_unit, data, nframes > avail ? avail : nframes);
}
if (frames < 0) {
int r = snd_pcm_recover(s->output_unit, frames, 1);
if (r < 0) {
return SA_ERROR_SYSTEM;
}
} else {
size_t bytes = snd_pcm_frames_to_bytes(s->output_unit, frames);
nframes -= frames;
data = ((unsigned char *)data) + bytes;
s->bytes_written += bytes;
}
}
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* General query and support functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
snd_pcm_sframes_t avail;
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
do {
avail = snd_pcm_avail_update(s->output_unit);
if (avail < 0) {
int r = snd_pcm_recover(s->output_unit, avail, 1);
if (r < 0) {
return SA_ERROR_SYSTEM;
}
continue;
}
break;
} while (1);
*size = snd_pcm_frames_to_bytes(s->output_unit, avail);
return SA_SUCCESS;
}
int
sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
snd_pcm_sframes_t delay;
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (position != SA_POSITION_WRITE_SOFTWARE) {
return SA_ERROR_NOT_SUPPORTED;
}
if (snd_pcm_state(s->output_unit) != SND_PCM_STATE_RUNNING) {
*pos = s->last_position;
return SA_SUCCESS;
}
if (snd_pcm_delay(s->output_unit, &delay) != 0) {
return SA_ERROR_SYSTEM;
}
/* delay means audio is 'x' frames behind what we've written. We need to
subtract the delay from the data written to return the actual bytes played.
due to buffering, the delay can be larger than the amount we've
written--in this case, report position as zero. */
*pos = s->bytes_written;
if (*pos >= snd_pcm_frames_to_bytes(s->output_unit, delay)) {
*pos -= snd_pcm_frames_to_bytes(s->output_unit, delay);
} else {
*pos = 0;
}
s->last_position = *pos;
return SA_SUCCESS;
}
int
sa_stream_pause(sa_stream_t *s) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (snd_pcm_pause(s->output_unit, 1) != 0)
return SA_ERROR_NOT_SUPPORTED;
return SA_SUCCESS;
}
int
sa_stream_resume(sa_stream_t *s) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (s->pulseaudio) {
s->resumed = 1;
}
if (snd_pcm_pause(s->output_unit, 0) != 0)
return SA_ERROR_NOT_SUPPORTED;
return SA_SUCCESS;
}
int
sa_stream_drain(sa_stream_t *s)
{
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (snd_pcm_state(s->output_unit) == SND_PCM_STATE_PREPARED) {
size_t min_samples = 0;
size_t min_bytes = 0;
void *buf;
if (sa_stream_get_min_write(s, &min_samples) < 0)
return SA_ERROR_SYSTEM;
min_bytes = snd_pcm_frames_to_bytes(s->output_unit, min_samples);
buf = malloc(min_bytes);
if (!buf)
return SA_ERROR_SYSTEM;
memset(buf, 0, min_bytes);
sa_stream_write(s, buf, min_bytes);
free(buf);
}
if (snd_pcm_state(s->output_unit) != SND_PCM_STATE_RUNNING) {
return SA_ERROR_INVALID;
}
snd_pcm_drain(s->output_unit);
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Extension functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_set_volume_abs(sa_stream_t *s, float vol) {
snd_mixer_t* mixer = 0;
snd_mixer_elem_t* elem = 0;
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (snd_mixer_open(&mixer, 0) < 0) {
return SA_ERROR_SYSTEM;
}
if (snd_mixer_attach(mixer, "default") < 0) {
snd_mixer_close(mixer);
return SA_ERROR_SYSTEM;
}
if (snd_mixer_selem_register(mixer, NULL, NULL) < 0) {
snd_mixer_close(mixer);
return SA_ERROR_SYSTEM;
}
if (snd_mixer_load(mixer) < 0) {
snd_mixer_close(mixer);
return SA_ERROR_SYSTEM;
}
#if 0
snd_mixer_elem_t* elem = 0;
for (elem = snd_mixer_first_elem(mixer); elem != NULL; elem = snd_mixer_elem_next(elem)) {
if (snd_mixer_selem_has_playback_volume(elem)) {
printf("Playback %s\n", snd_mixer_selem_get_name(elem));
}
else {
printf("No Playback: %s\n", snd_mixer_selem_get_name(elem));
}
}
#endif
elem = snd_mixer_first_elem(mixer);
if (elem && snd_mixer_selem_has_playback_volume(elem)) {
long min = 0;
long max = 0;
if (snd_mixer_selem_get_playback_volume_range(elem, &min, &max) >= 0) {
snd_mixer_selem_set_playback_volume_all(elem, (max-min)*vol + min);
}
}
snd_mixer_close(mixer);
return SA_SUCCESS;
}
int
sa_stream_get_volume_abs(sa_stream_t *s, float *vol) {
snd_mixer_t* mixer = 0;
snd_mixer_elem_t* elem = 0;
long value = 0;
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (snd_mixer_open(&mixer, 0) < 0) {
return SA_ERROR_SYSTEM;
}
if (snd_mixer_attach(mixer, "default") < 0) {
snd_mixer_close(mixer);
return SA_ERROR_SYSTEM;
}
if (snd_mixer_selem_register(mixer, NULL, NULL) < 0) {
snd_mixer_close(mixer);
return SA_ERROR_SYSTEM;
}
if (snd_mixer_load(mixer) < 0) {
snd_mixer_close(mixer);
return SA_ERROR_SYSTEM;
}
elem = snd_mixer_first_elem(mixer);
if (elem && snd_mixer_selem_get_playback_volume(elem, 0, &value) >= 0) {
long min = 0;
long max = 0;
if (snd_mixer_selem_get_playback_volume_range(elem, &min, &max) >= 0) {
*vol = (float)(value-min)/(float)(max-min);
}
}
snd_mixer_close(mixer);
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Unsupported functions
* -----------------------------------------------------------------------------
*/
#define UNSUPPORTED(func) func { return SA_ERROR_NOT_SUPPORTED; }
UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec))
UNSUPPORTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n))
UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode))
UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver))
UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback))
UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s))
UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name))
UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate))
UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size))
UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value))
UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode))
UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size))
UNSUPPORTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format))
UNSUPPORTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate))
UNSUPPORTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels))
UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value))
UNSUPPORTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode))
UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size))
UNSUPPORTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size))
UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size))
UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state))
UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error))
UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify))
UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_set_stream_type(sa_stream_t *s, const sa_stream_type_t stream_type))
const char *sa_strerror(int code) { return NULL; }

View File

@ -1,553 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <stdlib.h>
#include <time.h>
#include <jni.h>
#include "sydney_audio.h"
#include "android/log.h"
#ifndef ALOG
#if defined(DEBUG) || defined(FORCE_ALOG)
#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko - SYDNEY_AUDIO" , ## args)
#else
#define ALOG(args...)
#endif
#endif
/* Android implementation based on sydney_audio_mac.c */
#define NANOSECONDS_PER_SECOND 1000000000
#define NANOSECONDS_IN_MILLISECOND 1000000
#define MILLISECONDS_PER_SECOND 1000
/* android.media.AudioTrack */
struct AudioTrack {
jclass class;
jmethodID constructor;
jmethodID flush;
jmethodID getminbufsz;
jmethodID pause;
jmethodID play;
jmethodID release;
jmethodID setvol;
jmethodID stop;
jmethodID write;
jmethodID getpos;
};
enum AudioTrackMode {
MODE_STATIC = 0,
MODE_STREAM = 1
};
/* android.media.AudioManager */
enum AudioManagerStream {
STREAM_VOICE_CALL = 0,
STREAM_SYSTEM = 1,
STREAM_RING = 2,
STREAM_MUSIC = 3,
STREAM_ALARM = 4,
STREAM_NOTIFICATION = 5,
STREAM_DTMF = 8
};
/* android.media.AudioFormat */
enum AudioFormatChannel {
CHANNEL_OUT_MONO = 4,
CHANNEL_OUT_STEREO = 12
};
enum AudioFormatEncoding {
ENCODING_PCM_16BIT = 2,
ENCODING_PCM_8BIT = 3
};
struct sa_stream {
jobject output_unit;
jbyteArray output_buf;
unsigned int output_buf_size;
unsigned int rate;
unsigned int channels;
unsigned int isPaused;
int64_t amountWritten;
unsigned int bufferSize;
jclass at_class;
};
static struct AudioTrack at;
extern JNIEnv * GetJNIForThread();
static jclass
init_jni_bindings(JNIEnv *jenv) {
jclass class = (*jenv)->FindClass(jenv, "android/media/AudioTrack");
if (!class) {
return NULL;
}
at.constructor = (*jenv)->GetMethodID(jenv, class, "<init>", "(IIIIII)V");
at.flush = (*jenv)->GetMethodID(jenv, class, "flush", "()V");
at.getminbufsz = (*jenv)->GetStaticMethodID(jenv, class, "getMinBufferSize", "(III)I");
at.pause = (*jenv)->GetMethodID(jenv, class, "pause", "()V");
at.play = (*jenv)->GetMethodID(jenv, class, "play", "()V");
at.release = (*jenv)->GetMethodID(jenv, class, "release", "()V");
at.setvol = (*jenv)->GetMethodID(jenv, class, "setStereoVolume", "(FF)I");
at.stop = (*jenv)->GetMethodID(jenv, class, "stop", "()V");
at.write = (*jenv)->GetMethodID(jenv, class, "write", "([BII)I");
at.getpos = (*jenv)->GetMethodID(jenv, class, "getPlaybackHeadPosition", "()I");
return (*jenv)->NewGlobalRef(jenv, class);
}
/*
* -----------------------------------------------------------------------------
* Startup and shutdown functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_create_pcm(
sa_stream_t ** _s,
const char * client_name,
sa_mode_t mode,
sa_pcm_format_t format,
unsigned int rate,
unsigned int channels
) {
/*
* Make sure we return a NULL stream pointer on failure.
*/
if (_s == NULL) {
return SA_ERROR_INVALID;
}
*_s = NULL;
if (mode != SA_MODE_WRONLY) {
return SA_ERROR_NOT_SUPPORTED;
}
if (format != SA_PCM_FORMAT_S16_NE) {
return SA_ERROR_NOT_SUPPORTED;
}
if (channels != 1 && channels != 2) {
return SA_ERROR_NOT_SUPPORTED;
}
/*
* Allocate the instance and required resources.
*/
sa_stream_t *s;
if ((s = malloc(sizeof(sa_stream_t))) == NULL) {
return SA_ERROR_OOM;
}
s->output_unit = NULL;
s->output_buf = NULL;
s->output_buf_size = 0;
s->rate = rate;
s->channels = channels;
s->isPaused = 0;
s->amountWritten = 0;
s->bufferSize = 0;
*_s = s;
return SA_SUCCESS;
}
int
sa_stream_open(sa_stream_t *s) {
if (s == NULL) {
return SA_ERROR_NO_INIT;
}
if (s->output_unit != NULL) {
return SA_ERROR_INVALID;
}
JNIEnv *jenv = GetJNIForThread();
if (!jenv) {
return SA_ERROR_NO_DEVICE;
}
if ((*jenv)->PushLocalFrame(jenv, 4)) {
return SA_ERROR_OOM;
}
s->at_class = init_jni_bindings(jenv);
if (!s->at_class) {
(*jenv)->PopLocalFrame(jenv, NULL);
return SA_ERROR_NO_DEVICE;
}
int32_t chanConfig = s->channels == 1 ?
CHANNEL_OUT_MONO : CHANNEL_OUT_STEREO;
jint minsz = (*jenv)->CallStaticIntMethod(jenv, s->at_class, at.getminbufsz,
s->rate, chanConfig, ENCODING_PCM_16BIT);
if (minsz <= 0) {
(*jenv)->PopLocalFrame(jenv, NULL);
return SA_ERROR_INVALID;
}
s->bufferSize = s->rate * s->channels * sizeof(int16_t);
if (s->bufferSize < minsz) {
s->bufferSize = minsz;
}
jobject obj =
(*jenv)->NewObject(jenv, s->at_class, at.constructor,
STREAM_MUSIC,
s->rate,
chanConfig,
ENCODING_PCM_16BIT,
s->bufferSize,
MODE_STREAM);
jthrowable exception = (*jenv)->ExceptionOccurred(jenv);
if (exception) {
(*jenv)->ExceptionDescribe(jenv);
(*jenv)->ExceptionClear(jenv);
(*jenv)->PopLocalFrame(jenv, NULL);
return SA_ERROR_INVALID;
}
if (!obj) {
(*jenv)->PopLocalFrame(jenv, NULL);
return SA_ERROR_OOM;
}
s->output_unit = (*jenv)->NewGlobalRef(jenv, obj);
/* arbitrary buffer size. using a preallocated buffer avoids churning
the GC every audio write. */
s->output_buf_size = 4096 * s->channels * sizeof(int16_t);
jbyteArray buf = (*jenv)->NewByteArray(jenv, s->output_buf_size);
if (!buf) {
(*jenv)->ExceptionClear(jenv);
(*jenv)->DeleteGlobalRef(jenv, s->output_unit);
(*jenv)->PopLocalFrame(jenv, NULL);
return SA_ERROR_OOM;
}
s->output_buf = (*jenv)->NewGlobalRef(jenv, buf);
(*jenv)->PopLocalFrame(jenv, NULL);
ALOG("%p - New stream %u %u bsz=%u min=%u obsz=%u", s, s->rate, s->channels,
s->bufferSize, minsz, s->output_buf_size);
return SA_SUCCESS;
}
int
sa_stream_destroy(sa_stream_t *s) {
if (s == NULL) {
return SA_ERROR_NO_INIT;
}
JNIEnv *jenv = GetJNIForThread();
if (!jenv) {
return SA_SUCCESS;
}
if (s->output_buf) {
(*jenv)->DeleteGlobalRef(jenv, s->output_buf);
}
if (s->output_unit) {
(*jenv)->CallVoidMethod(jenv, s->output_unit, at.stop);
jthrowable exception = (*jenv)->ExceptionOccurred(jenv);
if (exception) {
(*jenv)->ExceptionDescribe(jenv);
(*jenv)->ExceptionClear(jenv);
return SA_ERROR_INVALID;
}
(*jenv)->CallVoidMethod(jenv, s->output_unit, at.flush);
(*jenv)->CallVoidMethod(jenv, s->output_unit, at.release);
(*jenv)->DeleteGlobalRef(jenv, s->output_unit);
}
if (s->at_class) {
(*jenv)->DeleteGlobalRef(jenv, s->at_class);
}
free(s);
ALOG("%p - Stream destroyed", s);
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Data read and write functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (nbytes == 0) {
return SA_SUCCESS;
}
JNIEnv *jenv = GetJNIForThread();
if ((*jenv)->PushLocalFrame(jenv, 2)) {
return SA_ERROR_OOM;
}
const jbyte *p = data;
jint r = 0;
size_t wrote = 0;
do {
size_t towrite = nbytes - wrote;
if (towrite > s->output_buf_size) {
towrite = s->output_buf_size;
}
(*jenv)->SetByteArrayRegion(jenv, s->output_buf, 0, towrite, p);
r = (*jenv)->CallIntMethod(jenv,
s->output_unit,
at.write,
s->output_buf,
0,
towrite);
if (r < 0) {
ALOG("%p - Write failed %d", s, r);
break;
}
p += r;
wrote += r;
s->amountWritten += r;
sa_stream_resume(s);
} while (wrote < nbytes);
(*jenv)->PopLocalFrame(jenv, NULL);
return r < 0 ? SA_ERROR_INVALID : SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* General query and support functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
/* No way to query the available buffer space directly, so calculate it from
the amount we've written and the current playback position. */
JNIEnv *jenv = GetJNIForThread();
int32_t framePosition = (*jenv)->CallIntMethod(jenv, s->output_unit, at.getpos);
int64_t bytePos = framePosition * s->channels * sizeof(int16_t);
*size = s->bufferSize - (s->amountWritten - bytePos);
/* Available buffer space can't exceed bufferSize. */
if (*size > s->bufferSize) {
*size = s->bufferSize;
}
ALOG("%p - Write Size aw=%lld bufsz=%u pos=%lld sz=%zu",
s, s->amountWritten, s->bufferSize, bytePos, *size);
return SA_SUCCESS;
}
int
sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
JNIEnv *jenv = GetJNIForThread();
*pos = (*jenv)->CallIntMethod(jenv, s->output_unit, at.getpos);
/* android returns number of frames, so:
position = frames * (PCM_16_BIT == 2 bytes) * channels
*/
*pos *= s->channels * sizeof(int16_t);
return SA_SUCCESS;
}
int
sa_stream_pause(sa_stream_t *s) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
JNIEnv *jenv = GetJNIForThread();
s->isPaused = 1;
/* Update stats */
ALOG("%p - pause", s);
(*jenv)->CallVoidMethod(jenv, s->output_unit, at.pause);
return SA_SUCCESS;
}
int
sa_stream_resume(sa_stream_t *s) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
ALOG("%p - resume", s);
JNIEnv *jenv = GetJNIForThread();
s->isPaused = 0;
(*jenv)->CallVoidMethod(jenv, s->output_unit, at.play);
return SA_SUCCESS;
}
int
sa_stream_drain(sa_stream_t *s)
{
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
/* This is somewhat of a hack (see bug 693131). The AudioTrack documentation
doesn't make it clear how much data must be written before a chunk of data is
played, and experimentation with short streams required filling the entire
allocated buffer. To guarantee that short streams (and the end of longer
streams) are audible, write an entire bufferSize of silence before sleeping.
This guarantees the short write logic in sa_stream_write is hit and the
stream is playing before sleeping. Note that the sleep duration is
calculated from the duration of audio written before writing silence. */
size_t available;
sa_stream_get_write_size(s, &available);
void *p = calloc(1, s->bufferSize);
sa_stream_write(s, p, s->bufferSize);
free(p);
/* There is no way with the Android SDK to determine exactly how
long to playback. So estimate and sleep for that long. */
unsigned long long x = (s->bufferSize - available) * 1000 / s->channels / s->rate /
sizeof(int16_t) * NANOSECONDS_IN_MILLISECOND;
ALOG("%p - Drain - flush %u, sleep for %llu ns", s, available, x);
struct timespec ts = {x / NANOSECONDS_PER_SECOND, x % NANOSECONDS_PER_SECOND};
nanosleep(&ts, NULL);
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Extension functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_set_volume_abs(sa_stream_t *s, float vol) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
JNIEnv *jenv = GetJNIForThread();
(*jenv)->CallIntMethod(jenv, s->output_unit, at.setvol,
(jfloat)vol, (jfloat)vol);
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Unsupported functions
* -----------------------------------------------------------------------------
*/
#define UNSUPPORTED(func) func { return SA_ERROR_NOT_SUPPORTED; }
UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec))
UNSUPPORTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n))
UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode))
UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver))
UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback))
UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s))
UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name))
UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate))
UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size))
UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value))
UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode))
UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size))
UNSUPPORTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format))
UNSUPPORTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate))
UNSUPPORTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels))
UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value))
UNSUPPORTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode))
UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size))
UNSUPPORTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size))
UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size))
UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state))
UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error))
UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify))
UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_volume_abs(sa_stream_t *s, float *vol))
UNSUPPORTED(int sa_stream_get_min_write(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_set_stream_type(sa_stream_t *s, const sa_stream_type_t stream_type))
const char *sa_strerror(int code) { return NULL; }

View File

@ -1,456 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <stdlib.h>
#include <time.h>
extern "C" {
#include "sydney_audio.h"
}
#include "gonk/AudioTrack.h"
#include "android/log.h"
#if defined(DEBUG) || defined(FORCE_ALOG)
#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko - SYDNEY_AUDIO" , ## args)
#else
#define ALOG(args...)
#endif
/* Gonk implementation based on sydney_audio_android.c */
#define NANOSECONDS_PER_SECOND 1000000000
#define NANOSECONDS_IN_MILLISECOND 1000000
#define MILLISECONDS_PER_SECOND 1000
using namespace android;
struct sa_stream {
AudioTrack *output_unit;
unsigned int rate;
unsigned int channels;
unsigned int isPaused;
int64_t amountWritten;
unsigned int bufferSize;
int streamType;
};
/*
* -----------------------------------------------------------------------------
* Startup and shutdown functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_create_pcm(
sa_stream_t ** _s,
const char * client_name,
sa_mode_t mode,
sa_pcm_format_t format,
unsigned int rate,
unsigned int channels
) {
/*
* Make sure we return a NULL stream pointer on failure.
*/
if (_s == NULL) {
return SA_ERROR_INVALID;
}
*_s = NULL;
if (mode != SA_MODE_WRONLY) {
return SA_ERROR_NOT_SUPPORTED;
}
if (format != SA_PCM_FORMAT_S16_NE) {
return SA_ERROR_NOT_SUPPORTED;
}
if (channels != 1 && channels != 2) {
return SA_ERROR_NOT_SUPPORTED;
}
/*
* Allocate the instance and required resources.
*/
sa_stream_t *s;
if ((s = (sa_stream_t *)malloc(sizeof(sa_stream_t))) == NULL) {
return SA_ERROR_OOM;
}
s->output_unit = NULL;
s->rate = rate;
s->channels = channels;
s->isPaused = 0;
s->amountWritten = 0;
s->bufferSize = 0;
s->streamType = AudioSystem::SYSTEM;
*_s = s;
return SA_SUCCESS;
}
/* Assign audio stream type for argument used by AudioTrack class */
int
sa_stream_set_stream_type(sa_stream_t *s, const sa_stream_type_t stream_type)
{
if (s->output_unit != NULL) {
return SA_ERROR_INVALID;
}
switch (stream_type)
{
case SA_STREAM_TYPE_VOICE_CALL:
s->streamType = AudioSystem::VOICE_CALL;
break;
case SA_STREAM_TYPE_SYSTEM:
s->streamType = AudioSystem::SYSTEM;
break;
case SA_STREAM_TYPE_RING:
s->streamType = AudioSystem::RING;
break;
case SA_STREAM_TYPE_MUSIC:
s->streamType = AudioSystem::MUSIC;
break;
case SA_STREAM_TYPE_ALARM:
s->streamType = AudioSystem::ALARM;
break;
case SA_STREAM_TYPE_NOTIFICATION:
s->streamType = AudioSystem::NOTIFICATION;
break;
case SA_STREAM_TYPE_BLUETOOTH_SCO:
s->streamType = AudioSystem::BLUETOOTH_SCO;
break;
case SA_STREAM_TYPE_ENFORCED_AUDIBLE:
s->streamType = AudioSystem::ENFORCED_AUDIBLE;
break;
case SA_STREAM_TYPE_DTMF:
s->streamType = AudioSystem::DTMF;
break;
case SA_STREAM_TYPE_FM:
s->streamType = AudioSystem::FM;
break;
default:
return SA_ERROR_INVALID;
}
return SA_SUCCESS;
}
int
sa_stream_open(sa_stream_t *s) {
if (s == NULL) {
return SA_ERROR_NO_INIT;
}
if (s->output_unit != NULL) {
return SA_ERROR_INVALID;
}
int32_t chanConfig = s->channels == 1 ?
AUDIO_CHANNEL_OUT_MONO : AUDIO_CHANNEL_OUT_STEREO;
int frameCount;
if (AudioTrack::getMinFrameCount(&frameCount, s->streamType,
s->rate) != NO_ERROR) {
return SA_ERROR_INVALID;
}
s->bufferSize = frameCount * s->channels * sizeof(int16_t);
AudioTrack *track =
new AudioTrack(s->streamType,
s->rate,
AudioSystem::PCM_16_BIT,
chanConfig,
s->bufferSize / s->channels / sizeof(int16_t),
0,
NULL, NULL,
0,
0);
if (track->initCheck() != NO_ERROR) {
delete track;
return SA_ERROR_INVALID;
}
s->output_unit = track;
ALOG("%p - New stream rate=%u chan=%u bsz=%u", s, s->rate, s->channels, s->bufferSize);
return SA_SUCCESS;
}
int
sa_stream_destroy(sa_stream_t *s) {
if (s == NULL) {
return SA_ERROR_NO_INIT;
}
static bool firstLeaked = 0;
if (s->output_unit) {
s->output_unit->stop();
s->output_unit->flush();
// XXX: Figure out why we crash if we don't leak the first AudioTrack
if (firstLeaked)
delete s->output_unit;
else
firstLeaked = true;
}
free(s);
ALOG("%p - Stream destroyed", s);
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Data read and write functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (nbytes == 0) {
return SA_SUCCESS;
}
const char *p = (char *)data;
ssize_t r = 0;
size_t wrote = 0;
do {
size_t towrite = nbytes - wrote;
r = s->output_unit->write(p, towrite);
if (r < 0) {
ALOG("%p - Write failed %d", s, r);
break;
}
p += r;
wrote += r;
s->amountWritten += r;
sa_stream_resume(s);
} while (wrote < nbytes);
return r < 0 ? SA_ERROR_INVALID : SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* General query and support functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
/* No way to query the available buffer space directly, so calculate it from
the amount we've written and the current playback position. */
uint32_t framePosition = 0;
s->output_unit->getPosition(&framePosition);
int64_t bytePos = framePosition * s->channels * sizeof(int16_t);
*size = s->bufferSize - (s->amountWritten - bytePos);
/* Available buffer space can't exceed bufferSize. */
if (*size > s->bufferSize) {
*size = s->bufferSize;
}
ALOG("%p - Write Size aw=%lld bufsz=%u pos=%lld sz=%u",
s, s->amountWritten, s->bufferSize, bytePos, *size);
return SA_SUCCESS;
}
int
sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
uint32_t framePosition;
if (s->output_unit->getPosition(&framePosition) != NO_ERROR)
return SA_ERROR_INVALID;
/* android returns number of frames, so:
position = frames * (PCM_16_BIT == 2 bytes) * channels
*/
*pos = framePosition * s->channels * sizeof(int16_t);
return SA_SUCCESS;
}
int
sa_stream_pause(sa_stream_t *s) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
s->isPaused = 1;
ALOG("%p - pause", s);
s->output_unit->pause();
return SA_SUCCESS;
}
int
sa_stream_resume(sa_stream_t *s) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
ALOG("%p - resume", s);
s->isPaused = 0;
s->output_unit->start();
return SA_SUCCESS;
}
int
sa_stream_drain(sa_stream_t *s)
{
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
/* This is somewhat of a hack (see bug 693131). The AudioTrack documentation
doesn't make it clear how much data must be written before a chunk of data is
played, and experimentation with short streams required filling the entire
allocated buffer. To guarantee that short streams (and the end of longer
streams) are audible, write an entire bufferSize of silence before sleeping.
This guarantees the short write logic in sa_stream_write is hit and the
stream is playing before sleeping. Note that the sleep duration is
calculated from the duration of audio written before writing silence. */
size_t available;
sa_stream_get_write_size(s, &available);
void *p = calloc(1, s->bufferSize);
sa_stream_write(s, p, s->bufferSize);
free(p);
/* There is no way with the Android SDK to determine exactly how
long to playback. So estimate and sleep for that long. */
unsigned long long x = (s->bufferSize - available) * 1000 / s->channels / s->rate /
sizeof(int16_t) * NANOSECONDS_IN_MILLISECOND;
ALOG("%p - Drain - flush %u, sleep for %llu ns", s, available, x);
struct timespec ts = {(time_t)(x / NANOSECONDS_PER_SECOND),
(time_t)(x % NANOSECONDS_PER_SECOND)};
nanosleep(&ts, NULL);
s->output_unit->flush();
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Extension functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_set_volume_abs(sa_stream_t *s, float vol) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
s->output_unit->setVolume(vol, vol);
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Unsupported functions
* -----------------------------------------------------------------------------
*/
#define UNSUPPORTED(func) func { return SA_ERROR_NOT_SUPPORTED; }
UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec))
UNSUPPORTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n))
UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode))
UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver))
UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback))
UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s))
UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name))
UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate))
UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size))
UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value))
UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode))
UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size))
UNSUPPORTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format))
UNSUPPORTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate))
UNSUPPORTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels))
UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value))
UNSUPPORTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode))
UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size))
UNSUPPORTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size))
UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size))
UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state))
UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error))
UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify))
UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_volume_abs(sa_stream_t *s, float *vol))
UNSUPPORTED(int sa_stream_get_min_write(sa_stream_t *s, size_t *size))
const char *sa_strerror(int code) { return NULL; }

View File

@ -1,736 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <pthread.h>
#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
#include "sydney_audio.h"
/*
* The Mac's audio interface is based on a "pull" I/O model, which means you
* can't just provide a data buffer and tell the audio device to play; you must
* register a callback and provide data as the device asks for it. To support
* sydney audio's "write-to-play" style interface, we have to buffer up the
* data as it arrives and feed it to the callback as required.
*
* This is handled by a simple linked list of buffers; data is always written
* to the tail and read from the head. Each buffer tracks the start and end
* positions of its contained data. Buffers are allocated when the tail buffer
* fills, and freed when the head buffer empties. There is always at least one
* buffer allocated.
*
* s e s e s e + data read
* +++##### -> ######## -> ####---- # data written
* ^ ^ - empty
* bl_head bl_tail
*/
typedef struct sa_buf sa_buf;
struct sa_buf {
unsigned int size;
unsigned int start;
unsigned int end;
sa_buf * next;
unsigned char data[0];
};
struct sa_stream {
AudioUnit output_unit;
pthread_mutex_t mutex;
bool playing;
int64_t bytes_played;
int64_t bytes_played_last;
/* audio format info */
unsigned int rate;
unsigned int n_channels;
unsigned int bytes_per_ch;
/* buffer list */
sa_buf * bl_head;
sa_buf * bl_tail;
int n_bufs;
size_t buffer_size;
};
/*
* Allow up to a second of audio to be buffered.
*/
#define BUF_SIZE_MS 200
#define BUF_LIMIT 5
#if BUF_LIMIT < 2
#error BUF_LIMIT must be at least 2!
#endif
static OSStatus audio_callback(void *arg, AudioUnitRenderActionFlags *action_flags,
const AudioTimeStamp *time_stamp, UInt32 bus_num, UInt32 n_frames, AudioBufferList *data);
static sa_buf *new_buffer(size_t bufsz);
/*
* -----------------------------------------------------------------------------
* Startup and shutdown functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_create_pcm(
sa_stream_t ** _s,
const char * client_name,
sa_mode_t mode,
sa_pcm_format_t format,
unsigned int rate,
unsigned int n_channels
) {
sa_stream_t * s;
/*
* Make sure we return a NULL stream pointer on failure.
*/
if (_s == NULL) {
return SA_ERROR_INVALID;
}
*_s = NULL;
if (mode != SA_MODE_WRONLY) {
return SA_ERROR_NOT_SUPPORTED;
}
if (format != SA_PCM_FORMAT_S16_NE) {
return SA_ERROR_NOT_SUPPORTED;
}
/*
* Allocate the instance and required resources.
*/
if ((s = malloc(sizeof(sa_stream_t))) == NULL) {
return SA_ERROR_OOM;
}
s->output_unit = NULL;
s->playing = FALSE;
s->bytes_played = 0;
s->bytes_played_last = 0;
s->rate = rate;
s->n_channels = n_channels;
s->bytes_per_ch = 2;
s->buffer_size = s->bytes_per_ch *
((rate * n_channels * BUF_SIZE_MS) / 1000);
/* round buffer_size up to ensure it's divisible by the number of bytes per frame. */
if (s->buffer_size % (s->bytes_per_ch * n_channels) != 0) {
s->buffer_size += (s->bytes_per_ch * n_channels) - (s->buffer_size % (s->bytes_per_ch * n_channels));
}
if ((s->bl_head = new_buffer(s->buffer_size)) == NULL) {
free(s);
return SA_ERROR_OOM;
}
s->bl_tail = s->bl_head;
s->n_bufs = 1;
if (pthread_mutex_init(&s->mutex, NULL) != 0) {
free(s->bl_head);
free(s);
return SA_ERROR_SYSTEM;
}
*_s = s;
return SA_SUCCESS;
}
int
sa_stream_open(sa_stream_t *s) {
ComponentDescription desc;
Component comp;
AURenderCallbackStruct input;
AudioStreamBasicDescription fmt;
if (s == NULL) {
return SA_ERROR_NO_INIT;
}
if (s->output_unit != NULL) {
return SA_ERROR_INVALID;
}
/*
* Open the default audio output unit.
*/
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
comp = FindNextComponent(NULL, &desc);
if (comp == NULL) {
return SA_ERROR_NO_DEVICE;
}
if (OpenAComponent(comp, &s->output_unit) != noErr) {
return SA_ERROR_NO_DEVICE;
}
/*
* Set up the render callback used to feed audio data into the output unit.
*/
input.inputProc = audio_callback;
input.inputProcRefCon = s;
if (AudioUnitSetProperty(s->output_unit, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, &input, sizeof(input)) != 0) {
return SA_ERROR_SYSTEM;
}
/*
* Set up the format description for our audio data. Apple uses the
* following terminology:
*
* sample = a single data value for one channel
* frame = a set of samples that includes one sample for each channel
* packet = the smallest indivisible block of audio data; for uncompressed
* audio (which is what we have), this is one frame
* rate = the number of complete frames per second
*
* Note that this definition of frame differs from, well, pretty much everyone
* else's. See this really long link for more info:
*
* http://developer.apple.com/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/Reference/reference.html#//apple_ref/c/tdef/AudioStreamBasicDescription
*/
fmt.mFormatID = kAudioFormatLinearPCM;
fmt.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
#ifdef __BIG_ENDIAN__
kLinearPCMFormatFlagIsBigEndian |
#endif
kLinearPCMFormatFlagIsPacked;
fmt.mSampleRate = s->rate;
fmt.mChannelsPerFrame = s->n_channels;
fmt.mBitsPerChannel = s->bytes_per_ch * 8;
fmt.mFramesPerPacket = 1; /* uncompressed audio */
fmt.mBytesPerFrame = fmt.mChannelsPerFrame * fmt.mBitsPerChannel / 8;
fmt.mBytesPerPacket = fmt.mBytesPerFrame * fmt.mFramesPerPacket;
/*
* We're feeding data in to the output bus of the audio system, so we set
* the format description on the input scope of the device, using the very
* obvious element value of 0 to indicate the output bus.
*
* http://developer.apple.com/technotes/tn2002/tn2091.html
*/
if (AudioUnitSetProperty(s->output_unit, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &fmt, sizeof(AudioStreamBasicDescription)) != 0) {
return SA_ERROR_NOT_SUPPORTED;
}
if (AudioUnitInitialize(s->output_unit) != 0) {
return SA_ERROR_SYSTEM;
}
return SA_SUCCESS;
}
int
sa_stream_destroy(sa_stream_t *s) {
int result = SA_SUCCESS;
if (s == NULL) {
return SA_SUCCESS;
}
/*
* Shut down the audio output device. Don't hold the mutex when stopping
* the audio device, because it is possible to deadlock with this thread
* holding mutex then waiting on an internal Core Audio lock, and with the
* callback thread holding the Core Audio lock and waiting on the mutex.
* This does not need to be protected by the mutex anyway because
* AudioOutputUnitStop, when called from the non-callback thread, blocks
* until in-flight callbacks complete and the HAL shuts down. See:
* http://lists.apple.com/archives/coreaudio-api/2005/Dec/msg00055.html
*/
if (s->output_unit != NULL) {
if (s->playing && AudioOutputUnitStop(s->output_unit) != 0) {
result = SA_ERROR_SYSTEM;
}
if (AudioUnitUninitialize(s->output_unit) != 0) {
result = SA_ERROR_SYSTEM;
}
if (CloseComponent(s->output_unit) != noErr) {
result = SA_ERROR_SYSTEM;
}
}
/*
* Release resources.
*/
if (pthread_mutex_destroy(&s->mutex) != 0) {
result = SA_ERROR_SYSTEM;
}
while (s->bl_head != NULL) {
sa_buf * next = s->bl_head->next;
free(s->bl_head);
s->bl_head = next;
}
free(s);
return result;
}
/*
* -----------------------------------------------------------------------------
* Data read and write functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
int result = SA_SUCCESS;
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (nbytes == 0) {
return SA_SUCCESS;
}
pthread_mutex_lock(&s->mutex);
/*
* Append the new data to the end of our buffer list.
*/
while (1) {
unsigned int avail = s->bl_tail->size - s->bl_tail->end;
if (nbytes <= avail) {
/*
* The new data will fit into the current tail buffer, so
* just copy it in and we're done.
*/
memcpy(s->bl_tail->data + s->bl_tail->end, data, nbytes);
s->bl_tail->end += nbytes;
break;
} else {
/*
* Copy what we can into the tail and allocate a new buffer
* for the rest.
*/
memcpy(s->bl_tail->data + s->bl_tail->end, data, avail);
s->bl_tail->end += avail;
data = ((unsigned char *)data) + avail;
nbytes -= avail;
/*
* If we still have data left to copy but we've hit the limit of
* allowable buffer allocations, we need to spin for a bit to allow
* the audio callback function to slurp some more data up.
*/
if (nbytes > 0 && s->n_bufs == BUF_LIMIT) {
#ifdef TIMING_TRACE
printf("#"); /* too much audio data */
#endif
if (!s->playing) {
/*
* We haven't even started playing yet! That means the
* BUF_SIZE_MS/BUF_LIMIT values are too low... Not much we can
* do here; spinning won't help because the audio callback
* hasn't been enabled yet. Oh well, error time.
*/
printf("Too much audio data received before audio device enabled!\n");
result = SA_ERROR_SYSTEM;
break;
}
while (s->n_bufs == BUF_LIMIT) {
struct timespec ts = {0, 1000000};
pthread_mutex_unlock(&s->mutex);
nanosleep(&ts, NULL);
pthread_mutex_lock(&s->mutex);
}
}
/*
* Allocate a new tail buffer, and go 'round again to fill it up.
*/
if ((s->bl_tail->next = new_buffer(s->buffer_size)) == NULL) {
result = SA_ERROR_OOM;
break;
}
s->n_bufs++;
s->bl_tail = s->bl_tail->next;
} /* if (nbytes <= avail), else */
} /* while (1) */
pthread_mutex_unlock(&s->mutex);
/*
* Once we have our first block of audio data, enable the audio callback
* function. This doesn't need to be protected by the mutex, because
* s->playing is not used in the audio callback thread, and it's probably
* better not to be inside the lock when we enable the audio callback.
*/
if (!s->playing) {
if (AudioOutputUnitStart(s->output_unit) != 0) {
return SA_ERROR_SYSTEM;
}
s->playing = TRUE;
}
return result;
}
static OSStatus
audio_callback(
void * arg,
AudioUnitRenderActionFlags * action_flags,
const AudioTimeStamp * time_stamp,
UInt32 bus_num,
UInt32 n_frames,
AudioBufferList * data
) {
sa_stream_t * s = arg;
unsigned char * dst;
unsigned int bytes_per_frame;
unsigned int bytes_to_copy;
#ifdef TIMING_TRACE
printf("."); /* audio read 'tick' */
#endif
/*
* We're dealing with interleaved data, so the system should only
* have provided one buffer to be filled.
*/
assert(data->mNumberBuffers == 1);
pthread_mutex_lock(&s->mutex);
dst = data->mBuffers[0].mData;
bytes_per_frame = s->n_channels * s->bytes_per_ch;
bytes_to_copy = n_frames * bytes_per_frame;
s->bytes_played += s->bytes_played_last;
s->bytes_played_last = 0;
/*
* Consume data from the start of the buffer list.
*/
while (1) {
unsigned int avail = s->bl_head->end - s->bl_head->start;
assert(s->bl_head->start <= s->bl_head->end);
if (avail >= bytes_to_copy) {
/*
* We have all we need in the head buffer, so just grab it and go.
*/
memcpy(dst, s->bl_head->data + s->bl_head->start, bytes_to_copy);
s->bl_head->start += bytes_to_copy;
s->bytes_played_last += bytes_to_copy;
break;
} else {
sa_buf * next;
/*
* Copy what we can from the head and move on to the next buffer.
*/
memcpy(dst, s->bl_head->data + s->bl_head->start, avail);
s->bl_head->start += avail;
dst += avail;
bytes_to_copy -= avail;
s->bytes_played_last += avail;
/*
* We want to free the now-empty buffer, but not if it's also the
* current tail. If it is the tail, we don't have enough data to fill
* the destination buffer, so we'll just zero it out and give up.
*/
next = s->bl_head->next;
if (next == NULL) {
#ifdef TIMING_TRACE
printf("!"); /* not enough audio data */
#endif
memset(dst, 0, bytes_to_copy);
break;
}
free(s->bl_head);
s->bl_head = next;
s->n_bufs--;
} /* if (avail >= bytes_to_copy), else */
} /* while (1) */
pthread_mutex_unlock(&s->mutex);
return noErr;
}
/*
* -----------------------------------------------------------------------------
* General query and support functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
unsigned int avail;
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
pthread_mutex_lock(&s->mutex);
/*
* The sum of the free space in the tail buffer plus the size of any new
* buffers represents the write space available before blocking.
*/
avail = s->bl_tail->size - s->bl_tail->end;
avail += (BUF_LIMIT - s->n_bufs) * s->buffer_size;
*size = avail;
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
int
sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (position != SA_POSITION_WRITE_SOFTWARE) {
return SA_ERROR_NOT_SUPPORTED;
}
pthread_mutex_lock(&s->mutex);
*pos = s->bytes_played;
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
int
sa_stream_pause(sa_stream_t *s) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
/*
* Don't hold the mutex when stopping the audio device, because it is
* possible to deadlock with this thread holding mutex then waiting on an
* internal Core Audio lock, and with the callback thread holding the Core
* Audio lock and waiting on the mutex.
*/
if (AudioOutputUnitStop(s->output_unit) != 0) {
return SA_ERROR_SYSTEM;
}
s->playing = FALSE;
return SA_SUCCESS;
}
int
sa_stream_resume(sa_stream_t *s) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
/*
* Don't hold the mutex when starting the audio device, because it is
* possible to deadlock with this thread holding mutex then waiting on an
* internal Core Audio lock, and with the callback thread holding the Core
* Audio lock and waiting on the mutex.
*/
if (AudioOutputUnitStart(s->output_unit) != 0) {
return SA_ERROR_SYSTEM;
}
s->playing = TRUE;
return SA_SUCCESS;
}
static sa_buf *
new_buffer(size_t bufsz) {
sa_buf * b = malloc(sizeof(sa_buf) + bufsz);
if (b != NULL) {
b->size = bufsz;
b->start = 0;
b->end = 0;
b->next = NULL;
}
return b;
}
int
sa_stream_drain(sa_stream_t *s)
{
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (!s->playing) {
return SA_ERROR_INVALID;
}
while (1) {
sa_buf * b;
size_t used = 0;
struct timespec ts = {0, 1000000};
pthread_mutex_lock(&s->mutex);
for (b = s->bl_head; b != NULL; b = b->next) {
used += b->end - b->start;
}
pthread_mutex_unlock(&s->mutex);
if (used == 0) {
break;
}
nanosleep(&ts, NULL);
}
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Extension functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_set_volume_abs(sa_stream_t *s, float vol) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
pthread_mutex_lock(&s->mutex);
AudioUnitSetParameter(s->output_unit, kHALOutputParam_Volume,
kAudioUnitParameterFlag_Output, 0, vol, 0);
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
int
sa_stream_get_volume_abs(sa_stream_t *s, float *vol) {
Float32 local_vol = 0;
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
pthread_mutex_lock(&s->mutex);
AudioUnitGetParameter(s->output_unit, kHALOutputParam_Volume,
kAudioUnitParameterFlag_Output, 0, &local_vol);
*vol = local_vol;
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Unsupported functions
* -----------------------------------------------------------------------------
*/
#define UNSUPPORTED(func) func { return SA_ERROR_NOT_SUPPORTED; }
UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec))
UNSUPPORTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n))
UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode))
UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver))
UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback))
UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s))
UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name))
UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate))
UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size))
UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value))
UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode))
UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size))
UNSUPPORTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format))
UNSUPPORTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate))
UNSUPPORTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels))
UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value))
UNSUPPORTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode))
UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size))
UNSUPPORTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size))
UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size))
UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state))
UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error))
UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify))
UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_min_write(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_set_stream_type(sa_stream_t *s, const sa_stream_type_t stream_type))
const char *sa_strerror(int code) { return NULL; }

View File

@ -1,905 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*****************************************************************************/
/* OVERVIEW
*
* Unlike other DART implementations which pull data into the backend
* as needed, this one relies on the upstream code to provide sufficient
* data in a well regulated stream. If other activities in the system
* interrupt that stream, the sound device may run out of data. While
* it should simply pause until more data is available, on some machines
* a buffer underrun causes the device to stop responding and to ignore
* new data until an MCI_STOP or MCI_PAUSE command is issued.
*
* The solution used here is to track the number of buffers in use and
* to pause the device when the count falls below a threshold. Writing
* a new buffer to the device causes playback to resume automatically.
* To support this scheme, the code uses atomic operations on 2 counters
* to pass buffer counts between its two threads (the app's decode thread
* and DART's event thread). It also has the event thread do as little
* as possible to ensure it's not busy when a buffer-free event occurs.
*
*/
/*****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "sydney_audio.h"
#define INCL_DOS
#define INCL_MCIOS2
#include <os2.h>
#include <os2me.h>
#include <386/builtin.h>
/*****************************************************************************/
/* this will have to be changed to a variable
* if other than 16-bit samples are ever supported */
#define SAOS2_SAMPLE_SIZE 2
/* the number of buffers to allocate; each buffer requires
* 64kb of linear address space in the low-mem private arena;
* actual physical memory used depends on each buffer's size */
#define SAOS2_BUF_CNT 40
/* the minimum number of milliseconds worth of data required before
* a buffer is written to the device - the actual number of ms per
* write will usually be greater; the size of each buffer is based
* on this figure and the stream's rate & number of channels */
#define SAOS2_MS_PER_WRITE 40
/* if the number of buffers in use is less than this value,
* os2_mixer_event() will pause the device to prevent an underrun */
#define SAOS2_UNDERRUN_CNT 2
/* wait 5 seconds for a buffer to become free -
* an indefinite wait invites a hung thread */
#define SAOS2_WAIT 5000
/*****************************************************************************/
/* Debug */
#ifdef DEBUG
#ifndef SAOS2_ERROR
#define SAOS2_ERROR
#endif
#endif
#ifdef SAOS2_ERROR
static int os2_error_msg(int rtn, char * func, char * msg, uint32_t err);
#define os2_error(rtn, func, msg, err) os2_error_msg(rtn, func, msg, err)
#else
#define os2_error(rtn, func, msg, err) rtn
#endif
/*****************************************************************************/
/* OS/2 implementation of sa_stream_t */
struct sa_stream {
/* audio format info */
const char * client_name;
sa_mode_t mode;
sa_pcm_format_t format;
uint32_t rate;
uint32_t nchannels;
uint32_t bps;
/* device info */
uint16_t hwDeviceID;
uint32_t hwMixHandle;
PMIXERPROC hwWriteProc;
/* buffer allocations */
int32_t bufCnt;
size_t bufSize;
size_t bufMin;
PMCI_MIX_BUFFER bufList;
/* buffer usage tracking */
volatile uint32_t freeNew;
int32_t freeCnt;
int32_t freeNdx;
volatile uint32_t usedNew;
int32_t usedCnt;
int32_t usedMin;
/* miscellaneous */
volatile uint32_t playing;
volatile uint32_t writeTime;
volatile uint32_t writeNew;
int64_t writePos;
};
/*****************************************************************************/
/* Private (static) Functions */
static int32_t os2_mixer_event(uint32_t ulStatus, PMCI_MIX_BUFFER pBuffer,
uint32_t ulFlags);
static void os2_stop_device(uint16_t hwDeviceID);
static int os2_pause_device(uint16_t hwDeviceID, uint32_t release);
static int os2_get_free_count(sa_stream_t *s, int32_t count);
/*****************************************************************************/
/* Mozilla-specific Additions */
/* load mdm.dll on demand */
static int os2_load_mdm(void);
/* invoke mciSendCommand() via a static variable */
typedef ULONG _System MCISENDCOMMAND(USHORT, USHORT, ULONG, PVOID, USHORT);
static MCISENDCOMMAND * _mciSendCommand = 0;
/*****************************************************************************/
/* Sydney Audio Functions */
/*****************************************************************************/
/** Normal way to open a PCM device */
int sa_stream_create_pcm(sa_stream_t ** s,
const char * client_name,
sa_mode_t mode,
sa_pcm_format_t format,
unsigned int rate,
unsigned int nchannels)
{
uint32_t status = SA_SUCCESS;
uint32_t size;
uint32_t rc;
sa_stream_t * sTemp = 0;
/* this do{}while(0) "loop" makes it easy to ensure
* resources are freed on exit if there's an error */
do {
/* load mdm.dll if it isn't already loaded */
if (os2_load_mdm() != SA_SUCCESS)
return SA_ERROR_SYSTEM;
if (mode != SA_MODE_WRONLY || format != SA_PCM_FORMAT_S16_LE)
return os2_error(SA_ERROR_NOT_SUPPORTED, "sa_stream_create_pcm",
"invalid mode or format", 0);
if (!s)
return os2_error(SA_ERROR_INVALID, "sa_stream_create_pcm",
"s is null", 0);
*s = 0;
/* the MCI_MIX_BUFFERs must be in low memory or terrible things will
* happen! - since there's extra space, put 'sa_stream' there too */
size = sizeof(sa_stream_t) + sizeof(PMCI_MIX_BUFFER) * SAOS2_BUF_CNT;
rc = DosAllocMem((void**)&sTemp, size,
PAG_COMMIT | PAG_READ | PAG_WRITE);
if (rc) {
status = os2_error(SA_ERROR_OOM, "sa_stream_create_pcm",
"DosAllocMem - rc=", rc);
break;
}
memset(sTemp, 0, size);
sTemp->bufList = (PMCI_MIX_BUFFER)&sTemp[1];
/* fill in the miscellanea */
sTemp->client_name = client_name;
sTemp->mode = mode;
sTemp->format = format;
sTemp->rate = rate;
sTemp->nchannels = nchannels;
sTemp->bps = rate * nchannels * SAOS2_SAMPLE_SIZE;
/* each buffer requires 64k of linear address space;
* the actual physical memory used is much less */
sTemp->bufCnt = SAOS2_BUF_CNT;
/* a buffer must contain at least 'bufmin' bytes before it's written
* to the device - this equates to SAOS2_MS_PER_WRITE worth of data */
sTemp->bufMin = (sTemp->bps * SAOS2_MS_PER_WRITE) / 1000;
/* 'bufSize' is 150% of 'bufmin' rounded up to the nearest page
* boundary, then rounded down to a multiple of the frame size;
* this ensures that all data delivered to sa_stream_write() will
* fit in a single buffer & that all committed memory can be used */
sTemp->bufSize = (((3 * sTemp->bufMin) / 2) + 0xfff) & ~0xfff;
sTemp->bufSize -= sTemp->bufSize % (SAOS2_SAMPLE_SIZE * nchannels);
*s = sTemp;
} while (0);
/* on error, free any allocations */
if (status != SA_SUCCESS && sTemp) {
if (sTemp)
DosFreeMem(sTemp);
}
return status;
}
/*****************************************************************************/
/** Initialise the device */
int sa_stream_open(sa_stream_t *s)
{
int status = SA_SUCCESS;
uint32_t rc;
int32_t ctr;
uint32_t bufCntRequested;
MCI_AMP_OPEN_PARMS AmpOpenParms;
MCI_MIXSETUP_PARMS MixSetupParms;
MCI_BUFFER_PARMS BufferParms;
if (!s)
return os2_error(SA_ERROR_NO_INIT, "sa_stream_open", "s is null", 0);
do {
/* s->bufCnt will be restored after successfully allocating buffers */
bufCntRequested = s->bufCnt;
s->bufCnt = 0;
/* open the Amp-Mixer using the default device in shared mode */
memset(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS));
AmpOpenParms.pszDeviceType = (PSZ)(MCI_DEVTYPE_AUDIO_AMPMIX | 0);
rc = _mciSendCommand(0, MCI_OPEN,
MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE,
(void*)&AmpOpenParms, 0);
if (LOUSHORT(rc)) {
status = os2_error(SA_ERROR_NO_DEVICE, "sa_stream_open",
"MCI_OPEN - rc=", LOUSHORT(rc));
break;
}
/* save the device ID */
s->hwDeviceID = AmpOpenParms.usDeviceID;
/* setup the Amp-Mixer to play wave data */
memset(&MixSetupParms, 0, sizeof(MCI_MIXSETUP_PARMS));
MixSetupParms.ulBitsPerSample = 16;
MixSetupParms.ulFormatTag = MCI_WAVE_FORMAT_PCM;
MixSetupParms.ulFormatMode = MCI_PLAY;
MixSetupParms.ulSamplesPerSec = s->rate;
MixSetupParms.ulChannels = s->nchannels;
MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
MixSetupParms.pmixEvent = (MIXEREVENT*)os2_mixer_event;
rc = _mciSendCommand(s->hwDeviceID, MCI_MIXSETUP,
MCI_WAIT | MCI_MIXSETUP_INIT,
(void*)&MixSetupParms, 0);
if (LOUSHORT(rc)) {
status = os2_error(SA_ERROR_NOT_SUPPORTED, "sa_stream_open",
"MCI_MIXSETUP - rc=", LOUSHORT(rc));
break;
}
/* save hw info we'll need later */
s->hwMixHandle = MixSetupParms.ulMixHandle;
s->hwWriteProc = MixSetupParms.pmixWrite;
/* allocate device buffers from the Amp-Mixer */
BufferParms.ulStructLength = sizeof(MCI_BUFFER_PARMS);
BufferParms.ulNumBuffers = bufCntRequested;
BufferParms.ulBufferSize = s->bufSize;
BufferParms.pBufList = s->bufList;
rc = _mciSendCommand(s->hwDeviceID, MCI_BUFFER,
MCI_WAIT | MCI_ALLOCATE_MEMORY,
(void*)&BufferParms, 0);
if (LOUSHORT(rc)) {
status = os2_error(SA_ERROR_OOM, "sa_stream_open",
"MCI_ALLOCATE_MEMORY - rc=", LOUSHORT(rc));
break;
}
/* MCI_ALLOCATE_MEMORY may have decreased the,
* number of buffers, so update the counts */
s->bufCnt = BufferParms.ulNumBuffers;
s->freeCnt = BufferParms.ulNumBuffers;
/* sa_stream_write() & os2_mixer_event() require these initializations */
s->usedMin = SAOS2_UNDERRUN_CNT;
for (ctr = 0; ctr < s->bufCnt; ctr++) {
s->bufList[ctr].ulStructLength = sizeof(MCI_MIX_BUFFER);
s->bufList[ctr].ulBufferLength = 0;
s->bufList[ctr].ulUserParm = (uint32_t)s;
}
} while (0);
return status;
}
/*****************************************************************************/
/** Close/destroy everything */
int sa_stream_destroy(sa_stream_t *s)
{
int status = SA_SUCCESS;
uint32_t rc;
MCI_GENERIC_PARMS GenericParms = { 0 };
MCI_BUFFER_PARMS BufferParms;
if (!s)
return os2_error(SA_ERROR_NO_INIT, "sa_stream_destroy", "s is null", 0);
/* if the device was opened, close it */
if (s->hwDeviceID) {
/* prevent os2_mixer_event() from reacting to a buffer under-run */
s->bufMin = 0;
s->playing = FALSE;
/* If another instance has already acquired the device the
* MCI commands below will fail, so re-acquire it temporarily.
* MCI_CLOSE will release the device to the previous owner. */
rc = _mciSendCommand(s->hwDeviceID, MCI_ACQUIREDEVICE,
MCI_WAIT,
(void*)&GenericParms, 0);
if (LOUSHORT(rc))
os2_error(0, "sa_stream_destroy",
"MCI_ACQUIREDEVICE - rc=", LOUSHORT(rc));
/* stop the device (which may not actually be playing) */
os2_stop_device(s->hwDeviceID);
/* if hardware buffers were allocated, free them */
if (s->bufCnt) {
BufferParms.hwndCallback = 0;
BufferParms.ulStructLength = sizeof(MCI_BUFFER_PARMS);
BufferParms.ulNumBuffers = s->bufCnt;
BufferParms.ulBufferSize = s->bufSize;
BufferParms.pBufList = s->bufList;
rc = _mciSendCommand(s->hwDeviceID, MCI_BUFFER,
MCI_WAIT | MCI_DEALLOCATE_MEMORY,
(void*)&BufferParms, 0);
if (LOUSHORT(rc))
status = os2_error(SA_ERROR_SYSTEM, "sa_stream_destroy",
"MCI_DEALLOCATE_MEMORY - rc=", LOUSHORT(rc));
}
rc = _mciSendCommand(s->hwDeviceID, MCI_CLOSE,
MCI_WAIT,
(void*)&GenericParms, 0);
if (LOUSHORT(rc))
status = os2_error(SA_ERROR_SYSTEM, "sa_stream_destroy",
"MCI_CLOSE - rc=", LOUSHORT(rc));
}
/* free other resources we allocated */
DosFreeMem(s);
return status;
}
/*****************************************************************************/
/** Interleaved playback function */
int sa_stream_write(sa_stream_t * s, const void * data, size_t nbytes)
{
uint32_t rc;
size_t cnt;
PMCI_MIX_BUFFER pHW;
if (!s)
return os2_error(SA_ERROR_NO_INIT, "sa_stream_write", "s is null", 0);
if (!data)
return os2_error(SA_ERROR_INVALID, "sa_stream_write", "data is null", 0);
/* exit if no data */
if (!nbytes)
return SA_SUCCESS;
/* This should only loop on the last write before sa_stream_drain()
* is called; at other times, 'nbytes' won't exceed 'bufSize'. */
while (nbytes) {
size_t offs;
size_t left;
/* get the count of free buffers, wait until at least one
* is available (in practice, this should never block) */
if (os2_get_free_count(s, 1))
return SA_ERROR_SYSTEM;
/* copy as much as will fit into the buffer */
pHW = &(s->bufList[s->freeNdx]);
offs = pHW->ulBufferLength;
left = s->bufSize - offs;
cnt = (nbytes > left) ? left : nbytes;
memcpy(&((char*)pHW->pBuffer)[offs], (char*)data, cnt);
pHW->ulBufferLength += cnt;
nbytes -= cnt;
data = (char*)data + cnt;
/* don't dispatch the buffer until it has bufMin bytes */
if (pHW->ulBufferLength < s->bufMin)
continue;
/* write the buffer to the device */
rc = s->hwWriteProc(s->hwMixHandle, pHW, 1);
if (LOUSHORT(rc)) {
pHW->ulBufferLength = 0;
return os2_error(SA_ERROR_SYSTEM, "sa_stream_write",
"mixWrite - rc=", LOUSHORT(rc));
}
/* signal the event thread that a new buffer is now in use */
__atomic_increment(&s->usedNew);
s->playing = TRUE;
s->freeCnt--;
s->freeNdx = (s->freeNdx + 1) % s->bufCnt;
}
return SA_SUCCESS;
}
/*****************************************************************************/
/** sync/timing */
int sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos)
{
uint32_t rc;
uint32_t then;
uint32_t now;
if (!s || !pos)
return os2_error(SA_ERROR_NO_INIT, "sa_stream_get_position",
"s or pos is null", 0);
if (position != SA_POSITION_WRITE_SOFTWARE)
return os2_error(SA_ERROR_NOT_SUPPORTED, "sa_stream_get_position",
"unsupported postion type=", position);
/* Return the count of bytes that are known to have been played
* already plus an adjustment for the number that may have been
* played since the last mixer event. Since both 'writePos' and
* 'writeTime' are volatile, the loop ensures both are in sync.
* Note: the MCI command to get stream position isn't usable -
* it returns a time value that resets when the stream is paused. */
do {
then = s->writeTime;
s->writePos += __atomic_xchg(&s->writeNew, 0);
*pos = s->writePos;
/* adjust if device is playing & there's been at least one write */
if (s->playing && s->writePos) {
DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &now, sizeof(now));
*pos += ((now - then) * s->bps) / 1000;
}
} while (then != s->writeTime);
return SA_SUCCESS;
}
/*****************************************************************************/
/** Resume playing after a pause */
int sa_stream_resume(sa_stream_t *s)
{
uint32_t rc;
MCI_GENERIC_PARMS GenericParms = { 0 };
if (!s)
return os2_error(SA_ERROR_NO_INIT, "sa_stream_resume",
"s is null", 0);
rc = _mciSendCommand(s->hwDeviceID, MCI_ACQUIREDEVICE,
MCI_WAIT,
(void*)&GenericParms, 0);
if (LOUSHORT(rc))
return os2_error(SA_ERROR_SYSTEM, "sa_stream_resume",
"MCI_ACQUIREDEVICE - rc=", LOUSHORT(rc));
/* this may produce a spurious error if the device
* was just acquired, so report it but ignore it */
rc = _mciSendCommand(s->hwDeviceID, MCI_RESUME,
MCI_WAIT,
(void*)&GenericParms, 0);
if (LOUSHORT(rc))
os2_error(SA_ERROR_SYSTEM, "sa_stream_resume",
"MCI_RESUME - rc=", LOUSHORT(rc));
/* reset the last write time so get_position() doesn't over-adjust */
DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT,
(void*)&s->writeTime, sizeof(s->writeTime));
s->playing = TRUE;
return SA_SUCCESS;
}
/*****************************************************************************/
/** Pause audio playback (do not empty the buffer) */
int sa_stream_pause(sa_stream_t *s)
{
if (!s)
return os2_error(SA_ERROR_NO_INIT, "sa_stream_pause", "s is null", 0);
/* pause & release device */
s->playing = FALSE;
return os2_pause_device(s->hwDeviceID, TRUE);
}
/*****************************************************************************/
/** Block until all audio has been played */
int sa_stream_drain(sa_stream_t *s)
{
int status = SA_SUCCESS;
char buf[32];
if (!s)
return os2_error(SA_ERROR_NO_INIT, "sa_stream_drain", "s is null", 0);
/* keep os2_mixer_event() from reacting to buffer under-runs */
s->usedMin = 0;
/* perform the smallest possible write to force any
* partially-filled buffer to be written to the device */
memset(buf, 0, sizeof(buf));
s->bufMin = 0;
sa_stream_write(s, buf, s->nchannels * SAOS2_SAMPLE_SIZE);
/* DART won't start playing until 2 buffers have been written,
* so write a dummy 2nd buffer if writePos is still zero */
if (!s->writePos)
s->writePos += __atomic_xchg(&s->writeNew, 0);
if (!s->writePos)
sa_stream_write(s, buf, s->nchannels * SAOS2_SAMPLE_SIZE);
/* wait for all buffers to become free */
if (!status)
status = os2_get_free_count(s, s->bufCnt);
s->playing = FALSE;
/* stop the device so it doesn't misbehave due to an under-run */
os2_stop_device(s->hwDeviceID);
return status;
}
/*****************************************************************************/
/** Query how much can be written without blocking */
int sa_stream_get_write_size(sa_stream_t *s, size_t *size)
{
if (!s)
return os2_error(SA_ERROR_NO_INIT, "sa_stream_get_write_size",
"s is null", 0);
/* return a non-zero value here in case the upstream code ignores
* the return code - if so, sa_stream_write() will fail instead */
if (os2_get_free_count(s, 0)) {
*size = s->bufSize;
return SA_ERROR_SYSTEM;
}
*size = s->freeCnt * s->bufSize;
return SA_SUCCESS;
}
/*****************************************************************************/
/** set absolute volume using a value ranging from 0.0 to 1.0 */
int sa_stream_set_volume_abs(sa_stream_t *s, float vol)
{
uint32_t rc;
MCI_SET_PARMS SetParms;
if (!s)
return os2_error(SA_ERROR_NO_INIT, "sa_stream_set_volume_abs",
"s is null", 0);
/* convert f.p. value to an integer value ranging
* from 0 to 100 and apply to both channels */
SetParms.ulLevel = (vol * 100);
SetParms.ulAudio = MCI_SET_AUDIO_ALL;
rc = _mciSendCommand(s->hwDeviceID, MCI_SET,
MCI_WAIT | MCI_SET_AUDIO | MCI_SET_VOLUME,
(void*)&SetParms, 0);
if (LOUSHORT(rc))
return os2_error(SA_ERROR_SYSTEM, "sa_stream_set_volume_abs",
"MCI_SET_VOLUME - rc=", LOUSHORT(rc));
return SA_SUCCESS;
}
/*****************************************************************************/
/** get absolute volume as a value ranging from 0.0 to 1.0 */
int sa_stream_get_volume_abs(sa_stream_t *s, float *vol)
{
int status = SA_SUCCESS;
uint32_t rc;
MCI_STATUS_PARMS StatusParms;
if (!s || !vol)
return os2_error(SA_ERROR_NO_INIT, "sa_stream_get_volume_abs",
"s or vol is null", 0);
memset(&StatusParms, 0, sizeof(MCI_STATUS_PARMS));
StatusParms.ulItem = MCI_STATUS_VOLUME;
rc = _mciSendCommand(s->hwDeviceID, MCI_STATUS,
MCI_WAIT | MCI_STATUS_ITEM,
(void*)&StatusParms, 0);
if (LOUSHORT(rc)) {
/* if there's an error, return a reasonable value */
StatusParms.ulReturn = (50 | 50 << 16);
status = os2_error(SA_ERROR_SYSTEM, "sa_stream_get_volume_abs",
"MCI_STATUS_VOLUME - rc=", LOUSHORT(rc));
}
/* left channel is the low-order word, right channel is the
* high-order word - convert the average of the channels from
* an integer (range 0 - 100) to a floating point value */
*vol = (LOUSHORT(StatusParms.ulReturn) +
HIUSHORT(StatusParms.ulReturn)) / 200.0;
return status;
}
/*****************************************************************************/
/* Private (static) Functions */
/*****************************************************************************/
/** signal the decode thread that a buffer is available -
** this runs on a separate high-priority thread created by DART */
static int32_t os2_mixer_event(uint32_t ulStatus, PMCI_MIX_BUFFER pBuffer,
uint32_t ulFlags)
{
sa_stream_t * s;
/* check for errors */
if (ulFlags & MIX_STREAM_ERROR)
os2_error(0, "os2_mixer_event", "MIX_STREAM_ERROR - status=", ulStatus);
if (!(ulFlags & MIX_WRITE_COMPLETE))
return os2_error(TRUE, "os2_mixer_event",
"unexpected event - flag=", ulFlags);
if (!pBuffer || !pBuffer->ulUserParm)
return os2_error(TRUE, "os2_mixer_event", "null pointer", 0);
/* Note: this thread doesn't use a mutex to avoid a deadlock with the one
* DART uses to prevent MCI operations while this function is running */
s = (sa_stream_t *)pBuffer->ulUserParm;
/* update the number of buffers that are now in use */
s->usedCnt += __atomic_xchg(&s->usedNew, 0);
s->usedCnt--;
/* if fewer than 2 buffers are in use, enter recovery mode -
* if we wait until they're all free, it's often too late; */
if (s->usedCnt < s->usedMin) {
s->playing = FALSE;
os2_pause_device(s->hwDeviceID, FALSE);
os2_error(0, "os2_mixer_event",
"too few buffers in use - recovering", 0);
}
/* pass the number of newly played bytes to the other thread;
* get the time so the other thread can estimate how many
* additional bytes have been consumed since this event */
__atomic_add(&s->writeNew, pBuffer->ulBufferLength);
pBuffer->ulBufferLength = 0;
DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT,
(void*)&s->writeTime, sizeof(s->writeTime));
/* signal the decode thread that a buffer is available */
__atomic_increment(&s->freeNew);
return TRUE;
}
/*****************************************************************************/
/** stop playback */
static void os2_stop_device(uint16_t hwDeviceID)
{
uint32_t rc;
MCI_GENERIC_PARMS GenericParms = { 0 };
rc = _mciSendCommand(hwDeviceID, MCI_STOP,
MCI_WAIT,
(void*)&GenericParms, 0);
if (LOUSHORT(rc))
os2_error(0, "os2_stop_device", "MCI_STOP - rc=", LOUSHORT(rc));
return;
}
/*****************************************************************************/
/** pause playback and optionally release device */
static int os2_pause_device(uint16_t hwDeviceID, uint32_t release)
{
uint32_t rc;
MCI_GENERIC_PARMS GenericParms = { 0 };
rc = _mciSendCommand(hwDeviceID, MCI_PAUSE,
MCI_WAIT,
(void*)&GenericParms, 0);
if (LOUSHORT(rc))
return os2_error(SA_ERROR_SYSTEM, "os2_pause_device",
"MCI_PAUSE - rc=", LOUSHORT(rc));
if (release)
_mciSendCommand(hwDeviceID, MCI_RELEASEDEVICE,
MCI_WAIT,
(void*)&GenericParms, 0);
return SA_SUCCESS;
}
/*****************************************************************************/
/** update the count of free buffers, returning when 'count' are available */
static int os2_get_free_count(sa_stream_t *s, int32_t count)
{
uint32_t timeout = 0;
while (1) {
uint32_t now;
s->freeCnt += __atomic_xchg(&s->freeNew, 0);
if (s->freeCnt >= count)
break;
/* get the current time in milliseconds */
DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &now, sizeof(now));
if (!timeout)
timeout = now + SAOS2_WAIT;
if (now > timeout)
return os2_error(SA_ERROR_SYSTEM, "os2_get_free_count",
"timed-out waiting for free buffer(s)", 0);
DosSleep(1);
}
return SA_SUCCESS;
}
/*****************************************************************************/
#ifdef SAOS2_ERROR
/** display an error message & return whatever value was passed in */
static int os2_error_msg(int rtn, char * func, char * msg, uint32_t err)
{
if (!err)
fprintf(stderr, "sa_os2 error - %s: %s\n", func, msg);
else
fprintf(stderr, "sa_os2 error - %s: %s %u\n", func, msg, err);
fflush(stderr);
return rtn;
}
#endif
/*****************************************************************************/
/* Mozilla-specific Function */
/*****************************************************************************/
/** load mdm.dll & get the entrypoint for mciSendCommand() */
static int os2_load_mdm(void)
{
uint32_t rc;
HMODULE hmod;
char text[32];
if (_mciSendCommand)
return SA_SUCCESS;
rc = DosLoadModule(text, sizeof(text), "MDM", &hmod);
if (rc)
return os2_error(SA_ERROR_SYSTEM, "os2_load_mdm",
"DosLoadModule - rc=", rc);
/* the ordinal for mciSendCommand is '1' */
rc = DosQueryProcAddr(hmod, 1, 0, (PFN*)&_mciSendCommand);
if (rc) {
_mciSendCommand = 0;
return os2_error(SA_ERROR_SYSTEM, "os2_load_mdm",
"DosQueryProcAddr - rc=", rc);
}
return SA_SUCCESS;
}
/*****************************************************************************/
/* Not Implemented / Not Supported */
/*****************************************************************************/
#define UNSUPPORTED(func) func { return SA_ERROR_NOT_SUPPORTED; }
UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec))
UNSUPPORTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n))
UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode))
UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver))
UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback))
UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s))
UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name))
UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate))
UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size))
UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value))
UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode))
UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size))
UNSUPPORTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format))
UNSUPPORTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate))
UNSUPPORTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels))
UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value))
UNSUPPORTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode))
UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size))
UNSUPPORTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size))
UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size))
UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state))
UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error))
UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify))
UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_min_write(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_set_stream_type(sa_stream_t *s, const sa_stream_type_t stream_type))
const char *sa_strerror(int code) { return NULL; }
/*****************************************************************************/

View File

@ -1,698 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <stdio.h>
#include <stdlib.h>
#if defined(__OpenBSD__) || defined(__NetBSD__)
#include <soundcard.h>
#else
#include <sys/soundcard.h>
#endif
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <assert.h>
#include "sydney_audio.h"
// for versions newer than 3.6.1
#define OSS_VERSION(x, y, z) (x << 16 | y << 8 | z)
// support only versions newer than 3.6.1
#define SUPP_OSS_VERSION OSS_VERSION(3,0,1)
#if (SOUND_VERSION < SUPP_OSS_VERSION)
#error Unsupported OSS Version
#else
typedef struct sa_buf sa_buf;
struct sa_buf {
unsigned int size;
unsigned int start;
unsigned int end;
sa_buf * next;
unsigned char data[0];
};
struct sa_stream {
char* output_unit;
int output_fd;
pthread_t thread_id;
pthread_mutex_t mutex;
char playing;
int64_t bytes_played;
/* audio format info */
unsigned int rate;
unsigned int channels;
int format;
/* buffer list */
sa_buf * bl_head;
sa_buf * bl_tail;
int n_bufs;
};
/*
* Use a default buffer size with enough room for one second of audio,
* assuming stereo data at 44.1kHz with 32 bits per channel, and impose
* a generous limit on the number of buffers.
*/
#define BUF_SIZE (2 * 44100 * 4)
#define BUF_LIMIT 5
#if BUF_LIMIT < 2
#error BUF_LIMIT must be at least 2!
#endif
static void audio_callback(void* s);
static sa_buf *new_buffer(void);
/** Private functions - implementation specific */
/*!
* \brief private function mapping Sudney Audio format to OSS formats
* \param format - Sydney Audio API specific format
* \param - filled by the function with a value for corresponding OSS format
* \return - Sydney API error value as in ::sa_pcm_format_t
* */
static int oss_audio_format(sa_pcm_format_t sa_format, int *fmt) {
*fmt = -1;
switch (sa_format) {
case SA_PCM_FORMAT_U8:
*fmt = AFMT_U8;
break;
case SA_PCM_FORMAT_ULAW:
*fmt = AFMT_MU_LAW;
break;
case SA_PCM_FORMAT_ALAW:
*fmt = AFMT_A_LAW;
break;
/* 16-bit little endian (LE) format */
case SA_PCM_FORMAT_S16_LE:
*fmt = AFMT_S16_LE;
break;
/* 16-bit big endian (BE) format */
case SA_PCM_FORMAT_S16_BE:
*fmt = AFMT_S16_BE;
break;
#if SOUND_VERSION >= OSS_VERSION(4,0,0)
/* 24-bit formats (LSB aligned in 32 bit word) */
case SA_PCM_FORMAT_S24_LE:
*fmt = AFMT_S24_LE;
break;
/* 24-bit formats (LSB aligned in 32 bit word) */
case SA_PCM_FORMAT_S24_BE:
*fmt = AFMT_S24_BE;
break;
/* 32-bit format little endian */
case SA_PCM_FORMAT_S32_LE:
*fmt = AFMT_S32_LE;
break;
/* 32-bit format big endian */
case SA_PCM_FORMAT_S32_BE:
*fmt = AFMT_S32_BE;
break;
#endif
default:
return SA_ERROR_NOT_SUPPORTED;
break;
}
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Startup and shutdown functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_create_pcm(
sa_stream_t ** _s,
const char * client_name,
sa_mode_t mode,
sa_pcm_format_t format,
unsigned int rate,
unsigned int channels
) {
sa_stream_t * s = 0;
int fmt = 0;
/*
* Make sure we return a NULL stream pointer on failure.
*/
if (_s == NULL) {
return SA_ERROR_INVALID;
}
*_s = NULL;
if (mode != SA_MODE_WRONLY) {
return SA_ERROR_NOT_SUPPORTED;
}
if (oss_audio_format(format, &fmt) != SA_SUCCESS) {
return SA_ERROR_NOT_SUPPORTED;
}
/*
* Allocate the instance and required resources.
*/
if ((s = malloc(sizeof(sa_stream_t))) == NULL) {
return SA_ERROR_OOM;
}
if ((s->bl_head = new_buffer()) == NULL) {
free(s);
return SA_ERROR_OOM;
}
if (pthread_mutex_init(&s->mutex, NULL) != 0) {
free(s->bl_head);
free(s);
return SA_ERROR_SYSTEM;
}
s->output_unit = "/dev/dsp";
s->output_fd = -1;
s->thread_id = 0;
s->playing = 0;
s->bytes_played = 0;
s->rate = rate;
s->channels = channels;
s->format = fmt;
s->bl_tail = s->bl_head;
s->n_bufs = 1;
*_s = s;
return SA_SUCCESS;
}
int
sa_stream_open(sa_stream_t *s) {
if (s == NULL) {
return SA_ERROR_NO_INIT;
}
if (s->output_unit == NULL || s->output_fd != -1) {
return SA_ERROR_INVALID;
}
// open the default OSS device
if ((s->output_fd = open(s->output_unit, O_WRONLY, 0)) == -1) {
return SA_ERROR_NO_DEVICE;
}
// set the playback rate
if (ioctl(s->output_fd, SNDCTL_DSP_SPEED, &(s->rate)) < 0) {
close(s->output_fd);
s->output_fd = -1;
return SA_ERROR_NOT_SUPPORTED;
}
// set the channel numbers
if (ioctl(s->output_fd, SNDCTL_DSP_CHANNELS, &(s->channels)) < 0) {
close(s->output_fd);
s->output_fd = -1;
return SA_ERROR_NOT_SUPPORTED;
}
if (ioctl(s->output_fd, SNDCTL_DSP_SETFMT, &(s->format)) < 0 ) {
close(s->output_fd);
s->output_fd = -1;
return SA_ERROR_NOT_SUPPORTED;
}
return SA_SUCCESS;
}
int
sa_stream_destroy(sa_stream_t *s) {
int result = SA_SUCCESS;
pthread_t thread_id;
if (s == NULL) {
return SA_SUCCESS;
}
pthread_mutex_lock(&s->mutex);
thread_id = s->thread_id;
/*
* This causes the thread sending data to OSS to stop
*/
s->thread_id = 0;
/*
* Shut down the audio output device.
*/
if (s->output_fd != -1) {
if (s->playing && close(s->output_fd) < 0) {
result = SA_ERROR_SYSTEM;
}
}
pthread_mutex_unlock(&s->mutex);
pthread_join(thread_id, NULL);
/*
* Release resources.
*/
if (pthread_mutex_destroy(&s->mutex) != 0) {
result = SA_ERROR_SYSTEM;
}
while (s->bl_head != NULL) {
sa_buf * next = s->bl_head->next;
free(s->bl_head);
s->bl_head = next;
}
free(s);
return result;
}
/*
* -----------------------------------------------------------------------------
* Data read and write functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
int result = SA_SUCCESS;
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (nbytes == 0) {
return SA_SUCCESS;
}
pthread_mutex_lock(&s->mutex);
/*
* Append the new data to the end of our buffer list.
*/
while (1) {
unsigned int avail = s->bl_tail->size - s->bl_tail->end;
if (nbytes <= avail) {
/*
* The new data will fit into the current tail buffer, so
* just copy it in and we're done.
*/
memcpy(s->bl_tail->data + s->bl_tail->end, data, nbytes);
s->bl_tail->end += nbytes;
break;
} else {
/*
* Copy what we can into the tail and allocate a new buffer
* for the rest.
*/
memcpy(s->bl_tail->data + s->bl_tail->end, data, avail);
s->bl_tail->end += avail;
data = ((unsigned char *)data) + avail;
nbytes -= avail;
/*
* If we still have data left to copy but we've hit the limit of
* allowable buffer allocations, we need to spin for a bit to allow
* the audio callback function to slurp some more data up.
*/
if (nbytes > 0 && s->n_bufs == BUF_LIMIT) {
#ifdef TIMING_TRACE
printf("#"); /* too much audio data */
#endif
if (!s->playing) {
/*
* We haven't even started playing yet! That means the
* BUF_SIZE/BUF_LIMIT values are too low... Not much we can
* do here; spinning won't help because the audio callback
* hasn't been enabled yet. Oh well, error time.
*/
printf("Too much audio data received before audio device enabled!\n");
result = SA_ERROR_SYSTEM;
break;
}
while (s->n_bufs == BUF_LIMIT) {
struct timespec ts = {0, 1000000};
pthread_mutex_unlock(&s->mutex);
nanosleep(&ts, NULL);
pthread_mutex_lock(&s->mutex);
}
}
/*
* Allocate a new tail buffer, and go 'round again to fill it up.
*/
if ((s->bl_tail->next = new_buffer()) == NULL) {
result = SA_ERROR_OOM;
break;
}
s->n_bufs++;
s->bl_tail = s->bl_tail->next;
} /* if (nbytes <= avail), else */
} /* while (1) */
pthread_mutex_unlock(&s->mutex);
/*
* Once we have our first block of audio data, enable the audio callback
* function. This doesn't need to be protected by the mutex, because
* s->playing is not used in the audio callback thread, and it's probably
* better not to be inside the lock when we enable the audio callback.
*/
if (!s->playing) {
s->playing = 1;
if (pthread_create(&s->thread_id, NULL, (void *)audio_callback, s) != 0) {
result = SA_ERROR_SYSTEM;
}
}
return result;
}
static void audio_callback(void* data)
{
sa_stream_t* s = (sa_stream_t*)data;
audio_buf_info info;
char* buffer = 0;
unsigned int avail;
int frames;
#ifdef TIMING_TRACE
printf("."); /* audio read 'tick' */
#endif
ioctl(s->output_fd, SNDCTL_DSP_GETOSPACE, &info);
buffer = malloc(info.bytes);
while(1) {
char* dst = buffer;
unsigned int bytes_to_copy = info.bytes;
int bytes = info.bytes;
pthread_mutex_lock(&s->mutex);
if (!s->thread_id)
break;
/*
* Consume data from the start of the buffer list.
*/
while (1) {
assert(s->bl_head->start <= s->bl_head->end);
avail = s->bl_head->end - s->bl_head->start;
if (avail >= bytes_to_copy) {
/*
* We have all we need in the head buffer, so just grab it and go.
*/
memcpy(dst, s->bl_head->data + s->bl_head->start, bytes_to_copy);
s->bl_head->start += bytes_to_copy;
s->bytes_played += bytes_to_copy;
break;
} else {
sa_buf* next = 0;
/*
* Copy what we can from the head and move on to the next buffer.
*/
memcpy(dst, s->bl_head->data + s->bl_head->start, avail);
s->bl_head->start += avail;
dst += avail;
bytes_to_copy -= avail;
s->bytes_played += avail;
/*
* We want to free the now-empty buffer, but not if it's also the
* current tail. If it is the tail, we don't have enough data to fill
* the destination buffer, so we write less and give up.
*/
next = s->bl_head->next;
if (next == NULL) {
#ifdef TIMING_TRACE
printf("!"); /* not enough audio data */
#endif
bytes = bytes-bytes_to_copy;
break;
}
free(s->bl_head);
s->bl_head = next;
s->n_bufs--;
} /* if (avail >= bytes_to_copy), else */
} /* while (1) */
pthread_mutex_unlock(&s->mutex);
if(bytes > 0) {
frames = write(s->output_fd, buffer, bytes);
if (frames < 0) {
printf("error writing to sound device\n");
}
if (frames >= 0 && frames != bytes) {
printf("short write (expected %d, wrote %d)\n", (int)bytes, (int)frames);
}
}
}
free(buffer);
}
/*
* -----------------------------------------------------------------------------
* General query and support functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
sa_buf * b;
size_t used = 0;
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
pthread_mutex_lock(&s->mutex);
/*
* Sum up the used portions of our buffers and subtract that from
* the pre-defined max allowed allocation.
*/
for (b = s->bl_head; b != NULL; b = b->next) {
used += b->end - b->start;
}
*size = BUF_SIZE * BUF_LIMIT - used;
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
int
sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
int err;
count_info ptr;
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
if (position != SA_POSITION_WRITE_SOFTWARE) {
return SA_ERROR_NOT_SUPPORTED;
}
if ((err = ioctl(s->output_fd,
SNDCTL_DSP_GETOPTR,
&ptr)) <0) {
fprintf(stderr, "Error reading playback position\n");
return SA_ERROR_OOM;
}
pthread_mutex_lock(&s->mutex);
*pos = (int64_t)ptr.bytes;
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
int
sa_stream_pause(sa_stream_t *s) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
pthread_mutex_lock(&s->mutex);
#if 0 /* TODO */
AudioOutputUnitStop(s->output_unit);
#endif
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
int
sa_stream_resume(sa_stream_t *s) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
pthread_mutex_lock(&s->mutex);
/*
* The audio device resets its mSampleTime counter after pausing,
* so we need to clear our tracking value to keep that in sync.
*/
s->bytes_played = 0;
#if 0 /* TODO */
AudioOutputUnitStart(s->output_unit);
#endif
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
static sa_buf *
new_buffer(void) {
sa_buf * b = malloc(sizeof(sa_buf) + BUF_SIZE);
if (b != NULL) {
b->size = BUF_SIZE;
b->start = 0;
b->end = 0;
b->next = NULL;
}
return b;
}
/*
* -----------------------------------------------------------------------------
* Extension functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_set_volume_abs(sa_stream_t *s, float vol) {
if (s == NULL || s->output_fd == -1) {
return SA_ERROR_NO_INIT;
}
#if SOUND_VERSION >= OSS_VERSION(4,0,0)
int mvol = ((int)(100*vol)) | ((int)(100*vol) << 8);
if (ioctl(s->output_fd, SNDCTL_DSP_SETPLAYVOL, &mvol) < 0){
return SA_ERROR_SYSTEM;
}
return SA_SUCCESS;
#else
return SA_ERROR_NOT_SUPPORTED;
#endif
}
int
sa_stream_get_volume_abs(sa_stream_t *s, float *vol) {
if (vol == NULL) {
return SA_ERROR_INVALID;
}
*vol = 0.0f;
if (s == NULL || s->output_fd == -1) {
return SA_ERROR_NO_INIT;
}
#if SOUND_VERSION >= OSS_VERSION(4,0,0)
int mvol;
if (ioctl(s->output_fd, SNDCTL_DSP_SETPLAYVOL, &mvol) < 0){
return SA_ERROR_SYSTEM;
}
*vol = ((mvol & 0xFF) + (mvol >> 8)) / 200.0f;
return SA_SUCCESS;
#else
return SA_ERROR_NOT_SUPPORTED;
#endif
}
/*
* -----------------------------------------------------------------------------
* Unsupported functions
* -----------------------------------------------------------------------------
*/
#define UNSUPPORTED(func) func { return SA_ERROR_NOT_SUPPORTED; }
UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec))
UNSUPPORTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n))
UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode))
UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver))
UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback))
UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s))
UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name))
UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate))
UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size))
UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value))
UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode))
UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size))
UNSUPPORTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format))
UNSUPPORTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate))
UNSUPPORTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels))
UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value))
UNSUPPORTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode))
UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size))
UNSUPPORTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size))
UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size))
UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state))
UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error))
UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify))
UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_drain(sa_stream_t *s))
UNSUPPORTED(int sa_stream_get_min_write(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_set_stream_type(sa_stream_t *s, const sa_stream_type_t stream_type))
const char *sa_strerror(int code) { return NULL; }
#endif

View File

@ -1,686 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <pulse/pulseaudio.h>
#include "sydney_audio.h"
/* Pulseaudio implementation based heavily on sydney_audio_alsa.c */
/*
* The audio interface is based on a "pull" I/O model, which means you
* can't just provide a data buffer and tell the audio device to play; you must
* register a callback and provide data as the device asks for it. To support
* sydney audio's "write-to-play" style interface, we have to buffer up the
* data as it arrives and feed it to the callback as required.
*
* This is handled by a simple linked list of buffers; data is always written
* to the tail and read from the head. Each buffer tracks the start and end
* positions of its contained data. Buffers are allocated when the tail buffer
* fills, and freed when the head buffer empties. There is always at least one
* buffer allocated.
*
* s e s e s e + data read
* +++##### -> ######## -> ####---- # data written
* ^ ^ - empty
* bl_head bl_tail
*/
typedef struct sa_buf sa_buf;
struct sa_buf {
unsigned int size;
unsigned int start;
unsigned int end;
sa_buf * next;
unsigned char data[0];
};
struct sa_stream {
pa_context* context;
pa_stream* stream;
pa_sample_spec sample_spec;
pa_threaded_mainloop* m;
pthread_t thread_id;
pthread_mutex_t mutex;
char playing;
int64_t bytes_written;
char client_name[255];
/* buffer list */
sa_buf * bl_head;
sa_buf * bl_tail;
int n_bufs;
};
/*
* Use a default buffer size with enough room for one second of audio,
* assuming stereo data at 44.1kHz with 32 bits per channel, and impose
* a generous limit on the number of buffers.
*/
#define BUF_SIZE (2 * 44100 * 4)
#define BUF_LIMIT 5
#if BUF_LIMIT < 2
#error BUF_LIMIT must be at least 2!
#endif
static void audio_callback(void* data);
static void stream_write_callback(pa_stream *stream, size_t length, void *userdata);
static void stream_latency_update_callback(pa_stream *stream, void *userdata);
static void context_state_callback(pa_context *c, void *userdata);
static sa_buf *new_buffer(void);
/*
* -----------------------------------------------------------------------------
* Pulseaudio callback functions
* -----------------------------------------------------------------------------
*/
static void context_state_callback(pa_context *c, void *userdata) {
sa_stream_t* s = (sa_stream_t*)userdata;
switch (pa_context_get_state(c)) {
case PA_CONTEXT_READY:
case PA_CONTEXT_TERMINATED:
case PA_CONTEXT_FAILED:
pa_threaded_mainloop_signal(s->m, 0);
break;
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
break;
}
}
static void stream_state_callback(pa_stream *stream, void *userdata) {
sa_stream_t* s = (sa_stream_t*)userdata;
switch (pa_stream_get_state(stream)) {
case PA_STREAM_READY:
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
pa_threaded_mainloop_signal(s->m, 0);
break;
case PA_STREAM_UNCONNECTED:
case PA_STREAM_CREATING:
break;
}
}
static void stream_write_callback(pa_stream *stream, size_t length, void *userdata)
{
sa_stream_t* s = (sa_stream_t*)userdata;
pa_threaded_mainloop_signal(s->m, 0);
}
static void stream_latency_update_callback(pa_stream *stream, void *userdata)
{
sa_stream_t* s = (sa_stream_t*)userdata;
pa_threaded_mainloop_signal(s->m, 0);
}
/*
* -----------------------------------------------------------------------------
* Startup and shutdown functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_create_pcm(
sa_stream_t ** _s,
const char * client_name,
sa_mode_t mode,
sa_pcm_format_t format,
unsigned int rate,
unsigned int n_channels
) {
sa_stream_t * s = 0;
char *server = NULL;
/*
* Make sure we return a NULL stream pointer on failure.
*/
if (_s == NULL) {
return SA_ERROR_INVALID;
}
*_s = NULL;
if (mode != SA_MODE_WRONLY) {
return SA_ERROR_NOT_SUPPORTED;
}
if (format != SA_PCM_FORMAT_S16_LE) {
return SA_ERROR_NOT_SUPPORTED;
}
/*
* Allocate the instance and required resources.
*/
if ((s = malloc(sizeof(sa_stream_t))) == NULL) {
return SA_ERROR_OOM;
}
if ((s->bl_head = new_buffer()) == NULL) {
free(s);
return SA_ERROR_OOM;
}
if (pthread_mutex_init(&s->mutex, NULL) != 0) {
free(s->bl_head);
free(s);
return SA_ERROR_SYSTEM;
}
s->stream = NULL;
s->m = NULL;
s->thread_id = 0;
s->playing = 0;
s->bytes_written = 0;
s->bl_tail = s->bl_head;
s->n_bufs = 1;
s->sample_spec.format = PA_SAMPLE_S16LE;
s->sample_spec.channels = n_channels;
s->sample_spec.rate = rate;
strcpy(s->client_name, client_name);
/* Set up a new main loop */
s->m = pa_threaded_mainloop_new();
pa_threaded_mainloop_start(s->m);
pa_threaded_mainloop_lock(s->m);
/* Create a new connection context */
if (!(s->context = pa_context_new(pa_threaded_mainloop_get_api(s->m), "OggPlay"))) {
fprintf(stderr, "pa_context_new() failed.\n");
goto unlock_and_fail;
}
pa_context_set_state_callback(s->context, context_state_callback, s);
pa_context_connect(s->context, server, 0, NULL);
/* Wait until the context is ready */
pa_threaded_mainloop_wait(s->m);
if (pa_context_get_state(s->context) != PA_CONTEXT_READY) {
fprintf(stderr, "creating Pulseaudio Context failed\n");
goto unlock_and_fail;
}
pa_threaded_mainloop_unlock(s->m);
*_s = s;
return SA_SUCCESS;
unlock_and_fail:
pa_threaded_mainloop_unlock(s->m);
free(s);
return SA_ERROR_OOM;
}
int
sa_stream_open(sa_stream_t *s) {
if (s == NULL) {
return SA_ERROR_NO_INIT;
}
if (s->stream != NULL) {
return SA_ERROR_INVALID;
}
pa_threaded_mainloop_lock(s->m);
if (!(s->stream = pa_stream_new(s->context, s->client_name, &s->sample_spec, NULL))) {
fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(s->context)));
goto unlock_and_fail;
}
pa_stream_set_state_callback(s->stream, stream_state_callback, s);
pa_stream_set_write_callback(s->stream, stream_write_callback, s);
pa_stream_set_latency_update_callback(s->stream, stream_latency_update_callback, s);
if (pa_stream_connect_playback(s->stream, NULL, NULL, 0, NULL, NULL) < 0) {
fprintf(stderr, "pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(s->context)));
goto unlock_and_fail;
}
/* Wait until the stream is ready */
pa_threaded_mainloop_wait(s->m);
if (pa_stream_get_state(s->stream) != PA_STREAM_READY) {
fprintf(stderr, "Failed to connect stream: %s", pa_strerror(pa_context_errno(s->context)));
goto unlock_and_fail;
}
pa_threaded_mainloop_unlock(s->m);
if (!s->stream)
return SA_ERROR_NO_DEVICE;
return SA_SUCCESS;
unlock_and_fail:
pa_threaded_mainloop_unlock(s->m);
return SA_ERROR_NO_DEVICE;
}
int
sa_stream_destroy(sa_stream_t *s) {
if (s == NULL) {
return SA_SUCCESS;
}
pthread_mutex_lock(&s->mutex);
s->thread_id = 0;
pthread_mutex_unlock(&s->mutex);
pa_threaded_mainloop_lock(s->m);
pa_stream_disconnect(s->stream);
s->stream = NULL;
pa_context_disconnect(s->context);
pa_context_unref(s->context);
s->context = NULL;
pa_threaded_mainloop_unlock(s->m);
pa_threaded_mainloop_stop(s->m);
pa_threaded_mainloop_free(s->m);
pthread_mutex_destroy(&s->mutex);
while (s->bl_head != NULL) {
sa_buf * next = s->bl_head->next;
free(s->bl_head);
s->bl_head = next;
}
free(s);
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Data read and write functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
int result = SA_SUCCESS;
if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
if (nbytes == 0) {
return SA_SUCCESS;
}
pthread_mutex_lock(&s->mutex);
/*
* Append the new data to the end of our buffer list.
*/
while (1) {
unsigned int avail = s->bl_tail->size - s->bl_tail->end;
if (nbytes <= avail) {
/*
* The new data will fit into the current tail buffer, so
* just copy it in and we're done.
*/
memcpy(s->bl_tail->data + s->bl_tail->end, data, nbytes);
s->bl_tail->end += nbytes;
break;
} else {
/*
* Copy what we can into the tail and allocate a new buffer
* for the rest.
*/
memcpy(s->bl_tail->data + s->bl_tail->end, data, avail);
s->bl_tail->end += avail;
data = ((unsigned char *)data) + avail;
nbytes -= avail;
/*
* If we still have data left to copy but we've hit the limit of
* allowable buffer allocations, we need to spin for a bit to allow
* the audio callback function to slurp some more data up.
*/
if (nbytes > 0 && s->n_bufs == BUF_LIMIT) {
if (!s->playing) {
/*
* We haven't even started playing yet! That means the
* BUF_SIZE/BUF_LIMIT values are too low... Not much we can
* do here; spinning won't help because the audio callback
* hasn't been enabled yet. Oh well, error time.
*/
printf("Too much audio data received before audio device enabled!\n");
result = SA_ERROR_SYSTEM;
break;
}
while (s->n_bufs == BUF_LIMIT) {
struct timespec ts = {0, 1000000};
pthread_mutex_unlock(&s->mutex);
nanosleep(&ts, NULL);
pthread_mutex_lock(&s->mutex);
}
}
/*
* Allocate a new tail buffer, and go 'round again to fill it up.
*/
if ((s->bl_tail->next = new_buffer()) == NULL) {
result = SA_ERROR_OOM;
break;
}
s->n_bufs++;
s->bl_tail = s->bl_tail->next;
} /* if (nbytes <= avail), else */
} /* while (1) */
pthread_mutex_unlock(&s->mutex);
/*
* Once we have our first block of audio data, enable the audio callback
* function. This doesn't need to be protected by the mutex, because
* s->playing is not used in the audio callback thread, and it's probably
* better not to be inside the lock when we enable the audio callback.
*/
if (!s->playing) {
s->playing = 1;
if (pthread_create(&s->thread_id, NULL, (void *)audio_callback, s) != 0) {
result = SA_ERROR_SYSTEM;
}
}
return result;
}
static void audio_callback(void* data)
{
sa_stream_t* s = (sa_stream_t*)data;
unsigned int bytes_per_frame = s->sample_spec.channels * pa_sample_size(&s->sample_spec);
size_t buffer_size = s->sample_spec.rate * bytes_per_frame;
char* buffer = malloc(buffer_size);
while(1) {
char* dst = buffer;
size_t bytes_to_copy, bytes;
pa_threaded_mainloop_lock(s->m);
while(1) {
if (s == NULL || s->stream == NULL) {
if (s != NULL && s->m != NULL)
pa_threaded_mainloop_unlock(s->m);
goto free_buffer;
}
if ((bytes_to_copy = pa_stream_writable_size(s->stream)) == (size_t) -1) {
fprintf(stderr, "pa_stream_writable_size() failed: %s", pa_strerror(pa_context_errno(s->context)));
pa_threaded_mainloop_unlock(s->m);
goto free_buffer;
}
if(bytes_to_copy > 0)
break;
pa_threaded_mainloop_wait(s->m);
}
pa_threaded_mainloop_unlock(s->m);
if (bytes_to_copy > buffer_size)
bytes_to_copy = buffer_size;
bytes = bytes_to_copy;
pthread_mutex_lock(&s->mutex);
if (!s->thread_id) {
pthread_mutex_unlock(&s->mutex);
break;
}
/*
* Consume data from the start of the buffer list.
*/
while (1) {
unsigned int avail = s->bl_head->end - s->bl_head->start;
assert(s->bl_head->start <= s->bl_head->end);
if (avail >= bytes_to_copy) {
/*
* We have all we need in the head buffer, so just grab it and go.
*/
memcpy(dst, s->bl_head->data + s->bl_head->start, bytes_to_copy);
s->bl_head->start += bytes_to_copy;
break;
} else {
sa_buf* next = 0;
/*
* Copy what we can from the head and move on to the next buffer.
*/
memcpy(dst, s->bl_head->data + s->bl_head->start, avail);
s->bl_head->start += avail;
dst += avail;
bytes_to_copy -= avail;
/*
* We want to free the now-empty buffer, but not if it's also the
* current tail. If it is the tail, we don't have enough data to fill
* the destination buffer, so we write less and give up.
*/
next = s->bl_head->next;
if (next == NULL) {
bytes = bytes-bytes_to_copy;
break;
}
free(s->bl_head);
s->bl_head = next;
s->n_bufs--;
} /* if (avail >= bytes_to_copy), else */
} /* while (1) */
if(bytes > 0) {
pa_threaded_mainloop_lock(s->m);
if (pa_stream_write(s->stream, buffer, bytes, NULL, 0, PA_SEEK_RELATIVE) < 0) {
fprintf(stderr, "pa_stream_write() failed: %s", pa_strerror(pa_context_errno(s->context)));
pa_threaded_mainloop_unlock(s->m);
return;
}
pa_stream_update_timing_info(s->stream, NULL, NULL);
s->bytes_written += bytes;
pa_threaded_mainloop_unlock(s->m);
}
pthread_mutex_unlock(&s->mutex);
}
free_buffer:
free(buffer);
}
/*
* -----------------------------------------------------------------------------
* General query and support functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
sa_buf * b;
size_t used = 0;
if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
pthread_mutex_lock(&s->mutex);
/*
* Sum up the used portions of our buffers and subtract that from
* the pre-defined max allowed allocation.
*/
for (b = s->bl_head; b != NULL; b = b->next) {
used += b->end - b->start;
}
*size = BUF_SIZE * BUF_LIMIT - used;
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
int
sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
pa_usec_t usec;
if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
if (position != SA_POSITION_WRITE_SOFTWARE) {
return SA_ERROR_NOT_SUPPORTED;
}
pa_threaded_mainloop_lock(s->m);
if(pa_stream_get_time(s->stream, &usec) != PA_ERR_NODATA) {
*pos = pa_usec_to_bytes(usec, &s->sample_spec);
}
else {
*pos = s->bytes_written;
}
pa_threaded_mainloop_unlock(s->m);
return SA_SUCCESS;
}
int
sa_stream_pause(sa_stream_t *s) {
if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
return SA_SUCCESS;
}
int
sa_stream_resume(sa_stream_t *s) {
if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
pa_threaded_mainloop_lock(s->m);
s->bytes_written = 0;
pa_threaded_mainloop_unlock(s->m);
return SA_SUCCESS;
}
static sa_buf *
new_buffer(void) {
sa_buf * b = malloc(sizeof(sa_buf) + BUF_SIZE);
if (b != NULL) {
b->size = BUF_SIZE;
b->start = 0;
b->end = 0;
b->next = NULL;
}
return b;
}
/*
* -----------------------------------------------------------------------------
* Extension functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_set_volume_abs(sa_stream_t *s, float vol) {
pa_cvolume cv;
if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
pa_cvolume_set(&cv, s->sample_spec.channels, pa_sw_volume_from_dB(vol));
return SA_SUCCESS;
}
int
sa_stream_get_volume_abs(sa_stream_t *s, float *vol) {
if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
printf("sa_stream_get_volume_abs not implemented\n");
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Unsupported functions
* -----------------------------------------------------------------------------
*/
#define UNSUPPORTED(func) func { return SA_ERROR_NOT_SUPPORTED; }
UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec))
UNSUPPORTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n))
UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode))
UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver))
UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback))
UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s))
UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name))
UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate))
UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size))
UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value))
UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode))
UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size))
UNSUPPORTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format))
UNSUPPORTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate))
UNSUPPORTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels))
UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value))
UNSUPPORTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode))
UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size))
UNSUPPORTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size))
UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size))
UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state))
UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error))
UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify))
UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_drain(sa_stream_t *s))
UNSUPPORTED(int sa_stream_get_min_write(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_set_stream_type(sa_stream_t *s, const sa_stream_type_t stream_type))
const char *sa_strerror(int code) { return NULL; }

View File

@ -1,723 +0,0 @@
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stropts.h>
#include <unistd.h>
#include <sys/audio.h>
#include <sys/stat.h>
#include <sys/mixer.h>
#include "sydney_audio.h"
/* Sun Audio implementation based heavily on sydney_audio_mac.c */
#define DEFAULT_AUDIO_DEVICE "/dev/audio"
#define DEFAULT_DSP_DEVICE "/dev/dsp"
/* Macros copied from audio_oss.h */
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (C) 4Front Technologies 1996-2008.
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#define OSSIOCPARM_MASK 0x1fff /* parameters must be < 8192 bytes */
#define OSSIOC_VOID 0x00000000 /* no parameters */
#define OSSIOC_OUT 0x20000000 /* copy out parameters */
#define OSSIOC_IN 0x40000000 /* copy in parameters */
#define OSSIOC_INOUT (OSSIOC_IN|OSSIOC_OUT)
#define OSSIOC_SZ(t) ((sizeof (t) & OSSIOCPARM_MASK) << 16)
#define __OSSIO(x, y) ((int)(OSSIOC_VOID|(x<<8)|y))
#define __OSSIOR(x, y, t) ((int)(OSSIOC_OUT|OSSIOC_SZ(t)|(x<<8)|y))
#define __OSSIOWR(x, y, t) ((int)(OSSIOC_INOUT|OSSIOC_SZ(t)|(x<<8)|y))
#define SNDCTL_DSP_SPEED __OSSIOWR('P', 2, int)
#define SNDCTL_DSP_CHANNELS __OSSIOWR('P', 6, int)
#define SNDCTL_DSP_SETFMT __OSSIOWR('P', 5, int) /* Selects ONE fmt */
#define SNDCTL_DSP_GETPLAYVOL __OSSIOR('P', 24, int)
#define SNDCTL_DSP_SETPLAYVOL __OSSIOWR('P', 24, int)
#define SNDCTL_DSP_HALT_OUTPUT __OSSIO('P', 34)
#define AFMT_S16_LE 0x00000010
#define AFMT_S16_BE 0x00000020
#ifdef SA_LITTLE_ENDIAN
#define AFMT_S16_NE AFMT_S16_LE
#else
#define AFMT_S16_NE AFMT_S16_BE
#endif
typedef struct sa_buf sa_buf;
struct sa_buf {
unsigned int size;
unsigned int start;
unsigned int end;
sa_buf * next;
unsigned char data[];
};
struct sa_stream {
bool using_oss;
int output_fd;
pthread_t thread_id;
pthread_mutex_t mutex;
bool playing;
int64_t bytes_played;
/* audio format info */
unsigned int rate;
unsigned int n_channels;
unsigned int bytes_per_ch;
/* buffer list */
sa_buf * bl_head;
sa_buf * bl_tail;
int n_bufs;
};
/* Use a default buffer size with enough room for one second of audio,
* assuming stereo data at 44.1kHz with 32 bits per channel, and impose
* a generous limit on the number of buffers.
*/
#define BUF_SIZE (2 * 44100 * 4)
#define BUF_LIMIT 5
#if BUF_LIMIT < 2
#error BUF_LIMIT must be at least 2!
#endif
static void *audio_callback(void *s);
static sa_buf *new_buffer(void);
static int shutdown_device(sa_stream_t *s);
/*
* -----------------------------------------------------------------------------
* Startup and shutdown functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_create_pcm(
sa_stream_t ** _s,
const char * client_name,
sa_mode_t mode,
sa_pcm_format_t format,
unsigned int rate,
unsigned int n_channels
) {
/*
* Make sure we return a NULL stream pointer on failure.
*/
if (_s == NULL) {
return SA_ERROR_INVALID;
}
*_s = NULL;
if (mode != SA_MODE_WRONLY) {
return SA_ERROR_NOT_SUPPORTED;
}
if (format != SA_PCM_FORMAT_S16_NE) {
return SA_ERROR_NOT_SUPPORTED;
}
/*
* Allocate the instance and required resources.
*/
sa_stream_t *s;
if ((s = malloc(sizeof(sa_stream_t))) == NULL) {
return SA_ERROR_OOM;
}
if ((s->bl_head = new_buffer()) == NULL) {
free(s);
return SA_ERROR_SYSTEM;
}
if (pthread_mutex_init(&s->mutex, NULL) != 0) {
free(s->bl_head);
free(s);
return SA_ERROR_SYSTEM;
}
s->output_fd = -1;
s->playing = false;
s->bytes_played = 0;
s->rate = rate;
s->n_channels = n_channels;
s->bytes_per_ch = 2;
s->bl_tail = s->bl_head;
s->n_bufs = 1;
*_s = s;
return SA_SUCCESS;
}
int
sa_stream_open(sa_stream_t *s) {
if (s == NULL) {
return SA_ERROR_NO_INIT;
}
if (s->output_fd != -1) {
return SA_ERROR_INVALID;
}
/*
* Open the default audio output unit.
*/
/* If UTAUDIODEV is set, use it with Sun Audio interface */
char * sa_device_name = getenv("UTAUDIODEV");
char * dsp_device_name = NULL;
if (!sa_device_name) {
dsp_device_name = getenv("AUDIODSP");
if (!dsp_device_name) {
dsp_device_name = DEFAULT_DSP_DEVICE;
}
sa_device_name = getenv("AUDIODEV");
if (!sa_device_name) {
sa_device_name = DEFAULT_AUDIO_DEVICE;
}
}
int fd = -1;
s->using_oss = false;
/* Try to use OSS if available */
if (dsp_device_name) {
fd = open(dsp_device_name, O_WRONLY | O_NONBLOCK);
if (fd >= 0) {
s->using_oss = true;
}
}
/* Try Sun Audio */
if (!s->using_oss) {
fd = open(sa_device_name, O_WRONLY | O_NONBLOCK);
}
if (fd < 0)
{
printf("Open %s failed:%s.\n", sa_device_name, strerror(errno));
return SA_ERROR_NO_DEVICE;
}
if (s->using_oss) {
/* set the playback rate */
if (ioctl(fd, SNDCTL_DSP_SPEED, &(s->rate)) < 0) {
close(fd);
return SA_ERROR_NOT_SUPPORTED;
}
/* set the channel numbers */
if (ioctl(fd, SNDCTL_DSP_CHANNELS, &(s->n_channels)) < 0) {
close(fd);
return SA_ERROR_NOT_SUPPORTED;
}
int format = AFMT_S16_NE;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0) {
close(fd);
return SA_ERROR_NOT_SUPPORTED;
}
s->output_fd = fd;
return SA_SUCCESS;
}
audio_info_t audio_info;
AUDIO_INITINFO(&audio_info)
audio_info.play.sample_rate = s->rate;
audio_info.play.channels = s->n_channels;
audio_info.play.precision = s->bytes_per_ch * 8;
/* Signed Linear PCM encoding */
audio_info.play.encoding = AUDIO_ENCODING_LINEAR;
if (ioctl(fd, AUDIO_SETINFO, &audio_info) == -1) {
printf("ioctl AUDIO_SETINFO failed.\n");
close(fd);
return SA_ERROR_NOT_SUPPORTED;
}
s->output_fd = fd;
return SA_SUCCESS;
}
int
sa_stream_destroy(sa_stream_t *s) {
if (s == NULL) {
return SA_SUCCESS;
}
/*
* Join the thread.
*/
bool thread_created = false;
pthread_mutex_lock(&s->mutex);
if (s->playing) {
thread_created = true;
s->playing = false;
}
pthread_mutex_unlock(&s->mutex);
if (thread_created) {
pthread_join(s->thread_id, NULL);
}
int result = SA_SUCCESS;
/*
* Shutdown the audio output device.
*/
result = shutdown_device(s);
/*
* Release resouces.
*/
if (pthread_mutex_destroy(&s->mutex) != 0) {
result = SA_ERROR_SYSTEM;
}
while (s->bl_head != NULL) {
sa_buf * next = s->bl_head->next;
free(s->bl_head);
s->bl_head = next;
}
free(s);
return result;
}
/*
* -----------------------------------------------------------------------------
* Data read and write functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
if (s == NULL || s->output_fd == -1) {
return SA_ERROR_NO_INIT;
}
if (nbytes == 0) {
return SA_SUCCESS;
}
pthread_mutex_lock(&s->mutex);
/*
* Append the new data to the end of our buffer list.
*/
int result = SA_SUCCESS;
while (1) {
unsigned int avail = s->bl_tail->size - s->bl_tail->end;
if (nbytes <= avail) {
/*
* The new data will fit into the current tail buffer, so
* just copy it in and we're done.
*/
memcpy(s->bl_tail->data + s->bl_tail->end, data, nbytes);
s->bl_tail->end += nbytes;
break;
} else {
/*
* Copy what we can into the tail and allocate a new buffer
* for the rest.
*/
memcpy(s->bl_tail->data + s->bl_tail->end, data, avail);
s->bl_tail->end += avail;
data = ((unsigned char *)data) + avail;
nbytes -= avail;
/*
* If we still have data left to copy but we've hit the limit of
* allowable buffer allocations, we need to spin for a bit to allow
* the audio callback function to slurp some more data up.
*/
if (nbytes > 0 && s->n_bufs == BUF_LIMIT) {
#ifdef TIMING_TRACE
printf("#"); /* too much audio data */
#endif
if (!s->playing) {
/*
* We haven't even started playing yet! That means the
* BUF_SIZE/BUF_LIMIT values are too low... Not much we can
* do here; spinning won't help because the audio callback
* hasn't been enabled yet. Oh well, error time.
*/
printf("Too much audio data received before audio device enabled!\n");
result = SA_ERROR_SYSTEM;
break;
}
while (s->n_bufs == BUF_LIMIT) {
pthread_mutex_unlock(&s->mutex);
struct timespec ts = {0, 1000000};
nanosleep(&ts, NULL);
pthread_mutex_lock(&s->mutex);
}
}
/*
* Allocate a new tail buffer, and go 'round again to fill it up.
*/
if ((s->bl_tail->next = new_buffer()) == NULL) {
result = SA_ERROR_OOM;
break;
}
s->n_bufs++;
s->bl_tail = s->bl_tail->next;
} /* if (nbytes <= avail), else */
} /* while (1) */
/*
* Once we have our first block of audio data, enable the audio callback
* function.
*/
if (!s->playing) {
s->playing = true;
if (pthread_create(&s->thread_id, NULL, audio_callback, s) != 0) {
result = SA_ERROR_SYSTEM;
}
}
pthread_mutex_unlock(&s->mutex);
return result;
}
static void *
audio_callback(void *data) {
sa_stream_t *s = data;
pthread_mutex_lock(&s->mutex);
while (s->playing) {
/*
* Consume data from the start of the buffer list.
*/
while (s->output_fd != -1) {
unsigned int avail = s->bl_head->end - s->bl_head->start;
if (avail > 0) {
int written = write(s->output_fd, s->bl_head->data + s->bl_head->start, avail);
if (written == -1) {
break; /* Try again later. */
}
s->bl_head->start += written;
s->bytes_played += written;
if (written < avail) {
break;
}
}
sa_buf * next = s->bl_head->next;
if (next == NULL) {
#ifdef TIMING_TRACE
printf("!"); /* not enough audio data */
#endif
break;
}
free(s->bl_head);
s->bl_head = next;
s->n_bufs--;
} /* while (s->output_fd != -1) */
pthread_mutex_unlock(&s->mutex);
struct timespec ts = {0, 1000000};
nanosleep(&ts, NULL);
pthread_mutex_lock(&s->mutex);
} /* s->playing */
pthread_mutex_unlock(&s->mutex);
return NULL;
}
/*
* -----------------------------------------------------------------------------
* General query and support functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
if (s == NULL || s->output_fd == -1) {
return SA_ERROR_NO_INIT;
}
pthread_mutex_lock(&s->mutex);
/*
* The sum of the free space in the tail buffer plus the size of any new
* buffers represents the write space available before blocking.
*/
unsigned int avail = s->bl_tail->size - s->bl_tail->end;
avail += (BUF_LIMIT - s->n_bufs) * BUF_SIZE;
*size = avail;
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
/* ---------------------------------------------------------------------------
* General query and support functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
if (s == NULL || s->output_fd == -1) {
return SA_ERROR_NO_INIT;
}
if (position != SA_POSITION_WRITE_SOFTWARE) {
return SA_ERROR_NOT_SUPPORTED;
}
pthread_mutex_lock(&s->mutex);
*pos = s->bytes_played;
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
int
sa_stream_drain(sa_stream_t *s) {
if (s == NULL || s->output_fd == -1) {
return SA_ERROR_NO_INIT;
}
while (1) {
pthread_mutex_lock(&s->mutex);
sa_buf * b;
size_t used = 0;
for (b = s->bl_head; b != NULL; b = b->next) {
used += b->end - b->start;
}
pthread_mutex_unlock(&s->mutex);
if (used == 0) {
break;
}
struct timespec ts = {0, 1000000};
nanosleep(&ts, NULL);
}
return SA_SUCCESS;
}
int
sa_stream_pause(sa_stream_t *s) {
if (s == NULL || s->output_fd == -1) {
return SA_ERROR_NO_INIT;
}
pthread_mutex_lock(&s->mutex);
int result = shutdown_device(s);
if (result == SA_SUCCESS) {
s->output_fd = -1;
}
pthread_mutex_unlock(&s->mutex);
return result;
}
int
sa_stream_resume(sa_stream_t *s) {
if (s == NULL) {
return SA_ERROR_NO_INIT;
}
pthread_mutex_lock(&s->mutex);
int result = sa_stream_open(s);
pthread_mutex_unlock(&s->mutex);
return result;
}
static sa_buf *
new_buffer(void) {
sa_buf * b = malloc(sizeof(sa_buf) + BUF_SIZE);
if (b != NULL) {
b->size = BUF_SIZE;
b->start = 0;
b->end = 0;
b->next = NULL;
}
return b;
}
static int
shutdown_device(sa_stream_t *s) {
if (s->output_fd != -1)
{
/* Flush buffer. */
if (s->using_oss) {
ioctl(s->output_fd, SNDCTL_DSP_HALT_OUTPUT);
} else {
ioctl(s->output_fd, I_FLUSH);
}
if (close(s->output_fd) < 0)
{
return SA_ERROR_SYSTEM;
}
}
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Extension functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_set_volume_abs(sa_stream_t *s, float vol) {
if (s == NULL || s->output_fd == -1) {
return SA_ERROR_NO_INIT;
}
if (s->using_oss) {
int mvol = ((int)(100 * vol)) | ((int)(100 * vol) << 8);
if (ioctl(s->output_fd, SNDCTL_DSP_SETPLAYVOL, &mvol) < 0) {
return SA_ERROR_SYSTEM;
}
return SA_SUCCESS;
}
unsigned int newVolume = (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN) * vol + AUDIO_MIN_GAIN;
/* Check if the new volume is valid or not */
if ( newVolume < AUDIO_MIN_GAIN || newVolume > AUDIO_MAX_GAIN )
return SA_ERROR_INVALID;
pthread_mutex_lock(&s->mutex);
audio_info_t audio_info;
AUDIO_INITINFO(&audio_info);
audio_info.play.gain = newVolume;
int err = ioctl(s->output_fd, AUDIO_SETINFO, &audio_info);
pthread_mutex_unlock(&s->mutex);
if (err == -1)
{
perror("sa_stream_set_volume_abs failed\n");
return SA_ERROR_SYSTEM;
}
return SA_SUCCESS;
}
int
sa_stream_get_volume_abs(sa_stream_t *s, float *vol) {
if (s == NULL || s->output_fd == -1) {
return SA_ERROR_NO_INIT;
}
if (s->using_oss) {
int mvol;
if (ioctl(s->output_fd, SNDCTL_DSP_GETPLAYVOL, &mvol) < 0){
return SA_ERROR_SYSTEM;
}
*vol = ((mvol & 0xFF) + (mvol >> 8)) / 200.0f;
return SA_SUCCESS;
}
pthread_mutex_lock(&s->mutex);
audio_info_t audio_info;
AUDIO_INITINFO(&audio_info);
int err = ioctl(s->output_fd, AUDIO_GETINFO, &audio_info);
pthread_mutex_unlock(&s->mutex);
if (err == -1)
{
perror("sa_stream_get_volume_abs failed\n");
return SA_ERROR_SYSTEM;
}
*vol = (float)((audio_info.play.gain - AUDIO_MIN_GAIN))/(AUDIO_MAX_GAIN - AUDIO_MIN_GAIN);
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Unsupported functions
* -----------------------------------------------------------------------------
*/
#define UNSUPPORTED(func) func { return SA_ERROR_NOT_SUPPORTED; }
UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec))
UNSUPPORTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size))
UNSUPPORTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n))
UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode))
UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable))
UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver))
UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback))
UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s))
UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name))
UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate))
UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size))
UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value))
UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode))
UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size))
UNSUPPORTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format))
UNSUPPORTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate))
UNSUPPORTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels))
UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value))
UNSUPPORTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode))
UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size))
UNSUPPORTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size))
UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size))
UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state))
UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error))
UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify))
UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes))
UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_get_min_write(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_set_stream_type(sa_stream_t *s, const sa_stream_type_t stream_type))
const char *sa_strerror(int code) { return NULL; }

View File

@ -1,759 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "sydney_audio.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <mmreg.h>
#include <mmsystem.h>
#include <math.h>
#include <assert.h>
#define BLOCK_COUNT 10
#define DEFAULT_DEVICE_NAME "Default WAVE Device"
#define DEFAULT_DEVICE WAVE_MAPPER
#define BLOCK_DURATION_MS 100
#define BYTES_PER_SAMPLE 2
#define VERBOSE_OUTPUT 1
// INFO: if you get weird compile errors make sure there is no extra chars pass '\'
#if defined(VERBOSE_OUTPUT)
#define WAVE_ERROR_VERBOSE(error, message) \
switch (error) { \
case MMSYSERR_ALLOCATED: \
printf("[WAVE API] Device allocation error returned while executing %s\n", message); \
break; \
case MMSYSERR_BADDEVICEID: \
printf("[WAVE API] Wrong device ID error returned while executing %s\n", message); \
break; \
case MMSYSERR_NODRIVER: \
printf("[WAVE API] System driver not present error returned while executing %s\n", message); \
break; \
case MMSYSERR_INVALHANDLE: \
printf("[WAVE API] Invalid device handle error returned while executing %s\n", message); \
break; \
case MMSYSERR_NOMEM: \
printf("[WAVE API] No memory error returned while executing %s\n", message); \
break; \
case MMSYSERR_NOTSUPPORTED: \
printf("[WAVE API] Not supported error returned while executing %s\n", message); \
break; \
case WAVERR_BADFORMAT: \
printf("[WAVE API] Not valid audio format returned while executing %s\n", message); \
break; \
case WAVERR_SYNC: \
printf("[WAVE API] Device synchronous error returned while executing %s\n", message); \
break; \
default: \
printf("[WAVE API] Error while executing %s\n", message); \
break; \
}
#else
#define WAVE_ERROR_VERBOSE(error, message) \
do {} while(0)
#endif
#define HANDLE_WAVE_ERROR(status, location) \
if (status != MMSYSERR_NOERROR) { \
WAVE_ERROR_VERBOSE(status, location); \
return getSAErrorCode(status); \
}
#define ERROR_IF_NO_INIT(handle) \
if (handle == NULL) { \
return SA_ERROR_NO_INIT; \
}
/* local implementation of the sa_stream_t type */
struct sa_stream {
char* deviceName;
UINT device;
UINT channels;
UINT rate;
sa_mode_t rwMode;
sa_pcm_format_t format;
HWAVEOUT hWaveOut;
HANDLE callbackEvent;
CRITICAL_SECTION waveCriticalSection;
WAVEHDR* waveBlocks;
volatile int waveFreeBlockCount;
int waveCurrentBlock;
int playing;
size_t blockSize;
};
/** Forward definitions of audio api specific functions */
int allocateBlocks(int size, int count, WAVEHDR** blocks);
int freeBlocks(WAVEHDR* blocks);
int openAudio(sa_stream_t *s);
int closeAudio(sa_stream_t * s);
int writeBlock(sa_stream_t *s, WAVEHDR* current);
int writeAudio(sa_stream_t *s, LPSTR data, int bytes);
int getSAErrorCode(int waveErrorCode);
void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD_PTR dwInstance,
DWORD_PTR dwParam1, DWORD_PTR dwParam2);
/** Normal way to open a PCM device */
int sa_stream_create_pcm(sa_stream_t **s,
const char *client_name,
sa_mode_t mode,
sa_pcm_format_t format,
unsigned int rate,
unsigned int nchannels) {
sa_stream_t * _s = NULL;
ERROR_IF_NO_INIT(s);
*s = NULL;
/* FIX ME: for formats different than PCM extend using WAVEFORMATEXTENSIBLE */
if (format != SA_PCM_FORMAT_S16_NE) {
/* If we ever support non 16bit sound formats, we need to change the use of
* BYTES_PER_SAMPLE in the blockSize calculation below. */
return SA_ERROR_NOT_SUPPORTED;
}
if (mode != SA_MODE_WRONLY) {
return SA_ERROR_NOT_SUPPORTED;
}
if ((_s = (sa_stream_t*)calloc(1, sizeof(sa_stream_t))) == NULL) {
return SA_ERROR_OOM;
}
_s->rwMode = mode;
_s->format = format;
_s->rate = rate;
_s->channels = nchannels;
_s->deviceName = DEFAULT_DEVICE_NAME;
_s->device = DEFAULT_DEVICE;
_s->playing = 0;
_s->blockSize = BYTES_PER_SAMPLE * nchannels * ((rate * BLOCK_DURATION_MS) / 1000);
/* Other parts of the code assumes that the block size is evenly
divisible by 2. */
assert((_s->blockSize & 1) != 1);
assert((_s->blockSize % BYTES_PER_SAMPLE) == 0);
assert(((_s->blockSize / BYTES_PER_SAMPLE) % nchannels) == 0);
*s = _s;
return SA_SUCCESS;
}
/** Initialise the device */
int sa_stream_open(sa_stream_t *s) {
int status = SA_SUCCESS;
ERROR_IF_NO_INIT(s);
switch (s->rwMode) {
case SA_MODE_WRONLY:
status = openAudio(s);
break;
default:
status = SA_ERROR_NOT_SUPPORTED;
break;
}
return status;
}
int sa_stream_get_min_write(sa_stream_t *s, size_t *size) {
ERROR_IF_NO_INIT(s);
*size = s->blockSize;
return SA_SUCCESS;
}
/** Interleaved playback function */
int sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
int status = SA_SUCCESS;
ERROR_IF_NO_INIT(s);
status = writeAudio(s, (LPSTR)data, nbytes);
return status;
}
/** Query how much can be written without blocking */
int sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
unsigned int avail;
WAVEHDR* current;
ERROR_IF_NO_INIT(s);
EnterCriticalSection(&(s->waveCriticalSection));
avail = (s->waveFreeBlockCount-1) * s->blockSize;
if (s->waveFreeBlockCount != BLOCK_COUNT) {
current = &(s->waveBlocks[s->waveCurrentBlock]);
avail += s->blockSize - current->dwUser;
}
LeaveCriticalSection(&(s->waveCriticalSection));
*size = avail;
return SA_SUCCESS;
}
/** Close/destroy everything */
int sa_stream_destroy(sa_stream_t *s) {
int status;
ERROR_IF_NO_INIT(s);
/* close and release all allocated resources */
status = closeAudio(s);
free(s);
return status;
}
#define LEFT_CHANNEL_MASK 0x0000FFFF
#define RIGHT_CHANNEL_MASK 0xFFFF0000
/**
* retrieved volume as an int in a scale from 0x0000 to 0xFFFF
* only one value for all channels
*/
int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n) {
int status;
DWORD volume;
WORD left;
WORD right;
ERROR_IF_NO_INIT(s);
status = waveOutGetVolume(s->hWaveOut, &volume);
HANDLE_WAVE_ERROR(status, "reading audio volume level");
left = volume & LEFT_CHANNEL_MASK;
right = (volume & RIGHT_CHANNEL_MASK) >> 16;
vol[0] = (int32_t)(left + right /2);
return SA_SUCCESS;
}
/** changes volume as an int in a scale from 0x0000 to 0xFFFF*/
int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n) {
int status;
DWORD volume;
WORD left;
WORD right;
ERROR_IF_NO_INIT(s);
volume = (DWORD)vol[0];
left = volume & LEFT_CHANNEL_MASK;
right = left;
volume = (left << 16) | right;
status = waveOutSetVolume(s->hWaveOut, volume);
HANDLE_WAVE_ERROR(status, "setting new audio volume level");
return SA_SUCCESS;
}
/** sync/timing */
int sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
int status;
MMTIME mm;
ERROR_IF_NO_INIT(s);
if (position != SA_POSITION_WRITE_HARDWARE) {
return SA_ERROR_NOT_SUPPORTED;
}
// request playback progress in bytes
mm.wType = TIME_BYTES;
status = waveOutGetPosition(s->hWaveOut, &mm, sizeof(MMTIME));
HANDLE_WAVE_ERROR(status, "reading audio buffer position");
*pos = (int64_t)mm.u.cb;
return SA_SUCCESS;
}
/* Control/xrun */
/** Resume playing after a pause */
int sa_stream_resume(sa_stream_t *s) {
int status;
ERROR_IF_NO_INIT(s);
status = waveOutRestart(s->hWaveOut);
HANDLE_WAVE_ERROR(status, "resuming audio playback");
s->playing = 1;
return SA_SUCCESS;
}
/** Pause audio playback (do not empty the buffer) */
int sa_stream_pause(sa_stream_t *s) {
int status;
ERROR_IF_NO_INIT(s);
status = waveOutPause(s->hWaveOut);
HANDLE_WAVE_ERROR(status, "resuming audio playback");
s->playing = 0;
return SA_SUCCESS;
}
/** Block until all audio has been played */
int sa_stream_drain(sa_stream_t *s) {
int status;
WAVEHDR* current;
ERROR_IF_NO_INIT(s);
current = &(s->waveBlocks[s->waveCurrentBlock]);
if (current->dwUser) {
/* We've got pending audio which hasn't been written, we must write it to
the hardware, else it will never be played. */
status = writeBlock(s, current);
HANDLE_WAVE_ERROR(status, "writing audio to audio device");
}
if (!s->playing) {
return SA_ERROR_INVALID;
}
/* wait for all blocks to complete */
EnterCriticalSection(&(s->waveCriticalSection));
while(s->waveFreeBlockCount < BLOCK_COUNT) {
LeaveCriticalSection(&(s->waveCriticalSection));
Sleep(10);
EnterCriticalSection(&(s->waveCriticalSection));
}
LeaveCriticalSection(&(s->waveCriticalSection));
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
* Private WAVE API specific functions
* -----------------------------------------------------------------------------
*/
/**
* \brief - allocate buffer for writing to system WAVE audio device
* \param size - size of each audio block
* \param cound - number of blocks to be allocated
* \param blocks - pointer to the blocks buffer to be allocated
* \return - completion status
*/
int allocateBlocks(int size, int count, WAVEHDR** blocks)
{
unsigned char* buffer;
int i;
WAVEHDR* headers;
DWORD totalBufferSize = (size + sizeof(WAVEHDR)) * count;
/* allocate memory on heap for the entire set in one go */
if((buffer = HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
totalBufferSize
)) == NULL) {
printf("Memory allocation error\n");
return SA_ERROR_OOM;
}
/* and set up the pointers to each bit */
headers = *blocks = (WAVEHDR*)buffer;
buffer += sizeof(WAVEHDR) * count;
for(i = 0; i < count; i++) {
headers[i].dwBufferLength = size;
headers[i].lpData = buffer;
buffer += size;
}
return SA_SUCCESS;
}
/**
* \brief - free allocated audio buffer
* \param blocks - pointer to allocated the buffer of audio bloks
* \return - completion status
*/
int freeBlocks(WAVEHDR* blocks)
{
if (blocks == NULL)
return SA_ERROR_INVALID;
/* and this is why allocateBlocks works the way it does */
HeapFree(GetProcessHeap(), 0, blocks);
blocks = NULL;
return SA_SUCCESS;
}
/**
* \brief - open system default WAVE device
* \param s - sydney audio stream handle
* \return - completion status
*/
int openAudio(sa_stream_t *s) {
int status;
WAVEFORMATEX wfx;
UINT supported = FALSE;
status = allocateBlocks(s->blockSize, BLOCK_COUNT, &(s->waveBlocks));
HANDLE_WAVE_ERROR(status, "allocating audio buffer blocks");
s->waveFreeBlockCount = BLOCK_COUNT;
s->waveCurrentBlock = 0;
wfx.nSamplesPerSec = (DWORD)s->rate; /* sample rate */
wfx.wBitsPerSample = 16; /* sample size */
wfx.nChannels = s->channels; /* channels */
wfx.cbSize = 0; /* size of _extra_ info */
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nBlockAlign = (wfx.wBitsPerSample * wfx.nChannels) >> 3;
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
supported = waveOutOpen(NULL, WAVE_MAPPER, &wfx, (DWORD_PTR)0, (DWORD_PTR)0,
WAVE_FORMAT_QUERY);
if (supported == MMSYSERR_NOERROR) { // audio device opened sucessfully
status = waveOutOpen((LPHWAVEOUT)&(s->hWaveOut), WAVE_MAPPER, &wfx,
(DWORD_PTR)waveOutProc, (DWORD_PTR)s, CALLBACK_FUNCTION);
if (status != MMSYSERR_NOERROR) {
freeBlocks(s->waveBlocks);
s->waveBlocks = NULL;
HANDLE_WAVE_ERROR(status, "opening audio device for playback");
}
}
else if (supported == WAVERR_BADFORMAT) {
printf("Requested format not supported.\n");
// clean up the memory
freeBlocks(s->waveBlocks);
s->waveBlocks = NULL;
return SA_ERROR_NOT_SUPPORTED;
}
else {
printf("Error opening default audio device.\n");
// clean up the memory
freeBlocks(s->waveBlocks);
s->waveBlocks = NULL;
return SA_ERROR_SYSTEM;
}
// create notification for data written to a device
s->callbackEvent = CreateEvent(0, FALSE, FALSE, 0);
// initialise critical section for operations on waveFreeBlockCound variable
InitializeCriticalSection(&(s->waveCriticalSection));
return SA_SUCCESS;
}
/**
* \brief - closes opened audio device handle
* \param s - sydney audio stream handle
* \return - completion status
*/
int closeAudio(sa_stream_t * s) {
int status, i, result;
result = SA_SUCCESS;
// reseting audio device and flushing buffers
status = waveOutReset(s->hWaveOut);
if (status != MMSYSERR_NOERROR) {
result = getSAErrorCode(status);
}
if (s->waveBlocks) {
/* wait for all blocks to complete */
while(s->waveFreeBlockCount < BLOCK_COUNT) {
Sleep(10);
}
/* unprepare any blocks that are still prepared */
for(i = 0; i < s->waveFreeBlockCount; i++) {
if(s->waveBlocks[i].dwFlags & WHDR_PREPARED) {
status = waveOutUnprepareHeader(s->hWaveOut, &(s->waveBlocks[i]), sizeof(WAVEHDR));
if (status != MMSYSERR_NOERROR) {
result = getSAErrorCode(status);
}
}
}
freeBlocks(s->waveBlocks);
s->waveBlocks = NULL;
}
status = waveOutClose(s->hWaveOut);
if (status != MMSYSERR_NOERROR) {
result = getSAErrorCode(status);
}
s->playing = 0;
DeleteCriticalSection(&(s->waveCriticalSection));
CloseHandle(s->callbackEvent);
return result;
}
/**
* \brief - writes a WAVEHDR block of PCM audio samples to hardware.
* \param s - valid handle to opened sydney stream
* \param current - pointer to WAVEHDR storing audio samples to be played
* \return - completion status
*/
int writeBlock(sa_stream_t *s, WAVEHDR* current) {
int status;
ERROR_IF_NO_INIT(s);
current->dwBufferLength = current->dwUser;
/* write to audio device */
waveOutPrepareHeader(s->hWaveOut, current, sizeof(WAVEHDR));
status = waveOutWrite(s->hWaveOut, current, sizeof(WAVEHDR));
HANDLE_WAVE_ERROR(status, "writing audio to audio device");
EnterCriticalSection(&(s->waveCriticalSection));
s->waveFreeBlockCount--;
LeaveCriticalSection(&(s->waveCriticalSection));
/*
* point to the next block
*/
(s->waveCurrentBlock)++;
(s->waveCurrentBlock) %= BLOCK_COUNT;
s->playing = 1;
return SA_SUCCESS;
}
/**
* \brief - writes PCM audio samples to audio device
* \param s - valid handle to opened sydney stream
* \param data - pointer to memory storing audio samples to be played
* \param nsamples - number of samples in the memory pointed by previous parameter
* \return - completion status
*/
int writeAudio(sa_stream_t *s, LPSTR data, int bytes) {
UINT status;
WAVEHDR* current;
int remain;
current = &(s->waveBlocks[s->waveCurrentBlock]);
while(bytes > 0) {
/*
* wait for a block to become free
*/
while (!(s->waveFreeBlockCount))
WaitForSingleObject(s->callbackEvent, INFINITE);
/* first make sure the header we're going to use is unprepared */
if(current->dwFlags & WHDR_PREPARED) {
status = waveOutUnprepareHeader(s->hWaveOut, current, sizeof(WAVEHDR));
HANDLE_WAVE_ERROR(status, "preparing audio headers for writing");
}
if(bytes < (int)(s->blockSize - current->dwUser)) {
memcpy(current->lpData + current->dwUser, data, bytes);
current->dwUser += bytes;
break;
}
/* remain is even as s->blockSize and dwUser are even too */
remain = s->blockSize - current->dwUser;
memcpy(current->lpData + current->dwUser, data, remain);
current->dwUser += remain;
bytes -= remain;
data += remain;
status = writeBlock(s, current);
HANDLE_WAVE_ERROR(status, "writing audio to audio device");
current = &(s->waveBlocks[s->waveCurrentBlock]);
current->dwUser = 0;
}
return SA_SUCCESS;
}
/**
* \brief - audio callback function called when next WAVE header is played by audio device
*/
void CALLBACK waveOutProc(HWAVEOUT hWaveOut,
UINT uMsg,
DWORD_PTR dwInstance,
DWORD_PTR dwParam1,
DWORD_PTR dwParam2)
{
/*
* pointer to free block counter
*/
sa_stream_t* handle = (sa_stream_t*)dwInstance;
/*
* ignore calls that occur due to openining and closing the
* device.
*/
if(uMsg != WOM_DONE)
return;
EnterCriticalSection(&(handle->waveCriticalSection));
(handle->waveFreeBlockCount)++;
if ((handle->waveFreeBlockCount) == 1)
SetEvent(handle->callbackEvent);
LeaveCriticalSection(&(handle->waveCriticalSection));
}
/**
* \brief - converts frequently reported WAVE error codes to Sydney audio API codes
*/
int getSAErrorCode(int waveErrorCode) {
int error = SA_ERROR_NOT_SUPPORTED;
switch (waveErrorCode) {
case MMSYSERR_NOERROR:
error = SA_SUCCESS;
break;
case MMSYSERR_ALLOCATED:
error = SA_ERROR_SYSTEM;
break;
case MMSYSERR_BADDEVICEID:
error = SA_ERROR_INVALID;
break;
case MMSYSERR_NODRIVER:
error = SA_ERROR_NO_DRIVER;
break;
case MMSYSERR_NOTSUPPORTED:
error = SA_ERROR_NOT_SUPPORTED;
break;
case MMSYSERR_NOMEM:
error = SA_ERROR_OOM;
break;
case MMSYSERR_INVALHANDLE:
error = SA_ERROR_INVALID;
break;
case WAVERR_BADFORMAT:
error = SA_ERROR_NOT_SUPPORTED;
break;
case WAVERR_SYNC:
error = SA_ERROR_NOT_SUPPORTED;
break;
}
return error;
}
/*
* -----------------------------------------------------------------------------
* Functions to be implemented next
* -----------------------------------------------------------------------------
*/
#define NOT_IMPLEMENTED(func) func { return SA_ERROR_NOT_SUPPORTED; }
/* "Soft" params */
NOT_IMPLEMENTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size))
NOT_IMPLEMENTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size))
NOT_IMPLEMENTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size))
NOT_IMPLEMENTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size))
/** Set the mapping between channels and the loudspeakers */
NOT_IMPLEMENTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n))
/* Query functions */
NOT_IMPLEMENTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode))
NOT_IMPLEMENTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format))
NOT_IMPLEMENTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate))
NOT_IMPLEMENTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels))
NOT_IMPLEMENTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size))
NOT_IMPLEMENTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size))
NOT_IMPLEMENTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size))
NOT_IMPLEMENTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size))
NOT_IMPLEMENTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size))
NOT_IMPLEMENTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n))
/*
* -----------------------------------------------------------------------------
* Unsupported functions
* -----------------------------------------------------------------------------
*/
#define UNSUPPORTED(func) func { return SA_ERROR_NOT_SUPPORTED; }
/** Create an opaque (e.g. AC3) codec stream */
UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec))
/** Whether xruns cause the card to reset */
UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode))
/** Set the device to non-interleaved mode */
UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable))
/** Require dynamic sample rate */
UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable))
/** Select driver */
UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver))
/** Start callback */
UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback))
/** Stop callback */
UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s))
/** Change the device connected to the stream */
UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name))
/** volume in hundreths of dB*/
UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
/** Change the sampling rate */
UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate))
/** Change some meta data that is attached to the stream */
UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size))
/** Associate opaque user data */
UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value))
/* Hardware-related. This is implementation-specific and hardware specific. */
UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction))
UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction))
/* Query functions */
UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size))
UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value))
UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode))
UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled))
UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size))
UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size))
UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction))
UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction))
/** Get current state of the audio device */
UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state))
/** Obtain the error code */
UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error))
/** Obtain the notification code */
UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify))
/* Blocking IO calls */
/** Interleaved capture function */
UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes))
/** Non-interleaved capture function */
UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes))
/** Non-interleaved playback function */
UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes))
/** Interleaved playback function with seek offset */
UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
/** Non-interleaved playback function with seek offset */
UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
/** Query how much can be read without blocking */
UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size))
UNSUPPORTED(int sa_stream_set_stream_type(sa_stream_t *s, const sa_stream_type_t stream_type))
/** Return a human readable error */
const char *sa_strerror(int code);

View File

@ -24,7 +24,7 @@ LIBS = \
$(DEPTH)/netwerk/srtp/src/$(LIB_PREFIX)nksrtp_s.$(LIB_SUFFIX) \
$(NULL)
ifneq (,$(MOZ_CUBEB)$(MOZ_SYDNEYAUDIO))
ifdef MOZ_CUBEB
ifdef MOZ_ALSA
LIBS += \
$(MOZ_ALSA_LIBS) \

View File

@ -425,7 +425,7 @@ endif
endif
ifneq (,$(MOZ_CUBEB)$(MOZ_SYDNEYAUDIO))
ifdef MOZ_CUBEB
ifdef MOZ_ALSA
EXTRA_DSO_LDOPTS += $(MOZ_ALSA_LIBS)
endif
@ -519,27 +519,6 @@ OS_LIBS += \
$(NULL)
endif
ifeq ($(OS_ARCH),Darwin)
ifdef MOZ_SYDNEYAUDIO
OS_LIBS += \
-framework Carbon \
-framework CoreAudio \
-framework AudioToolbox \
-framework AudioUnit \
-framework IOKit \
-framework Foundation \
-framework AppKit \
-framework Security \
$(NULL)
endif
endif
ifneq (,$(filter NetBSD OpenBSD,$(OS_ARCH)))
ifdef MOZ_SYDNEYAUDIO
EXTRA_DSO_LDOPTS += -lossaudio
endif
endif
ifeq (OpenBSD,$(OS_ARCH))
ifdef MOZ_CUBEB
EXTRA_DSO_LDOPTS += -lsndio

View File

@ -85,9 +85,6 @@ if CONFIG['MOZ_VP8'] and not CONFIG['MOZ_NATIVE_LIBVPX']:
if CONFIG['MOZ_OGG']:
add_tier_dir('platform', ['media/libogg', 'media/libtheora'])
if CONFIG['MOZ_SYDNEYAUDIO']:
add_tier_dir('platform', 'media/libsydneyaudio')
if CONFIG['MOZ_WEBRTC']:
add_tier_dir('platform', [
'media/webrtc',