diff --git a/desmume/src/NDSSystem.cpp b/desmume/src/NDSSystem.cpp index 7213e2aca..b437f7df3 100644 --- a/desmume/src/NDSSystem.cpp +++ b/desmume/src/NDSSystem.cpp @@ -80,6 +80,8 @@ int lagframecounter; int LagFrameFlag; int lastLag; int TotalLagFrames; +u8 MicSampleSelection = 0; +std::vector> micSamples; TSCalInfo TSCal; @@ -2593,6 +2595,7 @@ void NDS_Reset() LidClosed = FALSE; countLid = 0; + MicSampleSelection = 0; resetUserInput(); diff --git a/desmume/src/NDSSystem.h b/desmume/src/NDSSystem.h index 5164370dc..0456ef61b 100644 --- a/desmume/src/NDSSystem.h +++ b/desmume/src/NDSSystem.h @@ -32,6 +32,7 @@ #include "types.h" #include +#include #if defined(_WINDOWS) && !defined(WXPORT) #include "pathsettings.h" @@ -398,6 +399,7 @@ struct UserTouch struct UserMicrophone { u32 micButtonPressed; + u8 micSample; }; struct UserInput { @@ -628,6 +630,8 @@ extern struct TCommonSettings { extern std::string InputDisplayString; extern int LagFrameFlag; extern int lastLag, TotalLagFrames; +extern u8 MicSampleSelection; +extern std::vector> micSamples; void MovieSRAM(); diff --git a/desmume/src/mic.h b/desmume/src/mic.h index c71774096..f176faf65 100644 --- a/desmume/src/mic.h +++ b/desmume/src/mic.h @@ -24,7 +24,7 @@ #ifdef WIN32 static char MicSampleName[256]; -bool LoadSample(const char *name); +bool LoadSamples(const char *name); #endif extern int MicDisplay; diff --git a/desmume/src/movie.cpp b/desmume/src/movie.cpp index 27f592df4..bbbf51bcb 100644 --- a/desmume/src/movie.cpp +++ b/desmume/src/movie.cpp @@ -41,7 +41,9 @@ bool autoMovieBackup = true; #define FCEU_PrintError LOG -#define MOVIE_VERSION 1 +//version 1 - was the main version for a long time, most of 201x +//version 2 - march 2019, added mic sample +#define MOVIE_VERSION 2 #ifdef WIN32 #include ".\windows\main.h" @@ -105,9 +107,6 @@ bool MovieRecord::Compare(MovieRecord& compareRec) //Check Stylus if (this->touch.padding != compareRec.touch.padding) return false; - if (this->touch.touch != compareRec.touch.touch) return false; - if (this->touch.x != compareRec.touch.x) return false; - if (this->touch.y != compareRec.touch.y) return false; //Check comamnds //if new commands are ever recordable, they need to be added here if we go with this method @@ -165,6 +164,7 @@ void MovieRecord::parse(MovieData* md, EMUFILE* fp) touch.x = u32DecFromIstream(fp); touch.y = u32DecFromIstream(fp); touch.touch = u32DecFromIstream(fp); + touch.micsample = u32DecFromIstream(fp); fp->fgetc(); //eat the pipe @@ -183,7 +183,8 @@ void MovieRecord::dump(MovieData* md, EMUFILE* fp, int index) dumpPad(fp, pad); putdec(fp,touch.x); fp->fputc(' '); putdec(fp,touch.y); fp->fputc(' '); - putdec(fp,touch.touch); + putdec(fp,touch.touch); fp->fputc(' '); + putdec(fp,touch.micsample); fp->fputc('|'); //each frame is on a new line @@ -213,6 +214,18 @@ void MovieData::truncateAt(int frame) } +void MovieData::installMicSample(std::string& key, std::string& val) +{ + //which sample? + int which = atoi(key.c_str()+strlen("micsample")); + + //make sure we have this many + if(micSamples.size()fprintf("sram %s\n", BytesToString(&sram[0],sram.size()).c_str()); + for(int i=0;i<256;i++) + { + //TODO - how do these get put in here + if(micSamples.size() > i) + { + char tmp[32]; + sprintf(tmp,"micsample%d",i); + fp->fprintf("%s %s\n",tmp, BytesToString(&micSamples[i][0],micSamples[i].size()).c_str()); + } + } + if(binary) { //put one | to start the binary dump @@ -377,8 +414,12 @@ bool LoadFM2(MovieData& movieData, EMUFILE* fp, int size, bool stopAfterHeader) fp->fread(buf,9); fp->fseek(curr, SEEK_SET); // if(fp->fail()) return false; - if(memcmp(buf,"version 1",9)) - return false; + bool version1 = memcmp(buf, "version 1", 9)==0; + bool version2 = memcmp(buf, "version 2", 9)==0; + + if(version1) {} + else if(version2) {} + else return false; std::string key,value; enum { @@ -604,6 +645,11 @@ const char* _CDECL_ FCEUI_LoadMovie(const char *fname, bool _read_only, bool tas bool success = MovieData::loadSramFrom(&currMovieData.sram); if(!success) return "failed to load sram"; } + + #ifdef _WIN32 + ::micSamples = currMovieData.micSamples; + #endif + freshMovie = true; ClearAutoHold(); @@ -688,6 +734,7 @@ void FCEUI_SaveMovie(const char *fname, std::wstring author, int flag, std::stri currMovieData.romSerial = gameInfo.ROMserial; currMovieData.romFilename = path.GetRomName(); currMovieData.rtcStart = rtcstart; + currMovieData.micSamples = ::micSamples; // reset firmware (some games can write to it) if (CommonSettings.UseExtFirmware == false) @@ -755,6 +802,7 @@ void FCEUI_SaveMovie(const char *fname, std::wstring author, int flag, std::stri if(mr->command_microphone()) input.mic.micButtonPressed = 1; else input.mic.micButtonPressed = 0; + input.mic.micSample = MicSampleSelection; if(mr->command_reset()) NDS_Reset(); @@ -833,6 +881,7 @@ void FCEUI_SaveMovie(const char *fname, std::wstring author, int flag, std::stri mr.touch.touch = input.touch.isTouch ? 1 : 0; mr.touch.x = input.touch.isTouch ? input.touch.touchX >> 4 : 0; mr.touch.y = input.touch.isTouch ? input.touch.touchY >> 4 : 0; + mr.touch.micsample = MicSampleSelection; assert(mr.touch.touch || (!mr.touch.x && !mr.touch.y)); //assert(nds.touchX == input.touch.touchX && nds.touchY == input.touch.touchY); @@ -1268,3 +1317,13 @@ void FCEUI_MakeBackupMovie(bool dispMessage) } } +void BinaryDataFromString(std::string &inStringData, std::vector *outBinaryData) +{ + int len = Base64StringToBytesLength(inStringData); + if(len == -1) len = HexStringToBytesLength(inStringData); // wasn't base64, try hex + if(len >= 1) + { + outBinaryData->resize(len); + StringToBytes(inStringData, &outBinaryData->front(), len); // decodes either base64 or hex + } +} diff --git a/desmume/src/movie.h b/desmume/src/movie.h index a7da82bad..a4fddc36c 100644 --- a/desmume/src/movie.h +++ b/desmume/src/movie.h @@ -71,6 +71,7 @@ public: struct { u8 x, y; u8 touch; + u8 micsample; }; u32 padding; @@ -144,6 +145,7 @@ public: std::vector sram; std::vector records; std::vector comments; + std::vector > micSamples; int rerecordCount; Desmume_Guid guid; @@ -185,6 +187,7 @@ public: void truncateAt(int frame); void installValue(std::string& key, std::string& val); + void installMicSample(std::string& key, std::string& val); int dump(EMUFILE* fp, bool binary); void clearRecordRange(int start, int len); void insertEmpty(int at, int frames); @@ -228,4 +231,5 @@ extern bool movie_readonly; extern bool ShowInputDisplay; void FCEUI_MakeBackupMovie(bool dispMessage); DateTime FCEUI_MovieGetRTCDefault(); +void BinaryDataFromString(std::string &inStringData, std::vector *outBinaryData); #endif diff --git a/desmume/src/windows/hotkey.cpp b/desmume/src/windows/hotkey.cpp index b1cd091ba..7424b6e72 100644 --- a/desmume/src/windows/hotkey.cpp +++ b/desmume/src/windows/hotkey.cpp @@ -289,6 +289,21 @@ void HK_StateQuickLoadSlot(int, bool justPressed) void HK_MicrophoneKeyDown(int, bool justPressed) { NDS_setMic(1); } void HK_MicrophoneKeyUp(int) { NDS_setMic(0); } +void Mic_NextSample(); +void Mic_PrevSample(); + +void HK_NextSampleKeyDown(int, bool justPressed) +{ + Mic_NextSample(); + osd->addLine("Mic sample %d selected", MicSampleSelection); +} + +void HK_PrevSampleKeyDown(int, bool justPressed) +{ + Mic_PrevSample(); + osd->addLine("Mic sample %d selected", MicSampleSelection); +} + void HK_AutoHoldKeyDown(int, bool justPressed) {AutoHoldPressed = true;} void HK_AutoHoldKeyUp(int) {AutoHoldPressed = false;} @@ -652,6 +667,18 @@ void InitCustomKeys (SCustomKeys *keys) keys->Microphone.page = HOTKEY_PAGE_MAIN; keys->Microphone.key = NULL; + keys->PrevSample.handleKeyDown = HK_PrevSampleKeyDown; + keys->PrevSample.code = "Mic Prev Sample"; + keys->PrevSample.name = L"Mic Prev Sample"; + keys->PrevSample.page = HOTKEY_PAGE_MAIN; + keys->PrevSample.key = NULL; + + keys->NextSample.handleKeyDown = HK_NextSampleKeyDown; + keys->NextSample.code = "Mic Next Sample"; + keys->NextSample.name = L"Mic Next Sample"; + keys->NextSample.page = HOTKEY_PAGE_MAIN; + keys->NextSample.key = NULL; + keys->AutoHold.handleKeyDown = HK_AutoHoldKeyDown; keys->AutoHold.handleKeyUp = HK_AutoHoldKeyUp; keys->AutoHold.code = "AutoHold"; diff --git a/desmume/src/windows/hotkey.h b/desmume/src/windows/hotkey.h index e99005bab..748e6f13b 100644 --- a/desmume/src/windows/hotkey.h +++ b/desmume/src/windows/hotkey.h @@ -79,7 +79,7 @@ struct SCustomKeys #ifdef HAVE_JIT SCustomKey CpuMode, JitBlockSizeDec, JitBlockSizeInc; #endif - SCustomKey FrameAdvance, FastForward, FastForwardToggle, IncreaseSpeed, DecreaseSpeed, FrameLimitToggle, Microphone, IncreasePressure, DecreasePressure, ToggleStylusJitter; + SCustomKey FrameAdvance, FastForward, FastForwardToggle, IncreaseSpeed, DecreaseSpeed, FrameLimitToggle, Microphone, PrevSample, NextSample, IncreasePressure, DecreasePressure, ToggleStylusJitter; SCustomKey PlayMovie, RecordMovie, StopMovie, ToggleReadOnly; diff --git a/desmume/src/windows/main.cpp b/desmume/src/windows/main.cpp index 0fb1f14b3..08af507b1 100644 --- a/desmume/src/windows/main.cpp +++ b/desmume/src/windows/main.cpp @@ -2772,14 +2772,18 @@ static void RefreshMicSettings() Mic_DeInit_Physical(); if(CommonSettings.micMode == TCommonSettings::Sample) { - if(!LoadSample(MicSampleName)) + if(!LoadSamples(MicSampleName)) { MessageBox(NULL, "Unable to read the mic sample", "DeSmuME", (MB_OK | MB_ICONEXCLAMATION)); } + else + { + osd->addLine("Mic sample %d selected", MicSampleSelection); + } } else { - LoadSample(NULL); + LoadSamples(NULL); if(CommonSettings.micMode == TCommonSettings::Physical) { Mic_Init_Physical(); diff --git a/desmume/src/windows/mic.cpp b/desmume/src/windows/mic.cpp index a7e3c84a4..2a7acbcac 100644 --- a/desmume/src/windows/mic.cpp +++ b/desmume/src/windows/mic.cpp @@ -34,7 +34,6 @@ #include int MicDisplay; -int SampleLoaded=0; #define MIC_CHECKERR(hr) if(hr != MMSYSERR_NOERROR) return FALSE; @@ -76,12 +75,8 @@ static int CALLBACK waveInProc(HWAVEIN wavein, UINT msg, DWORD instance, DWORD_P return 0; } -static char* samplebuffer = NULL; -static int samplebuffersize = 0; static FILE* fp = NULL; -EMUFILE_MEMORY newWavData; - static bool dataChunk(EMUFILE* inf) { bool found = false; @@ -102,11 +97,14 @@ static bool dataChunk(EMUFILE* inf) if (memcmp(chunk_id, "data", 4) == 0) { found = true; u8* temp = new u8[chunk_length]; - if(inf->fread(temp,chunk_length) != chunk_length) { + if(inf->fread(temp,chunk_length) != chunk_length || chunk_length == 0) { delete[] temp; return false; } - newWavData.fwrite(temp,chunk_length); + micSamples.resize(micSamples.size()+1); + std::vector& thisSample = micSamples[micSamples.size()-1]; + thisSample.resize(chunk_length); + memcpy(&thisSample[0], temp, chunk_length); delete[] temp; chunk_length = 0; } @@ -166,11 +164,27 @@ static bool formatChunk(EMUFILE* inf) return false; } +void Mic_PrevSample() +{ + if(MicSampleSelection==0) return; + MicSampleSelection--; +} + +void Mic_NextSample() +{ + if(MicSampleSelection==255) return; + MicSampleSelection++; + if(MicSampleSelection >= micSamples.size()) + { + if(micSamples.size()==0) + MicSampleSelection = 0; + else + MicSampleSelection = micSamples.size()-1; + } +} + bool LoadSample(const char *name) { - SampleLoaded = 0; - if(!name) return true; - EMUFILE_FILE inf(name,"rb"); if(inf.fail()) return false; @@ -201,17 +215,62 @@ bool LoadSample(const char *name) return false; } - delete[] samplebuffer; - samplebuffersize = (int)newWavData.size(); - samplebuffer = new char[samplebuffersize]; - memcpy(samplebuffer,newWavData.buf(),samplebuffersize); - new(&newWavData) EMUFILE_MEMORY(); - - SampleLoaded=1; - return true; } +bool LoadSamples(const char *name) +{ + //unload any existing mic samples + micSamples.resize(0); + + //select NO mic sample + micReadSamplePos = 0; + MicSampleSelection = 0; + + //if we're disabling the mic samples system, just bail now + if (!name || !*name) return true; + + //analyze the filename for _0 at the end. anything with _0 at the end is assumed to be the beginning of a series of files + //(and if not, it can still be loaded just fine) + const char* ext = strrchr(name,'.'); + + //in case the filename had no extension... it's an error. + if(!ext) return false; + + const char* maybe_0 = ext-2; + + //in case this was an absurdly short filename + if(ext 0) { - //use a sample - //TODO: what if a movie is active? - // for now I'm going to hope that if anybody records a movie with a sample loaded, - // either they know what they're doing and plan to distribute the sample, - // or they're playing a game where it doesn't even matter or they never press the mic button. - tmp = samplebuffer[micReadSamplePos >> 1]; - micReadSamplePos++; - if(micReadSamplePos == samplebuffersize*2) - micReadSamplePos=0; + //why is this reading every sample twice? I did this for some reason in 57dbe9128d0f8cbb4bd79154fb9cda3ab6fab386 + //maybe the game reads two samples in succession to check them or something. stuff definitely works better with the two-in-a-row + if (micReadSamplePos == micSamples[MicSampleSelection].size()*2) + { + tmp = 0x80; //silence, with 8 bit signed values + } + else + { + tmp = micSamples[MicSampleSelection][micReadSamplePos >> 1]; + + //nintendogs seems sensitive to clipped samples, at least. + //by clamping these, we get better sounds on playback + //I don't know how important it is, but I like nintendogs to sound good at least.. + if(tmp == 0) tmp = 1; + if(tmp == 255) tmp = 254; + + micReadSamplePos++; + if(micReadSamplePos == micSamples[MicSampleSelection].size()*2) + { + printf("Ended mic sample MicSampleSelection\n"); + } + } } else {