Mic device WIP. define USE_PORTAUDIO and link with portaudio to test it.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@2153 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
Shawn Hoffman 2009-02-08 18:25:25 +00:00
parent 175abfef0a
commit c3dbbb1f23
4 changed files with 199 additions and 182 deletions

View File

@ -23,17 +23,15 @@
#include "EXI_Device.h" #include "EXI_Device.h"
#include "EXI_DeviceMic.h" #include "EXI_DeviceMic.h"
bool MicButton; bool MicButton = false;
bool IsOpen; bool IsOpen;
// Doing it this way since it's Linux only atm due to portaudio, even though the lib is crossplatform
// I had to include libs in the DolphinWX Sconscript file which I thought was BS. //#define USE_PORTAUDIO
// So I'm committing with all the code ifdeff'ed out #ifndef USE_PORTAUDIO
#if 1
void SetMic(bool Value) void SetMic(bool Value){}
{}
bool GetMic()
{return false;}
CEXIMic::CEXIMic(int _Index){} CEXIMic::CEXIMic(int _Index){}
CEXIMic::~CEXIMic(){} CEXIMic::~CEXIMic(){}
bool CEXIMic::IsPresent() {return false;} bool CEXIMic::IsPresent() {return false;}
@ -41,79 +39,86 @@ void CEXIMic::SetCS(int cs){}
void CEXIMic::Update(){} void CEXIMic::Update(){}
void CEXIMic::TransferByte(u8 &byte){} void CEXIMic::TransferByte(u8 &byte){}
bool CEXIMic::IsInterruptSet(){return false;} bool CEXIMic::IsInterruptSet(){return false;}
#else #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>
#include <stdio.h>
unsigned char InputData[128*44100]; // Max Data is 128 samples at 44100 #ifdef _WIN32
PaStream *stream; #pragma comment(lib, "C:/Users/Shawn/Desktop/portaudio/portaudio-v19/portaudio_x64.lib")
PaError err; #endif
unsigned short SFreq;
unsigned short SNum; unsigned char InputData[128*44100]; // Max Data is 128 samples at 44100
unsigned int Sample; PaStream *stream;
bool m_bInterruptSet; PaError err;
bool Sampling; unsigned short SFreq;
unsigned short SNum;
unsigned int Sample;
bool m_bInterruptSet;
bool Sampling;
void SetMic(bool Value) void SetMic(bool Value)
{ {
if(Value != MicButton) MicButton = Value;
if(Sampling)
{ {
MicButton = Value; if(MicButton)
printf("Mic is set to %s\n", MicButton ? "true" : "false"); Pa_StartStream( stream );
if(Sampling) else
{ Pa_StopStream( stream );
if(MicButton)
Pa_StartStream( stream );
else
Pa_StopStream( stream );
}
} }
} }
bool GetMic()
{
return MicButton;
}
static unsigned int k = 0; static unsigned int k = 0;
int patestCallback( const void *inputBuffer, void *outputBuffer, int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo, const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, PaStreamCallbackFlags statusFlags,
void *userData ) void *userData )
{ {
unsigned char *data = (unsigned char*)inputBuffer; unsigned char *data = (unsigned char*)inputBuffer;
unsigned int i; unsigned int i;
for( i=0; i<framesPerBuffer && k < (SFreq*SNum); i++, k++ ) for( i=0; i<framesPerBuffer && k < (SFreq*SNum); i++, k++ )
{ {
InputData[k] = data[i]; InputData[k] = data[i];
} }
m_bInterruptSet = true; m_bInterruptSet = true;
return 0; return 0;
} }
//////////////////////////////////////////////////////////////////////////
// EXI Mic Device
//////////////////////////////////////////////////////////////////////////
CEXIMic::CEXIMic(int _Index) CEXIMic::CEXIMic(int _Index)
{ {
Index = _Index; Index = _Index;
memset(&Status.U16, 0 , sizeof(u16));
command = 0; command = 0;
Sample = 0; Sample = 0;
m_uPosition = 0; m_uPosition = 0;
formatDelay = 0;
ID = 0x0a000000;
m_bInterruptSet = false; m_bInterruptSet = false;
MicButton = false; MicButton = false;
IsOpen = false; IsOpen = false;
Pa_Initialize(); err = Pa_Initialize();
if (err != paNoError)
LOGV(EXPANSIONINTERFACE, 0, "EXI MIC: PortAudio Initialize error %s", Pa_GetErrorText(err));
} }
CEXIMic::~CEXIMic() CEXIMic::~CEXIMic()
{ {
Pa_CloseStream( stream ); err = Pa_CloseStream( stream );
Pa_Terminate(); if (err != paNoError)
LOGV(EXPANSIONINTERFACE, 0, "EXI MIC: PortAudio Close error %s", Pa_GetErrorText(err));
err = Pa_Terminate();
if (err != paNoError)
LOGV(EXPANSIONINTERFACE, 0, "EXI MIC: PortAudio Terminate error %s", Pa_GetErrorText(err));
} }
bool CEXIMic::IsPresent() bool CEXIMic::IsPresent()
@ -132,8 +137,15 @@ void CEXIMic::SetCS(int cs)
{ {
switch (command) switch (command)
{ {
default: case cmdWakeUp:
//printf("Don't know Command %x\n", command); // 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.
// (shuffle2) Perhaps we should just clear the buffer and effectively "reset" here?
LOGV(EXPANSIONINTERFACE, 1, "EXI MIC: WakeUp cmd");
break;
default:
LOGV(EXPANSIONINTERFACE, 1, "EXI MIC: unknown CS command %02x\n", command);
break; break;
} }
} }
@ -147,7 +159,7 @@ bool CEXIMic::IsInterruptSet()
{ {
if(m_bInterruptSet) if(m_bInterruptSet)
{ {
//m_bInterruptSet = false; m_bInterruptSet = false;
return true; return true;
} }
else else
@ -158,17 +170,11 @@ bool CEXIMic::IsInterruptSet()
void CEXIMic::TransferByte(u8 &byte) void CEXIMic::TransferByte(u8 &byte)
{ {
LOGV(EXPANSIONINTERFACE, 1, "EXI MIC: > %02x", byte);
if (m_uPosition == 0) if (m_uPosition == 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.
if(command == cmdClearStatus)
{
byte = 0xFF;
m_uPosition = 0;
m_bInterruptSet = false;
}
} }
else else
{ {
@ -176,111 +182,118 @@ void CEXIMic::TransferByte(u8 &byte)
{ {
case cmdID: case cmdID:
if (m_uPosition == 1) if (m_uPosition == 1)
;//byte = 0x80; // dummy cycle ;//byte = 0x80; // dummy cycle - taken from memcard, it doesn't seem to need it here
else else
byte = (u8)(ID >> (24-(((m_uPosition-2) & 3) * 8))); byte = (u8)(EXI_DEVTYPE_MIC >> (24-(((m_uPosition-2) & 3) * 8)));
break; break;
// Setting Bits: REMEMBER THIS! D:< case cmdGetStatus:
// <ector--> var |= (1 << bitnum_from_0)
// <ector--> var &= ~(1 << bitnum_from_0) clears
case cmdStatus:
{
if(GetMic())
{ {
Status.U16 |= (1 << 7); if (m_uPosition != 1 && m_uPosition != 2)
LOGV(EXPANSIONINTERFACE, 0, "EXI MIC: WARNING GetStatus @ pos: %d should never happen", m_uPosition);
if((!Status.button && MicButton)||(Status.button && !MicButton))
LOGV(EXPANSIONINTERFACE, 0, "EXI MIC: Mic button %s", MicButton ? "pressed" : "released");
Status.button = MicButton ? 1 : 0;
byte = Status.U8[ (m_uPosition - 1) ? 0 : 1];
} }
else break;
{
Status.U16 &= ~(1 << 7);
}
byte = (u8)(Status.U16 >> (24-(((m_uPosition-2) & 3) * 8)));
}
break;
case cmdSetStatus: case cmdSetStatus:
{
Status.U8[ (m_uPosition - 1) ? 0 : 1] = byte;
if(m_uPosition == 2)
{ {
printf("Status is 0x%04x ", Status.U16); // 0x80 0xXX 0xYY
//Status is 0x7273 1 1 0 0 1 1 1 0\ 0 1 0 0 1 1 1 0 // cmd pos1 pos2
//Status is 0x4b00
// 0 0 0 0 0 0 0 0 : Bit 0-7: Unknown // Here we assign the byte to the proper place in Status and update portaudio settings
// 1 : Bit 8 : 1 : Button Pressed Status.U8[ (m_uPosition - 1) ? 0 : 1] = byte;
// 1 : Bit 9 : 1 ? Overflow?
// 0 : Bit 10 : Unknown related to 0 and 15 values It seems if(m_uPosition == 2)
// 1 0 : Bit 11-12 : Sample Rate, 00-11025, 01-22050, 10-44100, 11-??
// 0 1 : Bit 13-14 : Period Length, 00-32, 01-64, 10-128, 11-???
// 0 : Bit 15 : If We Are Sampling or Not
if((Status.U16 >> 15) & 1) // We ARE Sampling
{ {
printf("We are now Sampling"); Sampling = (Status.sampling == 1) ? true : false;
Sampling = true;
} switch (Status.sRate)
else {
{ case 0:
Sampling = false;
// Only set to false once we have run out of Data?
//m_bInterruptSet = false;
}
if(!(Status.U16 >> 11) & 1)
if((Status.U16 >> 12) & 1 )
SFreq = 22050;
else
SFreq = 11025; SFreq = 11025;
else break;
SFreq = 44100; case 1:
SFreq = 22050;
if(!(Status.U16 >> 13) & 1) break;
if((Status.U16 >> 14) & 1) case 2:
SNum = 64; SFreq = 44100;
else break;
default:
LOGV(EXPANSIONINTERFACE, 0, "EXI MIC: Trying to set unknown sampling rate");
SFreq = 44100;
break;
}
switch (Status.pLength)
{
case 0:
SNum = 32; SNum = 32;
else break;
SNum = 128; case 1:
SNum = 64;
for(int a = 0;a < 16;a++) break;
printf("%d ", (Status.U16 >> a) & 1); case 2:
printf("\n"); SNum = 128;
if(!IsOpen) break;
{ default:
// Open Our PortAudio Stream LOGV(EXPANSIONINTERFACE, 0, "EXI MIC: Trying to set unknown period length");
err = Pa_OpenDefaultStream( &stream, SNum = 128;
1, break;
0, }
paUInt8 ,
SFreq, LOGV(EXPANSIONINTERFACE, 0, "//////////////////////////////////////////////////////////////////////////");
SNum, LOGV(EXPANSIONINTERFACE, 0, "EXI MIC: Status is now 0x%04x", Status.U16);
patestCallback, LOGV(EXPANSIONINTERFACE, 0, "\tbutton %i\tsRate %i\tpLength %i\tsampling %i\n",
NULL); Status.button, Status.sRate, Status.pLength, Status.sampling);
if( err != paNoError )
printf("error %s\n", Pa_GetErrorText (err)); if(!IsOpen)
IsOpen = true; {
// Open Our PortAudio Stream
// (shuffle2) This (and the callback) are probably wrong, I can't test
err = Pa_OpenDefaultStream( &stream,
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)
{
LOGV(EXPANSIONINTERFACE, 0, "EXI MIC: PortAudio error %s", Pa_GetErrorText(err));
}
else
IsOpen = true;
}
} }
} }
} break;
break;
case cmdGetBuffer: case cmdGetBuffer:
static unsigned long At = 0; {
printf("POS %d\n", m_uPosition); static unsigned long At = 0;
// Are we not able to return all the data then? LOGV(EXPANSIONINTERFACE, 0, "EXI MIC: POS %d\n", m_uPosition);
// I think if we set the Interrupt to false, it reads another 64 // Are we not able to return all the data then?
// Will Look in to it. // I think if we set the Interrupt to false, it reads another 64
// Set to False here? Prevents lock ups maybe? // Will Look in to it.
if(At >= SNum){ // (sonicadvance1) Set to False here? Prevents lock ups maybe?
At = 0; // (shuffle2) It seems to play nice with interrupts for the most part.
k = 0; if(At >= SNum){
m_bInterruptSet = false; At = 0;
k = 0;
m_bInterruptSet = true;
}
byte = 0xAB;//InputData[At]; (shuffle2) just sending constant dummy data for now
At++;
} }
byte = InputData[At]; break;
At++;
break;
default: default:
printf("Don't know command %x in Byte transfer\n", command); LOGV(EXPANSIONINTERFACE, 0, "EXI MIC: unknown command byte %02x\n", command);
break; break;
} }
} }
m_uPosition++; m_uPosition++;
LOGV(EXPANSIONINTERFACE, 1, "EXI MIC: < %02x", byte);
} }
#endif #endif

View File

@ -30,23 +30,18 @@ public:
private: private:
enum
{
EXI_DEVTYPE_MIC = 0x0A000000
};
enum enum
{ {
cmdID = 0x00, cmdID = 0x00,
cmdStatus = 0x40, cmdGetStatus = 0x40,
cmdSetStatus = 0x80, cmdSetStatus = 0x80,
cmdGetBuffer = 0x20, cmdGetBuffer = 0x20,
cmdWriteBuffer = 0x82, cmdWakeUp = 0xFF,
cmdReadStatus = 0x83,
cmdReadID = 0x85,
cmdReadErrorBuffer = 0x86,
cmdWakeUp = 0x87,
cmdSleep = 0x88,
cmdClearStatus = 0x89,
cmdSectorErase = 0xF1,
cmdPageProgram = 0xF2,
cmdExtraByteProgram = 0xF3,
cmdChipErase = 0xF4,
}; };
// STATE_TO_SAVE // STATE_TO_SAVE
@ -56,22 +51,25 @@ private:
{ {
u16 U16; u16 U16;
u8 U8[2]; u8 U8[2];
struct
{
unsigned :8; // Unknown
unsigned button :1; // 1: Button Pressed
unsigned unk1 :1; // 1 ? Overflow?
unsigned unk2 :1; // Unknown related to 0 and 15 values It seems
unsigned sRate :2; // Sample Rate, 00-11025, 01-22050, 10-44100, 11-??
unsigned pLength :2; // Period Length, 00-32, 01-64, 10-128, 11-???
unsigned sampling :1; // If We Are Sampling or Not
};
}; };
int Index; int Index;
u32 m_uPosition; u32 m_uPosition;
u32 formatDelay; uStatus Status;
uStatus Status;
//! memory card parameters
unsigned int ID;
unsigned int address;
protected: protected:
virtual void TransferByte(u8 &byte); virtual void TransferByte(u8 &byte);
}; };
void SetMic(bool Value); void SetMic(bool Value);
bool GetMic();
#endif #endif

View File

@ -590,6 +590,7 @@
LinkIncremental="1" LinkIncremental="1"
SuppressStartupBanner="true" SuppressStartupBanner="true"
GenerateManifest="false" GenerateManifest="false"
GenerateDebugInformation="true"
ProgramDatabaseFile="$(TargetDir)$(TargetName).pdb" ProgramDatabaseFile="$(TargetDir)$(TargetName).pdb"
RandomizedBaseAddress="1" RandomizedBaseAddress="1"
DataExecutionPrevention="0" DataExecutionPrevention="0"

View File

@ -334,6 +334,8 @@ void DInput_Read(int _numPAD, SPADStatus* _pPADStatus)
if (dinput.diks[pad[_numPAD].keyForControl[CTL_DPADLEFT]] & 0xFF){_pPADStatus->button |= PAD_BUTTON_LEFT;} if (dinput.diks[pad[_numPAD].keyForControl[CTL_DPADLEFT]] & 0xFF){_pPADStatus->button |= PAD_BUTTON_LEFT;}
if (dinput.diks[pad[_numPAD].keyForControl[CTL_DPADRIGHT]]& 0xFF){_pPADStatus->button |= PAD_BUTTON_RIGHT;} if (dinput.diks[pad[_numPAD].keyForControl[CTL_DPADRIGHT]]& 0xFF){_pPADStatus->button |= PAD_BUTTON_RIGHT;}
if (dinput.diks[pad[_numPAD].keyForControl[CTL_START]] & 0xFF){_pPADStatus->button |= PAD_BUTTON_START;} if (dinput.diks[pad[_numPAD].keyForControl[CTL_START]] & 0xFF){_pPADStatus->button |= PAD_BUTTON_START;}
_pPADStatus->MicButton = (dinput.diks[pad[_numPAD].keyForControl[CTL_MIC]] & 0xFF) ? true : false;
} }
bool XInput_Read(int XPadPlayer, SPADStatus* _pPADStatus) bool XInput_Read(int XPadPlayer, SPADStatus* _pPADStatus)
@ -381,6 +383,8 @@ bool XInput_Read(int XPadPlayer, SPADStatus* _pPADStatus)
if (xpad.wButtons & XINPUT_GAMEPAD_DPAD_UP) {_pPADStatus->button |= PAD_BUTTON_UP;} if (xpad.wButtons & XINPUT_GAMEPAD_DPAD_UP) {_pPADStatus->button |= PAD_BUTTON_UP;}
if (xpad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) {_pPADStatus->button |= PAD_BUTTON_DOWN;} if (xpad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) {_pPADStatus->button |= PAD_BUTTON_DOWN;}
//_pPADStatus->MicButton = (xpad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? true : false;
return true; return true;
} }
else else
@ -742,7 +746,8 @@ void LoadConfig()
DIK_G, DIK_G,
DIK_F, DIK_F,
DIK_H, DIK_H,
DIK_LSHIFT DIK_LSHIFT, //halfpress
DIK_M //Mic
}; };
#elif defined(HAVE_X11) && HAVE_X11 #elif defined(HAVE_X11) && HAVE_X11
const int defaultKeyForControl[NUMCONTROLS] = const int defaultKeyForControl[NUMCONTROLS] =
@ -768,7 +773,7 @@ void LoadConfig()
XK_f, XK_f,
XK_h, XK_h,
XK_Shift_L, //halfpress XK_Shift_L, //halfpress
XK_p XK_p //Mic
}; };
#elif defined(HAVE_COCOA) && HAVE_COCOA #elif defined(HAVE_COCOA) && HAVE_COCOA
const int defaultKeyForControl[NUMCONTROLS] = const int defaultKeyForControl[NUMCONTROLS] =
@ -794,7 +799,7 @@ void LoadConfig()
3, 3,
4, 4,
56, //halfpress 56, //halfpress
35 35 //Mic
}; };
#endif #endif
IniFile file; IniFile file;