Mercurial > hg > dmlib
view src/dmengine.c @ 2534:d6b9410f1b1b
Fix some enums.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sun, 12 Jul 2020 01:19:19 +0300 |
parents | f7d54ac51880 |
children | 9807ae37ad69 |
line wrap: on
line source
/* * dmlib * -- Demo engine / editor common code and definitions * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2012-2015 Tecnic Software productions (TNSP) */ #include "dmengine.h" #include "dmimage.h" #include <SDL_timer.h> #ifdef DM_USE_TREMOR #include <tremor/ivorbiscodec.h> #include <tremor/ivorbisfile.h> #endif DMEngineData engine; DMEffect *engineEffects = NULL; int nengineEffects = 0, nengineEffectsAlloc = 0; int engineRegisterEffect(const DMEffect *ef) { if (ef == NULL) return DMERR_NULLPTR; // Allocate more space for effects if (nengineEffects + 1 >= nengineEffectsAlloc) { nengineEffectsAlloc += 16; engineEffects = dmRealloc(engineEffects, sizeof(DMEffect) * nengineEffectsAlloc); if (engineEffects == NULL) { return dmErrorDBG(DMERR_INIT_FAIL, "Could not expand effects structure.\n"); } } // Copy effects structure memcpy(engineEffects + nengineEffects, ef, sizeof(DMEffect)); nengineEffects++; return DMERR_OK; } int engineInitializeEffects(DMEngineData *engine) { int i, res; dmFree(engine->effectData); engine->effectData = dmCalloc(nengineEffectsAlloc, sizeof(void *)); if (engine->effectData == NULL) { return dmErrorDBG(DMERR_INIT_FAIL, "Could not expand effects data structure.\n"); } for (i = 0; i < nengineEffects; i++) { DMEffect *eff = &engineEffects[i]; if (eff->init != NULL && (res = eff->init(engine, eff, &engine->effectData[i])) != DMERR_OK) return res; } return DMERR_OK; } void engineShutdownEffects(DMEngineData *engine) { if (engine != NULL && engine->effectData != NULL) { int i; for (i = 0; i < nengineEffects; i++) { DMEffect *eff = &engineEffects[i]; if (eff->shutdown != NULL) eff->shutdown(engine, eff, engine->effectData[i]); } dmFree(engine->effectData); engine->effectData = NULL; } } DMEffect *engineFindEffect(const char *name, const int nparams) { int i; for (i = 0; i < nengineEffects; i++) { DMEffect *eff = &engineEffects[i]; if (strcmp(eff->name, name) == 0 && eff->nparams == nparams) return eff; } return NULL; } DMEffect *engineFindEffectByName(const char *name) { int i; for (i = 0; i < nengineEffects; i++) { DMEffect *eff = &engineEffects[i]; if (strcmp(eff->name, name) == 0) return eff; } return NULL; } static int engineResImageLoad(DMResource *res) { if ((res->resData = dmLoadImage(res)) != NULL) return DMERR_OK; else return dmferror(res); } static void engineResImageFree(DMResource *res) { SDL_FreeSurface((SDL_Surface *)res->resData); } static BOOL engineResImageProbe(DMResource *res, const char *fext) { (void) res; return fext != NULL && (strcasecmp(fext, ".jpg") == 0 || strcasecmp(fext, ".png") == 0); } #ifdef JSS_SUP_XM static int engineResXMModuleLoad(DMResource *res) { return jssLoadXM(res, (JSSModule **) &(res->resData), FALSE); } static void engineResXMModuleFree(DMResource *res) { jssFreeModule((JSSModule *) res->resData); } static BOOL engineResXMModuleProbe(DMResource *res, const char *fext) { (void) res; return fext != NULL && strcasecmp(fext, ".xm") == 0; } #endif #ifdef JSS_SUP_JSSMOD static int engineResJSSModuleLoad(DMResource *res) { return jssLoadJSSMOD(res, (JSSModule **) &(res->resData), FALSE); } static void engineResJSSModuleFree(DMResource *res) { jssFreeModule((JSSModule *) res->resData); } static BOOL engineResJSSModuleProbe(DMResource *res, const char *fext) { (void) res; return fext != NULL && (strcasecmp(fext, ".jss") == 0 || strcasecmp(fext, ".jmod") == 0); } #endif #ifdef DM_USE_TREMOR static size_t vorbisFileRead(void *ptr, size_t size, size_t nmemb, void *datasource) { return dmfread(ptr, size, nmemb, (DMResource *) datasource); } static int vorbisFileSeek(void *datasource, ogg_int64_t offset, int whence) { return dmfseek((DMResource *) datasource, offset, whence); } static int vorbisFileClose(void *datasource) { (void) datasource; return 0; } static long vorbisFileTell(void *datasource) { return dmftell((DMResource *) datasource); } static ov_callbacks vorbisFileCBS = { vorbisFileRead, vorbisFileSeek, vorbisFileClose, vorbisFileTell }; static int engineResVorbisLoad(DMResource *res) { OggVorbis_File vf; dmMsg(2, "vorbisfile '%s', %" DM_PRIu_SIZE_T " bytes resource loading\n", res->filename, res->memSize); if (ov_open_callbacks(res, &vf, NULL, 0, vorbisFileCBS) < 0) return DMERR_FOPEN; res->resSize = ov_pcm_total(&vf, -1) * 2 * 2; if ((res->resData = dmMalloc(res->resSize + 16)) == NULL) { ov_clear(&vf); return DMERR_MALLOC; } dmMsg(2, "rdataSize=%" DM_PRIu_SIZE_T " bytes?\n", res->resSize); BOOL eof = FALSE; int left = res->resSize; char *ptr = res->resData; int current_section; while (!eof && left > 0) { int ret = ov_read(&vf, ptr, left > 4096 ? 4096 : left, ¤t_section); if (ret == 0) eof = TRUE; else if (ret < 0) { ov_clear(&vf); return DMERR_INVALID_DATA; } else { left -= ret; ptr += ret; } } ov_clear(&vf); return DMERR_OK; } static void engineResVorbisFree(DMResource *res) { dmFree(res->resData); } static BOOL engineResVorbisProbe(DMResource *res, const char *fext) { (void) res; return fext != NULL && (strcasecmp(fext, ".ogg") == 0); } #endif static DMResourceDataOps engineResOps[] = { { engineResImageProbe, engineResImageLoad, engineResImageFree }, #ifdef JSS_SUP_JSSMOD { engineResJSSModuleProbe, engineResJSSModuleLoad, engineResJSSModuleFree }, #endif #ifdef JSS_SUP_XM { engineResXMModuleProbe, engineResXMModuleLoad, engineResXMModuleFree }, #endif #ifdef DM_USE_TREMOR { engineResVorbisProbe, engineResVorbisLoad, engineResVorbisFree }, #endif }; static const int nengineResOps = sizeof(engineResOps) / sizeof(engineResOps[0]); int engineClassifier(DMResource *res) { int i; char *fext; if (res == NULL) return DMERR_NULLPTR; fext = strrchr(res->filename, '.'); for (i = 0; i < nengineResOps; i++) { DMResourceDataOps *rops = &engineResOps[i]; if (rops->probe != NULL && rops->probe(res, fext)) { res->rops = rops; return DMERR_OK; } } return DMERR_OK; } void *engineGetResource(DMEngineData *eng, const char *name) { DMResource *res; if (eng == NULL) { dmErrorDBGMsg( "Engine not initialized but engineGetResource('%s') called.\n", name); return NULL; } if ((res = dmResourceFind(eng->resources, name)) != NULL) { if (res->resData != NULL) return res->resData; dmErrorDBGMsg( "Could not find resource DATA '%s' (resource was not preloaded).\n", name); return NULL; } else { dmErrorDBGMsg( "Could not find resource '%s'.\n", name); return NULL; } } #ifdef DM_USE_JSS void engineGetJSSInfo(DMEngineData *eng, BOOL *playing, int *order, JSSPattern **pat, int *npattern, int *row) { JSS_LOCK(eng->jssPlr); *playing = eng->jssPlr->isPlaying; *row = eng->jssPlr->row; *pat = eng->jssPlr->pattern; *npattern = eng->jssPlr->npattern; *order = eng->jssPlr->order; JSS_UNLOCK(eng->jssPlr); } void engineGetJSSChannelInfo(DMEngineData *eng, const int channel, int *ninst, int *nextInst, int *freq, int *note) { JSS_LOCK(eng->jssPlr); JSSPlayerChannel *chn = &(eng->jssPlr->channels[channel]); *ninst = chn->ninstrument; *nextInst = chn->nextInstrument; *freq = chn->freq; *note = chn->note; JSS_UNLOCK(eng->jssPlr); } #endif int engineGetTick(DMEngineData *engine) { return engine->frameTime - engine->startTime - engine->offsetTime; } float engineGetTimeDT(DMEngineData *engine) { return (float) engineGetTick(engine) / 1000.0f; } int engineGetTimeDTi(DMEngineData *engine) { return (float) engineGetTick(engine) / 1000; } int engineGetTime(DMEngineData *engine, int t) { return engineGetTick(engine) - (1000 * t); } int engineGetDT(DMEngineData *engine, int t) { return engineGetTime(engine, t) / 1000; } int engineGetVideoAspect(int width, int height) { if (width > 0 && height > 0) return (width * 1000) / height; else return 1; } void enginePauseAudio(DMEngineData *engine, int status) { if (status) engine->audioStatus = SDL_AUDIO_PAUSED; else engine->audioStatus = SDL_AUDIO_PLAYING; SDL_PauseAudio(status); } void engineAudioCallback(void *userdata, Uint8 *stream, int len) { DMEngineData *engine = (DMEngineData *) userdata; if (engine == NULL) return; dmMutexLock(engine->audioStreamMutex); // Update variables for analysis buffer (FFT, etc.) engine->audioStreamBuf = stream; engine->audioStreamLen = len / engine->audioSampleSize; // Update audio stream time position engine->audioTimePos += (1000 * engine->audioStreamLen) / engine->optAfmt.freq; // If paused, just render nothing if (engine->paused) { memset(stream, 0, len); } else // Otherwise, render audio switch (engine->optAudioSetup) { #ifdef DM_USE_JSS case DM_ASETUP_JSS: if (engine->jssDev != NULL) jvmRenderAudio(engine->jssDev, stream, len / engine->audioSampleSize); break; #endif #ifdef DM_USE_TREMOR case DM_ASETUP_TREMOR: if (engine->audioPos + len >= engine->audioRes->resSize) engine->exitFlag = TRUE; else { memcpy(stream, (Uint8 *) engine->audioRes->resData + engine->audioPos, len); engine->audioPos += len; } break; #endif default: break; } // First round of audio callback sets up the // necessary variables for audio synchro if (engine->startTimeAudio == -1 && engine->startTime != -1) { engine->startTimeAudio = SDL_GetTicks(); engine->offsetTime = engine->startTimeAudio - engine->startTime; } dmMutexUnlock(engine->audioStreamMutex); } static int engineAudioThreadFunc(void *userdata) { DMEngineData *engine = (DMEngineData *) userdata; if (engine == NULL) return 0; do { dmMutexLock(engine->audioStreamMutex); if (engine->audioStatus == SDL_AUDIO_PLAYING) { engineAudioCallback(userdata, engine->audioSimBuf, engine->audioSimBufSize); } dmMutexUnlock(engine->audioStreamMutex); SDL_Delay(engine->audioSimDelay); } while (!engine->audioSimDone); return 0; } int engineInitAudioParts(DMEngineData *engine) { if (engine == NULL) return DMERR_NULLPTR; engine->audioStreamMutex = dmCreateMutex(); engine->audioStatus = SDL_AUDIO_STOPPED; engine->optAfmt.callback = engineAudioCallback; engine->optAfmt.userdata = (void *) engine; engine->audioSampleSize = engine->optAfmt.channels; switch (engine->optAfmt.format) { case AUDIO_S16SYS: case AUDIO_U16SYS: engine->audioSampleSize *= 2; break; } if (SDL_OpenAudio(&engine->optAfmt, NULL) < 0) { // We'll let this pass, as we want to support no-sound. dmMsg(0, "Couldn't open SDL audio, falling back to no sound: %s\n", SDL_GetError()); // Set up simulated audio thread engine->audioSimDelay = 1000 / 45; engine->audioSimBufSize = (engine->optAfmt.freq / 45) * engine->audioSampleSize; engine->audioSimBuf = dmMalloc(engine->audioSimBufSize); engine->audioSimDone = FALSE; engine->audioSimThread = SDL_CreateThread(engineAudioThreadFunc, "DMLib Audio Simulation Thread", engine); if (engine->audioSimThread == NULL) return DMERR_INIT_FAIL; } return DMERR_OK; }