// Hyperbolic Rogue -- sound effects and music // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details /** \file sound.cpp * \brief sound effects and music */ #include "hyper.h" namespace hr { #if HDR void playSound(cell *c, const string& fname, int vol = 100); void resetmusic(); #endif #if HDR /** RogueViz may be used for situations where music does not correspond to lands, so we allow extra IDs */ static constexpr int MUSIC_MAX = 500; #endif EX const char *musicfile = ""; EX bool audio; EX string musiclicense; EX string musfname[MUSIC_MAX]; EX int musicvolume = 60; EX int effvolume = 60; EX bool music_available; EX int musiclength[MUSIC_MAX]; EX eLand getCurrentLandForMusic() { eLand id = ((anims::center_music()) && centerover) ? centerover->land : cwt.at->land; if(isHaunted(id)) id = laHaunted; if(id == laWarpSea) id = laWarpCoast; if(id == laMercuryRiver) id = laTerracotta; return id; } EX void playSeenSound(cell *c) { if(!c->monst) return; bool nearme = c->cpdist <= 7; forCellEx(c2, c) if(c2->cpdist <= 7) nearme = true; if(!nearme) return; if(among(c->monst, moEagle, moWindCrow, moAcidBird)) playSound(c, "seen-eagle"); else if(c->monst == moEarthElemental) playSound(c, "seen-earth"); else if(c->monst == moAirElemental) playSound(c, "seen-air"); else if(c->monst == moPhaser) playSound(c, "seen-frog1"); else if(c->monst == moFrog) playSound(c, "seen-frog2"); else if(c->monst == moVaulter) playSound(c, "seen-frog3"); else if(c->monst == moWaterElemental) playSound(c, "seen-water"); else if(c->monst == moFireElemental) playSound(c, "fire"); else if(c->monst == moDragonHead) playSound(c, "seen-dragon"); else if(c->monst == moWorm) playSound(c, "seen-sandworm"); else if(c->monst == moSkeleton && c->land != laDungeon) playSound(c, "seen-skeleton"); else if(c->monst == moHexSnake) playSound(c, "seen-snake"); else if(c->monst == moWolf || c->monst == moRedFox) playSound(c, "seen-woof"); else if(isTroll(c->monst)) playSound(c, "seen-troll"); else if(c->monst == moNecromancer) playSound(c, "seen-necromancer"); else if(c->monst == moGhost) playSound(c, "seen-ghost"); else if(c->monst == moRoseBeauty) playSound(c, princessgender() ? "seen-rosebeauty" : "seen-gardener"); else if(c->monst == moVizier) playSound(c, "seen-vizier"); else if(c->monst == moFireFairy) playSound(c, "seen-fairy"); else if(c->monst == moCultist) playSound(c, "seen-cultist"); else if(c->monst == moPyroCultist) playSound(c, "seen-cultistfire"); else if(c->monst == moCultistLeader) playSound(c, "seen-cultistleader"); } #if CAP_SDLAUDIO bool loaded[MUSIC_MAX]; Mix_Music* music[MUSIC_MAX]; EX int musicpos[MUSIC_MAX]; EX int musstart; int musfadeval = 2000; eLand cid = laNone; EX hookset hooks_music; EX hookset hooks_sync_music; EX bool music_out_of_focus = false; EX void handlemusic() { DEBBI(DF_GRAPH, ("handle music")); if(audio && musicvolume) { eLand id = getCurrentLandForMusic(); if(callhandlers(false, hooks_music, id)) return; if(outoffocus && !music_out_of_focus) id = eLand(0); if(musfname[id] == "LAST") id = cid; if(!loaded[id] && !memory_issues()) { loaded[id] = true; // printf("loading (%d)> %s\n", id, musfname[id].c_str()); // reuse music if(musfname[id] != "") { for(int i=0; i hooks_resetmusic; EX void resetmusic() { if(audio && musicvolume) { Mix_FadeOutMusic(3000); cid = laNone; for(int i=0; i= 0 && id < MUSIC_MAX) { if(buf[5] == 'L' && buf[6] == '=') musiclength[id] = atoi(buf+7); else if(buf[5] == '*' && buf[6] == '/') musfname[id] = dir2 + (buf+7); else musfname[id] = buf+5; music_available = true; } else { fprintf(stderr, "warning: bad soundtrack id, use the following format:\n"); fprintf(stderr, "[##] */filename\n"); fprintf(stderr, "where ## are two digits, and */ is optional and replaced by path to the music\n"); fprintf(stderr, "alternatively LAST = reuse the last track instead of having a special one"); } // printf("[%2d] %s\n", id, buf); } else if(buf[0] == '#') { } else { musiclicense += buf; musiclicense += "\n"; } } fclose(f); return true; } return false; } EX bool loadMusicInfo() { return loadMusicInfo(musicfile) || loadMusicInfo(HYPERPATH "hyperrogue-music.txt") || loadMusicInfo("./hyperrogue-music.txt") || loadMusicInfo("music/hyperrogue-music.txt") // Destination set by ./configure (in the GitHub repository) #ifdef MUSICDESTDIR || loadMusicInfo(MUSICDESTDIR) #endif #ifdef FHS || loadMusicInfo("/usr/share/hyperrogue/hyperrogue-music.txt") || (getenv("HOME") && loadMusicInfo(s0 + getenv("HOME") + "/.hyperrogue-music.txt")) #endif ; } EX void initAudio() { audio = loadMusicInfo(); if(audio) { if(Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 2048) != 0) { fprintf(stderr, "Unable to initialize audio: %s\n", Mix_GetError()); audio = false; } else { audio = true; Mix_AllocateChannels(16); } } } map chunks; #ifdef SOUNDDESTDIR string wheresounds = SOUNDDESTDIR; #else string wheresounds = HYPERPATH "sounds/"; #endif hookset hooks_sound; EX void playSound(cell *c, const string& fname, int vol) { LATE( hr::playSound(c, fname, vol); ) if(effvolume == 0) return; if(callhandlers(false, hooks_sound, fname, vol)) return; // printf("Play sound: %s\n", fname.c_str()); if(!chunks.count(fname)) { string s = wheresounds+fname+".ogg"; if(memory_issues()) return; memory_for_lib(); chunks[fname] = Mix_LoadWAV(s.c_str()); // printf("Loading, as %p\n", chunks[fname]); } Mix_Chunk *chunk = chunks[fname]; if(chunk) { Mix_VolumeChunk(chunk, effvolume * vol / 100); Mix_PlayChannel(-1, chunk, 0); } } EX void reuse_music_memory() { for(int i=0; i currently_played; for(int ch=0; ch<16; ch++) currently_played.insert(Mix_GetChunk(ch)); set to_free; for(auto& p: chunks) if(p.second) { if(currently_played.count(p.second)) { println(hlog, p.first, ": currently played"); } else { Mix_FreeChunk(p.second); to_free.insert(p.first); println(hlog, p.first, ": freed"); } } for(auto& s: to_free) chunks.erase(s); } #endif #if CAP_COMMANDLINE int read_sound_args() { using namespace arg; if(argis("-m")) { PHASE(1); shift(); musicfile = argcs(); } #if CAP_SDLAUDIO else if(argis("-se")) { PHASE(1); shift(); wheresounds = args(); } else if(argis("-musicfocus")) { music_out_of_focus = true; } #endif else if(argis("-svol")) { PHASEFROM(2); shift(); effvolume = argi(); } else if(argis("-mvol")) { PHASEFROM(2); shift(); musicvolume = argi(); } else return 1; return 0; } #if CAP_SDLAUDIO auto ah_sound = addHook(hooks_args, 0, read_sound_args) + addHook(hooks_clear_cache, 0, reuse_music_memory); #endif #endif #if !CAP_AUDIO EX void playSound(cell *c, const string& fname, int vol) { } EX void resetmusic() { } #endif }