/************************************************************************** * * * Copyright (C) 2000, Eclipse Productions * * * * Offical Source code of the Apollo Project. DO NOT DISTRIBUTE! Any * * unauthorized distribution of these files in any way, shape, or form * * is a direct infringement on the copyrights entitled to Eclipse * * Productions and you will be prosecuted to the fullest extent of * * the law. Have a nice day... ^_^ * * * *************************************************************************/ /************************************************************************** * * Revision History: * Date: Comment: * ----------------------------------------------------------------------- * 06-24-00 Initial Version (Andrew) * **************************************************************************/ /************************************************************************** * * Notes: * * -Designed to have the Audio interrupt fire at about the same time it * would as if we were going at 60FPS. Also correctly emulates everything * except for the LEN register, which I will have to verify. -KAH * **************************************************************************/ #include #include #include // For status window #include #include #include #include #include #include #include #include "WinMain.h" #include "EmuMain.h" #include "resource.h" #include "audiodll.h" void ScheduleEvent (u32, u32, u32, void *); #define AI_DMA_EVENT 0x5 #define AID_DMA_EVENT 0x7 extern u8* rdram; extern u8* idmem; extern u8* MI; extern u8* AI; // ****************** Testing Audio Stuff ***************** static u32 Frequency = 0; static u32 Length = 0; static u32 Status = 0; static double CountsPerByte; static u32 SecondBuff = 0; static u32 CurrentCount; static u32 CurrentLength; static u32 IntScheduled = 0; extern u32 VsyncTiming; // Need this so I can time things to VSync... void AiInterrupt () { if (SecondBuff != 0) { IntScheduled = (u32)((double)SecondBuff * CountsPerByte); ScheduleEvent (AID_DMA_EVENT, IntScheduled, 0, NULL); } else { Status &= ~0x40000000; } CurrentCount = MmuRegs[9]; CurrentLength = SecondBuff; SecondBuff = 0; Status &= 0x7FFFFFFF; } void AiCallBack () { if (SecondBuff == 0) Status &= 0x7FFFFFFF; } u32 GetLength () { double AiCounts = CountsPerByte * CurrentLength; AiCounts = AiCounts - (double)(MmuRegs[9] - CurrentCount); if (AiCounts < 0) return 0; return (u32)(AiCounts/CountsPerByte); //return 0; } u32 GetStatus () { return Status; //return 0; } void SetLength (u32 data) { u32 CountCycles; // Set Status to FULL for a few COUNT cycles if (CurrentLength == 0) { CurrentLength = data; CurrentCount = MmuRegs[9]; IntScheduled = (u32)((double)data * CountsPerByte); ScheduleEvent (AID_DMA_EVENT, IntScheduled, 0, NULL); } else { SecondBuff = data; Status |= 0x80000000; // ScheduleEvent (AI_DMA_EVENT, 20, 0, NULL); } Status |= 0x40000000; ((DWORD *)AI)[1] = data; snddll.AiLenChanged (); } void SetFrequency (u32 data) { u32 CountsPerSecond; Frequency = data; Frequency = (Frequency * 4); // This makes it Bytes Per Second... CountsPerSecond = 789000.0 * (float)60.0; // This will only work with NTSC... CountsPerByte = (double)CountsPerSecond / (double)Frequency; } // ****************** Interfacing for the Controller plugin **************************** AUDIODLL snddll; void SignalAiDone(void); static bool LoadFunctions () { snddll.CloseDLL = (void (__cdecl*)( void )) GetProcAddress(snddll.hinstLibAudio, "CloseDLL"); snddll.DllAbout = (void (__cdecl*)( HWND )) GetProcAddress(snddll.hinstLibAudio, "DllAbout"); snddll.DllConfig = (void (__cdecl*)( HWND )) GetProcAddress(snddll.hinstLibAudio, "DllConfig"); snddll.DllTest = (void (__cdecl*)( HWND )) GetProcAddress(snddll.hinstLibAudio, "DllTest"); snddll.ProcessAList = (void (__cdecl*)( void )) GetProcAddress(snddll.hinstLibAudio, "ProcessAList"); snddll.RomClosed = (void (__cdecl*)( void )) GetProcAddress(snddll.hinstLibAudio, "RomClosed"); snddll.InitiateAudio = (BOOL (__cdecl*)( AUDIO_INFO )) GetProcAddress(snddll.hinstLibAudio, "InitiateAudio"); snddll.GetDllInfo = (void (__cdecl*)( PLUGIN_INFO * )) GetProcAddress(snddll.hinstLibAudio, "GetDllInfo"); snddll.AiDacrateChanged = (void (__cdecl*)( int )) GetProcAddress(snddll.hinstLibAudio, "AiDacrateChanged"); snddll.AiLenChanged = (void (__cdecl*)( void )) GetProcAddress(snddll.hinstLibAudio, "AiLenChanged"); snddll.AiReadLength = (DWORD (__cdecl*)( void )) GetProcAddress(snddll.hinstLibAudio, "AiReadLength"); snddll.AiUpdate = (void (__cdecl*)( BOOL )) GetProcAddress(snddll.hinstLibAudio, "AiUpdate"); return true; } void CloseAudioPlugin () { if(snddll.CloseDLL) snddll.CloseDLL(); if(snddll.hinstLibAudio) FreeLibrary(snddll.hinstLibAudio); ZeroMemory (&snddll, sizeof(AUDIODLL)); } BOOL LoadAudioPlugin (char *libname) { PLUGIN_INFO Plugin_Info; ZeroMemory (&Plugin_Info, sizeof(Plugin_Info)); if (libname == NULL) { return FALSE; } if (snddll.hinstLibAudio != NULL) snddll.Close (); snddll.hinstLibAudio = LoadLibrary(libname); if (snddll.hinstLibAudio == NULL) { Debug (0, "Could not load %s because it couldn't be found!", libname); snddll.Close(); return FALSE; } snddll.GetDllInfo = (void (__cdecl*)(PLUGIN_INFO *)) GetProcAddress(snddll.hinstLibAudio, "GetDllInfo"); if(!snddll.GetDllInfo) { Debug (0, "%s does not conform to the plugin spec.", libname); return FALSE;//This isn't a plugin dll, so don't bother. } snddll.GetDllInfo(&Plugin_Info); if(Plugin_Info.Type == PLUGIN_TYPE_AUDIO) { if(Plugin_Info.Version > PLUGIN_SND_VERSION) { Debug (0, "%s is not compatable with Apollo. %X != %X", libname, Plugin_Info.Version, PLUGIN_INP_VERSION); snddll.Close(); return FALSE; } if (LoadFunctions () == false) { snddll.Close(); return FALSE; } } else { //Here we insert Audio code, controller code, etc. Debug (0, "%s dll isn't an Audio plugin!", libname); snddll.Close(); return FALSE; } return TRUE; } AUDIO_INFO SndInfo; // make it stay just in case the evil plugin doesn't store a local copy u32 DummyReg; static u32 FakeReg; void InitSNDPlugin () { ZeroMemory (&SndInfo, sizeof(AUDIO_INFO)); SndInfo.hwnd = GhWnd; SndInfo.hinst = GhInst; SndInfo.MemoryBswaped = TRUE; SndInfo.HEADER = RomMemory; SndInfo.RDRAM = rdram; SndInfo.DMEM = idmem; SndInfo.IMEM = (idmem+0x1000); SndInfo.AI_DRAM_ADDR_REG = (u32 *)(AI+0x00); SndInfo.AI_LEN_REG = (u32 *)(AI+0x04); SndInfo.AI_CONTROL_REG = (u32 *)(AI+0x08); SndInfo.AI_STATUS_REG = (u32 *)&FakeReg;//(AI+0x0C); SndInfo.AI_DACRATE_REG = (u32 *)(AI+0x10); SndInfo.AI_BITRATE_REG = (u32 *)(AI+0x14); SndInfo.MI_INTR_REG = (u32 *)&FakeReg;//(MI+0x08); SndInfo.CheckInterrupts = SignalAiDone; if (snddll.InitiateAudio (SndInfo) == FALSE) { CloseAudioPlugin (); Debug (0, "Plugin Initialization Failed."); } } void SignalAiDone(void) { //*SndInfo.AI_STATUS_REG |= 0x80000001; //ScheduleEvent (AI_DMA_EVENT, 200, 0, NULL); //if ((((u32*)MI)[3] & (((u32*)MI)[2])) & AI_INTERRUPT) //((u32*)MI)[3] |= AI_INTERRUPT; //((u32*)MI)[2] |= AI_INTERRUPT; //InterruptNeeded |= AI_INTERRUPT; //Debug (0, "Audio Interrupt"); } // ******************** END AUDIO STUFF ********************** /* u32 AI_second_delay = 0; //#define ENABLEWAVE // Enabled wave logging #ifdef ENABLEWAVE bool Logging = false; FILE* audio = NULL; bool MemoryBswaped = true; #define FREQ 44100 #define BPS 16 #define SEGMENTS 4 #define LOCK_SIZE (FREQ / 60) // Always 8bit mono (For the time being...) static WAVEFORMATEX wfx; struct WAVEFILEFORMAT { char riff[4]; DWORD filesize; char wavefmt[8]; DWORD fmtlength; WAVEFORMAT wfx; WORD bps; char data[4]; DWORD datasize; } wave; void Soundmemcpy(void * dest, const void * src, size_t count); void CloseWave () { wave.filesize = ftell (audio); fseek (audio, 0, SEEK_SET); fwrite (&wave, 1, sizeof(WAVEFILEFORMAT), audio); fclose (audio); } void LogWave (void *sndptr, DWORD sndlen, u32 freq) { unsigned char buffer[262144]; if (audio == NULL) { audio = fopen("C:\\audio.wav","wb"); if (audio == NULL) { Logging = false; return; } ZeroMemory (&wfx, sizeof(WAVEFORMATEX)); wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = 2; wfx.nSamplesPerSec = freq;//22050; wfx.wBitsPerSample = 16; wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; atexit (CloseWave); strcpy ((char *)wave.riff, "RIFF "); strcpy ((char *)wave.wavefmt, "WAVEfmt "); strcpy ((char *)wave.data, "data "); memcpy (&wave.wfx, &wfx, sizeof(WAVEFORMAT)); wave.fmtlength = sizeof(WAVEFORMAT)+2; wave.bps = wfx.wBitsPerSample; wave.filesize = 0; wave.datasize = 0; fwrite (&wave, 1, sizeof(WAVEFILEFORMAT), audio); //MessageBox (NULL, "This should only come up once", "AUDIO", MB_OK); Debug (0, "Logging Audio..."); } Soundmemcpy (buffer, sndptr, sndlen); fwrite(buffer, 1, sndlen, audio); wave.datasize += sndlen; } #endif void SignalAI(void) { extern u8* AI; extern u32 AiInterrupt; extern u32 CountInterrupt, VsyncInterrupt, VsyncTime; u32 dacRate = 49152000/(((u32*)AI)[4]+1); //NTSC conversion float seconds = ((float)((u32*)AI)[1]/dacRate)/2;//div2 because of it being stereo if (((u32*)AI)[5]==15) seconds /=2; //Debug(0,"status%08X len%X rate%i freq%u seconds%2.2f, counter%X",((u32*)AI)[3],((u32*)AI)[1],((u32*)AI)[5]+1,dacRate,seconds,(u32)(seconds*625000*60)); #ifdef ENABLEWAVE if (Logging == true) LogWave (returnMemPointer(((u32*)AI)[0]), ((u32*)AI)[1], dacRate); #endif //fwrite(returnMemPointer(((u32*)AI)[0]),((u32*)AI)[1],1,audio); //fclose(audio); if (((u32*)AI)[3] & 1) { AI_second_delay = (u32)(seconds*625000*60); } AiInterrupt = instructions + (u32)(seconds*625000*60); VsyncTime = MIN(MIN(CountInterrupt,AiInterrupt),VsyncInterrupt); } void SignalAiDone(void) { extern u8* AI; extern u32 AiInterrupt; if (((u32*)AI)[3] & 1) {//second sound in queue...unset full status, set interrupt, and redo AiInterrupt ((u32*)AI)[3] = 0x40000000; InterruptNeeded |= AI_INTERRUPT; AiInterrupt = AI_second_delay + instructions; } else {//only one sound in queue...unset busy ((u32*)AI)[3] = 0; AiInterrupt = -1; ((u32*)AI)[1] = 0; ((u32*)AI)[0] = 0; } } #ifdef ENABLEWAVE void Soundmemcpy(void * dest, const void * src, size_t count) { if (MemoryBswaped) { _asm { mov edi, dest mov ecx, src mov edx, 0 push ebx push edi memcpyloop1: mov ax, word ptr [ecx + edx] mov bx, word ptr [ecx + edx + 2] mov word ptr [edi + edx + 2],ax mov word ptr [edi + edx],bx add edx, 4 mov ax, word ptr [ecx + edx] mov bx, word ptr [ecx + edx + 2] mov word ptr [edi + edx + 2],ax mov word ptr [edi + edx],bx add edx, 4 cmp edx, count jb memcpyloop1 pop edi pop ebx } } else { _asm { mov edi, dest mov ecx, src mov edx, 0 memcpyloop2: mov ax, word ptr [ecx + edx] xchg ah,al mov word ptr [edi + edx],ax add edx, 2 mov ax, word ptr [ecx + edx] xchg ah,al mov word ptr [edi + edx],ax add edx, 2 mov ax, word ptr [ecx + edx] xchg ah,al mov word ptr [edi + edx],ax add edx, 2 mov ax, word ptr [ecx + edx] xchg ah,al mov word ptr [edi + edx],ax add edx, 2 cmp edx, count jb memcpyloop2 } } } #endif */