Bug 683822 - Delete media/libsydneyaudio patches and update.sh, since this is no longer maintained. r=doublec

This commit is contained in:
Matthew Gregan 2011-09-01 17:56:46 +12:00
parent da0b8fa230
commit ff0c4ed5ec
13 changed files with 4 additions and 3629 deletions

View File

@ -1,32 +1,4 @@
The source from this directory was copied from the libsydneyaudio svn
source using the update.sh script. The only changes made were those
applied by update.sh and the addition/upate of Makefile.in files for
the Mozilla build system.
http://svn.annodex.net/libsydneyaudio/trunk
The svn revision number used was r3895.
pause-resume.patch is applied to implement and fix issues
with pausing and resuming audio streams.
include-CoreServices.patch is applied to include CoreServices headers
on Mac OS X. Fixes a build failure on Mac OS X 10.6. b=492072
sydney_os2_base.patch: Bug 448918 - add OS/2 support (this patch should
be removed when OS/2 support is added upstream)
sydney_os2_moz.patch: Bug 448918 - add Mozilla-specific code to
sydney_audio_os2.c
bug495794_closeAudio.patch fixes a crash when destroying the sa_stream_t after
a failed attempt to open the stream.
bug495558_alsa_endian.patch adds support for big endian ALSA platforms.
sydney_aix.patch: Bug 499266 - add support for AIX 5.x
bug562488_oss_destroy_crash.patch: Fix crash in OSS backend when destroying stream.
bug564734-win32-drain.patch: Ensure we can drain audio stream when there's less
than one block's worth of data left to be written. Also ensure that data is played.
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,44 +0,0 @@
diff --git a/media/libsydneyaudio/src/sydney_audio_alsa.c b/media/libsydneyaudio/src/sydney_audio_alsa.c
--- a/media/libsydneyaudio/src/sydney_audio_alsa.c
+++ b/media/libsydneyaudio/src/sydney_audio_alsa.c
@@ -93,17 +93,17 @@ sa_stream_create_pcm(
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) {
+ 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;
@@ -137,17 +137,21 @@ sa_stream_open(sa_stream_t *s) {
if (snd_pcm_open(&s->output_unit,
"default",
SND_PCM_STREAM_PLAYBACK,
0) < 0) {
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;
return SA_ERROR_NOT_SUPPORTED;

View File

@ -1,105 +0,0 @@
diff --git a/media/libsydneyaudio/src/sydney_audio_waveapi.c b/media/libsydneyaudio/src/sydney_audio_waveapi.c
--- a/media/libsydneyaudio/src/sydney_audio_waveapi.c
+++ b/media/libsydneyaudio/src/sydney_audio_waveapi.c
@@ -416,29 +416,34 @@ int openAudio(sa_stream_t *s) {
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);
- HANDLE_WAVE_ERROR(status, "opening audio device for playback");
- printf("Audio device sucessfully opened\n");
+ 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);
+ 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. Exiting...\n");
- // clean up the memory
- freeBlocks(s->waveBlocks);
+ 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;
@@ -454,40 +459,43 @@ int closeAudio(sa_stream_t * s) {
result = SA_SUCCESS;
// reseting audio device and flushing buffers
status = waveOutReset(s->hWaveOut);
if (status != MMSYSERR_NOERROR) {
result = getSAErrorCode(status);
}
- /* wait for all blocks to complete */
- while(s->waveFreeBlockCount < BLOCK_COUNT) {
- Sleep(10);
+ 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;
}
- /* 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);
status = waveOutClose(s->hWaveOut);
if (status != MMSYSERR_NOERROR) {
result = getSAErrorCode(status);
}
DeleteCriticalSection(&(s->waveCriticalSection));
CloseHandle(s->callbackEvent);
- printf("[audio] audio resources cleanup completed\n");
return result;
}
/**
* \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

View File

@ -1,235 +0,0 @@
diff --git a/media/libsydneyaudio/src/sydney_audio_alsa.c b/media/libsydneyaudio/src/sydney_audio_alsa.c
--- a/media/libsydneyaudio/src/sydney_audio_alsa.c
+++ b/media/libsydneyaudio/src/sydney_audio_alsa.c
@@ -315,16 +315,19 @@ sa_stream_resume(sa_stream_t *s) {
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_RUNNING) {
+ return SA_ERROR_INVALID;
+ }
snd_pcm_drain(s->output_unit);
return SA_SUCCESS;
}
/*
* -----------------------------------------------------------------------------
diff --git a/media/libsydneyaudio/src/sydney_audio_mac.c b/media/libsydneyaudio/src/sydney_audio_mac.c
--- a/media/libsydneyaudio/src/sydney_audio_mac.c
+++ b/media/libsydneyaudio/src/sydney_audio_mac.c
@@ -396,20 +396,20 @@ sa_stream_write(sa_stream_t *s, const vo
/*
* 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;
- if (AudioOutputUnitStart(s->output_unit) != 0) {
- result = SA_ERROR_SYSTEM;
- }
}
return result;
}
static OSStatus
audio_callback(
@@ -553,17 +553,20 @@ sa_stream_pause(sa_stream_t *s) {
}
/*
* 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.
*/
- AudioOutputUnitStop(s->output_unit);
+ if (AudioOutputUnitStop(s->output_unit) != 0) {
+ return SA_ERROR_SYSTEM;
+ }
+ s->playing = FALSE;
return SA_SUCCESS;
}
int
sa_stream_resume(sa_stream_t *s) {
@@ -581,17 +584,20 @@ sa_stream_resume(sa_stream_t *s) {
pthread_mutex_unlock(&s->mutex);
/*
* 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.
*/
- AudioOutputUnitStart(s->output_unit);
+ if (AudioOutputUnitStart(s->output_unit) != 0) {
+ return SA_ERROR_SYSTEM;
+ }
+ s->playing = TRUE;
return SA_SUCCESS;
}
static sa_buf *
new_buffer(void) {
sa_buf * b = malloc(sizeof(sa_buf) + BUF_SIZE);
@@ -607,16 +613,20 @@ new_buffer(void) {
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) {
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);
diff --git a/media/libsydneyaudio/src/sydney_audio_waveapi.c b/media/libsydneyaudio/src/sydney_audio_waveapi.c
--- a/media/libsydneyaudio/src/sydney_audio_waveapi.c
+++ b/media/libsydneyaudio/src/sydney_audio_waveapi.c
@@ -111,16 +111,18 @@ struct sa_stream {
sa_pcm_format_t format;
HWAVEOUT hWaveOut;
HANDLE callbackEvent;
CRITICAL_SECTION waveCriticalSection;
WAVEHDR* waveBlocks;
volatile int waveFreeBlockCount;
int waveCurrentBlock;
+
+ int playing;
};
/** 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);
@@ -157,16 +159,17 @@ int sa_stream_create_pcm(sa_stream_t **s
}
_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 = _s;
return SA_SUCCESS;
}
/** Initialise the device */
int sa_stream_open(sa_stream_t *s) {
int status = SA_SUCCESS;
@@ -300,33 +303,41 @@ int sa_stream_get_position(sa_stream_t *
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) {
ERROR_IF_NO_INIT(s);
+ 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));
@@ -484,16 +495,18 @@ int closeAudio(sa_stream_t * s) {
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 PCM audio samples to audio device
* \param s - valid handle to opened sydney stream
@@ -545,16 +558,18 @@ int writeAudio(sa_stream_t *s, LPSTR dat
/*
* point to the next block
*/
(s->waveCurrentBlock)++;
(s->waveCurrentBlock) %= BLOCK_COUNT;
current = &(s->waveBlocks[s->waveCurrentBlock]);
current->dwUser = 0;
+
+ s->playing = 1;
}
return SA_SUCCESS;
}
/**
* \brief - audio callback function called when next WAVE header is played by audio device
*/
void CALLBACK waveOutProc(

View File

@ -1,49 +0,0 @@
diff --git a/media/libsydneyaudio/src/sydney_audio_oss.c b/media/libsydneyaudio/src/sydney_audio_oss.c
--- a/media/libsydneyaudio/src/sydney_audio_oss.c
+++ b/media/libsydneyaudio/src/sydney_audio_oss.c
@@ -253,39 +253,44 @@ sa_stream_open(sa_stream_t *s) {
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 ALSA to stop
+ * 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;

View File

@ -1,149 +0,0 @@
diff --git a/media/libsydneyaudio/src/sydney_audio_waveapi.c b/media/libsydneyaudio/src/sydney_audio_waveapi.c
--- a/media/libsydneyaudio/src/sydney_audio_waveapi.c
+++ b/media/libsydneyaudio/src/sydney_audio_waveapi.c
@@ -121,16 +121,17 @@ struct sa_stream {
};
/** 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 dwInstance, DWORD dwParam1, DWORD dwParam2);
/** Normal way to open a PCM device */
int sa_stream_create_pcm(sa_stream_t **s,
@@ -320,20 +321,32 @@ int sa_stream_pause(sa_stream_t *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));
@@ -502,16 +515,48 @@ int closeAudio(sa_stream_t * s) {
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) {
@@ -536,40 +581,27 @@ int writeAudio(sa_stream_t *s, LPSTR dat
if(bytes < (int)(BLOCK_SIZE - current->dwUser)) {
memcpy(current->lpData + current->dwUser, data, bytes);
current->dwUser += bytes;
break;
}
/* remain is even as BLOCK_SIZE and dwUser are even too */
- remain = BLOCK_SIZE - current->dwUser;
+ remain = BLOCK_SIZE - current->dwUser;
memcpy(current->lpData + current->dwUser, data, remain);
+ current->dwUser += remain;
bytes -= remain;
data += remain;
- current->dwBufferLength = BLOCK_SIZE;
- /* write to audio device */
- waveOutPrepareHeader(s->hWaveOut, current, sizeof(WAVEHDR));
- status = waveOutWrite(s->hWaveOut, current, sizeof(WAVEHDR));
+
+ status = writeBlock(s, current);
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;
current = &(s->waveBlocks[s->waveCurrentBlock]);
current->dwUser = 0;
-
- s->playing = 1;
}
return SA_SUCCESS;
}
/**
* \brief - audio callback function called when next WAVE header is played by audio device
*/
void CALLBACK waveOutProc(

View File

@ -1,21 +0,0 @@
diff --git a/media/libsydneyaudio/src/sydney_audio_mac.c b/media/libsydneyaudio/src/sydney_audio_mac.c
--- a/media/libsydneyaudio/src/sydney_audio_mac.c
+++ b/media/libsydneyaudio/src/sydney_audio_mac.c
@@ -29,16 +29,17 @@
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** *
*/
#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

View File

@ -1,69 +0,0 @@
diff --git a/media/libsydneyaudio/src/sydney_audio_alsa.c b/media/libsydneyaudio/src/sydney_audio_alsa.c
index a57f640..5c2f113 100644
--- a/media/libsydneyaudio/src/sydney_audio_alsa.c
+++ b/media/libsydneyaudio/src/sydney_audio_alsa.c
@@ -307,9 +307,9 @@ sa_stream_pause(sa_stream_t *s) {
if (s == NULL || s->output_unit == NULL) {
return SA_ERROR_NO_INIT;
}
-#if 0 /* TODO */
- AudioOutputUnitStop(s->output_unit);
-#endif
+
+ if (snd_pcm_pause(s->output_unit, 1) != 0)
+ return SA_ERROR_NOT_SUPPORTED;
return SA_SUCCESS;
}
@@ -322,12 +322,8 @@ sa_stream_resume(sa_stream_t *s) {
return SA_ERROR_NO_INIT;
}
- /*
- * 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 = s->bytes_written = 0;
-
+ if (snd_pcm_pause(s->output_unit, 0) != 0)
+ return SA_ERROR_NOT_SUPPORTED;
return SA_SUCCESS;
}
diff --git a/media/libsydneyaudio/src/sydney_audio_mac.c b/media/libsydneyaudio/src/sydney_audio_mac.c
index 8a7621c..d0fa148 100644
--- a/media/libsydneyaudio/src/sydney_audio_mac.c
+++ b/media/libsydneyaudio/src/sydney_audio_mac.c
@@ -70,6 +70,7 @@ struct sa_stream {
pthread_mutex_t mutex;
bool playing;
int64_t bytes_played;
+ int64_t total_bytes_played;
/* audio format info */
unsigned int rate;
@@ -153,6 +154,7 @@ sa_stream_create_pcm(
s->output_unit = NULL;
s->playing = FALSE;
s->bytes_played = 0;
+ s->total_bytes_played = 0;
s->rate = rate;
s->n_channels = n_channels;
s->bytes_per_ch = 2;
@@ -536,7 +538,7 @@ sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
}
pthread_mutex_lock(&s->mutex);
- *pos = s->bytes_played;
+ *pos = s->total_bytes_played + s->bytes_played;
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
@@ -573,6 +575,7 @@ sa_stream_resume(sa_stream_t *s) {
* The audio device resets its mSampleTime counter after pausing,
* so we need to clear our tracking value to keep that in sync.
*/
+ s->total_bytes_played += s->bytes_played;
s->bytes_played = 0;
pthread_mutex_unlock(&s->mutex);

View File

@ -1,535 +0,0 @@
diff -r b9e9ae011ce4 media/libsydneyaudio/include/sydney_audio.h
--- a/media/libsydneyaudio/include/sydney_audio.h Mon Apr 12 10:01:34 2010 +0200
+++ b/media/libsydneyaudio/include/sydney_audio.h Mon Apr 12 03:25:04 2010 -0500
@@ -80,16 +80,18 @@ async-signal safe.
# 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
diff -r b9e9ae011ce4 media/libsydneyaudio/src/Makefile.in
--- a/media/libsydneyaudio/src/Makefile.in Mon Apr 12 10:01:34 2010 +0200
+++ b/media/libsydneyaudio/src/Makefile.in Mon Apr 12 03:25:27 2010 -0500
@@ -78,15 +78,21 @@ OS_LIBS += -framework CoreAudio -framewo
endif
ifeq ($(OS_ARCH),SunOS)
CSRCS = \
sydney_audio_sunaudio.c \
$(NULL)
endif
+ifeq ($(OS_ARCH),AIX)
+CSRCS = \
+ sydney_audio_aix.c \
+ $(NULL)
+endif
+
ifeq ($(OS_ARCH),WINNT)
OS_LIBS += winmm.lib
endif
include $(topsrcdir)/config/rules.mk
LOCAL_INCLUDES += -I$(srcdir)/../include
diff -r b9e9ae011ce4 media/libsydneyaudio/src/sydney_audio_aix.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/media/libsydneyaudio/src/sydney_audio_aix.c Mon Apr 12 03:25:27 2010 -0500
@@ -0,0 +1,484 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Initial Developer of the Original Code is
+ * Shailendra N Jain<shailen.n.jain@gmail.com>
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** *
+ */
+#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))
+
+const char *sa_strerror(int code) { return NULL; }
+

View File

@ -1,551 +0,0 @@
diff --git a/media/libsydneyaudio/src/Makefile.in b/media/libsydneyaudio/src/Makefile.in
--- a/media/libsydneyaudio/src/Makefile.in
+++ b/media/libsydneyaudio/src/Makefile.in
@@ -51,7 +51,11 @@ CSRCS = \
$(NULL)
endif
-ifeq ($(OS_ARCH),Linux)
+ifeq ($(OS_TARGET),Android)
+CSRCS = \
+ sydney_audio_android.c \
+ $(NULL)
+else ifeq ($(OS_ARCH),Linux)
CSRCS = \
sydney_audio_alsa.c \
$(NULL)
diff --git a/media/libsydneyaudio/src/sydney_audio_android.c b/media/libsydneyaudio/src/sydney_audio_android.c
new file mode 100644
--- /dev/null
+++ b/media/libsydneyaudio/src/sydney_audio_android.c
@@ -0,0 +1,530 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Initial Developer of the Original Code is
+ * CSIRO
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Michael Martin
+ * Michael Wu <mwu@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** *
+ */
+
+#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_IN_MILLISECOND 1000000
+#define MILLISECONDS_PER_SECOND 1000
+
+/* android.media.AudioTrack */
+struct AudioTrack {
+ jclass class;
+ jmethodID constructor;
+ jmethodID flush;
+ jmethodID pause;
+ jmethodID play;
+ 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;
+
+ unsigned int rate;
+ unsigned int channels;
+ unsigned int isPaused;
+
+ int64_t lastStartTime;
+ int64_t timePlaying;
+ 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)->NewGlobalRef(jenv,
+ (*jenv)->FindClass(jenv,
+ "android/media/AudioTrack"));
+ at.constructor = (*jenv)->GetMethodID(jenv, class, "<init>", "(IIIIII)V");
+ at.flush = (*jenv)->GetMethodID(jenv, class, "flush", "()V");
+ at.pause = (*jenv)->GetMethodID(jenv, class, "pause", "()V");
+ at.play = (*jenv)->GetMethodID(jenv, class, "play", "()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 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->rate = rate;
+ s->channels = channels;
+ s->isPaused = 0;
+
+ s->lastStartTime = 0;
+ s->timePlaying = 0;
+ s->amountWritten = 0;
+
+ s->bufferSize = rate * channels;
+
+ *_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);
+
+ int32_t chanConfig = s->channels == 1 ?
+ CHANNEL_OUT_MONO : CHANNEL_OUT_STEREO;
+
+ jobject obj =
+ (*jenv)->NewObject(jenv, s->at_class, at.constructor,
+ STREAM_MUSIC,
+ s->rate,
+ chanConfig,
+ ENCODING_PCM_16BIT,
+ s->bufferSize,
+ MODE_STREAM);
+
+ if (!obj) {
+ (*jenv)->DeleteGlobalRef(jenv, s->at_class);
+ (*jenv)->PopLocalFrame(jenv, NULL);
+ return SA_ERROR_OOM;
+ }
+
+ s->output_unit = (*jenv)->NewGlobalRef(jenv, obj);
+ (*jenv)->PopLocalFrame(jenv, NULL);
+
+ ALOG("%x - New stream %d %d", s, s->rate, s->channels);
+ 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;
+
+ (*jenv)->DeleteGlobalRef(jenv, s->output_unit);
+ (*jenv)->DeleteGlobalRef(jenv, s->at_class);
+ free(s);
+
+ ALOG("%x - 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;
+ }
+
+ jbyteArray bytearray = (*jenv)->NewByteArray(jenv, nbytes);
+ if (!bytearray) {
+ (*jenv)->ExceptionClear(jenv);
+ (*jenv)->PopLocalFrame(jenv, NULL);
+ return SA_ERROR_OOM;
+ }
+
+ jbyte *byte = (*jenv)->GetByteArrayElements(jenv, bytearray, NULL);
+ if (!byte) {
+ (*jenv)->PopLocalFrame(jenv, NULL);
+ return SA_ERROR_OOM;
+ }
+
+ memcpy(byte, data, nbytes);
+
+ size_t wroteSoFar = 0;
+ jint retval;
+
+ do {
+ retval = (*jenv)->CallIntMethod(jenv,
+ s->output_unit,
+ at.write,
+ bytearray,
+ wroteSoFar,
+ nbytes - wroteSoFar);
+ if (retval < 0) {
+ ALOG("%x - Write failed %d", s, retval);
+ break;
+ }
+
+ wroteSoFar += retval;
+
+ if (wroteSoFar != nbytes) {
+
+ /* android doesn't start playing until we explictly call play. */
+ if (!s->isPaused)
+ sa_stream_resume(s);
+
+ struct timespec ts = {0, 100000000}; /* .10s */
+ nanosleep(&ts, NULL);
+ }
+ } while(wroteSoFar < nbytes);
+
+ s->amountWritten += nbytes;
+
+ (*jenv)->ReleaseByteArrayElements(jenv, bytearray, byte, 0);
+
+ (*jenv)->PopLocalFrame(jenv, NULL);
+
+ return retval < 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 android API for this, so estimate based on how much we have played and
+ * how much we have written.
+ */
+ *size = s->bufferSize - ((s->timePlaying * s->channels * s->rate /
+ MILLISECONDS_PER_SECOND) - s->amountWritten);
+ ALOG("%x - Write Size %d", s, *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;
+ }
+
+ ALOG("%x - get position", s);
+
+ 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 */
+ if (s->lastStartTime != 0) {
+ /* if lastStartTime is not zero, so playback has started */
+ struct timespec current_time;
+ clock_gettime(CLOCK_REALTIME, &current_time);
+ int64_t ticker = current_time.tv_sec * 1000 + current_time.tv_nsec / 1000000;
+ s->timePlaying += ticker - s->lastStartTime;
+ }
+ ALOG("%x - Pause total time playing: %lld total written: %lld", s, s->timePlaying, s->amountWritten);
+
+ (*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("%x - resume", s);
+
+ JNIEnv *jenv = GetJNIForThread();
+ s->isPaused = 0;
+
+ /* Update stats */
+ struct timespec current_time;
+ clock_gettime(CLOCK_REALTIME, &current_time);
+ int64_t ticker = current_time.tv_sec * 1000 + current_time.tv_nsec / 1000000;
+ s->lastStartTime = ticker;
+
+ (*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;
+ }
+
+ /* There is no way with the Android SDK to determine exactly how
+ long to playback. So estimate and sleep for the long.
+ */
+
+ size_t available;
+ sa_stream_get_write_size(s, &available);
+
+ long x = (s->bufferSize - available) * 1000 / s->channels / s->rate / sizeof(int16_t) * NANOSECONDS_IN_MILLISECOND;
+ ALOG("%x - Drain - sleep for %f ns", s, x);
+
+ struct timespec ts = {0, x};
+ 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))
+
+const char *sa_strerror(int code) { return NULL; }
+

View File

@ -1,920 +0,0 @@
diff --git a/media/libsydneyaudio/include/sydney_audio.h b/media/libsydneyaudio/include/sydney_audio.h
--- a/media/libsydneyaudio/include/sydney_audio.h
+++ b/media/libsydneyaudio/include/sydney_audio.h
@@ -95,18 +95,18 @@ 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)
-// (left << 16 | right) (16 bits per channel)
+#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 {
diff --git a/media/libsydneyaudio/src/sydney_audio_os2.c b/media/libsydneyaudio/src/sydney_audio_os2.c
new file mode 100644
--- /dev/null
+++ b/media/libsydneyaudio/src/sydney_audio_os2.c
@@ -0,0 +1,889 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Initial Developers of the Original Code are
+ * Andrew Zabolotny (libdart) - Copyright (C) 1998
+ * CSIRO (libsydneyaudio)- Copyright (C) 2007
+ * Richard Walsh (OS/2 implementation) - Copyright (C) 2008
+ * Portions created by the Initial Developers are Copyright (c) 1998,2007,2008,
+ * the Initial Developers. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** *
+ */
+
+/*****************************************************************************/
+/* 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 2 event semaphores 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>
+
+/*****************************************************************************/
+
+/* 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 - the ogg decoder typically
+ * writes 8k at a time, so this works out to roughly 1/2 second */
+#define SAOS2_BUF_CNT 11
+
+/* this could be as large as 65535 but making it smaller helps
+ * avoid having the DART event thread think it's running out of
+ * buffers if the decoder sends larger chunks of data less often */
+#define SAOS2_RAW_BUFSIZE 16384
+
+/* playback states */
+#define SAOS2_INIT 0
+#define SAOS2_RECOVER 1
+#define SAOS2_PLAY 2
+#define SAOS2_EXIT 3
+
+/* an indefinite wait invites a hung thread */
+#define SAOS2_SEM_WAIT 5000
+
+/* the only 2 return codes we care about */
+#ifndef INCL_DOSERRORS
+#define ERROR_ALREADY_POSTED 299
+#define ERROR_ALREADY_RESET 300
+#endif
+
+/*****************************************************************************/
+/* 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;
+
+ /* device info */
+ uint16_t hwDeviceID;
+ uint32_t hwMixHandle;
+ PMIXERPROC hwWriteProc;
+
+ /* buffer allocations */
+ int32_t bufCnt;
+ size_t bufSize;
+ PMCI_MIX_BUFFER bufList;
+
+ /* buffer usage tracking */
+ HEV freeSem;
+ int32_t freeCnt;
+ int32_t freeNdx;
+ int32_t readyCnt;
+ int32_t readyNdx;
+ HEV usedSem;
+ volatile int32_t usedCnt;
+
+ /* miscellaneous */
+ volatile int32_t state;
+ int64_t writePos;
+};
+
+/*****************************************************************************/
+/* Private (static) Functions */
+
+static int32_t os2_mixer_event(uint32_t ulStatus, PMCI_MIX_BUFFER pBuffer,
+ uint32_t ulFlags);
+static int os2_write_to_device(sa_stream_t *s);
+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);
+
+/*****************************************************************************/
+/* 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 {
+ 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];
+
+ /* set the number of buffers; round the buffer
+ * size down to the nearest multiple of a frame; */
+ sTemp->bufCnt = SAOS2_BUF_CNT;
+ sTemp->bufSize = SAOS2_RAW_BUFSIZE -
+ (SAOS2_RAW_BUFSIZE % (SAOS2_SAMPLE_SIZE * nchannels));
+
+ /* create event semaphores to signal free buffers */
+ rc = DosCreateEventSem(0, &sTemp->freeSem, 0, FALSE);
+ if (!rc)
+ rc = DosCreateEventSem(0, &sTemp->usedSem, 0, FALSE);
+ if (rc) {
+ status = os2_error(SA_ERROR_SYSTEM, "sa_stream_create_pcm",
+ "DosCreateEventSem - rc=", rc);
+ break;
+ }
+
+ /* fill in the miscellanea */
+ sTemp->client_name = client_name;
+ sTemp->mode = mode;
+ sTemp->format = format;
+ sTemp->rate = rate;
+ sTemp->nchannels = nchannels;
+
+ *s = sTemp;
+
+} while (0);
+
+ /* on error, free any allocations */
+ if (status != SA_SUCCESS && sTemp) {
+ if (sTemp->freeSem)
+ DosCloseEventSem(sTemp->freeSem);
+ if (sTemp->usedSem)
+ DosCloseEventSem(sTemp->usedSem);
+ 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 */
+ 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->state = SAOS2_EXIT;
+
+ /* 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 */
+ if (s->freeSem)
+ DosCloseEventSem(s->freeSem);
+ if (s->usedSem)
+ DosCloseEventSem(s->usedSem);
+ 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) {
+
+ /* 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]);
+ cnt = (nbytes > s->bufSize) ? s->bufSize : nbytes;
+ memcpy(pHW->pBuffer, (char*)data, cnt);
+ pHW->ulBufferLength = cnt;
+ nbytes -= cnt;
+ data = (char*)data + cnt;
+
+ /* adjust cnts & indices, then send the buffer to the device */
+ s->freeCnt--;
+ s->freeNdx = (s->freeNdx + 1) % s->bufCnt;
+ s->readyCnt++;
+ if (os2_write_to_device(s))
+ return SA_ERROR_SYSTEM;
+ }
+
+ return SA_SUCCESS;
+}
+
+/*****************************************************************************/
+
+/** sync/timing */
+
+int sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos)
+{
+ uint32_t rc;
+
+ 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);
+
+ /* this is the nbr of bytes that are known to have been played
+ * already; the MCI command to get stream position isn't usable -
+ * it returns a time value that resets when the stream is paused */
+ *pos = s->writePos;
+
+ 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));
+
+ rc = mciSendCommand(s->hwDeviceID, MCI_RESUME,
+ MCI_WAIT,
+ (void*)&GenericParms, 0);
+ if (LOUSHORT(rc))
+ return os2_error(SA_ERROR_SYSTEM, "sa_stream_resume",
+ "MCI_RESUME - rc=", LOUSHORT(rc));
+
+ 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 */
+ 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->state = SAOS2_EXIT;
+
+ /* DART won't start playing until 2 buffers have been written,
+ * so write a dummy 2nd buffer if any buffers are in use */
+ if (s->freeCnt < SAOS2_BUF_CNT) {
+ memset(buf, 0, sizeof(buf));
+ sa_stream_write(s, buf, s->nchannels * SAOS2_SAMPLE_SIZE);
+ }
+
+ /* write all remaining buffers to the device */
+ if (s->readyCnt)
+ status = os2_write_to_device(s);
+
+ /* wait for all buffers to become free */
+ if (!status)
+ status = os2_get_free_count(s, s->bufCnt);
+
+ /* 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;
+ }
+
+ /* limiting each write to a single buffer
+ * produces smoother results in some cases */
+ *size = s->freeCnt ? s->bufSize : 0;
+
+ 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)
+{
+ uint32_t rc;
+ int32_t posted;
+ sa_stream_t * s;
+
+ /* check for errors */
+ if (ulFlags & MIX_STREAM_ERROR)
+ rc = 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 */
+ rc = DosResetEventSem(s->usedSem, (unsigned long*)&posted);
+ if (rc && rc != ERROR_ALREADY_RESET) {
+ posted = 0;
+ rc = os2_error(rc, "os2_mixer_event", "DosResetEventSem - rc=", rc);
+ }
+ s->usedCnt += posted - 1;
+
+ /* 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 < 2 && s->state == SAOS2_PLAY) {
+ s->state = SAOS2_RECOVER;
+ os2_pause_device(s->hwDeviceID, FALSE);
+ rc = os2_error(rc, "os2_mixer_event",
+ "too few buffers in use - recovering", 0);
+ }
+
+ /* setting the write position after the buffer has been played yields
+ * far more accurate timing than setting it in sa_stream_write() */
+ s->writePos = s->writePos + (int64_t)pBuffer->ulBufferLength;
+ pBuffer->ulBufferLength = 0;
+
+ /* signal the decode thread that a buffer is available */
+ rc = DosPostEventSem(s->freeSem);
+ if (rc && rc != ERROR_ALREADY_POSTED)
+ rc = os2_error(rc, "os2_mixer_event", "DosPostEventSem - rc=", rc);
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+/** write as many buffers as available to the device */
+
+static int os2_write_to_device(sa_stream_t *s)
+{
+ uint32_t rc;
+ int32_t cnt;
+ int32_t ctr;
+
+ /* this executes twice if bufList wraps, otherwise just once */
+ while (s->readyCnt) {
+
+ /* deal with wrap */
+ cnt = (s->readyNdx + s->readyCnt > s->bufCnt) ?
+ (s->bufCnt - s->readyNdx) : s->readyCnt;
+
+ /* if the write fails, abort */
+ rc = s->hwWriteProc(s->hwMixHandle, &(s->bufList[s->readyNdx]), cnt);
+ if (LOUSHORT(rc))
+ return os2_error(SA_ERROR_SYSTEM, "os2_write_to_device",
+ "mixWrite - rc=", LOUSHORT(rc));
+
+ /* signal the event thread that 'cnt' buffers are now in use */
+ for (ctr = 0; ctr < cnt; ctr++) {
+ rc = DosPostEventSem(s->usedSem);
+ if (rc && rc != ERROR_ALREADY_POSTED)
+ return os2_error(SA_ERROR_SYSTEM, "os2_write_to_device",
+ "DosPostEventSem - rc=", rc);
+ }
+
+ /* advance to the next entry */
+ s->readyNdx = (s->readyNdx + cnt) % s->bufCnt;
+ s->readyCnt -= cnt;
+ }
+
+ /* if state is INIT or RECOVER, change to PLAY */
+ if (s->state < SAOS2_PLAY)
+ s->state = SAOS2_PLAY;
+
+ return SA_SUCCESS;
+}
+
+/*****************************************************************************/
+
+/** 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 rc;
+ int32_t posted;
+
+ while (1) {
+ rc = DosResetEventSem(s->freeSem, (unsigned long*)&posted);
+ if (rc && rc != ERROR_ALREADY_RESET)
+ return os2_error(SA_ERROR_SYSTEM, "os2_get_free_count",
+ "DosResetEventSem - rc=", rc);
+
+ s->freeCnt += posted;
+ if (s->freeCnt >= count)
+ break;
+
+ rc = DosWaitEventSem(s->freeSem, SAOS2_SEM_WAIT);
+ if (rc)
+ return os2_error(SA_ERROR_SYSTEM, "os2_get_free_count",
+ "DosWaitEventSem - rc=", rc);
+ }
+
+ 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
+
+/*****************************************************************************/
+/* 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))
+
+const char *sa_strerror(int code) { return NULL; }
+
+/*****************************************************************************/
+

View File

@ -1,901 +0,0 @@
diff --git a/media/libsydneyaudio/src/sydney_audio_os2.c b/media/libsydneyaudio/src/sydney_audio_os2.c
--- a/media/libsydneyaudio/src/sydney_audio_os2.c
+++ b/media/libsydneyaudio/src/sydney_audio_os2.c
@@ -44,64 +44,60 @@
* 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 2 event semaphores 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.
+ * 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 - the ogg decoder typically
- * writes 8k at a time, so this works out to roughly 1/2 second */
-#define SAOS2_BUF_CNT 11
-
-/* this could be as large as 65535 but making it smaller helps
- * avoid having the DART event thread think it's running out of
- * buffers if the decoder sends larger chunks of data less often */
-#define SAOS2_RAW_BUFSIZE 16384
+/* 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
-/* playback states */
-#define SAOS2_INIT 0
-#define SAOS2_RECOVER 1
-#define SAOS2_PLAY 2
-#define SAOS2_EXIT 3
+/* 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
-/* an indefinite wait invites a hung thread */
-#define SAOS2_SEM_WAIT 5000
+/* 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
-/* the only 2 return codes we care about */
-#ifndef INCL_DOSERRORS
-#define ERROR_ALREADY_POSTED 299
-#define ERROR_ALREADY_RESET 300
-#endif
+/* 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
@@ -120,52 +116,64 @@
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 */
- HEV freeSem;
+ volatile uint32_t freeNew;
int32_t freeCnt;
int32_t freeNdx;
- int32_t readyCnt;
- int32_t readyNdx;
- HEV usedSem;
- volatile int32_t usedCnt;
+ volatile uint32_t usedNew;
+ int32_t usedCnt;
+ int32_t usedMin;
/* miscellaneous */
- volatile int32_t state;
+ 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 int os2_write_to_device(sa_stream_t *s);
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,
@@ -176,16 +184,20 @@ int sa_stream_create_pcm(sa_stream_t
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;
@@ -199,49 +211,45 @@ do {
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];
- /* set the number of buffers; round the buffer
- * size down to the nearest multiple of a frame; */
- sTemp->bufCnt = SAOS2_BUF_CNT;
- sTemp->bufSize = SAOS2_RAW_BUFSIZE -
- (SAOS2_RAW_BUFSIZE % (SAOS2_SAMPLE_SIZE * nchannels));
-
- /* create event semaphores to signal free buffers */
- rc = DosCreateEventSem(0, &sTemp->freeSem, 0, FALSE);
- if (!rc)
- rc = DosCreateEventSem(0, &sTemp->usedSem, 0, FALSE);
- if (rc) {
- status = os2_error(SA_ERROR_SYSTEM, "sa_stream_create_pcm",
- "DosCreateEventSem - rc=", rc);
- break;
- }
-
/* 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->freeSem)
- DosCloseEventSem(sTemp->freeSem);
- if (sTemp->usedSem)
- DosCloseEventSem(sTemp->usedSem);
if (sTemp)
DosFreeMem(sTemp);
}
return status;
}
/*****************************************************************************/
@@ -265,17 +273,17 @@ 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,
+ 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;
}
@@ -287,17 +295,17 @@ do {
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,
+ 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;
}
@@ -306,31 +314,32 @@ do {
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,
+ 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);
@@ -350,50 +359,57 @@ int sa_stream_destroy(sa_stream_t *s
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->state = SAOS2_EXIT;
+ 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,
+ 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,
+ 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 */
- if (s->freeSem)
- DosCloseEventSem(s->freeSem);
- if (s->usedSem)
- DosCloseEventSem(s->usedSem);
DosFreeMem(s);
return status;
}
/*****************************************************************************/
/** Interleaved playback function */
@@ -411,61 +427,95 @@ int sa_stream_write(sa_stream_t * s,
/* 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]);
- cnt = (nbytes > s->bufSize) ? s->bufSize : nbytes;
- memcpy(pHW->pBuffer, (char*)data, cnt);
- pHW->ulBufferLength = cnt;
+
+ 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;
- /* adjust cnts & indices, then send the buffer to the device */
+ /* 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;
- s->readyCnt++;
- if (os2_write_to_device(s))
- return SA_ERROR_SYSTEM;
}
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);
- /* this is the nbr of bytes that are known to have been played
- * already; the MCI command to get stream position isn't usable -
- * it returns a time value that resets when the stream is paused */
- *pos = s->writePos;
+ /* 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 */
@@ -473,75 +523,86 @@ 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,
+ 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));
- rc = mciSendCommand(s->hwDeviceID, MCI_RESUME,
+ /* 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))
- return os2_error(SA_ERROR_SYSTEM, "sa_stream_resume",
- "MCI_RESUME - rc=", 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->state = SAOS2_EXIT;
+ 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 any buffers are in use */
- if (s->freeCnt < SAOS2_BUF_CNT) {
- memset(buf, 0, sizeof(buf));
+ * 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);
- }
-
- /* write all remaining buffers to the device */
- if (s->readyCnt)
- status = os2_write_to_device(s);
/* 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;
}
/*****************************************************************************/
@@ -556,19 +617,17 @@ int sa_stream_get_write_size(sa_stre
/* 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;
}
- /* limiting each write to a single buffer
- * produces smoother results in some cases */
- *size = s->freeCnt ? s->bufSize : 0;
+ *size = s->freeCnt * s->bufSize;
return SA_SUCCESS;
}
/*****************************************************************************/
/** set absolute volume using a value ranging from 0.0 to 1.0 */
@@ -581,17 +640,17 @@ int sa_stream_set_volume_abs(sa_stre
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,
+ 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;
}
@@ -608,17 +667,17 @@ int sa_stream_get_volume_abs(sa_stre
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,
+ 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));
}
@@ -638,118 +697,70 @@ int sa_stream_get_volume_abs(sa_stre
/*****************************************************************************/
/** 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)
{
- uint32_t rc;
- int32_t posted;
sa_stream_t * s;
/* check for errors */
if (ulFlags & MIX_STREAM_ERROR)
- rc = os2_error(0, "os2_mixer_event", "MIX_STREAM_ERROR - status=", ulStatus);
+ 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 */
- rc = DosResetEventSem(s->usedSem, (unsigned long*)&posted);
- if (rc && rc != ERROR_ALREADY_RESET) {
- posted = 0;
- rc = os2_error(rc, "os2_mixer_event", "DosResetEventSem - rc=", rc);
- }
- s->usedCnt += posted - 1;
+ 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 < 2 && s->state == SAOS2_PLAY) {
- s->state = SAOS2_RECOVER;
+ if (s->usedCnt < s->usedMin) {
+ s->playing = FALSE;
os2_pause_device(s->hwDeviceID, FALSE);
- rc = os2_error(rc, "os2_mixer_event",
- "too few buffers in use - recovering", 0);
+ os2_error(0, "os2_mixer_event",
+ "too few buffers in use - recovering", 0);
}
- /* setting the write position after the buffer has been played yields
- * far more accurate timing than setting it in sa_stream_write() */
- s->writePos = s->writePos + (int64_t)pBuffer->ulBufferLength;
+ /* 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 */
- rc = DosPostEventSem(s->freeSem);
- if (rc && rc != ERROR_ALREADY_POSTED)
- rc = os2_error(rc, "os2_mixer_event", "DosPostEventSem - rc=", rc);
+ __atomic_increment(&s->freeNew);
return TRUE;
}
/*****************************************************************************/
-/** write as many buffers as available to the device */
-
-static int os2_write_to_device(sa_stream_t *s)
-{
- uint32_t rc;
- int32_t cnt;
- int32_t ctr;
-
- /* this executes twice if bufList wraps, otherwise just once */
- while (s->readyCnt) {
-
- /* deal with wrap */
- cnt = (s->readyNdx + s->readyCnt > s->bufCnt) ?
- (s->bufCnt - s->readyNdx) : s->readyCnt;
-
- /* if the write fails, abort */
- rc = s->hwWriteProc(s->hwMixHandle, &(s->bufList[s->readyNdx]), cnt);
- if (LOUSHORT(rc))
- return os2_error(SA_ERROR_SYSTEM, "os2_write_to_device",
- "mixWrite - rc=", LOUSHORT(rc));
-
- /* signal the event thread that 'cnt' buffers are now in use */
- for (ctr = 0; ctr < cnt; ctr++) {
- rc = DosPostEventSem(s->usedSem);
- if (rc && rc != ERROR_ALREADY_POSTED)
- return os2_error(SA_ERROR_SYSTEM, "os2_write_to_device",
- "DosPostEventSem - rc=", rc);
- }
-
- /* advance to the next entry */
- s->readyNdx = (s->readyNdx + cnt) % s->bufCnt;
- s->readyCnt -= cnt;
- }
-
- /* if state is INIT or RECOVER, change to PLAY */
- if (s->state < SAOS2_PLAY)
- s->state = SAOS2_PLAY;
-
- return SA_SUCCESS;
-}
-
-/*****************************************************************************/
-
/** stop playback */
static void os2_stop_device(uint16_t hwDeviceID)
{
uint32_t rc;
MCI_GENERIC_PARMS GenericParms = { 0 };
- rc = mciSendCommand(hwDeviceID, MCI_STOP,
+ 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;
}
@@ -757,54 +768,56 @@ static void os2_stop_device(uint16_t hwD
/** 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,
+ 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,
+ _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 rc;
- int32_t posted;
+ uint32_t timeout = 0;
while (1) {
- rc = DosResetEventSem(s->freeSem, (unsigned long*)&posted);
- if (rc && rc != ERROR_ALREADY_RESET)
- return os2_error(SA_ERROR_SYSTEM, "os2_get_free_count",
- "DosResetEventSem - rc=", rc);
+ uint32_t now;
- s->freeCnt += posted;
+ s->freeCnt += __atomic_xchg(&s->freeNew, 0);
if (s->freeCnt >= count)
break;
- rc = DosWaitEventSem(s->freeSem, SAOS2_SEM_WAIT);
- if (rc)
+ /* 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",
- "DosWaitEventSem - rc=", rc);
+ "timed-out waiting for free buffer(s)", 0);
+
+ DosSleep(1);
}
return SA_SUCCESS;
}
/*****************************************************************************/
#ifdef SAOS2_ERROR
@@ -820,16 +833,47 @@ static int os2_error_msg(int rtn, char
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))

View File

@ -1,18 +0,0 @@
# Usage: ./update.sh <libsydneyaudio_src_directory>
#
# Copies the needed files from a directory containing the original
# libsydneyaudio source that we need for the Mozilla HTML5 media support.
cp $1/include/sydney_audio.h include/sydney_audio.h
cp $1/src/*.c src/
cp $1/AUTHORS ./AUTHORS
patch -p4 <pause-resume.patch
patch -p4 <include-CoreServices.patch
patch -p4 <sydney_os2_base.patch
patch -p4 <sydney_os2_moz.patch
patch -p3 <bug495794_closeAudio.patch
patch -p3 <bug495558_alsa_endian.patch
patch -p3 <bug525401_drain_deadlock.patch
patch -p3 <sydney_aix.patch
patch -p3 <bug562488_oss_destroy_crash.patch
patch -p3 <bug564734-win32-drain.patch
patch -p3 <sydney_android.patch