Several Sweeeet fixes...

- Audio lag is now fixed - using the OpenSLES library (courtesy of Emile Belanger's Dark Places build for Android)
- 120hz mode can be used on the Quest 2 by adding the following to the command line:
``` -r 120 ```
This commit is contained in:
Simon 2021-12-19 20:32:37 +00:00
parent 678877bda4
commit 605d1edb6e
20 changed files with 4440 additions and 230 deletions

View file

@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.drbeef.quakequest"
android:versionCode="18"
android:versionName="1.4.10"
android:versionName="1.4.11"
android:installLocation="auto" >
<!-- Tell the system this app requires OpenGL ES 3.1. -->

View file

@ -8,7 +8,7 @@ include $(CLEAR_VARS)
LOCAL_CFLAGS := -std=c99
LOCAL_MODULE := quakequest
LOCAL_LDLIBS := -llog -landroid -lGLESv3 -lEGL # include default libraries
LOCAL_LDLIBS := -llog -landroid -lGLESv3 -lEGL -lOpenSLES # include default libraries
LOCAL_C_INCLUDES := ../QuakeQuestSrc/ \
../darkplaces/ \
@ -17,12 +17,13 @@ $(SUPPORT_LIBS)/liboggvorbis/include
LOCAL_SHARED_LIBRARIES := vrapi libvorbis libogg libvorbis-jni
SRC_SND_COMMON := \
darkplaces/snd_opensl.c \
darkplaces/snd_main.c \
darkplaces/snd_mem.c \
darkplaces/snd_mix.c \
darkplaces/snd_ogg.c \
darkplaces/snd_modplug.c \
darkplaces/snd_wav.c \
darkplaces/snd_modplug.c
###### Common objects and flags #####
@ -128,7 +129,6 @@ LOCAL_SRC_FILES := \
darkplaces/sys_linux.c \
darkplaces/vid_android.c \
darkplaces/thread_pthread.c \
darkplaces/snd_android.c \
$(SRC_SND_COMMON) \
$(SRC_COMMON)

View file

@ -89,10 +89,11 @@ PFNEGLGETSYNCATTRIBKHRPROC eglGetSyncAttribKHR;
//Let's go to the maximum!
int CPU_LEVEL = 4;
int GPU_LEVEL = 4;
int REFRESH = -1;
int NUM_MULTI_SAMPLES = 1;
float SS_MULTIPLIER = 1.2f;
float maximumSupportedFramerate=60.0; //The lowest default framerate
float selectedFramerate=60.0; //The lowest default framerate
extern float worldPosition[3];
float hmdPosition[3];
@ -127,6 +128,7 @@ float degrees(float rad) {
struct arg_dbl *ss;
struct arg_int *cpu;
struct arg_int *gpu;
struct arg_int *refresh;
struct arg_dbl *msaa;
struct arg_end *end;
@ -174,74 +176,8 @@ extern int key_consoleactive;
static bool quake_initialised = false;
static JavaVM *jVM;
static jobject audioBuffer=0;
static jobject audioCallbackObj=0;
static jobject qquestCallbackObj=0;
jmethodID android_initAudio;
jmethodID android_writeAudio;
jmethodID android_pauseAudio;
jmethodID android_resumeAudio;
jmethodID android_terminateAudio;
void jni_initAudio(void *buffer, int size)
{
ALOGV("Calling: jni_initAudio");
JNIEnv *env;
jobject tmp;
(*jVM)->GetEnv(jVM, (void**) &env, JNI_VERSION_1_4);
tmp = (*env)->NewDirectByteBuffer(env, buffer, size);
audioBuffer = (jobject)(*env)->NewGlobalRef(env, tmp);
return (*env)->CallVoidMethod(env, audioCallbackObj, android_initAudio, size);
}
void jni_writeAudio(int offset, int length)
{
ALOGV("Calling: jni_writeAudio");
if (audioBuffer==0) return;
JNIEnv *env;
if (((*jVM)->GetEnv(jVM, (void**) &env, JNI_VERSION_1_4))<0)
{
(*jVM)->AttachCurrentThread(jVM,&env, NULL);
}
(*env)->CallVoidMethod(env, audioCallbackObj, android_writeAudio, audioBuffer, offset, length);
}
void jni_pauseAudio()
{
ALOGV("Calling: jni_pauseAudio");
if (audioBuffer==0) return;
JNIEnv *env;
if (((*jVM)->GetEnv(jVM, (void**) &env, JNI_VERSION_1_4))<0)
{
(*jVM)->AttachCurrentThread(jVM,&env, NULL);
}
(*env)->CallVoidMethod(env, audioCallbackObj, android_pauseAudio);
}
void jni_resumeAudio()
{
ALOGV("Calling: jni_resumeAudio");
if (audioBuffer==0) return;
JNIEnv *env;
if (((*jVM)->GetEnv(jVM, (void**) &env, JNI_VERSION_1_4))<0)
{
(*jVM)->AttachCurrentThread(jVM,&env, NULL);
}
(*env)->CallVoidMethod(env, audioCallbackObj, android_resumeAudio);
}
void jni_terminateAudio()
{
ALOGV("Calling: jni_terminateAudio");
if (audioBuffer==0) return;
JNIEnv *env;
if (((*jVM)->GetEnv(jVM, (void**) &env, JNI_VERSION_1_4))<0)
{
(*jVM)->AttachCurrentThread(jVM,&env, NULL);
}
(*env)->CallVoidMethod(env, audioCallbackObj, android_terminateAudio);
}
//Timing stuff for joypad control
static long oldtime=0;
@ -1202,8 +1138,8 @@ static void ovrApp_HandleVrModeChanges( ovrApp * app )
if ( app->Ovr != NULL )
{
//AmmarkoV : Set our refresh rate..!
ovrResult result = vrapi_SetDisplayRefreshRate(app->Ovr,maximumSupportedFramerate);
if (result == ovrSuccess) { ALOGV("Changed refresh rate. %f Hz",maximumSupportedFramerate); } else
ovrResult result = vrapi_SetDisplayRefreshRate(app->Ovr, selectedFramerate);
if (result == ovrSuccess) { ALOGV("Changed refresh rate. %f Hz", selectedFramerate); } else
{ ALOGV("Failed to change refresh rate to 90Hz Result=%d",result); }
vrapi_SetClockLevels( app->Ovr, app->CpuLevel, app->GpuLevel );
@ -1729,8 +1665,8 @@ static void ovrApp_HandleInput( ovrApp * app )
//This section corrects for the fact that the controller actually controls direction of movement, but we want to move relative to the direction the
//player is facing for positional tracking
float multiplier = /*arbitrary value that works ->*/
2300.0f / (cl_movementspeed.value * ((offHandTrackedRemoteState->Buttons & ovrButton_Trigger) ? cl_movespeedkey.value : 1.0f));
float multiplier = (float)(2300.0f * (selectedFramerate / 72.0) ) /
(cl_movementspeed.value * ((offHandTrackedRemoteState->Buttons & ovrButton_Trigger) ? cl_movespeedkey.value : 1.0f));
vec2_t v;
rotateAboutOrigin(-positionDeltaThisFrame[0] * multiplier,
@ -2227,18 +2163,39 @@ void * AppThreadFunction( void * parm )
float refreshRatesArray[16];
if (numberOfRefreshRates > 16 ) { numberOfRefreshRates = 16; }
vrapi_GetSystemPropertyFloatArray(&java, VRAPI_SYS_PROP_SUPPORTED_DISPLAY_REFRESH_RATES,&refreshRatesArray[0], numberOfRefreshRates);
for (int i = 0; i < numberOfRefreshRates; i++) {
//ALOGV("Supported refresh rate : %g Hz", refreshRatesArray[i]);
if (maximumSupportedFramerate<refreshRatesArray[i])
{
maximumSupportedFramerate=refreshRatesArray[i];
}
}
if (maximumSupportedFramerate>90.0)
bool foundRefresh = false;
for (int i = 0; i < numberOfRefreshRates; i++)
{
//Select the max framerate
if (selectedFramerate < refreshRatesArray[i])
{
selectedFramerate = refreshRatesArray[i];
}
//If users supplied refresh on the command line make sure it is one of the valid ones
if (REFRESH == refreshRatesArray[i])
{
ALOGV("Soft limiting to 90.0 Hz as per John carmack's request ( https://www.onlinepeeps.org/oculus-quest-2-according-to-carmack-in-the-future-also-at-120-hz/ );P");
maximumSupportedFramerate=90.0;
foundRefresh = true;
}
}
//User supplied invalid (or didn't set it)
if (!foundRefresh)
{
REFRESH = -1;
}
if (REFRESH == -1) {
//Cap to 90fps for Quest 2
if (selectedFramerate > 90.0) {
ALOGV("Soft limiting to 90.0 Hz as per John carmack's request ( https://www.onlinepeeps.org/oculus-quest-2-according-to-carmack-in-the-future-also-at-120-hz/ );P");
selectedFramerate = 90.0;
}
}
else
{
selectedFramerate = REFRESH;
}
//-----------------------------------------------------------------------------------------------------------
@ -2302,13 +2259,11 @@ void * AppThreadFunction( void * parm )
case MESSAGE_ON_RESUME:
{
//If we get here, then user has opted not to quit
jni_resumeAudio();
appState.Resumed = true;
break;
}
case MESSAGE_ON_PAUSE:
{
jni_pauseAudio();
appState.Resumed = false;
break;
}
@ -2414,7 +2369,7 @@ void * AppThreadFunction( void * parm )
#endif
if (hmdType == VRAPI_DEVICE_TYPE_OCULUSQUEST2) {
ovrResult result = vrapi_SetDisplayRefreshRate(appState.Ovr,maximumSupportedFramerate);
ovrResult result = vrapi_SetDisplayRefreshRate(appState.Ovr, selectedFramerate);
}
// Get the HMD pose, predicted for the middle of the time period during which
@ -2534,7 +2489,6 @@ void * AppThreadFunction( void * parm )
} else if (runStatus == 2)
{
Host_Shutdown();
jni_terminateAudio();
runStatus++;
} else if (runStatus == 3)
{
@ -2605,6 +2559,7 @@ JNIEXPORT jlong JNICALL Java_com_drbeef_quakequest_GLES3JNILib_onCreate( JNIEnv
ss = arg_dbl0("s", "supersampling", "<double>", "super sampling value (e.g. 1.0)"),
cpu = arg_int0("c", "cpu", "<int>", "CPU perf index 1-3 (default: 2)"),
gpu = arg_int0("g", "gpu", "<int>", "GPU perf index 1-3 (default: 3)"),
refresh = arg_int0("r", "refresh", "<int>", "Refresh Rate (default: Quest 1: 72, Quest 2: 90)"),
msaa = arg_dbl0("m", "msaa", "<int>", "msaa value 1-4 (default 1)"), // Don't think this actually works
end = arg_end(20)
};
@ -2649,6 +2604,11 @@ JNIEXPORT jlong JNICALL Java_com_drbeef_quakequest_GLES3JNILib_onCreate( JNIEnv
GPU_LEVEL = gpu->ival[0];
}
if (refresh->count > 0 && refresh->ival[0] > 0 && refresh->ival[0] <= 120)
{
REFRESH = refresh->ival[0];
}
if (msaa->count > 0 && msaa->dval[0] > 0 && msaa->dval[0] < 5)
{
NUM_MULTI_SAMPLES = msaa->dval[0];
@ -2667,21 +2627,10 @@ JNIEXPORT jlong JNICALL Java_com_drbeef_quakequest_GLES3JNILib_onCreate( JNIEnv
}
JNIEXPORT void JNICALL Java_com_drbeef_quakequest_GLES3JNILib_setCallbackObjects(JNIEnv *env, jobject obj, jobject obj1, jobject obj2)
JNIEXPORT void JNICALL Java_com_drbeef_quakequest_GLES3JNILib_setCallbackObjects(JNIEnv *env, jobject obj, jobject obj2)
{
jclass audioCallbackClass;
(*jVM)->GetEnv(jVM, (void**) &env, JNI_VERSION_1_4);
audioCallbackObj = (jobject)(*env)->NewGlobalRef(env, obj1);
audioCallbackClass = (*env)->GetObjectClass(env, audioCallbackObj);
android_initAudio = (*env)->GetMethodID(env,audioCallbackClass,"initAudio","(I)V");
android_writeAudio = (*env)->GetMethodID(env,audioCallbackClass,"writeAudio","(Ljava/nio/ByteBuffer;II)V");
android_pauseAudio = (*env)->GetMethodID(env,audioCallbackClass,"pauseAudio","()V");
android_resumeAudio = (*env)->GetMethodID(env,audioCallbackClass,"resumeAudio","()V");
android_terminateAudio = (*env)->GetMethodID(env,audioCallbackClass,"terminateAudio","()V");
jclass qquestCallbackClass;
qquestCallbackObj = (jobject)(*env)->NewGlobalRef(env, obj2);
@ -2823,8 +2772,3 @@ JNIEXPORT void JNICALL Java_com_drbeef_quakequest_GLES3JNILib_onSurfaceDestroyed
appThread->NativeWindow = NULL;
}
JNIEXPORT void JNICALL Java_com_drbeef_quakequest_GLES3JNILib_requestAudioData(JNIEnv *env, jclass c, jlong handle)
{
ALOGV("Calling: QC_GetAudio");
QC_GetAudio();
}

View file

@ -3597,7 +3597,7 @@ static void M_Credits_Draw (void)
" QQQQQQQQ QQQQQQQQ ",
" QQQ QQQ ",
" Q Q ",
" Q Q v1.4.10");
" Q Q v1.4.11");
int i, l, linelength, firstline, lastline, lines;
for (i = 0, linelength = 0, firstline = 9999, lastline = -1;m_credits_message[i];i++)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,49 @@
//BSD
#ifndef SND_3DRAS_H
#define SND_3DRAS_H
#include "sound.h"
#define DEFAULT_SOUND_PACKET_VOLUME 255
#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0
#define CHANNELFLAG_NONE 0
#define CHANNELFLAG_FORCELOOP (1 << 0) // force looping even if the sound is not looped
#define CHANNELFLAG_LOCALSOUND (1 << 1) // INTERNAL USE. Not settable by S_SetChannelFlag
#define CHANNELFLAG_PAUSED (1 << 2)
#define CHANNELFLAG_FULLVOLUME (1 << 3) // isn't affected by the general volume
#define SFXFLAG_NONE 0
//#define SFXFLAG_FILEMISSING (1 << 0) // wasn't able to load the associated sound file
#define SFXFLAG_SERVERSOUND (1 << 1) // the sfx is part of the server precache list
//#define SFXFLAG_STREAMED (1 << 2) // informative only. You shouldn't need to know that
#define SFXFLAG_PERMANENTLOCK (1 << 3) // can never be freed (ex: used by the client code)
typedef struct channel_s{
struct channel_s* next;
void* rasptr;//Sound Event // This is also used to indicate a unused slot (when it's pointing to 0)
int entnum;// to allow overriding a specific sound
int entchannel;
unsigned int id;
} channel_t;
typedef struct entnum_s{
struct entnum_s *next;
int entnum;
vec3_t lastloc; //Since DP has no way of tracking the deletion, we will use this instead (great jumps indicate teleport or new ent
void *rasptr;//Sound Source // This is also used to indicate a unused slot (when it's pointing to 0)
} entnum_t;
struct sfx_s{
struct sfx_s *next;
char name[MAX_QPATH];
void* rasptr; //Sound Data// The sound data allocated in the lib
int locks;
unsigned int flags; // cf SFXFLAG_* defines
//unsigned int loopstart; // in sample frames. equals total_length if not looped
//unsigned int total_length; // in sample frames
};
#endif

View file

@ -0,0 +1,57 @@
// This file defines a few "basis measurement" types that extern program will need.
#ifndef Typedefs_h
#define Typedefs_h
#include <stdint.h>
///To address a location in a file.
typedef unsigned int FilePosition;
///This will express an Amount of something.
typedef uint64_t Amount;
/// Index expresses an address in a certain array of data.
typedef uint64_t Index;
/// A signed index, to access things that can be access before 0
typedef int64_t SignedIndex;
///The depth at witch we are tracing.
typedef unsigned int TraceDepth;
///The type of a Location (as in a messurement ... from 0)
typedef int64_t Location;
///The type of a Location on a texture
typedef float TextureLocation;
///The type of a Distance
typedef float Distance;
///The type of a Scalar type for use in: Normal3D, Dot product, Direction3D, ...
typedef float Scalar;
///Howmuch of something ?
typedef float Ratio;
///The type of a an EulerAngle for use in: EulerAngle2D, EulerAngle3D, ...
typedef float EulerAngle;
///The type that detemens the size of 1 Location. Expressed in meters/Location.
typedef float Scale;
///The frequency of something.
typedef float Frequency;
///The wavelength of a frequency
typedef Distance WaveLength;
/// Howmany samples we take per secod
typedef float SampleRate;
/// The type in witch we will express a SoundSample.
typedef float Sample;
/// The type that express the speed of sound (meter/second).
typedef float SoundSpeed;
/// The type that that express 1 Time. As in a small step. Note in the feature this will be a class. To make it ring.
typedef unsigned int Time;
typedef float Duration;
//typedef StrongType <unsigned int> Time; // Example of a strong typecheck
/// The amplitude of the SoundPower. This for export to an AudioOutputDevice.
typedef float SoundVolume;
/// How mutch power per square meter is received per meter (Watt/Meter^2)
typedef float SoundIntensity;
/// An expression of the power of sound source (Watt)
typedef float SoundPower; // W, The power of the sound source
typedef float LightIntensity;
typedef float LightPower;
typedef float Brightness;
typedef float Gamma;
typedef float Color;
typedef float RefractionIndex;
typedef unsigned int Resolution;
#endif

View file

@ -0,0 +1,524 @@
/*
Copyright (C) 2006 Mathieu Olivier
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
// ALSA module, used by Linux
#include "quakedef.h"
#include <alsa/asoundlib.h>
#include "snd_main.h"
#define NB_PERIODS 4
static snd_pcm_t* pcm_handle = NULL;
static snd_pcm_sframes_t expected_delay = 0;
static unsigned int alsasoundtime;
static snd_seq_t* seq_handle = NULL;
/*
====================
SndSys_Init
Create "snd_renderbuffer" with the proper sound format if the call is successful
May return a suggested format if the requested format isn't available
====================
*/
qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
{
const char* pcm_name, *seq_name;
int i, err, seq_client, seq_port;
snd_pcm_hw_params_t* hw_params = NULL;
snd_pcm_format_t snd_pcm_format;
snd_pcm_uframes_t buffer_size;
Con_Print ("SndSys_Init: using the ALSA module\n");
seq_name = NULL;
// COMMANDLINEOPTION: Linux ALSA Sound: -sndseqin <client>:<port> selects which sequencer port to use for input, by default no sequencer port is used (MIDI note events from that port get mapped to MIDINOTE<n> keys that can be bound)
i = COM_CheckParm ("-sndseqin"); // TODO turn this into a cvar, maybe
if (i != 0 && i < com_argc - 1)
seq_name = com_argv[i + 1];
if(seq_name)
{
seq_client = atoi(seq_name);
seq_port = 0;
if(strchr(seq_name, ':'))
seq_port = atoi(strchr(seq_name, ':') + 1);
Con_Printf ("SndSys_Init: seq input port has been set to \"%d:%d\". Enabling sequencer input...\n", seq_client, seq_port);
err = snd_seq_open (&seq_handle, "default", SND_SEQ_OPEN_INPUT, 0);
if (err < 0)
{
Con_Print ("SndSys_Init: can't open seq device\n");
goto seqdone;
}
err = snd_seq_set_client_name(seq_handle, gamename);
if (err < 0)
{
Con_Print ("SndSys_Init: can't set name of seq device\n");
goto seqerror;
}
err = snd_seq_create_simple_port(seq_handle, gamename, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION);
if(err < 0)
{
Con_Print ("SndSys_Init: can't create seq port\n");
goto seqerror;
}
err = snd_seq_connect_from(seq_handle, 0, seq_client, seq_port);
if(err < 0)
{
Con_Printf ("SndSys_Init: can't connect to seq port \"%d:%d\"\n", seq_client, seq_port);
goto seqerror;
}
err = snd_seq_nonblock(seq_handle, 1);
if(err < 0)
{
Con_Print ("SndSys_Init: can't make seq nonblocking\n");
goto seqerror;
}
goto seqdone;
seqerror:
snd_seq_close(seq_handle);
seq_handle = NULL;
}
seqdone:
// Check the requested sound format
if (requested->width < 1 || requested->width > 2)
{
Con_Printf ("SndSys_Init: invalid sound width (%hu)\n",
requested->width);
if (suggested != NULL)
{
memcpy (suggested, requested, sizeof (*suggested));
if (requested->width < 1)
suggested->width = 1;
else
suggested->width = 2;
Con_Printf ("SndSys_Init: suggesting sound width = %hu\n",
suggested->width);
}
return false;
}
if (pcm_handle != NULL)
{
Con_Print ("SndSys_Init: WARNING: Init called before Shutdown!\n");
SndSys_Shutdown ();
}
// Determine the name of the PCM handle we'll use
switch (requested->channels)
{
case 4:
pcm_name = "surround40";
break;
case 6:
pcm_name = "surround51";
break;
case 8:
pcm_name = "surround71";
break;
default:
pcm_name = "default";
break;
}
// COMMANDLINEOPTION: Linux ALSA Sound: -sndpcm <devicename> selects which pcm device to use, default is "default"
i = COM_CheckParm ("-sndpcm");
if (i != 0 && i < com_argc - 1)
pcm_name = com_argv[i + 1];
// Open the audio device
Con_Printf ("SndSys_Init: PCM device is \"%s\"\n", pcm_name);
err = snd_pcm_open (&pcm_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (err < 0)
{
Con_Printf ("SndSys_Init: can't open audio device \"%s\" (%s)\n",
pcm_name, snd_strerror (err));
return false;
}
// Allocate the hardware parameters
err = snd_pcm_hw_params_malloc (&hw_params);
if (err < 0)
{
Con_Printf ("SndSys_Init: can't allocate hardware parameters (%s)\n",
snd_strerror (err));
goto init_error;
}
err = snd_pcm_hw_params_any (pcm_handle, hw_params);
if (err < 0)
{
Con_Printf ("SndSys_Init: can't initialize hardware parameters (%s)\n",
snd_strerror (err));
goto init_error;
}
// Set the access type
err = snd_pcm_hw_params_set_access (pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0)
{
Con_Printf ("SndSys_Init: can't set access type (%s)\n",
snd_strerror (err));
goto init_error;
}
// Set the sound width
if (requested->width == 1)
snd_pcm_format = SND_PCM_FORMAT_U8;
else
snd_pcm_format = SND_PCM_FORMAT_S16;
err = snd_pcm_hw_params_set_format (pcm_handle, hw_params, snd_pcm_format);
if (err < 0)
{
Con_Printf ("SndSys_Init: can't set sound width to %hu (%s)\n",
requested->width, snd_strerror (err));
goto init_error;
}
// Set the sound channels
err = snd_pcm_hw_params_set_channels (pcm_handle, hw_params, requested->channels);
if (err < 0)
{
Con_Printf ("SndSys_Init: can't set sound channels to %hu (%s)\n",
requested->channels, snd_strerror (err));
goto init_error;
}
// Set the sound speed
err = snd_pcm_hw_params_set_rate (pcm_handle, hw_params, requested->speed, 0);
if (err < 0)
{
Con_Printf ("SndSys_Init: can't set sound speed to %u (%s)\n",
requested->speed, snd_strerror (err));
goto init_error;
}
// pick a buffer size that is a power of 2 (by masking off low bits)
buffer_size = i = (int)(requested->speed * 0.15f);
while (buffer_size & (buffer_size-1))
buffer_size &= (buffer_size-1);
// then check if it is the nearest power of 2 and bump it up if not
if (i - buffer_size >= buffer_size >> 1)
buffer_size *= 2;
err = snd_pcm_hw_params_set_buffer_size_near (pcm_handle, hw_params, &buffer_size);
if (err < 0)
{
Con_Printf ("SndSys_Init: can't set sound buffer size to %lu (%s)\n",
buffer_size, snd_strerror (err));
goto init_error;
}
// pick a period size near the buffer_size we got from ALSA
snd_pcm_hw_params_get_buffer_size (hw_params, &buffer_size);
buffer_size /= NB_PERIODS;
err = snd_pcm_hw_params_set_period_size_near(pcm_handle, hw_params, &buffer_size, 0);
if (err < 0)
{
Con_Printf ("SndSys_Init: can't set sound period size to %lu (%s)\n",
buffer_size, snd_strerror (err));
goto init_error;
}
err = snd_pcm_hw_params (pcm_handle, hw_params);
if (err < 0)
{
Con_Printf ("SndSys_Init: can't set hardware parameters (%s)\n",
snd_strerror (err));
goto init_error;
}
snd_pcm_hw_params_free (hw_params);
snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
expected_delay = 0;
alsasoundtime = 0;
if (snd_channellayout.integer == SND_CHANNELLAYOUT_AUTO)
Cvar_SetValueQuick (&snd_channellayout, SND_CHANNELLAYOUT_ALSA);
return true;
// It's not very clean, but it avoids a lot of duplicated code.
init_error:
if (hw_params != NULL)
snd_pcm_hw_params_free (hw_params);
snd_pcm_close(pcm_handle);
pcm_handle = NULL;
return false;
}
/*
====================
SndSys_Shutdown
Stop the sound card, delete "snd_renderbuffer" and free its other resources
====================
*/
void SndSys_Shutdown (void)
{
if (seq_handle != NULL)
{
snd_seq_close(seq_handle);
seq_handle = NULL;
}
if (pcm_handle != NULL)
{
snd_pcm_close(pcm_handle);
pcm_handle = NULL;
}
if (snd_renderbuffer != NULL)
{
Mem_Free(snd_renderbuffer->ring);
Mem_Free(snd_renderbuffer);
snd_renderbuffer = NULL;
}
}
/*
====================
SndSys_Recover
Try to recover from errors
====================
*/
static qboolean SndSys_Recover (int err_num)
{
int err;
// We can only do something on underrun ("broken pipe") errors
if (err_num != -EPIPE)
return false;
err = snd_pcm_prepare (pcm_handle);
if (err < 0)
{
Con_Printf ("SndSys_Recover: unable to recover (%s)\n",
snd_strerror (err));
// TOCHECK: should we stop the playback ?
return false;
}
return true;
}
/*
====================
SndSys_Write
====================
*/
static snd_pcm_sframes_t SndSys_Write (const unsigned char* buffer, unsigned int nbframes)
{
snd_pcm_sframes_t written;
written = snd_pcm_writei (pcm_handle, buffer, nbframes);
if (written < 0)
{
if (developer_insane.integer && vid_activewindow)
Con_DPrintf ("SndSys_Write: audio write returned %ld (%s)!\n",
written, snd_strerror (written));
if (SndSys_Recover (written))
{
written = snd_pcm_writei (pcm_handle, buffer, nbframes);
if (written < 0)
Con_DPrintf ("SndSys_Write: audio write failed again (error %ld: %s)!\n",
written, snd_strerror (written));
}
}
if (written > 0)
{
snd_renderbuffer->startframe += written;
expected_delay += written;
}
return written;
}
/*
====================
SndSys_Submit
Submit the contents of "snd_renderbuffer" to the sound card
====================
*/
void SndSys_Submit (void)
{
unsigned int startoffset, factor;
snd_pcm_uframes_t limit, nbframes;
snd_pcm_sframes_t written;
if (pcm_handle == NULL ||
snd_renderbuffer->startframe == snd_renderbuffer->endframe)
return;
startoffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
limit = snd_renderbuffer->maxframes - startoffset;
nbframes = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
if (nbframes > limit)
{
written = SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], limit);
if (written < 0 || (snd_pcm_uframes_t)written != limit)
return;
nbframes -= limit;
startoffset = 0;
}
written = SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], nbframes);
if (written < 0)
return;
}
/*
====================
SndSys_GetSoundTime
Returns the number of sample frames consumed since the sound started
====================
*/
unsigned int SndSys_GetSoundTime (void)
{
snd_pcm_sframes_t delay, timediff;
int err;
if (pcm_handle == NULL)
return 0;
err = snd_pcm_delay (pcm_handle, &delay);
if (err < 0)
{
if (developer_insane.integer && vid_activewindow)
Con_DPrintf ("SndSys_GetSoundTime: can't get playback delay (%s)\n",
snd_strerror (err));
if (! SndSys_Recover (err))
return 0;
err = snd_pcm_delay (pcm_handle, &delay);
if (err < 0)
{
Con_DPrintf ("SndSys_GetSoundTime: can't get playback delay, again (%s)\n",
snd_strerror (err));
return 0;
}
}
if (expected_delay < delay)
{
Con_DPrintf ("SndSys_GetSoundTime: expected_delay(%ld) < delay(%ld)\n",
expected_delay, delay);
timediff = 0;
}
else
timediff = expected_delay - delay;
expected_delay = delay;
alsasoundtime += (unsigned int)timediff;
return alsasoundtime;
}
/*
====================
SndSys_LockRenderBuffer
Get the exclusive lock on "snd_renderbuffer"
====================
*/
qboolean SndSys_LockRenderBuffer (void)
{
// Nothing to do
return true;
}
/*
====================
SndSys_UnlockRenderBuffer
Release the exclusive lock on "snd_renderbuffer"
====================
*/
void SndSys_UnlockRenderBuffer (void)
{
// Nothing to do
}
/*
====================
SndSys_SendKeyEvents
Send keyboard events originating from the sound system (e.g. MIDI)
====================
*/
void SndSys_SendKeyEvents(void)
{
snd_seq_event_t *event;
if(!seq_handle)
return;
for(;;)
{
if(snd_seq_event_input(seq_handle, &event) <= 0)
break;
if(event)
{
switch(event->type)
{
case SND_SEQ_EVENT_NOTEON:
if(event->data.note.velocity)
{
Key_Event(K_MIDINOTE0 + event->data.note.note, 0, true);
break;
}
case SND_SEQ_EVENT_NOTEOFF:
Key_Event(K_MIDINOTE0 + event->data.note.note, 0, false);
break;
}
}
}
}

View file

@ -0,0 +1,243 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include <sys/param.h>
#include <sys/audioio.h>
#ifndef SUNOS
# include <sys/endian.h>
#endif
#include <sys/ioctl.h>
#include <fcntl.h>
#ifndef SUNOS
# include <paths.h>
#endif
#include <unistd.h>
#include "snd_main.h"
static int audio_fd = -1;
/*
====================
SndSys_Init
Create "snd_renderbuffer" with the proper sound format if the call is successful
May return a suggested format if the requested format isn't available
====================
*/
qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
{
unsigned int i;
const char *snddev;
audio_info_t info;
// Open the audio device
#ifdef _PATH_SOUND
snddev = _PATH_SOUND;
#elif defined(SUNOS)
snddev = "/dev/audio";
#else
snddev = "/dev/sound";
#endif
audio_fd = open (snddev, O_WRONLY | O_NDELAY | O_NONBLOCK);
if (audio_fd < 0)
{
Con_Printf("Can't open the sound device (%s)\n", snddev);
return false;
}
AUDIO_INITINFO (&info);
#ifdef AUMODE_PLAY // NetBSD / OpenBSD
info.mode = AUMODE_PLAY;
#endif
info.play.sample_rate = requested->speed;
info.play.channels = requested->channels;
info.play.precision = requested->width * 8;
if (requested->width == 1)
#ifdef SUNOS
info.play.encoding = AUDIO_ENCODING_LINEAR8;
#else
info.play.encoding = AUDIO_ENCODING_ULINEAR;
#endif
else
#ifdef SUNOS
info.play.encoding = AUDIO_ENCODING_LINEAR;
#else
if (mem_bigendian)
info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
else
info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
#endif
if (ioctl (audio_fd, AUDIO_SETINFO, &info) != 0)
{
Con_Printf("Can't set up the sound device (%s)\n", snddev);
return false;
}
// TODO: check the parameters with AUDIO_GETINFO
// TODO: check AUDIO_ENCODINGFLAG_EMULATED with AUDIO_GETENC
snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
return true;
}
/*
====================
SndSys_Shutdown
Stop the sound card, delete "snd_renderbuffer" and free its other resources
====================
*/
void SndSys_Shutdown (void)
{
if (audio_fd >= 0)
{
close(audio_fd);
audio_fd = -1;
}
if (snd_renderbuffer != NULL)
{
Mem_Free(snd_renderbuffer->ring);
Mem_Free(snd_renderbuffer);
snd_renderbuffer = NULL;
}
}
/*
====================
SndSys_Submit
Submit the contents of "snd_renderbuffer" to the sound card
====================
*/
void SndSys_Submit (void)
{
unsigned int startoffset, factor, limit, nbframes;
int written;
if (audio_fd < 0 ||
snd_renderbuffer->startframe == snd_renderbuffer->endframe)
return;
startoffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
limit = snd_renderbuffer->maxframes - startoffset;
nbframes = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
if (nbframes > limit)
{
written = write (audio_fd, &snd_renderbuffer->ring[startoffset * factor], limit * factor);
if (written < 0)
{
Con_Printf("SndSys_Submit: audio write returned %d!\n", written);
return;
}
if (written % factor != 0)
Sys_Error("SndSys_Submit: nb of bytes written (%d) isn't aligned to a frame sample!\n", written);
snd_renderbuffer->startframe += written / factor;
if ((unsigned int)written < limit * factor)
{
Con_Printf("SndSys_Submit: audio can't keep up! (%u < %u)\n", written, limit * factor);
return;
}
nbframes -= limit;
startoffset = 0;
}
written = write (audio_fd, &snd_renderbuffer->ring[startoffset * factor], nbframes * factor);
if (written < 0)
{
Con_Printf("SndSys_Submit: audio write returned %d!\n", written);
return;
}
snd_renderbuffer->startframe += written / factor;
}
/*
====================
SndSys_GetSoundTime
Returns the number of sample frames consumed since the sound started
====================
*/
unsigned int SndSys_GetSoundTime (void)
{
audio_info_t info;
if (ioctl (audio_fd, AUDIO_GETINFO, &info) < 0)
{
Con_Print("Error: can't get audio info\n");
SndSys_Shutdown ();
return 0;
}
return info.play.samples;
}
/*
====================
SndSys_LockRenderBuffer
Get the exclusive lock on "snd_renderbuffer"
====================
*/
qboolean SndSys_LockRenderBuffer (void)
{
// Nothing to do
return true;
}
/*
====================
SndSys_UnlockRenderBuffer
Release the exclusive lock on "snd_renderbuffer"
====================
*/
void SndSys_UnlockRenderBuffer (void)
{
// Nothing to do
}
/*
====================
SndSys_SendKeyEvents
Send keyboard events originating from the sound system (e.g. MIDI)
====================
*/
void SndSys_SendKeyEvents(void)
{
// not supported
}

View file

@ -0,0 +1,399 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "quakedef.h"
#include <limits.h>
#include <pthread.h>
#include <CoreAudio/AudioHardware.h>
#include "snd_main.h"
#define CHUNK_SIZE 1024
static unsigned int submissionChunk = 0; // in sample frames
static unsigned int coreaudiotime = 0; // based on the number of chunks submitted so far
static qboolean s_isRunning = false;
static pthread_mutex_t coreaudio_mutex;
static AudioDeviceID outputDeviceID = kAudioDeviceUnknown;
static short *mixbuffer = NULL;
/*
====================
audioDeviceIOProc
====================
*/
static OSStatus audioDeviceIOProc(AudioDeviceID inDevice,
const AudioTimeStamp *inNow,
const AudioBufferList *inInputData,
const AudioTimeStamp *inInputTime,
AudioBufferList *outOutputData,
const AudioTimeStamp *inOutputTime,
void *inClientData)
{
float *outBuffer;
unsigned int frameCount, factor, sampleIndex;
float scale = 1.0f / SHRT_MAX;
outBuffer = (float*)outOutputData->mBuffers[0].mData;
factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
frameCount = 0;
if (snd_blocked)
scale = 0;
// Lock the snd_renderbuffer
if (SndSys_LockRenderBuffer())
{
unsigned int maxFrames, sampleCount;
unsigned int startOffset, endOffset;
const short *samples;
if (snd_usethreadedmixing)
{
S_MixToBuffer(mixbuffer, submissionChunk);
sampleCount = submissionChunk * snd_renderbuffer->format.channels;
for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
outBuffer[sampleIndex] = mixbuffer[sampleIndex] * scale;
// unlock the mutex now
SndSys_UnlockRenderBuffer();
return 0;
}
// Transfert up to a chunk of sample frames from snd_renderbuffer to outBuffer
maxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
if (maxFrames >= submissionChunk)
frameCount = submissionChunk;
else
frameCount = maxFrames;
// Convert the samples from shorts to floats. Scale the floats to be [-1..1].
startOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
endOffset = (snd_renderbuffer->startframe + frameCount) % snd_renderbuffer->maxframes;
if (startOffset > endOffset) // if the buffer wraps
{
sampleCount = (snd_renderbuffer->maxframes - startOffset) * snd_renderbuffer->format.channels;
samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
outBuffer[sampleIndex] = samples[sampleIndex] * scale;
outBuffer = &outBuffer[sampleCount];
sampleCount = frameCount * snd_renderbuffer->format.channels - sampleCount;
samples = (const short*)(&snd_renderbuffer->ring[0]);
for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
outBuffer[sampleIndex] = samples[sampleIndex] * scale;
}
else
{
sampleCount = frameCount * snd_renderbuffer->format.channels;
samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
outBuffer[sampleIndex] = samples[sampleIndex] * scale;
}
snd_renderbuffer->startframe += frameCount;
// unlock the mutex now
SndSys_UnlockRenderBuffer();
}
// If there was not enough samples, complete with silence samples
if (frameCount < submissionChunk)
{
unsigned int missingFrames;
missingFrames = submissionChunk - frameCount;
if (developer_insane.integer && vid_activewindow)
Con_DPrintf("audioDeviceIOProc: %u sample frames missing\n", missingFrames);
memset(&outBuffer[frameCount * snd_renderbuffer->format.channels], 0, missingFrames * sizeof(outBuffer[0]));
}
coreaudiotime += submissionChunk;
return 0;
}
/*
====================
SndSys_Init
Create "snd_renderbuffer" with the proper sound format if the call is successful
May return a suggested format if the requested format isn't available
====================
*/
qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
{
OSStatus status;
UInt32 propertySize, bufferByteCount;
AudioStreamBasicDescription streamDesc;
if (s_isRunning)
return true;
Con_Printf("Initializing CoreAudio...\n");
snd_threaded = false;
if(requested->width != 2)
{
// we can only do 16bit per sample for now
if(suggested != NULL)
{
memcpy (suggested, requested, sizeof (*suggested));
suggested->width = 2;
}
return false;
}
// Get the output device
propertySize = sizeof(outputDeviceID);
status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &outputDeviceID);
if (status)
{
Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioHardwarePropertyDefaultOutputDevice\n", (int)status);
return false;
}
if (outputDeviceID == kAudioDeviceUnknown)
{
Con_Printf("CoreAudio: outputDeviceID is kAudioDeviceUnknown\n");
return false;
}
// Configure the output device
propertySize = sizeof(bufferByteCount);
bufferByteCount = CHUNK_SIZE * sizeof(float) * requested->channels;
status = AudioDeviceSetProperty(outputDeviceID, NULL, 0, false, kAudioDevicePropertyBufferSize, propertySize, &bufferByteCount);
if (status)
{
Con_Printf("CoreAudio: AudioDeviceSetProperty() returned %d when setting kAudioDevicePropertyBufferSize to %d\n", (int)status, CHUNK_SIZE);
return false;
}
propertySize = sizeof(bufferByteCount);
status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &bufferByteCount);
if (status)
{
Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when setting kAudioDevicePropertyBufferSize\n", (int)status);
return false;
}
submissionChunk = bufferByteCount / sizeof(float);
if (submissionChunk % requested->channels != 0)
{
Con_Print("CoreAudio: chunk size is NOT a multiple of the number of channels\n");
return false;
}
submissionChunk /= requested->channels;
Con_Printf(" Chunk size = %d sample frames\n", submissionChunk);
// Print out the device status
propertySize = sizeof(streamDesc);
status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &streamDesc);
if (status)
{
Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioDevicePropertyStreamFormat\n", (int)status);
return false;
}
Con_Print (" Hardware format:\n");
Con_Printf(" %5d mSampleRate\n", (unsigned int)streamDesc.mSampleRate);
Con_Printf(" %c%c%c%c mFormatID\n",
(char)(streamDesc.mFormatID >> 24),
(char)(streamDesc.mFormatID >> 16),
(char)(streamDesc.mFormatID >> 8),
(char)(streamDesc.mFormatID >> 0));
Con_Printf(" %5u mBytesPerPacket\n", (unsigned int)streamDesc.mBytesPerPacket);
Con_Printf(" %5u mFramesPerPacket\n", (unsigned int)streamDesc.mFramesPerPacket);
Con_Printf(" %5u mBytesPerFrame\n", (unsigned int)streamDesc.mBytesPerFrame);
Con_Printf(" %5u mChannelsPerFrame\n", (unsigned int)streamDesc.mChannelsPerFrame);
Con_Printf(" %5u mBitsPerChannel\n", (unsigned int)streamDesc.mBitsPerChannel);
// Suggest proper settings if they differ
if (requested->channels != streamDesc.mChannelsPerFrame || requested->speed != streamDesc.mSampleRate)
{
if (suggested != NULL)
{
memcpy (suggested, requested, sizeof (*suggested));
suggested->channels = streamDesc.mChannelsPerFrame;
suggested->speed = streamDesc.mSampleRate;
}
return false;
}
if(streamDesc.mFormatID == kAudioFormatLinearPCM)
{
// Add the callback function
status = AudioDeviceAddIOProc(outputDeviceID, audioDeviceIOProc, NULL);
if (!status)
{
// We haven't sent any sample frames yet
coreaudiotime = 0;
if (pthread_mutex_init(&coreaudio_mutex, NULL) == 0)
{
if ((snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL)))
{
if ((mixbuffer = Mem_Alloc(snd_mempool, CHUNK_SIZE * sizeof(*mixbuffer) * requested->channels)))
{
// Start sound running
status = AudioDeviceStart(outputDeviceID, audioDeviceIOProc);
if (!status)
{
s_isRunning = true;
snd_threaded = true;
Con_Print(" Initialization successful\n");
return true;
}
else
Con_Printf("CoreAudio: AudioDeviceStart() returned %d\n", (int)status);
Mem_Free(mixbuffer);
mixbuffer = NULL;
}
else
Con_Print("CoreAudio: can't allocate memory for mixbuffer\n");
Mem_Free(snd_renderbuffer->ring);
Mem_Free(snd_renderbuffer);
snd_renderbuffer = NULL;
}
else
Con_Print("CoreAudio: can't allocate memory for ringbuffer\n");
pthread_mutex_destroy(&coreaudio_mutex);
}
else
Con_Print("CoreAudio: can't create pthread mutex\n");
AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
}
else
Con_Printf("CoreAudio: AudioDeviceAddIOProc() returned %d\n", (int)status);
}
else
Con_Print("CoreAudio: Default audio device doesn't support linear PCM!\n");
return false;
}
/*
====================
SndSys_Shutdown
Stop the sound card, delete "snd_renderbuffer" and free its other resources
====================
*/
void SndSys_Shutdown(void)
{
OSStatus status;
if (!s_isRunning)
return;
status = AudioDeviceStop(outputDeviceID, audioDeviceIOProc);
if (status)
{
Con_Printf("AudioDeviceStop: returned %d\n", (int)status);
return;
}
s_isRunning = false;
pthread_mutex_destroy(&coreaudio_mutex);
status = AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
if (status)
{
Con_Printf("AudioDeviceRemoveIOProc: returned %d\n", (int)status);
return;
}
if (snd_renderbuffer != NULL)
{
Mem_Free(snd_renderbuffer->ring);
Mem_Free(snd_renderbuffer);
snd_renderbuffer = NULL;
}
if (mixbuffer != NULL)
Mem_Free(mixbuffer);
mixbuffer = NULL;
}
/*
====================
SndSys_Submit
Submit the contents of "snd_renderbuffer" to the sound card
====================
*/
void SndSys_Submit (void)
{
// Nothing to do here (this sound module is callback-based)
}
/*
====================
SndSys_GetSoundTime
Returns the number of sample frames consumed since the sound started
====================
*/
unsigned int SndSys_GetSoundTime (void)
{
return coreaudiotime;
}
/*
====================
SndSys_LockRenderBuffer
Get the exclusive lock on "snd_renderbuffer"
====================
*/
qboolean SndSys_LockRenderBuffer (void)
{
return (pthread_mutex_lock(&coreaudio_mutex) == 0);
}
/*
====================
SndSys_UnlockRenderBuffer
Release the exclusive lock on "snd_renderbuffer"
====================
*/
void SndSys_UnlockRenderBuffer (void)
{
pthread_mutex_unlock(&coreaudio_mutex);
}
/*
====================
SndSys_SendKeyEvents
Send keyboard events originating from the sound system (e.g. MIDI)
====================
*/
void SndSys_SendKeyEvents(void)
{
// not supported
}

View file

@ -23,10 +23,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "snd_main.h"
#include "snd_ogg.h"
#include "snd_modplug.h"
#include "csprogs.h"
#include "cl_collision.h"
#ifdef CONFIG_CD
#include "cdaudio.h"
#endif
#define SND_MIN_SPEED 8000
@ -326,7 +327,7 @@ static void S_SoundList_f (void)
{
unsigned int size;
size = sfx->memsize;
size = (unsigned int)sfx->memsize;
Con_Printf ("%c%c%c(%5iHz %2db %6s) %8i : %s\n",
(sfx->loopstart < sfx->total_length) ? 'L' : ' ',
(sfx->flags & SFXFLAG_STREAMED) ? 'S' : ' ',
@ -915,7 +916,6 @@ void S_Init(void)
memset(channels, 0, MAX_CHANNELS * sizeof(channel_t));
OGG_OpenLibrary ();
ModPlug_OpenLibrary ();
}
@ -929,7 +929,6 @@ Shutdown and free all resources
void S_Terminate (void)
{
S_Shutdown ();
ModPlug_CloseLibrary ();
OGG_CloseLibrary ();
// Free all SFXs
@ -1494,7 +1493,7 @@ static void SND_Spatialize_WithSfx(channel_t *ch, qboolean isstatic, sfx_t *sfx)
if (snd_spatialization_occlusion.integer)
{
if(snd_spatialization_occlusion.integer & 1)
if(listener_pvs)
if(listener_pvs && cl.worldmodel)
{
int cluster = cl.worldmodel->brush.PointInLeaf(cl.worldmodel, ch->origin)->clusterindex;
if(cluster >= 0 && cluster < 8 * listener_pvsbytes && !CHECKPVSBIT(listener_pvs, cluster))
@ -1821,8 +1820,10 @@ void S_StopAllSounds (void)
if (snd_renderbuffer == NULL)
return;
#ifdef CONFIG_CD
// stop CD audio because it may be using a faketrack
CDAudio_Stop();
#endif
if (simsound || SndSys_LockRenderBuffer ())
{

View file

@ -28,6 +28,7 @@ static portable_sampleframe_t paintbuffer_unswapped[PAINTBUFFER_SIZE];
extern speakerlayout_t snd_speakerlayout; // for querying the listeners
#ifdef CONFIG_VIDEO_CAPTURE
static void S_CaptureAVISound(const portable_sampleframe_t *paintbuffer, size_t length)
{
size_t i;
@ -46,6 +47,7 @@ static void S_CaptureAVISound(const portable_sampleframe_t *paintbuffer, size_t
SCR_CaptureVideo_SoundFrame(paintbuffer_unswapped, length);
}
#endif
extern cvar_t snd_softclip;
@ -520,8 +522,10 @@ void S_MixToBuffer(void *stream, unsigned int bufferframes)
S_SoftClipPaintBuffer(paintbuffer, totalmixframes, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);
#ifdef CONFIG_VIDEO_CAPTURE
if (!snd_usethreadedmixing)
S_CaptureAVISound(paintbuffer, totalmixframes);
#endif
S_ConvertPaintBuffer(paintbuffer, outbytes, totalmixframes, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);

View file

@ -0,0 +1,169 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// snd_null.c -- include this instead of all the other snd_* files to have
// no sound code whatsoever
#include "quakedef.h"
cvar_t bgmvolume = {CVAR_SAVE, "bgmvolume", "1", "volume of background music (such as CD music or replacement files such as sound/cdtracks/track002.ogg)"};
cvar_t mastervolume = {CVAR_SAVE, "mastervolume", "1", "master volume"};
cvar_t volume = {CVAR_SAVE, "volume", "0.7", "volume of sound effects"};
cvar_t snd_staticvolume = {CVAR_SAVE, "snd_staticvolume", "1", "volume of ambient sound effects (such as swampy sounds at the start of e1m2)"};
cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0", "indicates the sound subsystem is active"};
cvar_t snd_mutewhenidle = {CVAR_SAVE, "snd_mutewhenidle", "1", "whether to disable sound output when game window is inactive"};
void S_Init (void)
{
Cvar_RegisterVariable(&bgmvolume);
Cvar_RegisterVariable(&mastervolume);
Cvar_RegisterVariable(&volume);
Cvar_RegisterVariable(&snd_staticvolume);
Cvar_RegisterVariable(&snd_initialized);
Cvar_RegisterVariable(&snd_mutewhenidle);
}
void S_Terminate (void)
{
}
void S_Startup (void)
{
}
void S_Shutdown (void)
{
}
void S_ClearUsed (void)
{
}
void S_PurgeUnused (void)
{
}
void S_StaticSound (sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
{
}
int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
{
return -1;
}
int S_StartSound_StartPosition_Flags (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float startposition, int flags, float fspeed)
{
return -1;
}
void S_StopChannel (unsigned int channel_ind, qboolean lockmutex, qboolean freesfx)
{
}
qboolean S_SetChannelFlag (unsigned int ch_ind, unsigned int flag, qboolean value)
{
return false;
}
void S_StopSound (int entnum, int entchannel)
{
}
void S_PauseGameSounds (qboolean toggle)
{
}
void S_SetChannelVolume (unsigned int ch_ind, float fvol)
{
}
sfx_t *S_PrecacheSound (const char *sample, qboolean complain, qboolean levelsound)
{
return NULL;
}
float S_SoundLength(const char *name)
{
return -1;
}
qboolean S_IsSoundPrecached (const sfx_t *sfx)
{
return false;
}
void S_UnloadAllSounds_f (void)
{
}
sfx_t *S_FindName (const char *name)
{
return NULL;
}
void S_Update(const matrix4x4_t *matrix)
{
}
void S_StopAllSounds (void)
{
}
void S_ExtraUpdate (void)
{
}
qboolean S_LocalSound (const char *s)
{
return false;
}
void S_BlockSound (void)
{
}
void S_UnblockSound (void)
{
}
int S_GetSoundRate(void)
{
return 0;
}
int S_GetSoundChannels(void)
{
return 0;
}
float S_GetChannelPosition (unsigned int ch_ind)
{
return -1;
}
float S_GetEntChannelPosition(int entnum, int entchannel)
{
return -1;
}
void SndSys_SendKeyEvents(void)
{
}

View file

@ -0,0 +1,399 @@
/*
Copyright (C) 2004 Andreas Kirsch
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include <math.h>
#include "snd_main.h"
//Updated by Emile Belanger for OpenSL
// for native audio
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <pthread.h>
static unsigned int sdlaudiotime = 0;
// engine interfaces
static SLObjectItf engineObject = NULL;
static SLEngineItf engineEngine;
// output mix interfaces
static SLObjectItf outputMixObject = NULL;
// buffer queue player interfaces
static SLObjectItf bqPlayerObject = NULL;
static SLPlayItf bqPlayerPlay;
#ifdef ANDROID_NDK
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
#else
static SLBufferQueueItf bqPlayerBufferQueue;
#endif
static SLEffectSendItf bqPlayerEffectSend;
static SLMuteSoloItf bqPlayerMuteSolo;
static SLVolumeItf bqPlayerVolume;
void assert(int v,const char * message)
{
// if (!v)
// LOGI("ASSERT: %s",message);
}
/*
// Note: SDL calls SDL_LockAudio() right before this function, so no need to lock the audio data here
static void Buffer_Callback (void *userdata, unsigned char *stream, int len)
{
unsigned int factor, RequestedFrames, MaxFrames, FrameCount;
unsigned int StartOffset, EndOffset;
factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
if ((unsigned int)len % factor != 0)
Sys_Error("SDL sound: invalid buffer length passed to Buffer_Callback (%d bytes)\n", len);
RequestedFrames = (unsigned int)len / factor;
if (SndSys_LockRenderBuffer())
{
if (snd_usethreadedmixing)
{
S_MixToBuffer(stream, RequestedFrames);
if (snd_blocked)
memset(stream, snd_renderbuffer->format.width == 1 ? 0x80 : 0, len);
SndSys_UnlockRenderBuffer();
return;
}
// Transfert up to a chunk of samples from snd_renderbuffer to stream
MaxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
if (MaxFrames > RequestedFrames)
FrameCount = RequestedFrames;
else
FrameCount = MaxFrames;
StartOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
EndOffset = (snd_renderbuffer->startframe + FrameCount) % snd_renderbuffer->maxframes;
if (StartOffset > EndOffset) // if the buffer wraps
{
unsigned int PartialLength1, PartialLength2;
PartialLength1 = (snd_renderbuffer->maxframes - StartOffset) * factor;
memcpy(stream, &snd_renderbuffer->ring[StartOffset * factor], PartialLength1);
PartialLength2 = FrameCount * factor - PartialLength1;
memcpy(&stream[PartialLength1], &snd_renderbuffer->ring[0], PartialLength2);
}
else
memcpy(stream, &snd_renderbuffer->ring[StartOffset * factor], FrameCount * factor);
snd_renderbuffer->startframe += FrameCount;
if (FrameCount < RequestedFrames && developer_insane.integer && vid_activewindow)
Con_DPrintf("SDL sound: %u sample frames missing\n", RequestedFrames - FrameCount);
sdlaudiotime += RequestedFrames;
SndSys_UnlockRenderBuffer();
}
}
*/
#define OPENSL_BUFF_LEN 1024
static unsigned char play_buffer[OPENSL_BUFF_LEN];
//NOTE!! There are definetly threading issues with this, but it appears to work for now...
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{
//LOGI("bqPlayerCallback");
unsigned int factor, RequestedFrames, MaxFrames, FrameCount;
unsigned int StartOffset, EndOffset;
factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
RequestedFrames = (unsigned int)OPENSL_BUFF_LEN / factor;
if (SndSys_LockRenderBuffer())
{
if (snd_usethreadedmixing)
{
S_MixToBuffer(play_buffer, RequestedFrames);
if (snd_blocked)
memset(play_buffer, snd_renderbuffer->format.width == 1 ? 0x80 : 0, OPENSL_BUFF_LEN);
(*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, play_buffer,OPENSL_BUFF_LEN);
SndSys_UnlockRenderBuffer();
return;
}
// Transfert up to a chunk of samples from snd_renderbuffer to stream
MaxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
if (MaxFrames > RequestedFrames)
FrameCount = RequestedFrames;
else
FrameCount = MaxFrames;
StartOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
EndOffset = (snd_renderbuffer->startframe + FrameCount) % snd_renderbuffer->maxframes;
if (StartOffset > EndOffset) // if the buffer wraps
{
unsigned int PartialLength1, PartialLength2;
PartialLength1 = (snd_renderbuffer->maxframes - StartOffset) * factor;
memcpy(play_buffer, &snd_renderbuffer->ring[StartOffset * factor], PartialLength1);
PartialLength2 = FrameCount * factor - PartialLength1;
memcpy(&play_buffer[PartialLength1], &snd_renderbuffer->ring[0], PartialLength2);
}
else
memcpy(play_buffer, &snd_renderbuffer->ring[StartOffset * factor], FrameCount * factor);
snd_renderbuffer->startframe += FrameCount;
if (FrameCount < RequestedFrames && developer_insane.integer && vid_activewindow)
Con_DPrintf("SDL sound: %u sample frames missing\n", RequestedFrames - FrameCount);
sdlaudiotime += RequestedFrames;
SndSys_UnlockRenderBuffer();
}
SLresult result;
//LOGI("Frame count = %d",FrameCount);
if (FrameCount == 0)
FrameCount = 1;
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, play_buffer,FrameCount * factor);
assert(SL_RESULT_SUCCESS == result,"Enqueue failed");
}
/*
====================
SndSys_Init
Create "snd_renderbuffer" with the proper sound format if the call is successful
May return a suggested format if the requested format isn't available
====================
*/
qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
{
unsigned int buffersize;
snd_threaded = false;
Con_DPrint ("SndSys_Init: using the SDL module\n");
buffersize = CeilPowerOf2((unsigned int)ceil((double)requested->speed / 25.0)); // 2048 bytes on 24kHz to 48kHz
Con_Printf("Wanted audio Specification:\n"
"\tChannels : %i\n"
"\tFormat : 0x%X\n"
"\tFrequency : %i\n"
"\tSamples : %i\n",
requested->channels, requested->width, requested->speed, buffersize);
//Opensl only does 44100
if (suggested != NULL)
{
suggested->channels = 2;
suggested->width = 2;
suggested->speed = 44100;
}
SLresult result;
// create engine
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
assert(SL_RESULT_SUCCESS == result,"slCreateEngine");
// realize the engine
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result,"Realize");
// get the engine interface, which is needed in order to create other objects
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
assert(SL_RESULT_SUCCESS == result,"GetInterface");
// create output mix
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
assert(SL_RESULT_SUCCESS == result,"CreateOutputMix");
// realize the output mix
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result,"Realize output mix");
//CREATE THE PLAYER
// configure audio source
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, 2, SL_SAMPLINGRATE_44_1,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN};
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
// configure audio sink
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
SLDataSink audioSnk = {&loc_outmix, NULL};
// create audio player
// LOGI("create audio player");
const SLInterfaceID ids[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
const SLboolean req[1] = {SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
1, ids, req);
assert(SL_RESULT_SUCCESS == result,"CreateAudioPlayer");
// realize the player
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result,"Realize AudioPlayer");
// get the play interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
assert(SL_RESULT_SUCCESS == result,"GetInterface AudioPlayer");
// get the buffer queue interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
&bqPlayerBufferQueue);
assert(SL_RESULT_SUCCESS == result,"GetInterface buffer queue");
// register callback on the buffer queue
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
assert(SL_RESULT_SUCCESS == result,"RegisterCallback");
// set the player's state to playing
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
assert(SL_RESULT_SUCCESS == result,"SetPlayState");
snd_threaded = true;
snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
if (snd_channellayout.integer == SND_CHANNELLAYOUT_AUTO)
Cvar_SetValueQuick (&snd_channellayout, SND_CHANNELLAYOUT_STANDARD);
sdlaudiotime = 0;
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, "\0", 1);
assert(SL_RESULT_SUCCESS == result,"Enqueue first buffer");
return true;
}
/*
====================
SndSys_Shutdown
Stop the sound card, delete "snd_renderbuffer" and free its other resources
====================
*/
void SndSys_Shutdown(void)
{
/* Make sure player is stopped */
(*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
/* Destroy the player */
(*engineObject)->Destroy(engineObject);
/* Destroy Output Mix object */
(*outputMixObject)->Destroy(outputMixObject);
if (snd_renderbuffer != NULL)
{
Mem_Free(snd_renderbuffer->ring);
Mem_Free(snd_renderbuffer);
snd_renderbuffer = NULL;
}
}
/*
====================
SndSys_Submit
Submit the contents of "snd_renderbuffer" to the sound card
====================
*/
void SndSys_Submit (void)
{
// Nothing to do here (this sound module is callback-based)
}
/*
====================
SndSys_GetSoundTime
Returns the number of sample frames consumed since the sound started
====================
*/
unsigned int SndSys_GetSoundTime (void)
{
return sdlaudiotime;
}
/*
====================
SndSys_LockRenderBuffer
Get the exclusive lock on "snd_renderbuffer"
====================
*/
qboolean SndSys_LockRenderBuffer (void)
{
//SDL_LockAudio();
return true;
}
/*
====================
SndSys_UnlockRenderBuffer
Release the exclusive lock on "snd_renderbuffer"
====================
*/
void SndSys_UnlockRenderBuffer (void)
{
//SDL_UnlockAudio();
}
/*
====================
SndSys_SendKeyEvents
Send keyboard events originating from the sound system (e.g. MIDI)
====================
*/
void SndSys_SendKeyEvents(void)
{
// not supported
}

View file

@ -0,0 +1,344 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// OSS module, used by Linux and FreeBSD
#include "quakedef.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <unistd.h>
#include "snd_main.h"
#define NB_FRAGMENTS 4
static int audio_fd = -1;
static int old_osstime = 0;
static unsigned int osssoundtime;
/*
====================
SndSys_Init
Create "snd_renderbuffer" with the proper sound format if the call is successful
May return a suggested format if the requested format isn't available
====================
*/
qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
{
int flags, ioctl_param, prev_value;
unsigned int fragmentsize;
Con_DPrint("SndSys_Init: using the OSS module\n");
// Check the requested sound format
if (requested->width < 1 || requested->width > 2)
{
Con_Printf("SndSys_Init: invalid sound width (%hu)\n",
requested->width);
if (suggested != NULL)
{
memcpy(suggested, requested, sizeof(*suggested));
if (requested->width < 1)
suggested->width = 1;
else
suggested->width = 2;
}
return false;
}
// Open /dev/dsp
audio_fd = open("/dev/dsp", O_WRONLY);
if (audio_fd < 0)
{
perror("/dev/dsp");
Con_Print("SndSys_Init: could not open /dev/dsp\n");
return false;
}
// Use non-blocking IOs if possible
flags = fcntl(audio_fd, F_GETFL);
if (flags != -1)
{
if (fcntl(audio_fd, F_SETFL, flags | O_NONBLOCK) == -1)
Con_Print("SndSys_Init : fcntl(F_SETFL, O_NONBLOCK) failed!\n");
}
else
Con_Print("SndSys_Init: fcntl(F_GETFL) failed!\n");
// Set the fragment size (up to "NB_FRAGMENTS" fragments of "fragmentsize" bytes)
fragmentsize = requested->speed * requested->channels * requested->width / 10;
fragmentsize = (unsigned int)ceilf((float)fragmentsize / (float)NB_FRAGMENTS);
fragmentsize = CeilPowerOf2(fragmentsize);
ioctl_param = (NB_FRAGMENTS << 16) | log2i(fragmentsize);
if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &ioctl_param) == -1)
{
Con_Print ("SndSys_Init: could not set the fragment size\n");
SndSys_Shutdown ();
return false;
}
Con_Printf ("SndSys_Init: using %u fragments of %u bytes\n",
ioctl_param >> 16, 1 << (ioctl_param & 0xFFFF));
// Set the sound width
if (requested->width == 1)
ioctl_param = AFMT_U8;
else
ioctl_param = AFMT_S16_NE;
prev_value = ioctl_param;
if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &ioctl_param) == -1 ||
ioctl_param != prev_value)
{
if (ioctl_param != prev_value && suggested != NULL)
{
memcpy(suggested, requested, sizeof(*suggested));
if (ioctl_param == AFMT_S16_NE)
suggested->width = 2;
else
suggested->width = 1;
}
Con_Printf("SndSys_Init: could not set the sound width to %hu\n",
requested->width);
SndSys_Shutdown();
return false;
}
// Set the sound channels
ioctl_param = requested->channels;
if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &ioctl_param) == -1 ||
ioctl_param != requested->channels)
{
if (ioctl_param != requested->channels && suggested != NULL)
{
memcpy(suggested, requested, sizeof(*suggested));
suggested->channels = ioctl_param;
}
Con_Printf("SndSys_Init: could not set the number of channels to %hu\n",
requested->channels);
SndSys_Shutdown();
return false;
}
// Set the sound speed
ioctl_param = requested->speed;
if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &ioctl_param) == -1 ||
(unsigned int)ioctl_param != requested->speed)
{
if ((unsigned int)ioctl_param != requested->speed && suggested != NULL)
{
memcpy(suggested, requested, sizeof(*suggested));
suggested->speed = ioctl_param;
}
Con_Printf("SndSys_Init: could not set the sound speed to %u\n",
requested->speed);
SndSys_Shutdown();
return false;
}
// TOCHECK: I'm not sure which channel layout OSS uses for 5.1 and 7.1
if (snd_channellayout.integer == SND_CHANNELLAYOUT_AUTO)
Cvar_SetValueQuick (&snd_channellayout, SND_CHANNELLAYOUT_ALSA);
old_osstime = 0;
osssoundtime = 0;
snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
return true;
}
/*
====================
SndSys_Shutdown
Stop the sound card, delete "snd_renderbuffer" and free its other resources
====================
*/
void SndSys_Shutdown (void)
{
// Stop the sound and close the device
if (audio_fd >= 0)
{
ioctl(audio_fd, SNDCTL_DSP_RESET, NULL);
close(audio_fd);
audio_fd = -1;
}
if (snd_renderbuffer != NULL)
{
Mem_Free(snd_renderbuffer->ring);
Mem_Free(snd_renderbuffer);
snd_renderbuffer = NULL;
}
}
/*
====================
SndSys_Write
====================
*/
static int SndSys_Write (const unsigned char* buffer, unsigned int nb_bytes)
{
int written;
unsigned int factor;
written = write (audio_fd, buffer, nb_bytes);
if (written < 0)
{
if (errno != EAGAIN)
Con_Printf ("SndSys_Write: audio write returned %d! (errno= %d)\n",
written, errno);
return written;
}
factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
if (written % factor != 0)
Sys_Error ("SndSys_Write: nb of bytes written (%d) isn't aligned to a frame sample!\n",
written);
snd_renderbuffer->startframe += written / factor;
if ((unsigned int)written < nb_bytes)
{
Con_DPrintf("SndSys_Submit: audio can't keep up! (%u < %u)\n",
written, nb_bytes);
}
return written;
}
/*
====================
SndSys_Submit
Submit the contents of "snd_renderbuffer" to the sound card
====================
*/
void SndSys_Submit (void)
{
unsigned int startoffset, factor, limit, nbframes;
int written;
if (audio_fd < 0 ||
snd_renderbuffer->startframe == snd_renderbuffer->endframe)
return;
startoffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
limit = snd_renderbuffer->maxframes - startoffset;
nbframes = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
if (nbframes > limit)
{
written = SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], limit * factor);
if (written < 0 || (unsigned int)written < limit * factor)
return;
nbframes -= limit;
startoffset = 0;
}
SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], nbframes * factor);
}
/*
====================
SndSys_GetSoundTime
Returns the number of sample frames consumed since the sound started
====================
*/
unsigned int SndSys_GetSoundTime (void)
{
struct count_info count;
int new_osstime;
unsigned int timediff;
if (ioctl (audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1)
{
Con_Print ("SndSys_GetSoundTimeDiff: can't ioctl (SNDCTL_DSP_GETOPTR)\n");
return 0;
}
new_osstime = count.bytes / (snd_renderbuffer->format.width * snd_renderbuffer->format.channels);
if (new_osstime >= old_osstime)
timediff = new_osstime - old_osstime;
else
{
Con_Print ("SndSys_GetSoundTime: osstime wrapped\n");
timediff = 0;
}
old_osstime = new_osstime;
osssoundtime += timediff;
return osssoundtime;
}
/*
====================
SndSys_LockRenderBuffer
Get the exclusive lock on "snd_renderbuffer"
====================
*/
qboolean SndSys_LockRenderBuffer (void)
{
// Nothing to do
return true;
}
/*
====================
SndSys_UnlockRenderBuffer
Release the exclusive lock on "snd_renderbuffer"
====================
*/
void SndSys_UnlockRenderBuffer (void)
{
// Nothing to do
}
/*
====================
SndSys_SendKeyEvents
Send keyboard events originating from the sound system (e.g. MIDI)
====================
*/
void SndSys_SendKeyEvents(void)
{
// not supported
}

View file

@ -0,0 +1,254 @@
/*
Copyright (C) 2004 Andreas Kirsch
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <math.h>
#include <SDL.h>
#include "quakedef.h"
#include "snd_main.h"
static unsigned int sdlaudiotime = 0;
// Note: SDL calls SDL_LockAudio() right before this function, so no need to lock the audio data here
static void Buffer_Callback (void *userdata, Uint8 *stream, int len)
{
unsigned int factor, RequestedFrames, MaxFrames, FrameCount;
unsigned int StartOffset, EndOffset;
factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
if ((unsigned int)len % factor != 0)
Sys_Error("SDL sound: invalid buffer length passed to Buffer_Callback (%d bytes)\n", len);
RequestedFrames = (unsigned int)len / factor;
if (SndSys_LockRenderBuffer())
{
if (snd_usethreadedmixing)
{
S_MixToBuffer(stream, RequestedFrames);
if (snd_blocked)
memset(stream, snd_renderbuffer->format.width == 1 ? 0x80 : 0, len);
SndSys_UnlockRenderBuffer();
return;
}
// Transfert up to a chunk of samples from snd_renderbuffer to stream
MaxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
if (MaxFrames > RequestedFrames)
FrameCount = RequestedFrames;
else
FrameCount = MaxFrames;
StartOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
EndOffset = (snd_renderbuffer->startframe + FrameCount) % snd_renderbuffer->maxframes;
if (StartOffset > EndOffset) // if the buffer wraps
{
unsigned int PartialLength1, PartialLength2;
PartialLength1 = (snd_renderbuffer->maxframes - StartOffset) * factor;
memcpy(stream, &snd_renderbuffer->ring[StartOffset * factor], PartialLength1);
PartialLength2 = FrameCount * factor - PartialLength1;
memcpy(&stream[PartialLength1], &snd_renderbuffer->ring[0], PartialLength2);
}
else
memcpy(stream, &snd_renderbuffer->ring[StartOffset * factor], FrameCount * factor);
snd_renderbuffer->startframe += FrameCount;
if (FrameCount < RequestedFrames && developer_insane.integer && vid_activewindow)
Con_DPrintf("SDL sound: %u sample frames missing\n", RequestedFrames - FrameCount);
sdlaudiotime += RequestedFrames;
SndSys_UnlockRenderBuffer();
}
}
/*
====================
SndSys_Init
Create "snd_renderbuffer" with the proper sound format if the call is successful
May return a suggested format if the requested format isn't available
====================
*/
qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
{
unsigned int buffersize;
SDL_AudioSpec wantspec;
SDL_AudioSpec obtainspec;
snd_threaded = false;
Con_DPrint ("SndSys_Init: using the SDL module\n");
// Init the SDL Audio subsystem
if( SDL_InitSubSystem( SDL_INIT_AUDIO ) ) {
Con_Print( "Initializing the SDL Audio subsystem failed!\n" );
return false;
}
buffersize = (unsigned int)ceil((double)requested->speed / 25.0); // 2048 bytes on 24kHz to 48kHz
// Init the SDL Audio subsystem
wantspec.callback = Buffer_Callback;
wantspec.userdata = NULL;
wantspec.freq = requested->speed;
wantspec.format = ((requested->width == 1) ? AUDIO_U8 : AUDIO_S16SYS);
wantspec.channels = requested->channels;
wantspec.samples = CeilPowerOf2(buffersize); // needs to be a power of 2 on some platforms.
Con_Printf("Wanted audio Specification:\n"
"\tChannels : %i\n"
"\tFormat : 0x%X\n"
"\tFrequency : %i\n"
"\tSamples : %i\n",
wantspec.channels, wantspec.format, wantspec.freq, wantspec.samples);
if( SDL_OpenAudio( &wantspec, &obtainspec ) )
{
Con_Printf( "Failed to open the audio device! (%s)\n", SDL_GetError() );
return false;
}
Con_Printf("Obtained audio specification:\n"
"\tChannels : %i\n"
"\tFormat : 0x%X\n"
"\tFrequency : %i\n"
"\tSamples : %i\n",
obtainspec.channels, obtainspec.format, obtainspec.freq, obtainspec.samples);
// If we haven't obtained what we wanted
if (wantspec.freq != obtainspec.freq ||
wantspec.format != obtainspec.format ||
wantspec.channels != obtainspec.channels)
{
SDL_CloseAudio();
// Pass the obtained format as a suggested format
if (suggested != NULL)
{
suggested->speed = obtainspec.freq;
// FIXME: check the format more carefully. There are plenty of unsupported cases
suggested->width = ((obtainspec.format == AUDIO_U8) ? 1 : 2);
suggested->channels = obtainspec.channels;
}
return false;
}
snd_threaded = true;
snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
if (snd_channellayout.integer == SND_CHANNELLAYOUT_AUTO)
Cvar_SetValueQuick (&snd_channellayout, SND_CHANNELLAYOUT_STANDARD);
sdlaudiotime = 0;
SDL_PauseAudio( false );
return true;
}
/*
====================
SndSys_Shutdown
Stop the sound card, delete "snd_renderbuffer" and free its other resources
====================
*/
void SndSys_Shutdown(void)
{
SDL_CloseAudio();
if (snd_renderbuffer != NULL)
{
Mem_Free(snd_renderbuffer->ring);
Mem_Free(snd_renderbuffer);
snd_renderbuffer = NULL;
}
}
/*
====================
SndSys_Submit
Submit the contents of "snd_renderbuffer" to the sound card
====================
*/
void SndSys_Submit (void)
{
// Nothing to do here (this sound module is callback-based)
}
/*
====================
SndSys_GetSoundTime
Returns the number of sample frames consumed since the sound started
====================
*/
unsigned int SndSys_GetSoundTime (void)
{
return sdlaudiotime;
}
/*
====================
SndSys_LockRenderBuffer
Get the exclusive lock on "snd_renderbuffer"
====================
*/
qboolean SndSys_LockRenderBuffer (void)
{
SDL_LockAudio();
return true;
}
/*
====================
SndSys_UnlockRenderBuffer
Release the exclusive lock on "snd_renderbuffer"
====================
*/
void SndSys_UnlockRenderBuffer (void)
{
SDL_UnlockAudio();
}
/*
====================
SndSys_SendKeyEvents
Send keyboard events originating from the sound system (e.g. MIDI)
====================
*/
void SndSys_SendKeyEvents(void)
{
// not supported
}

View file

@ -0,0 +1,897 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef SUPPORTDIRECTX
#ifndef DIRECTSOUND_VERSION
# define DIRECTSOUND_VERSION 0x0500 /* Version 5.0 */
#endif
#endif
#include <windows.h>
#include <mmsystem.h>
#ifdef SUPPORTDIRECTX
#include <dsound.h>
#endif
#include "qtypes.h"
#include "quakedef.h"
#include "snd_main.h"
// ==============================================================================
#ifndef _WAVEFORMATEXTENSIBLE_
#define _WAVEFORMATEXTENSIBLE_
typedef struct
{
WAVEFORMATEX Format;
union
{
WORD wValidBitsPerSample; // bits of precision
WORD wSamplesPerBlock; // valid if wBitsPerSample==0
WORD wReserved; // If neither applies, set to zero
} Samples;
DWORD dwChannelMask; // which channels are present in stream
GUID SubFormat;
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
#endif
#if !defined(WAVE_FORMAT_EXTENSIBLE)
# define WAVE_FORMAT_EXTENSIBLE 0xFFFE
#endif
// Some speaker positions
#ifndef SPEAKER_FRONT_LEFT
# define SPEAKER_FRONT_LEFT 0x1
# define SPEAKER_FRONT_RIGHT 0x2
# define SPEAKER_FRONT_CENTER 0x4
# define SPEAKER_LOW_FREQUENCY 0x8
# define SPEAKER_BACK_LEFT 0x10
# define SPEAKER_BACK_RIGHT 0x20
# define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
# define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
// ... we never use the other values
#endif
// KSDATAFORMAT_SUBTYPE_PCM = GUID "00000001-0000-0010-8000-00aa00389b71"
static const GUID MY_KSDATAFORMAT_SUBTYPE_PCM =
{
0x00000001,
0x0000,
0x0010,
{
0x80, 0x00,
0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
}
};
// ==============================================================================
extern HWND mainwindow;
static cvar_t snd_wav_partitionsize = {CVAR_SAVE, "snd_wav_partitionsize", "1024", "controls sound delay in samples, values too low will cause crackling, too high will cause delayed sounds"};
static qboolean sndsys_registeredcvars = false;
#ifdef SUPPORTDIRECTX
HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
#endif
// Wave output: queue of this many sound buffers to play, reused cyclically
#define WAV_BUFFERS 16
#define WAV_MASK (WAV_BUFFERS - 1)
static unsigned int wav_buffer_size;
// DirectSound output: 64KB in 1 buffer
//#define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->width * (fmt_ptr)->channels * (fmt_ptr)->speed / 2)
// LordHavoc: changed this to be a multiple of 32768
#define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->channels * 32768)
typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
#ifdef SUPPORTDIRECTX
static qboolean dsound_init;
static unsigned int dsound_time;
static qboolean primary_format_set;
#endif
static qboolean wav_init;
static int snd_sent, snd_completed;
static int prev_painted;
static unsigned int paintpot;
/*
* Global variables. Must be visible to window-procedure function
* so it can unlock and free the data block after it has been played.
*/
HANDLE hData;
HPSTR lpData, lpData2;
HGLOBAL hWaveHdr;
LPWAVEHDR lpWaveHdr;
HWAVEOUT hWaveOut;
WAVEOUTCAPS wavecaps;
DWORD gSndBufSize;
DWORD dwStartTime;
#ifdef SUPPORTDIRECTX
LPDIRECTSOUND pDS;
LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
HINSTANCE hInstDS;
#endif
qboolean SNDDMA_InitWav (void);
#ifdef SUPPORTDIRECTX
sndinitstat SNDDMA_InitDirect (void);
#endif
/*
==================
SndSys_BuildWaveFormat
==================
*/
static qboolean SndSys_BuildWaveFormat (const snd_format_t* requested, WAVEFORMATEXTENSIBLE* fmt_ptr)
{
WAVEFORMATEX* pfmtex;
memset (fmt_ptr, 0, sizeof(*fmt_ptr));
pfmtex = &fmt_ptr->Format;
pfmtex->nChannels = requested->channels;
pfmtex->wBitsPerSample = requested->width * 8;
pfmtex->nSamplesPerSec = requested->speed;
pfmtex->nBlockAlign = pfmtex->nChannels * pfmtex->wBitsPerSample / 8;
pfmtex->nAvgBytesPerSec = pfmtex->nSamplesPerSec * pfmtex->nBlockAlign;
// LordHavoc: disabled this WAVE_FORMAT_EXTENSIBLE support because it does not seem to be working
#if 0
if (requested->channels <= 2)
{
#endif
pfmtex->wFormatTag = WAVE_FORMAT_PCM;
pfmtex->cbSize = 0;
#if 0
}
else
{
pfmtex->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
pfmtex->cbSize = sizeof(*fmt_ptr) - sizeof(fmt_ptr->Format);
fmt_ptr->Samples.wValidBitsPerSample = fmt_ptr->Format.wBitsPerSample;
fmt_ptr->SubFormat = MY_KSDATAFORMAT_SUBTYPE_PCM;
// Build the channel mask
fmt_ptr->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
switch (requested->channels)
{
case 8:
fmt_ptr->dwChannelMask |= SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER;
// no break
case 6:
fmt_ptr->dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
// no break
case 4:
fmt_ptr->dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
break;
default:
Con_Printf("SndSys_BuildWaveFormat: invalid number of channels (%hu)\n", requested->channels);
return false;
}
}
#endif
return true;
}
#ifdef SUPPORTDIRECTX
/*
==================
SndSys_InitDirectSound
DirectSound 5 support
==================
*/
static sndinitstat SndSys_InitDirectSound (const snd_format_t* requested)
{
DSBUFFERDESC dsbuf;
DSBCAPS dsbcaps;
DWORD dwSize;
DSCAPS dscaps;
WAVEFORMATEXTENSIBLE format, pformat;
HRESULT hresult;
int reps;
if (! SndSys_BuildWaveFormat(requested, &format))
return SIS_FAILURE;
if (!hInstDS)
{
hInstDS = LoadLibrary("dsound.dll");
if (hInstDS == NULL)
{
Con_Print("Couldn't load dsound.dll\n");
return SIS_FAILURE;
}
pDirectSoundCreate = (HRESULT (__stdcall *)(GUID *, LPDIRECTSOUND *,IUnknown *))GetProcAddress(hInstDS,"DirectSoundCreate");
if (!pDirectSoundCreate)
{
Con_Print("Couldn't get DS proc addr\n");
return SIS_FAILURE;
}
}
while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
{
if (hresult != DSERR_ALLOCATED)
{
Con_Print("DirectSound create failed\n");
return SIS_FAILURE;
}
if (MessageBox (NULL,
"The sound hardware is in use by another app.\n\n"
"Select Retry to try to start sound again or Cancel to run Quake with no sound.",
"Sound not available",
MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
{
Con_Print("DirectSoundCreate failure\n hardware already in use\n");
return SIS_NOTAVAIL;
}
}
dscaps.dwSize = sizeof(dscaps);
if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps))
{
Con_Print("Couldn't get DS caps\n");
}
if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
{
Con_Print("No DirectSound driver installed\n");
SndSys_Shutdown ();
return SIS_FAILURE;
}
if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
{
Con_Print("Set coop level failed\n");
SndSys_Shutdown ();
return SIS_FAILURE;
}
// get access to the primary buffer, if possible, so we can set the
// sound hardware format
memset (&dsbuf, 0, sizeof(dsbuf));
dsbuf.dwSize = sizeof(DSBUFFERDESC);
dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
dsbuf.dwBufferBytes = 0;
dsbuf.lpwfxFormat = NULL;
memset(&dsbcaps, 0, sizeof(dsbcaps));
dsbcaps.dwSize = sizeof(dsbcaps);
primary_format_set = false;
// COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
if (!COM_CheckParm ("-snoforceformat"))
{
if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
{
pformat = format;
if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, (WAVEFORMATEX*)&pformat))
{
Con_Print("Set primary sound buffer format: no\n");
}
else
{
Con_Print("Set primary sound buffer format: yes\n");
primary_format_set = true;
}
}
}
// COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
if (!primary_format_set || !COM_CheckParm ("-primarysound"))
{
HRESULT result;
// create the secondary buffer we'll actually work with
memset (&dsbuf, 0, sizeof(dsbuf));
dsbuf.dwSize = sizeof(DSBUFFERDESC);
dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE(requested);
dsbuf.lpwfxFormat = (WAVEFORMATEX*)&format;
memset(&dsbcaps, 0, sizeof(dsbcaps));
dsbcaps.dwSize = sizeof(dsbcaps);
result = IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL);
if (result != DS_OK ||
requested->channels != format.Format.nChannels ||
requested->width != format.Format.wBitsPerSample / 8 ||
requested->speed != format.Format.nSamplesPerSec)
{
Con_Printf("DS:CreateSoundBuffer Failed (%d): channels=%u, width=%u, speed=%u\n",
(int)result, (unsigned)format.Format.nChannels, (unsigned)format.Format.wBitsPerSample / 8, (unsigned)format.Format.nSamplesPerSec);
SndSys_Shutdown ();
return SIS_FAILURE;
}
if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps))
{
Con_Print("DS:GetCaps failed\n");
SndSys_Shutdown ();
return SIS_FAILURE;
}
Con_Print("Using secondary sound buffer\n");
}
else
{
if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
{
Con_Print("Set coop level failed\n");
SndSys_Shutdown ();
return SIS_FAILURE;
}
if (DS_OK != IDirectSoundBuffer_GetCaps (pDSPBuf, &dsbcaps))
{
Con_Print("DS:GetCaps failed\n");
return SIS_FAILURE;
}
pDSBuf = pDSPBuf;
Con_Print("Using primary sound buffer\n");
}
// Make sure mixer is active
IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
Con_Printf(" %d channel(s)\n"
" %d bits/sample\n"
" %d samples/sec\n",
requested->channels, requested->width * 8, requested->speed);
gSndBufSize = dsbcaps.dwBufferBytes;
// initialize the buffer
reps = 0;
while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
{
if (hresult != DSERR_BUFFERLOST)
{
Con_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
SndSys_Shutdown ();
return SIS_FAILURE;
}
if (++reps > 10000)
{
Con_Print("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
SndSys_Shutdown ();
return SIS_FAILURE;
}
}
memset(lpData, 0, dwSize);
IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0);
IDirectSoundBuffer_Stop(pDSBuf);
IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
dwStartTime = 0;
dsound_time = 0;
snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
dsound_init = true;
return SIS_SUCCESS;
}
#endif
/*
==================
SndSys_InitMmsystem
Crappy windows multimedia base
==================
*/
static qboolean SndSys_InitMmsystem (const snd_format_t* requested)
{
WAVEFORMATEXTENSIBLE format;
int i;
HRESULT hr;
if (! SndSys_BuildWaveFormat(requested, &format))
return false;
// Open a waveform device for output using window callback
while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, (WAVEFORMATEX*)&format,
0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
{
if (hr != MMSYSERR_ALLOCATED)
{
Con_Print("waveOutOpen failed\n");
return false;
}
if (MessageBox (NULL,
"The sound hardware is in use by another app.\n\n"
"Select Retry to try to start sound again or Cancel to run Quake with no sound.",
"Sound not available",
MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
{
Con_Print("waveOutOpen failure;\n hardware already in use\n");
return false;
}
}
wav_buffer_size = bound(128, snd_wav_partitionsize.integer, 8192) * requested->channels * requested->width;
/*
* Allocate and lock memory for the waveform data. The memory
* for waveform data must be globally allocated with
* GMEM_MOVEABLE and GMEM_SHARE flags.
*/
gSndBufSize = WAV_BUFFERS * wav_buffer_size;
hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
if (!hData)
{
Con_Print("Sound: Out of memory.\n");
SndSys_Shutdown ();
return false;
}
lpData = (HPSTR)GlobalLock(hData);
if (!lpData)
{
Con_Print("Sound: Failed to lock.\n");
SndSys_Shutdown ();
return false;
}
memset (lpData, 0, gSndBufSize);
/*
* Allocate and lock memory for the header. This memory must
* also be globally allocated with GMEM_MOVEABLE and
* GMEM_SHARE flags.
*/
hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
if (hWaveHdr == NULL)
{
Con_Print("Sound: Failed to Alloc header.\n");
SndSys_Shutdown ();
return false;
}
lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
if (lpWaveHdr == NULL)
{
Con_Print("Sound: Failed to lock header.\n");
SndSys_Shutdown ();
return false;
}
memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
// After allocation, set up and prepare headers
for (i=0 ; i<WAV_BUFFERS ; i++)
{
lpWaveHdr[i].dwBufferLength = wav_buffer_size;
lpWaveHdr[i].lpData = lpData + i * wav_buffer_size;
if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
{
Con_Print("Sound: failed to prepare wave headers\n");
SndSys_Shutdown ();
return false;
}
}
snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
prev_painted = 0;
paintpot = 0;
snd_sent = 0;
snd_completed = 0;
wav_init = true;
return true;
}
/*
====================
SndSys_Init
Create "snd_renderbuffer" with the proper sound format if the call is successful
May return a suggested format if the requested format isn't available
====================
*/
qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
{
#ifdef SUPPORTDIRECTX
qboolean wavonly;
#endif
sndinitstat stat;
if (!sndsys_registeredcvars)
{
sndsys_registeredcvars = true;
Cvar_RegisterVariable(&snd_wav_partitionsize);
}
Con_Print ("SndSys_Init: using the Win32 module\n");
#ifdef SUPPORTDIRECTX
// COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
wavonly = (COM_CheckParm ("-wavonly") != 0);
dsound_init = false;
#endif
wav_init = false;
stat = SIS_FAILURE; // assume DirectSound won't initialize
#ifdef SUPPORTDIRECTX
// Init DirectSound
if (!wavonly)
{
stat = SndSys_InitDirectSound (requested);
if (stat == SIS_SUCCESS)
Con_Print("DirectSound initialized\n");
else
Con_Print("DirectSound failed to init\n");
}
#endif
// if DirectSound didn't succeed in initializing, try to initialize
// waveOut sound, unless DirectSound failed because the hardware is
// already allocated (in which case the user has already chosen not
// to have sound)
#ifdef SUPPORTDIRECTX
if (!dsound_init && (stat != SIS_NOTAVAIL))
#endif
{
if (SndSys_InitMmsystem (requested))
Con_Print("Wave sound (MMSYSTEM) initialized\n");
else
Con_Print("Wave sound failed to init\n");
}
#ifdef SUPPORTDIRECTX
return (dsound_init || wav_init);
#else
return wav_init;
#endif
}
/*
====================
SndSys_Shutdown
Stop the sound card, delete "snd_renderbuffer" and free its other resources
====================
*/
void SndSys_Shutdown (void)
{
#ifdef SUPPORTDIRECTX
if (pDSBuf)
{
IDirectSoundBuffer_Stop(pDSBuf);
IDirectSoundBuffer_Release(pDSBuf);
}
// only release primary buffer if it's not also the mixing buffer we just released
if (pDSPBuf && (pDSBuf != pDSPBuf))
{
IDirectSoundBuffer_Release(pDSPBuf);
}
if (pDS)
{
IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
IDirectSound_Release(pDS);
}
#endif
if (hWaveOut)
{
waveOutReset (hWaveOut);
if (lpWaveHdr)
{
unsigned int i;
for (i=0 ; i< WAV_BUFFERS ; i++)
waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
}
waveOutClose (hWaveOut);
if (hWaveHdr)
{
GlobalUnlock(hWaveHdr);
GlobalFree(hWaveHdr);
}
if (hData)
{
GlobalUnlock(hData);
GlobalFree(hData);
}
}
if (snd_renderbuffer != NULL)
{
Mem_Free(snd_renderbuffer);
snd_renderbuffer = NULL;
}
#ifdef SUPPORTDIRECTX
pDS = NULL;
pDSBuf = NULL;
pDSPBuf = NULL;
dsound_init = false;
#endif
hWaveOut = 0;
hData = 0;
hWaveHdr = 0;
lpData = NULL;
lpWaveHdr = NULL;
wav_init = false;
}
/*
====================
SndSys_Submit
Submit the contents of "snd_renderbuffer" to the sound card
====================
*/
void SndSys_Submit (void)
{
LPWAVEHDR h;
int wResult;
// DirectSound doesn't need this
if (!wav_init)
return;
paintpot += (snd_renderbuffer->endframe - prev_painted) * snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
if (paintpot > WAV_BUFFERS * wav_buffer_size)
paintpot = WAV_BUFFERS * wav_buffer_size;
prev_painted = snd_renderbuffer->endframe;
// submit new sound blocks
while (paintpot > wav_buffer_size)
{
h = lpWaveHdr + (snd_sent & WAV_MASK);
/*
* Now the data block can be sent to the output device. The
* waveOutWrite function returns immediately and waveform
* data is sent to the output device in the background.
*/
wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
if (wResult == MMSYSERR_NOERROR)
snd_sent++;
else if (wResult == WAVERR_STILLPLAYING)
{
if(developer_insane.integer)
Con_DPrint("waveOutWrite failed (too much sound data)\n");
//h->dwFlags |= WHDR_DONE;
//snd_sent++;
}
else
{
Con_Printf("waveOutWrite failed, error code %d\n", (int) wResult);
SndSys_Shutdown ();
return;
}
paintpot -= wav_buffer_size;
}
}
/*
====================
SndSys_GetSoundTime
Returns the number of sample frames consumed since the sound started
====================
*/
unsigned int SndSys_GetSoundTime (void)
{
unsigned int factor;
factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
#ifdef SUPPORTDIRECTX
if (dsound_init)
{
DWORD dwTime;
unsigned int diff;
IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL);
diff = (unsigned int)(dwTime - dwStartTime) % (unsigned int)gSndBufSize;
dwStartTime = dwTime;
dsound_time += diff / factor;
return dsound_time;
}
#endif
if (wav_init)
{
// Find which sound blocks have completed
for (;;)
{
if (snd_completed == snd_sent)
{
// Con_DPrint("Sound overrun\n");
break;
}
if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
break;
snd_completed++; // this buffer has been played
}
return (snd_completed * wav_buffer_size) / factor;
/*
* S_PaintAndSubmit: WARNING: newsoundtime (soundtime (275 < 134217707)
* apparently this sound time wraps quite early?
{
MMRESULT res;
MMTIME mmtime;
mmtime.wType = TIME_SAMPLES;
res = waveOutGetPosition(hWaveOut, &mmtime, sizeof(mmtime));
if(res == MMSYSERR_NOERROR)
return mmtime.u.sample;
}
*/
}
return 0;
}
#ifdef SUPPORTDIRECTX
static DWORD dsound_dwSize;
static DWORD dsound_dwSize2;
static DWORD *dsound_pbuf;
static DWORD *dsound_pbuf2;
#endif
/*
====================
SndSys_LockRenderBuffer
Get the exclusive lock on "snd_renderbuffer"
====================
*/
qboolean SndSys_LockRenderBuffer (void)
{
#ifdef SUPPORTDIRECTX
int reps;
HRESULT hresult;
DWORD dwStatus;
if (pDSBuf)
{
// if the buffer was lost or stopped, restore it and/or restart it
if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK)
Con_Print("Couldn't get sound buffer status\n");
if (dwStatus & DSBSTATUS_BUFFERLOST)
{
Con_Print("DSound buffer is lost!!\n");
IDirectSoundBuffer_Restore (pDSBuf);
}
if (!(dwStatus & DSBSTATUS_PLAYING))
IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
reps = 0;
while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
{
if (hresult != DSERR_BUFFERLOST)
{
Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
S_Shutdown ();
S_Startup ();
return false;
}
if (++reps > 10000)
{
Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
S_Shutdown ();
S_Startup ();
return false;
}
}
if ((void*)dsound_pbuf != snd_renderbuffer->ring)
Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n");
return true;
}
#endif
return wav_init;
}
/*
====================
SndSys_UnlockRenderBuffer
Release the exclusive lock on "snd_renderbuffer"
====================
*/
void SndSys_UnlockRenderBuffer (void)
{
#ifdef SUPPORTDIRECTX
if (pDSBuf)
IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);
#endif
}
/*
====================
SndSys_SendKeyEvents
Send keyboard events originating from the sound system (e.g. MIDI)
====================
*/
void SndSys_SendKeyEvents(void)
{
// not supported
}

View file

@ -49,8 +49,6 @@ import android.support.v4.content.ContextCompat;
private final boolean m_asynchronousTracking = false;
public static QQUESTAudioCallback mAudio;
@Override protected void onCreate( Bundle icicle )
{
Log.v( TAG, "----------------------------------------------------------------" );
@ -140,12 +138,8 @@ import android.support.v4.content.ContextCompat;
copy_asset("/sdcard/QuakeQuest/id1", "config.cfg");
copy_asset("/sdcard/QuakeQuest", "commandline.txt");
if (mAudio==null)
{
mAudio = new QQUESTAudioCallback();
}
GLES3JNILib.setCallbackObjects(mAudio, this);
GLES3JNILib.setCallbackObjects(this);
//Read these from a file and pass through
commandLineParams = new String("quake");

View file

@ -21,7 +21,5 @@ public class GLES3JNILib
public static native void onSurfaceChanged( long handle, Surface s );
public static native void onSurfaceDestroyed( long handle );
//Audio
public static native void requestAudioData();
public static native void setCallbackObjects(Object obj1, Object obj2);
public static native void setCallbackObjects(Object obj);
}

View file

@ -1,108 +0,0 @@
package com.drbeef.quakequest;
import java.nio.ByteBuffer;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
public class QQUESTAudioCallback {
public QuakeQuestAudioTrack mAudioTrack;
public ScheduledThreadPoolExecutor stpe;
byte[] mAudioData;
public static boolean reqThreadrunning=true;
public void initAudio(int size)
{
if(mAudioTrack != null) return;
size/=8;
mAudioData=new byte[size];
int sampleFreq = 44100;
int bufferSize = Math.max(size,AudioTrack.getMinBufferSize(sampleFreq, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT));
mAudioTrack = new QuakeQuestAudioTrack(AudioManager.STREAM_MUSIC,
sampleFreq,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize,
AudioTrack.MODE_STREAM);
mAudioTrack.play();
long sleeptime=(size*1000000000l)/(2*2*sampleFreq);
stpe=new ScheduledThreadPoolExecutor(5);
stpe.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
if (reqThreadrunning)
{
GLES3JNILib.requestAudioData( );
}
}
}, 0, sleeptime, TimeUnit.NANOSECONDS);
}
int sync=0;
public void writeAudio(ByteBuffer audioData, int offset, int len)
{
if(mAudioTrack == null)
return;
if (!reqThreadrunning)
return;
audioData.position(offset);
audioData.get(mAudioData, 0, len);
if (sync++%128==0)
mAudioTrack.flush();
mAudioTrack.write(mAudioData, 0, len);
}
public void pauseAudio()
{
if(mAudioTrack == null)
return;
mAudioTrack.pause();
mAudioTrack.flush();
reqThreadrunning=false;
}
public void resumeAudio()
{
if(mAudioTrack == null)
return;
mAudioTrack.play();
reqThreadrunning=true;
}
public void terminateAudio()
{
mAudioTrack.flush();
mAudioTrack.release();
mAudioTrack = null;
reqThreadrunning=false;
stpe.shutdown();
stpe = null;
}
}
class QuakeQuestAudioTrack extends AudioTrack
{
public QuakeQuestAudioTrack(int streamType, int sampleRateInHz,
int channelConfig, int audioFormat, int bufferSizeInBytes, int mode)
throws IllegalStateException {
super(streamType, sampleRateInHz, channelConfig, audioFormat,
bufferSizeInBytes, mode);
}
@Override
public void play() throws IllegalStateException {
flush();
super.play();
}
}