sound is so buggy.
This commit is contained in:
parent
becf8602d7
commit
550556e0e4
12 changed files with 303 additions and 160 deletions
|
@ -6,10 +6,10 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
|
||||||
function(setup_target NAME)
|
function(setup_target NAME)
|
||||||
set_property(TARGET ${NAME} PROPERTY CXX_STANDARD 14)
|
set_property(TARGET ${NAME} PROPERTY CXX_STANDARD 14)
|
||||||
set_property(TARGET ${NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
|
set_property(TARGET ${NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||||
target_compile_options(${NAME} PRIVATE -Wall -Wextra)
|
#target_compile_options(${NAME} PRIVATE -Wall -Wextra)
|
||||||
target_compile_options(${NAME} PRIVATE -fdiagnostics-color=always)
|
#target_compile_options(${NAME} PRIVATE -fdiagnostics-color=always)
|
||||||
target_compile_options(${NAME} PRIVATE $<$<CONFIG:DEBUG>:-ggdb -O2>)
|
#target_compile_options(${NAME} PRIVATE $<$<CONFIG:DEBUG>:-ggdb -O2>)
|
||||||
target_compile_options(${NAME} PRIVATE $<$<CONFIG:RELEASE>:-O3 -NDEBUG>)
|
#target_compile_options(${NAME} PRIVATE $<$<CONFIG:RELEASE>:-O3 -NDEBUG>)
|
||||||
endfunction(setup_target)
|
endfunction(setup_target)
|
||||||
|
|
||||||
add_subdirectory(game)
|
add_subdirectory(game)
|
||||||
|
|
|
@ -22,10 +22,6 @@ set(GAME_SRC
|
||||||
util.cpp
|
util.cpp
|
||||||
game.cpp
|
game.cpp
|
||||||
|
|
||||||
# TODO: make optional!
|
|
||||||
sound/sound.cpp
|
|
||||||
sound/sound_effects.cpp
|
|
||||||
|
|
||||||
state/object.cpp
|
state/object.cpp
|
||||||
state/explosion.cpp
|
state/explosion.cpp
|
||||||
state/trace.cpp
|
state/trace.cpp
|
||||||
|
@ -37,6 +33,14 @@ set(GAME_SRC
|
||||||
state/state.cpp
|
state/state.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(SOUND_LIBRARIES "")
|
||||||
|
|
||||||
|
# TODO: make optional!
|
||||||
|
set(GAME_SRC ${GAME_SRC} sound/sound.cpp sound/sound_effects.cpp)
|
||||||
|
|
||||||
|
set(SOUND_LIBRARIES -lportaudio -lsndfile)
|
||||||
|
#set(GAME_SRC "${GAME_SRC} sound/dummy_sound.cpp sound/dummy_sound_effects.cpp")
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
include_directories(${OPENGL_INCLUDE_DIR})
|
include_directories(${OPENGL_INCLUDE_DIR})
|
||||||
|
@ -47,4 +51,4 @@ include_directories(${assimp_INCLUDE_DIRS})
|
||||||
add_executable(game ${GAME_SRC})
|
add_executable(game ${GAME_SRC})
|
||||||
setup_target(game)
|
setup_target(game)
|
||||||
|
|
||||||
target_link_libraries(game X11 epoxy pthread ${assimp_LIBRARIES} assimp -lportaudio -lsndfile)
|
target_link_libraries(game X11 epoxy pthread ${assimp_LIBRARIES} assimp ${SOUND_LIBRARIES})
|
||||||
|
|
|
@ -10,7 +10,9 @@
|
||||||
#include "network/server.hpp"
|
#include "network/server.hpp"
|
||||||
#include "options.hpp"
|
#include "options.hpp"
|
||||||
|
|
||||||
|
#include "sound/sound.hpp"
|
||||||
#include "sound/sound_effects.hpp"
|
#include "sound/sound_effects.hpp"
|
||||||
|
|
||||||
#include "state/state_update_event.hpp"
|
#include "state/state_update_event.hpp"
|
||||||
|
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
@ -85,11 +87,11 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
|
|
||||||
//sound::SoundEffects *sounds = nullptr;
|
sound::SoundEffects *sounds = nullptr;
|
||||||
if (soundEnabled) {
|
if (soundEnabled) {
|
||||||
//if (sound::initSound()) {
|
if (sound::initSound()) {
|
||||||
//sounds = new sound::SoundEffects();
|
sounds = new sound::SoundEffects();
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Game game;
|
Game game;
|
||||||
|
@ -115,9 +117,12 @@ int main(int argc, char *argv[])
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//if (sounds != nullptr) {
|
if (sounds != nullptr) {
|
||||||
// sounds.advance(game->state());
|
// TODO: get time diff too
|
||||||
//}
|
sounds->advance(1/50.0f, game.state()->currentStateUpdateEvents());
|
||||||
|
// TODO: use flag to now when to do this.
|
||||||
|
sound::deleteOldSounds();
|
||||||
|
}
|
||||||
|
|
||||||
game.state()->applyAndClearAllOldStateUpdates();
|
game.state()->applyAndClearAllOldStateUpdates();
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,7 @@ namespace endofthejedi {
|
||||||
resize();
|
resize();
|
||||||
|
|
||||||
} else if (event.type == ClientMessage) {
|
} else if (event.type == ClientMessage) {
|
||||||
if (event.xclient.data.l[0] == m_atomWmDeleteWindow) {
|
if (event.xclient.data.l[0] == (int) m_atomWmDeleteWindow) {
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
} else if (event.type == DestroyNotify) {
|
} else if (event.type == DestroyNotify) {
|
||||||
|
|
|
@ -14,23 +14,38 @@
|
||||||
// for dumping sound
|
// for dumping sound
|
||||||
#include <sndfile.h>
|
#include <sndfile.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
namespace sound {
|
namespace sound {
|
||||||
|
|
||||||
typedef struct {
|
//struct Slot {
|
||||||
|
// bool used;
|
||||||
|
// bool usable;
|
||||||
|
// bool deleteOnCleanup;
|
||||||
|
// SoundHandle *handle;
|
||||||
|
//};
|
||||||
|
|
||||||
|
struct SoundContext {
|
||||||
PaStream *stream;
|
PaStream *stream;
|
||||||
|
|
||||||
|
// this locks access to the context
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
|
||||||
unsigned int framesPerBuffer;
|
unsigned int framesPerBuffer;
|
||||||
unsigned int sampleRate;
|
unsigned int sampleRate;
|
||||||
|
|
||||||
unsigned int sound_uid_counter;
|
size_t sound_uid_counter;
|
||||||
|
|
||||||
// constant
|
// constant
|
||||||
unsigned int maxNumSounds;
|
size_t maxNumSounds;
|
||||||
|
|
||||||
// num active sonuds
|
// index of highest sound or -1, if there are no sounds.
|
||||||
unsigned int highestSoundIndex;
|
// so 0 means no sounds, > 0 could mean different things depending on
|
||||||
|
// empty sounds.
|
||||||
|
volatile ssize_t highestSoundIndex;
|
||||||
|
|
||||||
// pointer to array of sound handles that has this number of active sounds
|
// pointer to array of sound handles that has this number of active sounds
|
||||||
|
//Slot *soundSlots;
|
||||||
SoundHandle **sounds;
|
SoundHandle **sounds;
|
||||||
|
|
||||||
bool dumping;
|
bool dumping;
|
||||||
|
@ -39,7 +54,7 @@ namespace sound {
|
||||||
SF_INFO sfinfo;
|
SF_INFO sfinfo;
|
||||||
|
|
||||||
bool init;
|
bool init;
|
||||||
} SoundContext;
|
};
|
||||||
|
|
||||||
// singleton so no arguments must be supplied for play() functions etc.
|
// singleton so no arguments must be supplied for play() functions etc.
|
||||||
static SoundContext context;
|
static SoundContext context;
|
||||||
|
@ -83,7 +98,7 @@ namespace sound {
|
||||||
|
|
||||||
if (handle->envelope != next) {
|
if (handle->envelope != next) {
|
||||||
#if 1
|
#if 1
|
||||||
printf("[sound] global time %4.3f #%d (%s) t=%f %5s -> %5s\n",
|
printf("[sound] global time %4.3f #%zd (%s) t=%f %5s -> %5s\n",
|
||||||
global_time,
|
global_time,
|
||||||
handle->uid,
|
handle->uid,
|
||||||
handle->name,
|
handle->name,
|
||||||
|
@ -107,9 +122,9 @@ namespace sound {
|
||||||
context.dumping = false;
|
context.dumping = false;
|
||||||
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printf("warning: stop dumping wav returned error: %d\n", err);
|
fprintf(stderr, "[sound] warning: stop dumping wav returned error: %d\n", err);
|
||||||
} else {
|
} else {
|
||||||
printf("sucessfully stopped dumping to file %s\n", context.dump_filename);
|
printf("[sound] sucessfully stopped dumping to file %s\n", context.dump_filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +133,7 @@ namespace sound {
|
||||||
// TODO: dumping in mono?
|
// TODO: dumping in mono?
|
||||||
|
|
||||||
if (!context.init || !context.dumping) {
|
if (!context.init || !context.dumping) {
|
||||||
printf("warning: can't dump samples: context not initialized or not currently dumping!\n");
|
fprintf(stderr, "[sound] warning: can't dump samples: context not initialized or not currently dumping!\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +150,7 @@ namespace sound {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.dumping) {
|
if (context.dumping) {
|
||||||
printf("warning: already dumping to %s!\n", context.dump_filename);
|
printf("[sound] warning: already dumping to %s!\n", context.dump_filename);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,12 +161,12 @@ namespace sound {
|
||||||
context.outfile = sf_open(filename, SFM_WRITE, &context.sfinfo);
|
context.outfile = sf_open(filename, SFM_WRITE, &context.sfinfo);
|
||||||
|
|
||||||
if (context.outfile == NULL) {
|
if (context.outfile == NULL) {
|
||||||
printf("Unable to dump to output file %s.\n", filename);
|
fprintf(stderr, "[sound] Unable to dump to output file %s.\n", filename);
|
||||||
sf_perror(NULL);
|
sf_perror(NULL);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
//printf("start dumping wav to file %s\n", filename);
|
printf("[sound] start dumping wav to file %s\n", filename);
|
||||||
context.dump_filename = filename;
|
context.dump_filename = filename;
|
||||||
context.dumping = true;
|
context.dumping = true;
|
||||||
return true;
|
return true;
|
||||||
|
@ -277,7 +292,7 @@ namespace sound {
|
||||||
if (n >= handle->numHistorySamples) {
|
if (n >= handle->numHistorySamples) {
|
||||||
// TODO clear history when looping?
|
// TODO clear history when looping?
|
||||||
// or add zero samples?
|
// or add zero samples?
|
||||||
printf("warning: try to access more history values than saved.\n");
|
printf("[sound] warning: try to access more history values than saved.\n");
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,6 +326,25 @@ namespace sound {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return false on no success
|
||||||
|
bool tryLock()
|
||||||
|
{
|
||||||
|
int ret = pthread_mutex_trylock(&context.mutex);
|
||||||
|
return ret == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait if does not lock
|
||||||
|
void lock()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&context.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock()
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&context.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// return false if playing done.
|
// return false if playing done.
|
||||||
static bool advanceSound(SoundHandle *handle, float *out, unsigned int framesPerBuffer)
|
static bool advanceSound(SoundHandle *handle, float *out, unsigned int framesPerBuffer)
|
||||||
{
|
{
|
||||||
|
@ -406,7 +440,7 @@ namespace sound {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
printf("warning: unimplemented sound type for #%d: %d\n", handle->uid, handle->_type);
|
printf("[sound] warning: unimplemented sound type for #%zd: %d\n", handle->uid, handle->_type);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,23 +491,31 @@ namespace sound {
|
||||||
// we have 32 bit float format so cast here
|
// we have 32 bit float format so cast here
|
||||||
float *out = (float *) outputBuffer;
|
float *out = (float *) outputBuffer;
|
||||||
|
|
||||||
|
{
|
||||||
// set all to zero
|
// set all to zero
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
for (i=0; i<framesPerBuffer; i++) {
|
for (i=0; i<framesPerBuffer; i++) {
|
||||||
*out++ = 0.0f;
|
*out++ = 0.0f;
|
||||||
*out++ = 0.0f;
|
*out++ = 0.0f;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// reset pointer
|
// reset pointer
|
||||||
out = (float *) outputBuffer;
|
out = (float *) outputBuffer;
|
||||||
|
|
||||||
// advance all sounds
|
// advance all sounds
|
||||||
|
ssize_t i;
|
||||||
for (i=0; i<context.highestSoundIndex; i++) {
|
for (i=0; i<context.highestSoundIndex; i++) {
|
||||||
SoundHandle *handle = context.sounds[i];
|
SoundHandle *handle = context.sounds[i];
|
||||||
// XXX
|
|
||||||
if (handle == NULL) {
|
if (handle == NULL) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (handle->deleteFlag) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (handle->isDeleted) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!handle->_done) {
|
if (!handle->_done) {
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -489,12 +531,14 @@ namespace sound {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: when we should not free in the loop, where else?
|
// TODO: when we should not free in the loop, where else?
|
||||||
if (handle->_done) {
|
if (handle->_done && !handle->deleteFlag) {
|
||||||
//printf("[sound] %f freeing sound #%d handle 0x%p\n", global_time, handle->uid, handle);
|
//printf("[sound] %f freeing sound #%d handle 0x%p\n", global_time, handle->uid, handle);
|
||||||
context.sounds[i] = NULL;
|
printf("[sound] %f mark sound #%zd handle 0x%p for deletion\n", global_time, handle->uid, handle);
|
||||||
if (!handle->keep_when_done) {
|
//context.sounds[i] = NULL;
|
||||||
deleteSound(handle);
|
handle->deleteFlag = true;
|
||||||
}
|
//if (!handle->keep_when_done) {
|
||||||
|
// deleteSound(handle);
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,10 +592,11 @@ namespace sound {
|
||||||
// TODO: add max number of simultanous playing sounds, that's more important.
|
// TODO: add max number of simultanous playing sounds, that's more important.
|
||||||
|
|
||||||
context.sound_uid_counter = 0;
|
context.sound_uid_counter = 0;
|
||||||
context.maxNumSounds = 50;
|
context.maxNumSounds = 10;
|
||||||
context.highestSoundIndex = 0;
|
context.highestSoundIndex = -1;
|
||||||
|
//context.soundSlots = (Slot*) calloc(context.maxNumSounds, 1000); // XXX HACK
|
||||||
context.sounds = (SoundHandle **) calloc(context.maxNumSounds, 1000); // XXX HACK
|
context.sounds = (SoundHandle **) calloc(context.maxNumSounds, 1000); // XXX HACK
|
||||||
assert(context.sounds != NULL);
|
//assert(context.soundSlots != NULL);
|
||||||
|
|
||||||
PaError err = Pa_Initialize();
|
PaError err = Pa_Initialize();
|
||||||
if (err != paNoError) {
|
if (err != paNoError) {
|
||||||
|
@ -647,21 +692,39 @@ namespace sound {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nextFreeIndex = 0;
|
ssize_t nextFreeIndex = 0;
|
||||||
while(nextFreeIndex < context.highestSoundIndex) {
|
while(nextFreeIndex <= context.highestSoundIndex) {
|
||||||
if (context.sounds[nextFreeIndex] == NULL) {
|
if (context.sounds[nextFreeIndex] == NULL || context.sounds[nextFreeIndex]->isDeleted) {
|
||||||
// got an empyt sound to use
|
// got an empty sound to use
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
nextFreeIndex++;
|
nextFreeIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextFreeIndex >= context.maxNumSounds) {
|
if (context.sounds[nextFreeIndex] != NULL) {
|
||||||
printf("error: can't create more sounds, got maximum of: %d\n", context.maxNumSounds);
|
printf("[sound] playSound() finally free'ing old sound: %zd\n",
|
||||||
|
context.sounds[nextFreeIndex]->uid);
|
||||||
|
|
||||||
|
free(context.sounds[nextFreeIndex]);
|
||||||
|
context.sounds[nextFreeIndex] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.sounds[nextFreeIndex] != NULL) {
|
||||||
|
fprintf(stderr, "[sound] warning: playSound() nextFreeIndex is "
|
||||||
|
"%zd but that sound is not NULL!\n", nextFreeIndex);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextFreeIndex+1 >= (ssize_t) context.maxNumSounds) {
|
||||||
|
printf("[sound] warning: can't create more sounds, got maximum of: %zd\n", context.maxNumSounds);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SoundHandle *handle = (SoundHandle *) calloc(1, sizeof(SoundHandle));
|
SoundHandle *handle = (SoundHandle *) calloc(1, sizeof(SoundHandle));
|
||||||
|
handle->deleteFlag = false;
|
||||||
|
handle->isDeleted = false;
|
||||||
|
handle->_done = false;
|
||||||
handle->envelope = New;
|
handle->envelope = New;
|
||||||
handle->amplitude = amplitude;
|
handle->amplitude = amplitude;
|
||||||
handle->minAmplitude = 0.0;
|
handle->minAmplitude = 0.0;
|
||||||
|
@ -674,7 +737,6 @@ namespace sound {
|
||||||
handle->state = 0.0;
|
handle->state = 0.0;
|
||||||
handle->leakage = 0.0;
|
handle->leakage = 0.0;
|
||||||
handle->scaling = 0.0;
|
handle->scaling = 0.0;
|
||||||
handle->_done = false;
|
|
||||||
handle->skipDirectionIfClamping = false;
|
handle->skipDirectionIfClamping = false;
|
||||||
|
|
||||||
handle->_useEnvelope = false;
|
handle->_useEnvelope = false;
|
||||||
|
@ -725,9 +787,16 @@ namespace sound {
|
||||||
|
|
||||||
context.sounds[nextFreeIndex] = handle;
|
context.sounds[nextFreeIndex] = handle;
|
||||||
|
|
||||||
if (nextFreeIndex == context.highestSoundIndex) {
|
if (nextFreeIndex == context.highestSoundIndex+1) {
|
||||||
context.highestSoundIndex++;
|
context.highestSoundIndex++;
|
||||||
}
|
}
|
||||||
|
//if (nextFreeIndex < context.highestSoundIndex) {
|
||||||
|
// fprintf(stderr, "[sound] warning: nextFreeIndex > context.highestSoundIndex, "
|
||||||
|
// "this should not happen: %zd %zd\n", nextFreeIndex, context.highestSoundIndex);
|
||||||
|
//}
|
||||||
|
|
||||||
|
printf("[sound] playSound() created sound #%zd name %s\n", handle->uid, handle->name);
|
||||||
|
printf("[sound] highestSoundIndex now %zd\n", context.highestSoundIndex);
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
@ -917,8 +986,9 @@ namespace sound {
|
||||||
int numActiveSounds(void)
|
int numActiveSounds(void)
|
||||||
{
|
{
|
||||||
int num = 0;
|
int num = 0;
|
||||||
size_t i;
|
ssize_t i;
|
||||||
for (i=0; i<context.highestSoundIndex; i++) {
|
for (i=0; i<=context.highestSoundIndex; i++) {
|
||||||
|
// TODO: active is when not done or not deleted!
|
||||||
if (context.sounds[i] != NULL) {
|
if (context.sounds[i] != NULL) {
|
||||||
num++;
|
num++;
|
||||||
}
|
}
|
||||||
|
@ -932,12 +1002,12 @@ namespace sound {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// just for sure
|
// just to be sure
|
||||||
handle->_done = true;
|
handle->_done = true;
|
||||||
|
|
||||||
// clear from that
|
// clear from that
|
||||||
size_t i;
|
ssize_t i;
|
||||||
for (i=0; i<context.highestSoundIndex; i++) {
|
for (i=0; i<=context.highestSoundIndex; i++) {
|
||||||
if (context.sounds[i] == handle) {
|
if (context.sounds[i] == handle) {
|
||||||
context.sounds[i] = NULL;
|
context.sounds[i] = NULL;
|
||||||
if (i+1 == context.highestSoundIndex) {
|
if (i+1 == context.highestSoundIndex) {
|
||||||
|
@ -965,4 +1035,37 @@ namespace sound {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deleteOldSounds(void)
|
||||||
|
{
|
||||||
|
ssize_t i;
|
||||||
|
for (i=0; i<=context.highestSoundIndex; i++) {
|
||||||
|
SoundHandle *handle = context.sounds[i];
|
||||||
|
if (handle != NULL && handle->deleteFlag) {
|
||||||
|
//context.sounds[i] = NULL;
|
||||||
|
printf("[sound] %f finally deleting sound #%zd handle 0x%p\n", global_time, handle->uid, handle);
|
||||||
|
if (handle->history != NULL) {
|
||||||
|
free(handle->history);
|
||||||
|
}
|
||||||
|
handle->isDeleted = true;
|
||||||
|
handle->deleteFlag = false;
|
||||||
|
//free(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t nextHighestSoundIndex = -1;
|
||||||
|
for (i=0; i<=context.highestSoundIndex; i++) {
|
||||||
|
SoundHandle *handle = context.sounds[i];
|
||||||
|
if (handle != NULL) {
|
||||||
|
nextHighestSoundIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.highestSoundIndex != nextHighestSoundIndex) {
|
||||||
|
printf("[sound] deleteOldSounds() setting hightest sound index from %zd to %zd\n",
|
||||||
|
context.highestSoundIndex, nextHighestSoundIndex);
|
||||||
|
|
||||||
|
context.highestSoundIndex = nextHighestSoundIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,12 +31,19 @@ namespace sound {
|
||||||
SoundDecayExp = 1
|
SoundDecayExp = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
struct SoundHandle {
|
||||||
// just for debugging/logging. don't rely on use
|
// just for debugging/logging. don't rely on use
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
// unique ID for accessing them via lookups etc. if sound is dead, this will be never reused
|
// unique ID for accessing them via lookups etc. if sound is dead, this will be never reused
|
||||||
int uid;
|
size_t uid;
|
||||||
|
|
||||||
|
// marked for deletion by the internal thread.
|
||||||
|
bool deleteFlag;
|
||||||
|
|
||||||
|
// is now deleted. when a new sound should be used, this can be reused
|
||||||
|
// and at the end deleteFlag and isDeleted should be cleared.
|
||||||
|
bool isDeleted;
|
||||||
|
|
||||||
// if true, don't deallocate once this is done.
|
// if true, don't deallocate once this is done.
|
||||||
bool keep_when_done;
|
bool keep_when_done;
|
||||||
|
@ -110,11 +117,15 @@ namespace sound {
|
||||||
float *history; // if != 0, history is a pointer to 2*numHistorySamples samples.
|
float *history; // if != 0, history is a pointer to 2*numHistorySamples samples.
|
||||||
size_t numHistorySamples; // if != 0, save history for this sound.
|
size_t numHistorySamples; // if != 0, save history for this sound.
|
||||||
size_t lastHistorySample; // index/2 of last history sample. used
|
size_t lastHistorySample; // index/2 of last history sample. used
|
||||||
} SoundHandle;
|
};
|
||||||
|
|
||||||
// initialize sound subsystem. return true if working, false on errors.
|
// initialize sound subsystem. return true if working, false on errors.
|
||||||
bool initSound(void);
|
bool initSound(void);
|
||||||
|
|
||||||
|
// free sounds that are not used anymore.
|
||||||
|
// must be called regularily by the application
|
||||||
|
void deleteOldSounds();
|
||||||
|
|
||||||
// start playing a sound.
|
// start playing a sound.
|
||||||
// args:
|
// args:
|
||||||
// type - type of sound.
|
// type - type of sound.
|
||||||
|
|
|
@ -2,76 +2,82 @@
|
||||||
|
|
||||||
#include "sound.hpp"
|
#include "sound.hpp"
|
||||||
|
|
||||||
|
#include "util.hpp"
|
||||||
|
|
||||||
namespace sound {
|
namespace sound {
|
||||||
#if 0
|
void SoundEffects::addSoundHandleForObject(game::Object *obj, SoundHandle *handle)
|
||||||
class MissileFlySoundEffect : public SoundEffect {
|
|
||||||
MissileFlySoundEffect(const game::Missile *missile)
|
|
||||||
: m_missileId(missile->id)
|
|
||||||
, m_handle(sound::playSound(sound::playFrequency(440, 0.3))
|
|
||||||
{
|
{
|
||||||
|
std::cout<<"add sound for object: " << obj->id << std::endl;
|
||||||
|
auto pair = std::pair<size_t, SoundHandle*>(obj->id, handle);
|
||||||
|
m_mapObjectToSoundHandle.insert(pair);
|
||||||
}
|
}
|
||||||
|
|
||||||
~MissileFlySoundEffect()
|
|
||||||
|
void SoundEffects::fadeOutSoundHandlesForObject(game::Object *obj, float fadeOutTime)
|
||||||
{
|
{
|
||||||
delete(m_handle);
|
std::cout<<"deleting sounds for object: " << obj->id << std::endl;
|
||||||
|
std::multimap<size_t, SoundHandle*>::iterator it;
|
||||||
|
for (it = m_mapObjectToSoundHandle.find(obj->id); it != m_mapObjectToSoundHandle.end(); it++) {
|
||||||
|
std::cout<<"sound #" << it->second->uid << " / " << it->second->name << std::endl;
|
||||||
|
fadeOut(it->second, fadeOutTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool advance(float dt)
|
size_t numRemoved = m_mapObjectToSoundHandle.erase(obj->id);
|
||||||
{
|
std::cout<<"removed total sounds: " << numRemoved << std::endl;
|
||||||
for (const game::StateUpdateEvent *event : updates) {
|
|
||||||
handleUpdateEvent(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add event if sth. comes into life or dies in a state
|
|
||||||
// cycle.. delete objects that died last cycle in the current cycle
|
|
||||||
// so pointers don't get invalid.
|
|
||||||
|
|
||||||
bool keep = false;
|
|
||||||
for (const game::Missile *missile : state->missiles) {
|
|
||||||
if (missile->id == m_missileId) {
|
|
||||||
keep = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!keep) {
|
|
||||||
sound::stopSound(m_handle);
|
|
||||||
sound::deleteSound(m_handle);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
SoundHandle *m_handle;
|
|
||||||
size_t m_missileId;
|
|
||||||
};
|
|
||||||
|
|
||||||
void SoundEffects::advance(float dt, const std::list<game::StateUpdateEvent*> &updates)
|
void SoundEffects::advance(float dt, const std::list<game::StateUpdateEvent*> &updates)
|
||||||
{
|
{
|
||||||
//StateUpdateEvent::LifeCycle::Create,
|
(void) dt;
|
||||||
//StateUpdateEvent::Type::Explosion
|
|
||||||
|
|
||||||
std::vector<SoundEffect*> rm;
|
for (const game::StateUpdateEvent *evt : updates) {
|
||||||
|
auto type = evt->eventType();
|
||||||
for (SoundEffect *effect : m_effects) {
|
auto cycle = evt->lifeCycle();
|
||||||
bool keep = effect->advance(dt, m_gameState);
|
if (type == game::EventType::Missile) {
|
||||||
if (!keep) {
|
if (cycle == game::LifeCycle::Create) {
|
||||||
rm.push_back(effect);
|
SoundHandle *handle = playFrequency(880.0, 0.1);
|
||||||
}
|
if (handle == nullptr) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
fadeIn(handle);
|
||||||
|
handle->name = "missile-fly-sound_fade-in";
|
||||||
|
|
||||||
for (SoundEffect *effect : rm) {
|
addSoundHandleForObject(evt->object(), handle);
|
||||||
m_effects.remove(rm);
|
|
||||||
delete(rm);
|
|
||||||
}
|
|
||||||
|
|
||||||
// spawn new sounds
|
} else if (cycle == game::LifeCycle::Destroy) {
|
||||||
//for (const game::Missile *missile : state->missiles) {
|
fadeOutSoundHandlesForObject(evt->object());
|
||||||
// for (SoundEffect
|
}
|
||||||
// if (missile->
|
} else if (type == game::EventType::Explosion) {
|
||||||
//}
|
#if 1
|
||||||
|
if (cycle == game::LifeCycle::Create) {
|
||||||
|
SoundHandle *handle = playSound(SoundBrownianNoise, 0.4);
|
||||||
|
if (handle == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
configureEnvelope(handle, 0.2, 0.0, 2.0);
|
||||||
|
|
||||||
|
// add big *boom* sound too
|
||||||
|
handle = playFrequency(200.0, 0.5);
|
||||||
|
if (handle == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
configureEnvelope(handle, 0.1, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundEffects::fadeIn(SoundHandle *handle, float fadeInTime)
|
||||||
|
{
|
||||||
|
// assume sound has just started
|
||||||
|
configureEnvelope(handle, fadeInTime, 1000000.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundEffects::fadeOut(SoundHandle *handle, float fadeOutTime)
|
||||||
|
{
|
||||||
|
// reset time so it imediately starts to fade out.
|
||||||
|
configureEnvelope(handle, 0.0, 0.0, fadeOutTime);
|
||||||
|
handle->time = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "state/state.hpp"
|
#include "state/state.hpp"
|
||||||
|
|
||||||
namespace sound {
|
namespace sound {
|
||||||
#if 0
|
struct SoundHandle;
|
||||||
namespace game {
|
|
||||||
class StateUpdateEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
class SoundEffect {
|
class SoundEffect {
|
||||||
public:
|
public:
|
||||||
virtual ~SoundEffects()
|
virtual ~SoundEffects()
|
||||||
|
@ -19,6 +18,7 @@ namespace sound {
|
||||||
// return false if it can be deleted
|
// return false if it can be deleted
|
||||||
virtual bool advance(float dt) = 0;
|
virtual bool advance(float dt) = 0;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
class SoundEffects {
|
class SoundEffects {
|
||||||
public:
|
public:
|
||||||
|
@ -26,11 +26,18 @@ namespace sound {
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleUpdateEvent(game::StateUpdateEvent *event);
|
|
||||||
void advance(float dt, const std::list<game::StateUpdateEvent*> &updates);
|
void advance(float dt, const std::list<game::StateUpdateEvent*> &updates);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::list<SoundEffect*> m_effects;
|
void fadeIn(SoundHandle *handle, float fadeInTime=0.3f);
|
||||||
}
|
void fadeOut(SoundHandle *handle, float fadeOutTime=0.3f);
|
||||||
#endif
|
|
||||||
|
void addSoundHandleForObject(game::Object *obj, SoundHandle *handle);
|
||||||
|
void fadeOutSoundHandlesForObject(game::Object *obj, float fadeOutTime=0.3f);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::list<SoundHandle*> m_soundHandles;
|
||||||
|
std::multimap<size_t, SoundHandle*> m_mapObjectToSoundHandle;
|
||||||
|
//std::list<SoundEffect*> m_effects;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace game {
|
||||||
|
|
||||||
// angles are supplied in degrees and are CCW
|
// angles are supplied in degrees and are CCW
|
||||||
Missile *missile = new Missile(
|
Missile *missile = new Missile(
|
||||||
state->generateMissileId(),
|
state->generateId(),
|
||||||
player,
|
player,
|
||||||
player->ship->position,
|
player->ship->position,
|
||||||
-util::deg2rad(m_angle),
|
-util::deg2rad(m_angle),
|
||||||
|
@ -30,7 +30,8 @@ namespace game {
|
||||||
{
|
{
|
||||||
(void) state;
|
(void) state;
|
||||||
|
|
||||||
return player->alive && player->energy >= player->speed;
|
//return player->alive && player->energy >= player->speed;
|
||||||
|
return player->alive;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChangeNameCommand::apply(Player *player, State *state) const
|
void ChangeNameCommand::apply(Player *player, State *state) const
|
||||||
|
@ -76,6 +77,8 @@ namespace game {
|
||||||
|
|
||||||
void DeveloperCommand::apply(Player *player, State *state) const
|
void DeveloperCommand::apply(Player *player, State *state) const
|
||||||
{
|
{
|
||||||
|
(void) player;
|
||||||
|
|
||||||
if (!state->developerMode()) {
|
if (!state->developerMode()) {
|
||||||
std::cout<<"ignoring dev command while not in developer mode: '"
|
std::cout<<"ignoring dev command while not in developer mode: '"
|
||||||
<< m_payload << "'" << std::endl;
|
<< m_payload << "'" << std::endl;
|
||||||
|
@ -96,6 +99,7 @@ namespace game {
|
||||||
|
|
||||||
void SetDeveloperModeCommand::apply(Player *player, State *state) const
|
void SetDeveloperModeCommand::apply(Player *player, State *state) const
|
||||||
{
|
{
|
||||||
|
(void) player;
|
||||||
// TODO: check if player is admin
|
// TODO: check if player is admin
|
||||||
|
|
||||||
state->setDeveloperMode(m_enable);
|
state->setDeveloperMode(m_enable);
|
||||||
|
|
|
@ -32,9 +32,6 @@ namespace game {
|
||||||
|
|
||||||
// TODO: clear shots etc. too
|
// TODO: clear shots etc. too
|
||||||
|
|
||||||
m_nextPlayerId = 0;
|
|
||||||
m_nextMissileId = 0;
|
|
||||||
m_nextShipId = 0;
|
|
||||||
m_time = 0.0;
|
m_time = 0.0;
|
||||||
m_shipRadius = 0.02;
|
m_shipRadius = 0.02;
|
||||||
m_maxMissileDistance = 2.0;
|
m_maxMissileDistance = 2.0;
|
||||||
|
@ -42,7 +39,6 @@ namespace game {
|
||||||
m_defaultEnergy = 10.0;
|
m_defaultEnergy = 10.0;
|
||||||
m_maxNumTraces = 10;
|
m_maxNumTraces = 10;
|
||||||
m_developerMode = devMode;
|
m_developerMode = devMode;
|
||||||
m_nextExplosionId = 0;
|
|
||||||
|
|
||||||
setPlayingFieldCenter(0, 0);
|
setPlayingFieldCenter(0, 0);
|
||||||
|
|
||||||
|
@ -144,7 +140,7 @@ namespace game {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ship *ship = new Ship(m_nextShipId++, spawnPos, m_shipRadius);
|
Ship *ship = new Ship(generateId(), spawnPos, m_shipRadius);
|
||||||
player->ship = ship;
|
player->ship = ship;
|
||||||
ships.push_back(ship);
|
ships.push_back(ship);
|
||||||
|
|
||||||
|
@ -157,7 +153,7 @@ namespace game {
|
||||||
|
|
||||||
size_t State::addPlayer()
|
size_t State::addPlayer()
|
||||||
{
|
{
|
||||||
Player *player = new Player(m_nextPlayerId++);
|
Player *player = new Player(generateId());
|
||||||
players.push_back(player);
|
players.push_back(player);
|
||||||
return player->id;
|
return player->id;
|
||||||
}
|
}
|
||||||
|
@ -483,7 +479,7 @@ namespace game {
|
||||||
}
|
}
|
||||||
|
|
||||||
Explosion *explosion = new Explosion(
|
Explosion *explosion = new Explosion(
|
||||||
m_nextExplosionId++, evt->position,
|
generateId(), evt->position,
|
||||||
evt->missileVelocity, evt->hit);
|
evt->missileVelocity, evt->hit);
|
||||||
|
|
||||||
explosions.push_back(explosion);
|
explosions.push_back(explosion);
|
||||||
|
@ -519,9 +515,9 @@ namespace game {
|
||||||
m_playingFieldSize = glm::vec2(width, height);
|
m_playingFieldSize = glm::vec2(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t State::generateMissileId()
|
size_t State::generateId()
|
||||||
{
|
{
|
||||||
return m_nextMissileId++;
|
return m_ids.makeNextId();
|
||||||
}
|
}
|
||||||
|
|
||||||
void State::applyAndClearAllOldStateUpdates()
|
void State::applyAndClearAllOldStateUpdates()
|
||||||
|
@ -532,8 +528,8 @@ namespace game {
|
||||||
case EventType::Explosion:
|
case EventType::Explosion:
|
||||||
{
|
{
|
||||||
ExplosionEvent *ee = static_cast<ExplosionEvent*>(evt);
|
ExplosionEvent *ee = static_cast<ExplosionEvent*>(evt);
|
||||||
//std::cout<<"got explosion delete event, finally deleting explosion #"
|
std::cout<<"got explosion delete event, finally deleting explosion #"
|
||||||
// << ee->explosion->id << std::endl;
|
<< ee->object()->id << std::endl;
|
||||||
|
|
||||||
delete(ee->object());
|
delete(ee->object());
|
||||||
}
|
}
|
||||||
|
@ -542,8 +538,8 @@ namespace game {
|
||||||
case EventType::Missile:
|
case EventType::Missile:
|
||||||
{
|
{
|
||||||
auto *me = static_cast<MissileEvent*>(evt);
|
auto *me = static_cast<MissileEvent*>(evt);
|
||||||
//std::cout<<"got missile delete event, finally deleting missile #"
|
std::cout<<"got missile delete event, finally deleting missile #"
|
||||||
// << me->missile->id << std::endl;
|
<< me->object()->id << std::endl;
|
||||||
|
|
||||||
delete(me->object());
|
delete(me->object());
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,25 @@ namespace game {
|
||||||
class Trace;
|
class Trace;
|
||||||
class Explosion;
|
class Explosion;
|
||||||
|
|
||||||
|
class IdGenerator {
|
||||||
|
public:
|
||||||
|
IdGenerator() : m_nextId(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t makeNextId()
|
||||||
|
{
|
||||||
|
return m_nextId++;
|
||||||
|
if (m_nextId == 0) {
|
||||||
|
std::cerr << "note: id counter just wrapped to 0, "
|
||||||
|
"funny things can happen now." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t m_nextId;
|
||||||
|
};
|
||||||
|
|
||||||
class State {
|
class State {
|
||||||
public:
|
public:
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
@ -105,7 +124,7 @@ namespace game {
|
||||||
void setPlayingFieldSize(int width, int height);
|
void setPlayingFieldSize(int width, int height);
|
||||||
void setPlayingFieldCenter(int width, int height);
|
void setPlayingFieldCenter(int width, int height);
|
||||||
|
|
||||||
size_t generateMissileId();
|
size_t generateId();
|
||||||
|
|
||||||
void applyAndClearAllOldStateUpdates();
|
void applyAndClearAllOldStateUpdates();
|
||||||
|
|
||||||
|
@ -159,10 +178,7 @@ namespace game {
|
||||||
float m_shipRadius;
|
float m_shipRadius;
|
||||||
float m_defaultEnergy;
|
float m_defaultEnergy;
|
||||||
int m_maxNumTraces;
|
int m_maxNumTraces;
|
||||||
size_t m_nextPlayerId;
|
IdGenerator m_ids;
|
||||||
size_t m_nextExplosionId;
|
|
||||||
size_t m_nextMissileId;
|
|
||||||
size_t m_nextShipId;
|
|
||||||
float m_time;
|
float m_time;
|
||||||
bool m_developerMode;
|
bool m_developerMode;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
import os
|
|
||||||
import random
|
|
||||||
|
|
||||||
for i in range(100):
|
|
||||||
a = random.randint(0, 360)
|
|
||||||
print("spawn #%d +shoot %d" % (i, a))
|
|
||||||
os.system("echo %d | ncat 192.168.0.191 3490" % a)
|
|
||||||
|
|
Loading…
Reference in a new issue