Fix gamecube microphone (button not implemented, yet :p).
Calls ExpansionInterface::UpdateInterrupts just before checking exceptions now.
This commit is contained in:
parent
856972f808
commit
5dc866bfc9
|
@ -127,7 +127,7 @@ IEXIDevice* EXIDevice_Create(TEXIDevices _EXIDevice)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EXIDEVICE_MIC:
|
case EXIDEVICE_MIC:
|
||||||
return new CEXIMic(1);
|
return new CEXIMic();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EXIDEVICE_ETH:
|
case EXIDEVICE_ETH:
|
||||||
|
|
|
@ -16,115 +16,131 @@
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
|
||||||
|
#if HAVE_PORTAUDIO
|
||||||
|
|
||||||
#include "FileUtil.h"
|
#include "FileUtil.h"
|
||||||
#include "StringUtil.h"
|
#include "StringUtil.h"
|
||||||
#include "../Core.h"
|
#include "../Core.h"
|
||||||
#include "../CoreTiming.h"
|
#include "../CoreTiming.h"
|
||||||
|
#include "SystemTimers.h"
|
||||||
|
|
||||||
#include "EXI_Device.h"
|
#include "EXI_Device.h"
|
||||||
#include "EXI_DeviceMic.h"
|
#include "EXI_DeviceMic.h"
|
||||||
|
|
||||||
// Unfortunately this must be enabled in Common.h for windows users. Scons should enable it otherwise
|
|
||||||
#if !HAVE_PORTAUDIO
|
|
||||||
|
|
||||||
void SetMic(bool Value){}
|
|
||||||
|
|
||||||
CEXIMic::CEXIMic(int _Index){}
|
|
||||||
CEXIMic::~CEXIMic(){}
|
|
||||||
bool CEXIMic::IsPresent() {return false;}
|
|
||||||
void CEXIMic::SetCS(int cs){}
|
|
||||||
void CEXIMic::Update(){}
|
|
||||||
void CEXIMic::TransferByte(u8 &byte){}
|
|
||||||
bool CEXIMic::IsInterruptSet(){return false;}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// We use PortAudio for cross-platform audio input.
|
|
||||||
// It needs the headers and a lib file for the dll
|
|
||||||
#include <portaudio.h>
|
#include <portaudio.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
static bool pa_init = false;
|
||||||
#pragma comment(lib, "C:/Users/Shawn/Desktop/portaudio/portaudio-v19/portaudio_x64.lib")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static bool MicButton = false;
|
void CEXIMic::StreamLog(const char *msg)
|
||||||
static bool IsOpen;
|
|
||||||
|
|
||||||
union InputData
|
|
||||||
{
|
{
|
||||||
s16 word;
|
DEBUG_LOG(EXPANSIONINTERFACE, "%s: %s",
|
||||||
u8 byte[2];
|
msg, Pa_GetErrorText(pa_error));
|
||||||
};
|
|
||||||
|
|
||||||
InputData inputData[64]; // 64 words = Max 128 bytes returned????
|
|
||||||
PaStream *stream;
|
|
||||||
PaError err;
|
|
||||||
unsigned short SFreq;
|
|
||||||
unsigned short SNum;
|
|
||||||
bool m_bInterruptSet;
|
|
||||||
bool Sampling;
|
|
||||||
|
|
||||||
|
|
||||||
void SetMic(bool Value)
|
|
||||||
{
|
|
||||||
MicButton = Value;
|
|
||||||
if(Sampling)
|
|
||||||
Pa_StartStream( stream );
|
|
||||||
else
|
|
||||||
Pa_StopStream( stream );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int patestCallback( const void *inputBuffer, void *outputBuffer,
|
void CEXIMic::StreamInit()
|
||||||
unsigned long frameCount,
|
|
||||||
const PaStreamCallbackTimeInfo* timeInfo,
|
|
||||||
PaStreamCallbackFlags statusFlags,
|
|
||||||
void *userData )
|
|
||||||
{
|
{
|
||||||
s16 *data = (s16*)inputBuffer;
|
// Setup the wonderful c-interfaced lib...
|
||||||
//s16 *out = (s16*)outputBuffer;
|
pa_error = paNoError;
|
||||||
|
if (!pa_init)
|
||||||
if (!m_bInterruptSet && Sampling)
|
|
||||||
{
|
{
|
||||||
for(unsigned int i = 0; i < SNum; ++i)
|
pa_error = Pa_Initialize();
|
||||||
|
|
||||||
|
if (pa_error != paNoError)
|
||||||
{
|
{
|
||||||
inputData[i].word = data[i];
|
StreamLog("Pa_Initialize");
|
||||||
//out[i] = inputData[i].word;
|
|
||||||
}
|
}
|
||||||
m_bInterruptSet = true;
|
else
|
||||||
|
pa_init = true;
|
||||||
}
|
}
|
||||||
return paContinue;
|
|
||||||
|
mic_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CEXIMic::StreamTerminate()
|
||||||
|
{
|
||||||
|
// TODO keep track of number of mics...
|
||||||
|
if (pa_init && --mic_count <= 0)
|
||||||
|
pa_error = Pa_Terminate();
|
||||||
|
|
||||||
|
if (pa_error != paNoError)
|
||||||
|
{
|
||||||
|
StreamLog("Pa_Terminate");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pa_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIMic::StreamStart()
|
||||||
|
{
|
||||||
|
// Open stream with current parameters
|
||||||
|
if (pa_init)
|
||||||
|
{
|
||||||
|
pa_error = Pa_OpenDefaultStream(&pa_stream, 1, 0, paInt16,
|
||||||
|
sample_rate, buff_size_samples, NULL, NULL);
|
||||||
|
StreamLog("Pa_OpenDefaultStream");
|
||||||
|
pa_error = Pa_StartStream(pa_stream);
|
||||||
|
StreamLog("Pa_StartStream");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIMic::StreamStop()
|
||||||
|
{
|
||||||
|
// Acts as if Pa_AbortStream was called
|
||||||
|
pa_error = Pa_CloseStream(pa_stream);
|
||||||
|
StreamLog("Pa_CloseStream");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIMic::StreamReadOne()
|
||||||
|
{
|
||||||
|
// Returns num samples or error
|
||||||
|
pa_error = Pa_GetStreamReadAvailable(pa_stream);
|
||||||
|
if (pa_error >= buff_size_samples)
|
||||||
|
{
|
||||||
|
pa_error = Pa_ReadStream(pa_stream, ring_buffer, buff_size_samples);
|
||||||
|
|
||||||
|
if (pa_error != paNoError)
|
||||||
|
{
|
||||||
|
status.buff_ovrflw = 1;
|
||||||
|
// Input overflowed - is re-setting the stream the only to recover?
|
||||||
|
StreamLog("Pa_ReadStream");
|
||||||
|
|
||||||
|
StreamStop();
|
||||||
|
StreamStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// EXI Mic Device
|
// EXI Mic Device
|
||||||
|
// This works by opening and starting a portaudio input stream when the is_active
|
||||||
|
// bit is set. The interrupt is scheduled in the future based on sample rate and
|
||||||
|
// buffer size settings. When the console handles the interrupt, it will send
|
||||||
|
// cmdGetBuffer, which is when we actually read data from a buffer portaudio fills
|
||||||
|
// in the background (ie on demand instead of realtime). Because of this we need
|
||||||
|
// to clear portaudio's buffer if emulation speed drops below realtime, or else
|
||||||
|
// a bad audio lag develops. It's actually kind of convenient because it gives
|
||||||
|
// us a way to detect if buff_ovrflw should be set.
|
||||||
|
|
||||||
CEXIMic::CEXIMic(int _Index)
|
u8 const CEXIMic::exi_id[] = { 0, 0x0a, 0, 0, 0 };
|
||||||
|
int CEXIMic::mic_count = 0;
|
||||||
|
|
||||||
|
CEXIMic::CEXIMic()
|
||||||
{
|
{
|
||||||
Index = _Index;
|
status.U16 = 0;
|
||||||
|
|
||||||
memset(&inputData, 0 , sizeof(inputData));
|
|
||||||
memset(&Status.U16, 0 , sizeof(u16));
|
|
||||||
command = 0;
|
command = 0;
|
||||||
m_uPosition = 0;
|
m_position = 0;
|
||||||
m_bInterruptSet = false;
|
ring_pos = 0;
|
||||||
MicButton = false;
|
next_int_ticks = 0;
|
||||||
IsOpen = false;
|
|
||||||
err = Pa_Initialize();
|
StreamInit();
|
||||||
if (err != paNoError)
|
|
||||||
ERROR_LOG(EXPANSIONINTERFACE, "EXI MIC: PortAudio Initialize error %s", Pa_GetErrorText(err));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CEXIMic::~CEXIMic()
|
CEXIMic::~CEXIMic()
|
||||||
{
|
{
|
||||||
err = Pa_CloseStream( stream );
|
StreamTerminate();
|
||||||
if (err != paNoError)
|
|
||||||
ERROR_LOG(EXPANSIONINTERFACE, "EXI MIC: PortAudio Close error %s", Pa_GetErrorText(err));
|
|
||||||
err = Pa_Terminate();
|
|
||||||
if (err != paNoError)
|
|
||||||
ERROR_LOG(EXPANSIONINTERFACE, "EXI MIC: PortAudio Terminate error %s", Pa_GetErrorText(err));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXIMic::IsPresent()
|
bool CEXIMic::IsPresent()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -132,38 +148,26 @@ bool CEXIMic::IsPresent()
|
||||||
void CEXIMic::SetCS(int cs)
|
void CEXIMic::SetCS(int cs)
|
||||||
{
|
{
|
||||||
if (cs) // not-selected to selected
|
if (cs) // not-selected to selected
|
||||||
m_uPosition = 0;
|
m_position = 0;
|
||||||
else
|
// Doesn't appear to do anything we care about
|
||||||
{
|
//else if (command == cmdReset)
|
||||||
switch (command)
|
|
||||||
{
|
|
||||||
case cmdID:
|
|
||||||
case cmdGetStatus:
|
|
||||||
case cmdSetStatus:
|
|
||||||
case cmdGetBuffer:
|
|
||||||
break;
|
|
||||||
case cmdWakeUp:
|
|
||||||
// This is probably not a command, but anyway...
|
|
||||||
// The command 0xff seems to be used to get in sync with the microphone or to wake it up.
|
|
||||||
// Normally, it is issued before any other command, or to recover from errors.
|
|
||||||
WARN_LOG(EXPANSIONINTERFACE, "EXI MIC: WakeUp cmd");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
WARN_LOG(EXPANSIONINTERFACE, "EXI MIC: unknown CS command %02x\n", command);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIMic::Update()
|
void CEXIMic::UpdateNextInterruptTicks()
|
||||||
{
|
{
|
||||||
|
next_int_ticks = CoreTiming::GetTicks() +
|
||||||
|
(SystemTimers::GetTicksPerSecond() / sample_rate) * buff_size_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXIMic::IsInterruptSet()
|
bool CEXIMic::IsInterruptSet()
|
||||||
{
|
{
|
||||||
if(m_bInterruptSet)
|
if (next_int_ticks && CoreTiming::GetTicks() >= next_int_ticks)
|
||||||
{
|
{
|
||||||
m_bInterruptSet = false;
|
if (status.is_active)
|
||||||
|
UpdateNextInterruptTicks();
|
||||||
|
else
|
||||||
|
next_int_ticks = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -174,124 +178,66 @@ bool CEXIMic::IsInterruptSet()
|
||||||
|
|
||||||
void CEXIMic::TransferByte(u8 &byte)
|
void CEXIMic::TransferByte(u8 &byte)
|
||||||
{
|
{
|
||||||
if (m_uPosition == 0)
|
if (m_position == 0)
|
||||||
{
|
{
|
||||||
command = byte; // first byte is command
|
command = byte; // first byte is command
|
||||||
byte = 0xFF; // would be tristate, but we don't care.
|
byte = 0xFF; // would be tristate, but we don't care.
|
||||||
}
|
m_position++;
|
||||||
else
|
return;
|
||||||
{
|
|
||||||
switch (command)
|
|
||||||
{
|
|
||||||
case cmdID:
|
|
||||||
if (m_uPosition == 1)
|
|
||||||
;//byte = 0x80; // dummy cycle - taken from memcard, it doesn't seem to need it here
|
|
||||||
else
|
|
||||||
byte = (u8)(EXI_DEVTYPE_MIC >> (24-(((m_uPosition-2) & 3) * 8)));
|
|
||||||
break;
|
|
||||||
case cmdGetStatus:
|
|
||||||
{
|
|
||||||
if (m_uPosition != 1 && m_uPosition != 2)
|
|
||||||
WARN_LOG(EXPANSIONINTERFACE, "EXI MIC: WARNING GetStatus @ pos: %d should never happen", m_uPosition);
|
|
||||||
if((!Status.button && MicButton)||(Status.button && !MicButton))
|
|
||||||
WARN_LOG(EXPANSIONINTERFACE, "EXI MIC: Mic button %s", MicButton ? "pressed" : "released");
|
|
||||||
|
|
||||||
Status.button = MicButton ? 1 : 0;
|
|
||||||
byte = Status.U8[ (m_uPosition - 1) ? 0 : 1];
|
|
||||||
INFO_LOG(EXPANSIONINTERFACE, "EXI MIC: Status is 0x%04x", Status.U16);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case cmdSetStatus:
|
|
||||||
{
|
|
||||||
// 0x80 0xXX 0xYY
|
|
||||||
// cmd pos1 pos2
|
|
||||||
|
|
||||||
// Here we assign the byte to the proper place in Status and update portaudio settings
|
|
||||||
Status.U8[ (m_uPosition - 1) ? 0 : 1] = byte;
|
|
||||||
|
|
||||||
if(m_uPosition == 2)
|
|
||||||
{
|
|
||||||
Sampling = (Status.sampling == 1) ? true : false;
|
|
||||||
|
|
||||||
switch (Status.sRate)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
SFreq = 11025;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
SFreq = 22050;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
SFreq = 44100;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ERROR_LOG(EXPANSIONINTERFACE, "EXI MIC: Trying to set unknown sampling rate");
|
|
||||||
SFreq = 44100;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (Status.pLength)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
SNum = 32;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
SNum = 64;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
SNum = 128;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ERROR_LOG(EXPANSIONINTERFACE, "EXI MIC: Trying to set unknown period length");
|
|
||||||
SNum = 128;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_LOG(EXPANSIONINTERFACE, "//////////////////////////////////////////////////////////////////////////");
|
|
||||||
DEBUG_LOG(EXPANSIONINTERFACE, "EXI MIC: Status is now 0x%04x", Status.U16);
|
|
||||||
DEBUG_LOG(EXPANSIONINTERFACE, "\tbutton %i\tsRate %i\tpLength %i\tsampling %i\n",
|
|
||||||
Status.button, SFreq, SNum, Status.sampling);
|
|
||||||
|
|
||||||
if(!IsOpen)
|
|
||||||
{
|
|
||||||
// Open Our PortAudio Stream
|
|
||||||
// (shuffle2) This (and the callback) could still be wrong
|
|
||||||
err = Pa_OpenDefaultStream(
|
|
||||||
&stream, // Our PaStream
|
|
||||||
1, // Input Channels
|
|
||||||
0, // Output Channels
|
|
||||||
paInt16, // Output format - GC wants PCM samples in signed 16-bit format
|
|
||||||
SFreq, // Sample Rate
|
|
||||||
SNum, // Period Length (frames per buffer)
|
|
||||||
patestCallback,// Our callback!
|
|
||||||
NULL); // Pointer passed to our callback
|
|
||||||
if (err != paNoError)
|
|
||||||
{
|
|
||||||
ERROR_LOG(EXPANSIONINTERFACE, "EXI MIC: PortAudio error %s", Pa_GetErrorText(err));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
IsOpen = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case cmdGetBuffer:
|
|
||||||
{
|
|
||||||
int pos = m_uPosition - 1;
|
|
||||||
// (sonicadvance1)I think if we set the Interrupt to false, it reads another 64
|
|
||||||
// Will Look in to it.
|
|
||||||
// (shuffle2)Seems like games just continuously get the buffer as long as
|
|
||||||
// they're sampling and the mic is generating interrupts
|
|
||||||
byte = inputData[pos].byte[ (pos & 1) ? 0 : 1 ];
|
|
||||||
INFO_LOG(EXPANSIONINTERFACE, "EXI MIC: GetBuffer%s%d/%d byte: 0x%02x",
|
|
||||||
(pos > 9) ? " " : " ", pos, SNum, byte);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ERROR_LOG(EXPANSIONINTERFACE, "EXI MIC: unknown command byte %02x\n", command);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
m_uPosition++;
|
|
||||||
|
int pos = m_position - 1;
|
||||||
|
|
||||||
|
switch (command)
|
||||||
|
{
|
||||||
|
case cmdID:
|
||||||
|
byte = exi_id[pos];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmdGetStatus:
|
||||||
|
byte = status.U8[pos ^ 1];
|
||||||
|
if (pos == 1 && status.buff_ovrflw)
|
||||||
|
status.buff_ovrflw = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmdSetStatus:
|
||||||
|
{
|
||||||
|
bool wasactive = status.is_active;
|
||||||
|
status.U8[pos ^ 1] = byte;
|
||||||
|
|
||||||
|
// safe to do since these can only be entered if both bytes of status have been written
|
||||||
|
if (!wasactive && status.is_active)
|
||||||
|
{
|
||||||
|
sample_rate = rate_base << status.sample_rate;
|
||||||
|
buff_size = ring_base << status.buff_size;
|
||||||
|
buff_size_samples = buff_size / sample_size;
|
||||||
|
|
||||||
|
UpdateNextInterruptTicks();
|
||||||
|
|
||||||
|
StreamStart();
|
||||||
|
}
|
||||||
|
else if (wasactive && !status.is_active)
|
||||||
|
{
|
||||||
|
StreamStop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmdGetBuffer:
|
||||||
|
if (ring_pos == 0)
|
||||||
|
{
|
||||||
|
// Can set buff_ovrflw
|
||||||
|
StreamReadOne();
|
||||||
|
}
|
||||||
|
byte = ring_buffer[ring_pos ^ 1];
|
||||||
|
ring_pos = (ring_pos + 1) % buff_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ERROR_LOG(EXPANSIONINTERFACE, "EXI MIC: unknown command byte %02x", command);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_position++;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -18,58 +18,90 @@
|
||||||
#ifndef _EXI_DEVICEMIC_H
|
#ifndef _EXI_DEVICEMIC_H
|
||||||
#define _EXI_DEVICEMIC_H
|
#define _EXI_DEVICEMIC_H
|
||||||
|
|
||||||
|
#if HAVE_PORTAUDIO
|
||||||
|
|
||||||
class CEXIMic : public IEXIDevice
|
class CEXIMic : public IEXIDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CEXIMic(int _Index);
|
CEXIMic();
|
||||||
virtual ~CEXIMic();
|
virtual ~CEXIMic();
|
||||||
void SetCS(int cs);
|
void SetCS(int cs);
|
||||||
void Update();
|
|
||||||
bool IsInterruptSet();
|
bool IsInterruptSet();
|
||||||
bool IsPresent();
|
bool IsPresent();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static int mic_count;
|
||||||
|
static u8 const exi_id[];
|
||||||
|
static int const sample_size = sizeof(s16);
|
||||||
|
static int const rate_base = 11025;
|
||||||
|
static int const ring_base = 32;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
|
||||||
EXI_DEVTYPE_MIC = 0x0A000000
|
|
||||||
};
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
{
|
||||||
cmdID = 0x00,
|
cmdID = 0x00,
|
||||||
cmdGetStatus = 0x40,
|
cmdGetStatus = 0x40,
|
||||||
cmdSetStatus = 0x80,
|
cmdSetStatus = 0x80,
|
||||||
cmdGetBuffer = 0x20,
|
cmdGetBuffer = 0x20,
|
||||||
cmdWakeUp = 0xFF,
|
cmdReset = 0xFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
// STATE_TO_SAVE
|
// STATE_TO_SAVE
|
||||||
int interruptSwitch;
|
u32 m_position;
|
||||||
int command;
|
int command;
|
||||||
union uStatus
|
union UStatus
|
||||||
{
|
{
|
||||||
u16 U16;
|
u16 U16;
|
||||||
u8 U8[2];
|
u8 U8[2];
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
u16 :8; // Unknown
|
u16 out :4; // MICSet/GetOut...???
|
||||||
u16 button :1; // 1: Button Pressed
|
u16 button :5; // Buttons. Top bit is mic button. Lowest bit is used for MICGetDeviceID (always 0)
|
||||||
u16 unk1 :1; // 1 ? Overflow?
|
u16 buff_ovrflw :1; // Ring buffer wrote over bytes which weren't read by console
|
||||||
u16 unk2 :1; // Unknown related to 0 and 15 values It seems
|
u16 gain :1; // Gain: 0dB or 15dB
|
||||||
u16 sRate :2; // Sample Rate, 00-11025, 01-22050, 10-44100, 11-??
|
u16 sample_rate :2; // Sample rate, 00-11025, 01-22050, 10-44100, 11-??
|
||||||
u16 pLength :2; // Period Length, 00-32, 01-64, 10-128, 11-???
|
u16 buff_size :2; // Ring buffer size in bytes, 00-32, 01-64, 10-128, 11-???
|
||||||
u16 sampling :1; // If We Are Sampling or Not
|
u16 is_active :1; // If we are sampling or not
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
int Index;
|
UStatus status;
|
||||||
u32 m_uPosition;
|
|
||||||
uStatus Status;
|
// status bits converted to nice numbers
|
||||||
|
int sample_rate;
|
||||||
|
int buff_size;
|
||||||
|
int buff_size_samples;
|
||||||
|
|
||||||
|
// 64 is the max size, can be 16 or 32 as well
|
||||||
|
int ring_pos;
|
||||||
|
u8 ring_buffer[64 * sample_size];
|
||||||
|
|
||||||
|
// 0 to disable interrupts, else it will be checked against current cpu ticks
|
||||||
|
// to determine if interrupt should be raised
|
||||||
|
u64 next_int_ticks;
|
||||||
|
void UpdateNextInterruptTicks();
|
||||||
|
|
||||||
|
// Streaming input interface
|
||||||
|
int pa_error; // PaError
|
||||||
|
void *pa_stream; // PaStream
|
||||||
|
|
||||||
|
void StreamLog(const char *msg);
|
||||||
|
void StreamInit();
|
||||||
|
void StreamTerminate();
|
||||||
|
void StreamStart();
|
||||||
|
void StreamStop();
|
||||||
|
void StreamReadOne();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void TransferByte(u8 &byte);
|
virtual void TransferByte(u8 &byte);
|
||||||
};
|
};
|
||||||
|
|
||||||
void SetMic(bool Value);
|
#else // HAVE_PORTAUDIO
|
||||||
|
|
||||||
|
class CEXIMic : public IEXIDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CEXIMic() {}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif // _EXI_DEVICEMIC_H
|
||||||
|
|
|
@ -206,14 +206,11 @@ void Jit64AsmRoutineManager::Generate()
|
||||||
ABI_CallFunction(reinterpret_cast<void *>(&CoreTiming::Advance));
|
ABI_CallFunction(reinterpret_cast<void *>(&CoreTiming::Advance));
|
||||||
|
|
||||||
testExceptions = GetCodePtr();
|
testExceptions = GetCodePtr();
|
||||||
TEST(32, M((void *)&PowerPC::ppcState.Exceptions), Imm32(0xFFFFFFFF));
|
MOV(32, R(EAX), M(&PC));
|
||||||
FixupBranch skipExceptions = J_CC(CC_Z);
|
MOV(32, M(&NPC), R(EAX));
|
||||||
MOV(32, R(EAX), M(&PC));
|
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExceptions));
|
||||||
MOV(32, M(&NPC), R(EAX));
|
MOV(32, R(EAX), M(&NPC));
|
||||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExceptions));
|
MOV(32, M(&PC), R(EAX));
|
||||||
MOV(32, R(EAX), M(&NPC));
|
|
||||||
MOV(32, M(&PC), R(EAX));
|
|
||||||
SetJumpTarget(skipExceptions);
|
|
||||||
|
|
||||||
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF));
|
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF));
|
||||||
J_CC(CC_Z, outerLoop, true);
|
J_CC(CC_Z, outerLoop, true);
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "CPUCoreBase.h"
|
#include "CPUCoreBase.h"
|
||||||
|
|
||||||
#include "../Host.h"
|
#include "../Host.h"
|
||||||
|
#include "HW/EXI.h"
|
||||||
|
|
||||||
CPUCoreBase *cpu_core_base;
|
CPUCoreBase *cpu_core_base;
|
||||||
|
|
||||||
|
@ -268,6 +269,10 @@ void Stop()
|
||||||
|
|
||||||
void CheckExceptions()
|
void CheckExceptions()
|
||||||
{
|
{
|
||||||
|
// Make sure we are checking against the latest EXI status. This is required
|
||||||
|
// for devices which interrupt frequently, such as the gc mic
|
||||||
|
ExpansionInterface::UpdateInterrupts();
|
||||||
|
|
||||||
// Read volatile data once
|
// Read volatile data once
|
||||||
u32 exceptions = ppcState.Exceptions;
|
u32 exceptions = ppcState.Exceptions;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue