📄 gbaapu.cpp
字号:
/*************************************************************************** DSemu - The Next Generation ** GBA PortAudio-emulated APU: Plugin Implementation [gbaapu.cpp] ** Copyright Imran Nazar, 2005; released under the BSD public licence. ** PortAudio used under the BSD license. ***************************************************************************/#include <string>#include <math.h>#include "portaudio.h"#include "defs.h"#include "events.h"#include "gbaapu.h"#include "config.h"#include "font5x7.h"#include "log.h"#include "err.h"// Pi's used to generate the sine table, so define it if it's not already.#ifndef M_PI# define M_PI 3.14159265358979#endif//---Static private class members------------------------------------------// Every plugin has an INFO structure attached, with info about the plugin.PLUGININFO gbaAPU::pInfo={ PLUGIN_TYPE_APU, 0x00010001, "GBA audio processor (PortAudio)", "DSemu-ng"};gbaAPU::IOREG gbaAPU::reg[0x28];std::string gbaAPU::pluginName;#define REG(x) reg[REG_ ## x].data// IO read flags: 1=Readable, 2=Writable, 3=Bothint gbaAPU::rflags[0x28]={ 3,3,3,0,3,0,3,0,3,3,3,0,3,0,3,0, 3,3,3,0,0,0,0,0,3,3,3,3,3,3,3,3, 3,3,0,0,0,0,0,0,};int gbaAPU::dmgChanged[4]={1,1,1,1};MMU32Plugin *gbaAPU::MMU;GUIPlugin *gbaAPU::GUI;gbaAPU *gbaAPU::cls;u16 *gbaAPU::ch2buf=NULL, *gbaAPU::ch2outbuf=NULL;int gbaAPU::ch2out;uint64_t gbaAPU::tsStart, gbaAPU::tsEnd;int gbaAPU::paused=1;PortAudioStream *gbaAPU::paStream;s16 gbaAPU::SinT[4096];//---Implementation--------------------------------------------------------// Fill the DMG soundwave buffersvoid gbaAPU::fillDMG(){ // Channel 2 u32 ticks; dmgChanged[2-1]=1; tsStart = tsEnd; tsEnd = GUI->getTimestamp() >> 6; ticks = (tsEnd - tsStart); u32 posStart, posEnd, pos; int halfcyclen; // printf("sound register change, %d ticks of sound to output.\n", ticks); if(ticks >= 4096) {// printf("That's more than 4kt, just fill the buf.\n"); posStart=0; posEnd=4095; } else { posStart = tsStart&4095; posEnd = tsEnd&4095; if(posEnd<posStart) posEnd+=4096;// printf("Filling from %d to %d.\n", posStart, posEnd); } if(posStart != posEnd) { int x=0; pos = posStart; halfcyclen = 2048-(REG(SND2CNT_H)&2047);// printf("Filling %d cycles.\n", halfcyclen*2);// halfcyclen = 512; do { x++; memset(ch2buf+(pos&4095), (x&1)?0x7F:0x80, halfcyclen*2); pos+=halfcyclen; } while(pos<posEnd); }// for(int i=0;i<4096;i++) ch2buf[i]=SinT[(i*3)&4095];//(i&1024)?32767:32768;}// PortAudio callback to dump the buffers to audio outputint gbaAPU::dump(void *in, void *out, unsigned long frames, PaTimestamp t, void *plg){ ((gbaAPU*)plg)->dumpDMG(in, out, frames, t); return 0;}// Dump the DMG buffers to a 16-bit outputvoid gbaAPU::dumpDMG(void *in, void *out, unsigned long frames, PaTimestamp tstamp){ s16 *o = (s16*)out; // Channel 2/* for(int i=0;i<64;i++) for(int j=0;j<4096;j++) ch2outbuf[(i*4096+j)>>3]=ch2buf[j];// memcpy(ch2outbuf+i*4096, ch2buf, 4096*2);*/ for(unsigned int i=0;i<frames;i+=8) o[i]=ch2buf[i*8]; // GUI->eventPush(262144, EVENT_SOUND_DUMP, (vfptr)dump, (void*)cls);}// If the APU has to be paused, the stream will be stopped. Conversely, if// the APU has to start up, the stream will be started.void gbaAPU::togglePause(){ switch(paused) { case 0: if(Pa_StopStream(paStream) != paNoError) Logger::log(pName) << "Failed to stop PortAudio stream."; paused=1; break; case 1: if(Pa_StartStream(paStream) != paNoError) Logger::log(pName) << "Failed to start PortAudio stream."; paused=0; break; }}// Functions to read and write the APU's IO ports. These are registered// with the MMU, and then called automatically when the appropriate// operation occurs.// NOTE: If a register is unreadable, it returns all 1's.u8 gbaAPU::rdB(u32 a){// char str[128]; if(reg[((a&0xFF)-0x60)>>1].flags) return reg[((a&0xFF)-0x60)>>1].b[a&1]; else return 0xFF;}u16 gbaAPU::rdH(u32 a){// char str[128]; if(reg[((a&0xFF)-0x60)>>1].flags) return reg[((a&0xFF)-0x60)>>1].data; else return 0xFFFF;}u32 gbaAPU::rdW(u32 a){// char str[128]; u32 w; w=(reg[((a&0xFF)-0x60)>>1].flags)?reg[((a&0xFF)-0x60)>>1].data:0xFFFF; w|=(reg[(((a&0xFF)-0x60)>>1)+1].flags)?(reg[(((a&0xFF)-0x60)>>1)+1].data)<<16:0xFFFF0000; return w;}// NOTE: Every time a change occurs to the sound card, the sound has// to be changed. So the buffers are re-filled.void gbaAPU::wrB(u32 a, u8 d){// char str[128]; if(reg[((a&0xFF)-0x60)>>1].flags) { reg[((a&0xFF)-0x60)>>1].b[a&1]=d; fillDMG(); }}void gbaAPU::wrH(u32 a, u16 d){// char str[128]; if(reg[((a&0xFF)-0x60)>>1].flags) { reg[((a&0xFF)-0x60)>>1].data=d; fillDMG(); }}void gbaAPU::wrW(u32 a, u32 d){// char str[128]; if(reg[((a&0xFF)-0x60)>>1].flags) reg[((a&0xFF)-0x60)>>1].data=d&65535; if(reg[(((a&0xFF)-0x60)>>1)+1].flags) reg[(((a&0xFF)-0x60)>>1)+1].data=d>>16; if((reg[((a&0xFF)-0x60)>>1].flags) || (reg[(((a&0xFF)-0x60)>>1)+1].flags)) fillDMG();}// Initialise plugin: Init PortAudio, register with the MMU// A 16-bit 32kHz stream is opened, to make life easy.gbaAPU::gbaAPU(std::string name, REQPTR req, UNREQPTR unreq){ pName = std::string(name); pClass = pName.substr(0, pName.find(".")+1); pRequest = req; pUnrequest = unreq; pluginName = std::string(pName); cls = this; if(Pa_Initialize() != paNoError) throw Exception(ERR_APU_INIT, pName, "PortAudio initialisation failed."); if(Pa_OpenDefaultStream(&paStream, 0, // no input 2, // stereo out paInt16, // Sample type/size, 16-bit signed int 32768, // Sample rate 512, // 1/64s per buffer 0, // default minimum number of buffers dump, // Our audio out handler this) != paNoError) throw Exception(ERR_APU_INIT, pName, "Failed to open PortAudio stream."); Logger::log(pName) << "PortAudio stream opened."; MMU = (MMU32Plugin*)pRequest("GBA_CPU0.mmu"); GUI = (GUIPlugin*)pRequest("UI"); ch2buf = new u16[4096]; ch2outbuf = new u16[262144]; if(!ch2buf || !ch2outbuf) throw Exception(ERR_APU_INIT, pName, "Unable to allocate Channel 2 buffer."); memset(ch2buf, 0, 4096*2); for(int i=0; i<0x28; ++i) { reg[i].data = 0; reg[i].flags = rflags[i]; } for(int i=0; i<5; ++i) MMU->mmioReg(0x06+i, rdB, rdH, rdW, wrB, wrH, wrW); Logger::log(pName) << "Registered with MMU."; // Initialise the sine table for(int i=0;i<4096;i++) SinT[i]=(s16)(sin((float)i*M_PI/2048.0)*32768.0); Logger::log(pName) << "Initialised.";}// Shut down plugin: terminate PA, free up buffersgbaAPU::~gbaAPU(){ if(Pa_StopStream(paStream) != paNoError) Logger::log(pName) << "Failed to stop PortAudio stream."; else { if(Pa_CloseStream(paStream) != paNoError) Logger::log(pName) << "Failed to close PortAudio stream."; } Pa_Terminate(); Logger::log(pName) << "PortAudio terminated."; if(ch2buf) { delete ch2buf; ch2buf = NULL; } if(ch2outbuf) { delete ch2outbuf; ch2outbuf = NULL; } pUnrequest("GBA_CPU0.mmu",1); MMU=NULL; pUnrequest("UI",0); GUI=NULL; Logger::log(pName) << "Shutdown.";}// Reset plugin: stop the stream, clear registersvoid gbaAPU::reset(){ if(Pa_StopStream(paStream) != paNoError) Logger::log(pName) << "Failed to stop PortAudio stream."; paused=1; for(int i=0;i<0x28;++i) reg[i].data = 0;// GUI->eventPush(262144, EVENT_SOUND_DUMP, (vfptr)dump, this); Logger::log(pName) << "Reset";}// Provide plugin status: Not much really to givevoid gbaAPU::status(int opt1=0, int opt2=0){}//---Plugin architecture support-------------------------------------------// Retrieve Plugin class from outside// Parameters: plg - Address of a pointer to a Plugin class to 'new'// name - FQPN of plugin as listed in INI file// req - Pointer to PluginRequest API function// unreq - Pointer to PluginUnrequest API functionEXPORTFUNC void getPlugin(Plugin **plg, std::string name, REQPTR req, UNREQPTR unreq){ *plg = new gbaAPU(name, req, unreq);}// Provide plugin version informationPLUGININFO *gbaAPU::getinfo(){ return &pInfo;}// Release plugin from outsidevoid gbaAPU::release(){ // Delete the Test plugin that was 'new'd in getPlugin. delete this;}/*** EOF: gbaapu.cpp *****************************************************/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -