#include "rogueviz.h" /** \brief Non-Euclidean reverb (and also Doppler effect) * * Compile with HyperRogue, run with -geo [geometry] -reverb filename.raw (e.g. -geo 534h -reverb) * * filename.raw should be in raw audio format (44100Hz, signed 16 bit, two channels). * * Press oo to configure the physical parameters. * **/ namespace rogueviz { namespace embed { bool in = false; bool started = false; struct sample { Sint16 left, right; Sint16& operator [] (int i) { return (&left) [i]; } }; /** original audio data */ vector orig; int current_sample = 0, prevt = 0, curt = 0; std::mutex lock; /** controls the volume */ ld maxsnd = 1; /** 0 = no absorption on walls, 1 = full absorption */ ld absorption = .1; /** how much time does it take to go 1 absolute unit, in seconds */ ld speed_of_sound = 10000 / 44100.; /** inter-aural distance */ ld iad = .05; vector to_play; void myAudio(void *userdata, Uint8* stream, int len) { if(isize(to_play) < current_sample + len) return; sample* samples = (sample*) stream; len /= sizeof(sample); lock.lock(); for(int i=0; i lastdist; array curdist; }; map infos; vector > sndbuffer; /** after each frame, write the simulated sound to sndbuffer and to_play */ void reverb_queue() { prevt = curt; int& used_ticks = inHighQual ? ticks : sc_ticks; curt = (used_ticks * (long long)(44100)) / 1000; if(prevt > curt) prevt = curt; if(curt - prevt > 44100) return; sndbuffer.resize(curt, {0, 0}); for(auto& ps: infos) { auto& p = ps.second; if(p.curframe != frameid) continue; if(p.lastframe != p.curframe-1) p.lastdist = p.curdist; int dist = celldistance(ps.first, cwt.at); ld base = pow(1-absorption, dist); if(dist > 2 && !inHighQual) continue; // if(ps.first == cwt.at) println(hlog, (p.curdist - p.lastdist) / (curt - prevt)); ld att0[2]; ld att1[2]; for(int ch=0; ch<2; ch++) att0[ch] = min(base / sin_auto(p.lastdist[ch]), 5); for(int ch=0; ch<2; ch++) att1[ch] = min(base / sin_auto(p.curdist[ch]), 5); for(int ch: {0,1}) for(int i=prevt; i maxsnd) maxsnd = sndbuffer[i][ch]; frameid++; lock.lock(); to_play.resize(curt); for(int i=prevt; i more silent; will increase automatically if too loud"); }); dialog::addBack(); dialog::display(); } void o_key(o_funcs& v) { v.push_back(named_dialog("reverb", show)); } void save_raw_audio() { if(in) { /* save the output as raw audio file */ /* (it can be added to the video using ffmpeg */ FILE *f = fopen("raw-audio.raw", "wb"); fwrite(&to_play[0], 4, to_play.size(), f); fclose(f); } } auto hchook = addHook(hooks_drawcell, 100, draw_bird) + addHook(hooks_frame, 100, reverb_queue) + addHook(hooks_o_key, 80, o_key) + addHook(anims::hooks_after_video, 80, save_raw_audio) + addHook(hooks_args, 100, [] { using namespace arg; if(0) ; else if(argis("-reverb")) { shift(); string fname = args(); FILE *f = fopen(fname.c_str(), "rb"); fseek(f, 0, SEEK_END); orig.resize(ftell(f)/sizeof(sample)); fseek(f, 0, SEEK_SET); fread(&orig[0], 4, orig.size(), f); fclose(f); println(hlog, "original size = ", isize(orig)); in = true; firstland = specialland = laCanvas; patterns::whichCanvas = 'r'; patterns::rwalls = 100; mapeditor::drawplayer = false; start_game(); if(!euclid) println(hlog, "edge = ", hdist(cgi.vertices_only[0], cgi.vertices_only[1]) * 10000 / 44100); /* Doppler effect is weird if scrolling if not smooth */ smooth_scrolling = true; /* disable the frustum culling (we need sound from every direction) */ frustum_culling = false; } else return 1; return 0; }); } }