mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 683822 - Delete media/libsydneyaudio patches and update.sh, since this is no longer maintained. r=doublec
This commit is contained in:
parent
da0b8fa230
commit
ff0c4ed5ec
@ -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.
|
||||
|
@ -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;
|
@ -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
|
@ -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(
|
@ -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;
|
@ -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(
|
@ -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
|
@ -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);
|
||||
|
@ -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; }
|
||||
+
|
@ -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, ¤t_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, ¤t_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; }
|
||||
+
|
@ -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; }
|
||||
+
|
||||
+/*****************************************************************************/
|
||||
+
|
@ -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))
|
@ -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
|
Loading…
Reference in New Issue
Block a user