diff --git a/desmume/src/NDSSystem.h b/desmume/src/NDSSystem.h index 7223fe7f3..b6faf316f 100644 --- a/desmume/src/NDSSystem.h +++ b/desmume/src/NDSSystem.h @@ -390,6 +390,7 @@ public: virtual BOOL WIFI_Host_InitSystem() { return FALSE; } virtual void WIFI_Host_ShutdownSystem() {} virtual BOOL AVI_IsRecording() { return FALSE; } + virtual BOOL WAV_IsRecording() { return FALSE; } virtual void USR_InfoMessage(const char *message) { printf("%s\n", message); } }; extern Driver* driver; diff --git a/desmume/src/SPU.cpp b/desmume/src/SPU.cpp index 23944218f..41673f050 100644 --- a/desmume/src/SPU.cpp +++ b/desmume/src/SPU.cpp @@ -998,7 +998,7 @@ void SPU_Emulate_core() spu_core_samples = (int)(samples); samples -= spu_core_samples; - if(driver->AVI_IsRecording()) + if(driver->AVI_IsRecording() || driver->WAV_IsRecording()) SPU_MixAudio(SPU_core,spu_core_samples); else SPU_MixAudio(SPU_core,spu_core_samples); diff --git a/desmume/src/windows/DeSmuME_2005.vcproj b/desmume/src/windows/DeSmuME_2005.vcproj index c8ecfd736..edb309a55 100644 --- a/desmume/src/windows/DeSmuME_2005.vcproj +++ b/desmume/src/windows/DeSmuME_2005.vcproj @@ -541,6 +541,14 @@ RelativePath=".\tileView.h" > + + + + diff --git a/desmume/src/windows/DeSmuME_2008.vcproj b/desmume/src/windows/DeSmuME_2008.vcproj index bbed9bd0b..2fda61296 100644 --- a/desmume/src/windows/DeSmuME_2008.vcproj +++ b/desmume/src/windows/DeSmuME_2008.vcproj @@ -664,6 +664,10 @@ RelativePath=".\tileView.cpp" > + + + + diff --git a/desmume/src/windows/DeSmuME_Intel.icproj b/desmume/src/windows/DeSmuME_Intel.icproj index 316826a1c..2a7ce46d7 100644 --- a/desmume/src/windows/DeSmuME_Intel.icproj +++ b/desmume/src/windows/DeSmuME_Intel.icproj @@ -281,6 +281,8 @@ RelativePath=".\throttle.h"/> + + diff --git a/desmume/src/windows/DeSmuME_Intel.vcproj b/desmume/src/windows/DeSmuME_Intel.vcproj index f80ef6d0c..00487de35 100644 --- a/desmume/src/windows/DeSmuME_Intel.vcproj +++ b/desmume/src/windows/DeSmuME_Intel.vcproj @@ -664,6 +664,10 @@ RelativePath=".\tileView.cpp" > + + + + diff --git a/desmume/src/windows/aviout.cpp b/desmume/src/windows/aviout.cpp index c663822d8..641bfc7d0 100644 --- a/desmume/src/windows/aviout.cpp +++ b/desmume/src/windows/aviout.cpp @@ -30,11 +30,11 @@ #include "debug.h" -void EMU_PrintError(const char* msg) { +static void EMU_PrintError(const char* msg) { LOG(msg); } -void EMU_PrintMessage(const char* msg) { +static void EMU_PrintMessage(const char* msg) { LOG(msg); } diff --git a/desmume/src/windows/hotkey.cpp b/desmume/src/windows/hotkey.cpp index a0687175a..500d0e76f 100644 --- a/desmume/src/windows/hotkey.cpp +++ b/desmume/src/windows/hotkey.cpp @@ -32,6 +32,7 @@ #include "ram_search.h" //In order to call UpdateRamSearch (for loadstate functions) #include "replay.h" #include "aviout.h" +#include "wavout.h" extern LRESULT OpenFile(); //adelikat: Made this an extern here instead of main.h Seemed icky not to limit the scope of this function @@ -169,6 +170,7 @@ void HK_AutoHoldClearKeyDown(int) { void HK_Reset(int) {ResetGame();} void HK_RecordAVI(int) { if (DRV_AviIsRecording()) AviEnd(); else AviRecordTo(); } +void HK_RecordWAV(int) { if (DRV_WavIsRecording()) WavEnd(); else WavRecordTo(); } void HK_ToggleFrame(int) {frameCounterDisplay ^= true;} void HK_ToggleFPS(int) {FpsDisplay ^= true;} @@ -435,6 +437,12 @@ void InitCustomKeys (SCustomKeys *keys) keys->StopMovie.page = HOTKEY_PAGE_MOVIE; keys->StopMovie.key = NULL; + keys->RecordWAV.handleKeyDown = HK_RecordWAV; + keys->RecordWAV.code = "RecordWAV"; + keys->RecordWAV.name = L"Record WAV"; + keys->RecordWAV.page = HOTKEY_PAGE_MAIN; + keys->RecordWAV.key = NULL; + keys->RecordAVI.handleKeyDown = HK_RecordAVI; keys->RecordAVI.code = "RecordAVI"; keys->RecordAVI.name = L"Record AVI"; diff --git a/desmume/src/windows/hotkey.h b/desmume/src/windows/hotkey.h index 5c939bb65..9951d38ca 100644 --- a/desmume/src/windows/hotkey.h +++ b/desmume/src/windows/hotkey.h @@ -78,7 +78,7 @@ struct SCustomKeys SCustomKey ToggleRasterizer; SCustomKey PrintScreen; //Screenshot - SCustomKey RecordAVI; + SCustomKey RecordWAV, RecordAVI; SCustomKey ToggleFrameCounter; SCustomKey ToggleFPS; diff --git a/desmume/src/windows/main.cpp b/desmume/src/windows/main.cpp index abf5850cf..80efdb091 100644 --- a/desmume/src/windows/main.cpp +++ b/desmume/src/windows/main.cpp @@ -80,6 +80,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "ramwatch.h" #include "ram_search.h" #include "aviout.h" +#include "wavout.h" #include "directx/ddraw.h" @@ -1128,6 +1129,8 @@ DWORD WINAPI run() //avi writing DRV_AviSoundUpdate(SPU_core->outbuf,spu_core_samples); DRV_AviVideoUpdate((u16*)GPU_screen); + //wav writing + DRV_WavSoundUpdate(SPU_core->outbuf,spu_core_samples); // if (!skipnextframe) // { @@ -1524,6 +1527,7 @@ static void ExitRunLoop() } BOOL AVI_IsRecording(); +BOOL WAV_IsRecording(); class WinDriver : public Driver { virtual BOOL WIFI_Host_InitSystem() { @@ -1559,6 +1563,11 @@ class WinDriver : public Driver return ::AVI_IsRecording(); } + virtual BOOL WAV_IsRecording() + { + return ::WAV_IsRecording(); + } + virtual void USR_InfoMessage(const char *message) { SetMessageToDisplay(message); @@ -1912,6 +1921,7 @@ int WINAPI WinMain (HINSTANCE hThisInstance, SaveRecentRoms(); NDS_DeInit(); DRV_AviEnd(); + DRV_WavEnd(); //------SHUTDOWN @@ -2265,6 +2275,62 @@ void AviRecordTo() NDS_UnPause(); } +void WavEnd() +{ + NDS_Pause(); + DRV_WavEnd(); + NDS_UnPause(); +} + +//Shows an Open File menu and starts recording an WAV +void WavRecordTo() +{ + NDS_Pause(); + + OPENFILENAME ofn; + char szChoice[MAX_PATH] = {0}; + + ////if we are playing a movie, construct the filename from the current movie. + ////else construct it from the filename. + //if(FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD)) + //{ + // extern char curMovieFilename[]; + // strcpy(szChoice, curMovieFilename); + // char* dot = strrchr(szChoice,'.'); + + // if (dot) + // { + // *dot='\0'; + // } + + // strcat(szChoice, ".wav"); + //} + //else + //{ + // extern char FileBase[]; + // sprintf(szChoice, "%s.wav", FileBase); + //} + + // wav record file browser + memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = MainWindow->getHWnd(); + ofn.lpstrFilter = "WAV Files (*.wav)\0*.wav\0\0"; + ofn.lpstrFile = szChoice; + ofn.lpstrDefExt = "wav"; + ofn.lpstrTitle = "Save WAV as"; + + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; + + if(GetSaveFileName(&ofn)) + { + DRV_WavBegin(szChoice); + } + + NDS_UnPause(); +} + void OpenRecentROM(int listNum) { if (listNum > MAX_RECENT_ROMS) return; //Just in case @@ -2523,6 +2589,9 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM LoadString(hAppInst, !DRV_AviIsRecording() ? IDM_FILE_RECORDAVI : IDM_FILE_STOPAVI, menuItemString, 256); mii.dwTypeData = menuItemString; SetMenuItemInfo(mainMenu, IDM_FILE_RECORDAVI, FALSE, &mii); + //Check if WAV is recording + LoadString(hAppInst, !DRV_WavIsRecording() ? IDM_FILE_RECORDWAV : IDM_FILE_STOPWAV, menuItemString, 256); + SetMenuItemInfo(mainMenu, IDM_FILE_RECORDWAV, FALSE, &mii); //Menu items dependent on a ROM loaded EnableMenuItem(mainMenu, IDM_GAME_INFO, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); @@ -2532,6 +2601,7 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM EnableMenuItem(mainMenu, IDM_PRINTSCREEN, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(mainMenu, IDM_QUICK_PRINTSCREEN, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(mainMenu, IDM_FILE_RECORDAVI, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(mainMenu, IDM_FILE_RECORDWAV, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(mainMenu, IDM_RESET, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(mainMenu, IDM_SHUT_UP, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(mainMenu, IDM_CHEATS_LIST, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); @@ -2867,6 +2937,12 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM else AviRecordTo(); break; + case IDM_FILE_RECORDWAV: + if (DRV_WavIsRecording()) + WavEnd(); + else + WavRecordTo(); + break; case IDM_STATE_LOAD: { OPENFILENAME ofn; diff --git a/desmume/src/windows/main.h b/desmume/src/windows/main.h index c906df9e8..d4f148aa0 100644 --- a/desmume/src/windows/main.h +++ b/desmume/src/windows/main.h @@ -16,6 +16,8 @@ void FrameAdvance(bool state); void ResetGame(); //Resets game (for the menu item & hotkey void AviRecordTo(); void AviEnd(); +void WavRecordTo(); +void WavEnd(); void SetMessageToDisplay(const char *message); //For sending output to the main screen extern bool ShowInputDisplay; diff --git a/desmume/src/windows/resource.h b/desmume/src/windows/resource.h index e54262207..bf5ae8918 100644 --- a/desmume/src/windows/resource.h +++ b/desmume/src/windows/resource.h @@ -510,6 +510,8 @@ #define ID_VIEW_DISPLAYMICROPHONE 40014 #define IDM_FILE_RECORDAVI 40015 #define IDM_FILE_STOPAVI 40016 +#define IDM_FILE_RECORDWAV 40017 +#define IDM_FILE_STOPWAV 40018 #define IDM_STOPMOVIE 40019 #define ID_FILE_RECENTROM 40034 #define IDC_SAVETYPE7 40037 diff --git a/desmume/src/windows/resources.rc b/desmume/src/windows/resources.rc index f3bd91d87..33b5cdc4a 100644 --- a/desmume/src/windows/resources.rc +++ b/desmume/src/windows/resources.rc @@ -239,6 +239,7 @@ MENU_PRINCIPAL MENU MENUITEM "Save Screenshot &As...", IDM_PRINTSCREEN MENUITEM "&Quick Screenshot", IDM_QUICK_PRINTSCREEN MENUITEM SEPARATOR + MENUITEM "Record WAV", IDM_FILE_RECORDWAV MENUITEM "Record AVI", IDM_FILE_RECORDAVI MENUITEM SEPARATOR MENUITEM "&Record Movie...", IDM_RECORD_MOVIE @@ -3685,6 +3686,8 @@ STRINGTABLE { IDM_FILE_RECORDAVI "Record AVI" IDM_FILE_STOPAVI "Stop AVI" + IDM_FILE_RECORDWAV "Record WAV" + IDM_FILE_STOPWAV "Stop WAV" } diff --git a/desmume/src/windows/wavout.cpp b/desmume/src/windows/wavout.cpp new file mode 100644 index 000000000..22e49661d --- /dev/null +++ b/desmume/src/windows/wavout.cpp @@ -0,0 +1,202 @@ +/* wavout.cpp + * + * Copyright (C) 2006-2008 Zeromus + * + * This file is part of DeSmuME + * + * DeSmuME is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * DeSmuME is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DeSmuME; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "main.h" //I added this so that this file could gain access to SetMessageToDisplay. +#include "types.h" +#include "windriver.h" +#include "console.h" +#include "gfx3d.h" + +#include +#include +#include + +#include "debug.h" + +static void EMU_PrintError(const char* msg) { + LOG(msg); +} + +static void EMU_PrintMessage(const char* msg) { + LOG(msg); +} + +bool DRV_WavBegin(const char* fname); +void DRV_WavEnd(); +void DRV_WavSoundUpdate(void* soundData, int soundLen); +bool DRV_WavIsRecording(); + +static struct WAVFile +{ + bool valid; + + WAVEFORMATEX wav_format_master; + WAVEFORMATEX wav_format; + HMMIO wav_file; + + MMCKINFO waveChunk; + MMCKINFO fmtChunk; + MMCKINFO dataChunk; +} *wav_file = NULL; + +static void wav_create(struct WAVFile** wav_out) +{ + *wav_out = (struct WAVFile*)malloc(sizeof(struct WAVFile)); + memset(*wav_out, 0, sizeof(struct WAVFile)); +} + +static void wav_destroy(struct WAVFile** wav_out) +{ + if(!(*wav_out)) + return; + + if((*wav_out)->wav_file) + { + mmioAscend((*wav_out)->wav_file, &(*wav_out)->dataChunk, 0); + mmioAscend((*wav_out)->wav_file, &(*wav_out)->waveChunk, 0); +// mmioFlush((*wav_out)->wav_file, 0); + mmioClose((*wav_out)->wav_file, 0); + (*wav_out)->wav_file = NULL; + } + + free(*wav_out); + *wav_out = NULL; +} + +static void set_sound_format(const WAVEFORMATEX* wave_format, struct WAVFile* wav_out) +{ + memcpy(&((*wav_out).wav_format_master), wave_format, sizeof(WAVEFORMATEX)); +} + +static int wav_open(const char* filename, const WAVEFORMATEX* pwfex) +{ + int error = 1; + int result = 0; + + do + { + // close existing first + DRV_WavEnd(); + + // create the object + wav_create(&wav_file); + if(!wav_file) + break; + + // add audio format + set_sound_format(pwfex, wav_file); + + // open the file + if(!(wav_file->wav_file = mmioOpen((LPSTR)filename, NULL, MMIO_CREATE|MMIO_WRITE))) + break; + + // create WAVE chunk + wav_file->waveChunk.fccType = mmioFOURCC('W', 'A', 'V', 'E'); + mmioCreateChunk(wav_file->wav_file, &wav_file->waveChunk, MMIO_CREATERIFF); + + // create Format chunk + wav_file->fmtChunk.ckid = mmioFOURCC('f', 'm', 't', ' '); + mmioCreateChunk(wav_file->wav_file, &wav_file->fmtChunk, 0); + // then write header + memcpy(&wav_file->wav_format, &wav_file->wav_format_master, sizeof(WAVEFORMATEX)); + wav_file->wav_format.cbSize = 0; + mmioWrite(wav_file->wav_file, (HPSTR) &wav_file->wav_format, sizeof(WAVEFORMATEX)); + mmioAscend(wav_file->wav_file, &wav_file->fmtChunk, 0); + + // create Data chunk + wav_file->dataChunk.ckid = mmioFOURCC('d', 'a', 't', 'a'); + mmioCreateChunk(wav_file->wav_file, &wav_file->dataChunk, 0); + + // success + error = 0; + result = 1; + wav_file->valid = true; + + } while(false); + + if(!result) + { + wav_destroy(&wav_file); + if(error) + EMU_PrintError("Error writing WAV file"); + } + + return result; +} + + +bool DRV_WavBegin(const char* fname) +{ + DRV_WavEnd(); + + WAVEFORMATEX wf; + wf.cbSize = sizeof(WAVEFORMATEX); + wf.nAvgBytesPerSec = 44100 * 4; + wf.nBlockAlign = 4; + wf.nChannels = 2; + wf.nSamplesPerSec = 44100; + wf.wBitsPerSample = 16; + wf.wFormatTag = WAVE_FORMAT_PCM; + WAVEFORMATEX* pwf = &wf; + + if(!wav_open(fname, pwf)) + return 0; + + EMU_PrintMessage("WAV recording started."); + SetMessageToDisplay("WAV recording started."); + + return 1; +} + +BOOL WAV_IsRecording() +{ + return wav_file && wav_file->valid; +} +void DRV_WavSoundUpdate(void* soundData, int soundLen) +{ + int nBytes; + + if(!WAV_IsRecording()) + return; + + nBytes = soundLen * wav_file->wav_format.nBlockAlign; + // assumes mmio system has been opened data chunk + mmioWrite(wav_file->wav_file, (HPSTR) soundData, nBytes); +// mmioFlush(wav_file->wav_file, 0); +} + +void DRV_WavEnd() +{ + if(!wav_file) + return; + + EMU_PrintMessage("WAV recording ended."); + SetMessageToDisplay("WAV recording ended."); + + wav_destroy(&wav_file); +} + +bool DRV_WavIsRecording() +{ + if(wav_file) + return true; + + return false; +} diff --git a/desmume/src/windows/wavout.h b/desmume/src/windows/wavout.h new file mode 100644 index 000000000..5d8565cb5 --- /dev/null +++ b/desmume/src/windows/wavout.h @@ -0,0 +1,4 @@ +bool DRV_WavBegin(const char* fname); +void DRV_WavEnd(); +void DRV_WavSoundUpdate(void* soundData, int soundLen); +bool DRV_WavIsRecording(); \ No newline at end of file