mirror of
https://github.com/DrBeef/QuakeQuest.git
synced 2024-11-21 11:40:59 +00:00
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:
parent
678877bda4
commit
605d1edb6e
20 changed files with 4440 additions and 230 deletions
|
@ -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. -->
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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++)
|
||||
|
|
1042
Projects/Android/jni/darkplaces/snd_3dras.c
Normal file
1042
Projects/Android/jni/darkplaces/snd_3dras.c
Normal file
File diff suppressed because it is too large
Load diff
49
Projects/Android/jni/darkplaces/snd_3dras.h
Normal file
49
Projects/Android/jni/darkplaces/snd_3dras.h
Normal 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
|
57
Projects/Android/jni/darkplaces/snd_3dras_typedefs.h
Normal file
57
Projects/Android/jni/darkplaces/snd_3dras_typedefs.h
Normal 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
|
524
Projects/Android/jni/darkplaces/snd_alsa.c
Normal file
524
Projects/Android/jni/darkplaces/snd_alsa.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
243
Projects/Android/jni/darkplaces/snd_bsd.c
Normal file
243
Projects/Android/jni/darkplaces/snd_bsd.c
Normal 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
|
||||
}
|
399
Projects/Android/jni/darkplaces/snd_coreaudio.c
Normal file
399
Projects/Android/jni/darkplaces/snd_coreaudio.c
Normal 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
|
||||
}
|
|
@ -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 ())
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
169
Projects/Android/jni/darkplaces/snd_null.c
Normal file
169
Projects/Android/jni/darkplaces/snd_null.c
Normal 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)
|
||||
{
|
||||
}
|
399
Projects/Android/jni/darkplaces/snd_opensl.c
Normal file
399
Projects/Android/jni/darkplaces/snd_opensl.c
Normal 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
|
||||
}
|
344
Projects/Android/jni/darkplaces/snd_oss.c
Normal file
344
Projects/Android/jni/darkplaces/snd_oss.c
Normal 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
|
||||
}
|
254
Projects/Android/jni/darkplaces/snd_sdl.c
Normal file
254
Projects/Android/jni/darkplaces/snd_sdl.c
Normal 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
|
||||
}
|
897
Projects/Android/jni/darkplaces/snd_win.c
Normal file
897
Projects/Android/jni/darkplaces/snd_win.c
Normal 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
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue