diff --git a/desmume/src/frontend/cocoa/ClientInputHandler.cpp b/desmume/src/frontend/cocoa/ClientInputHandler.cpp index 7d03dc54b..0d4484979 100644 --- a/desmume/src/frontend/cocoa/ClientInputHandler.cpp +++ b/desmume/src/frontend/cocoa/ClientInputHandler.cpp @@ -73,7 +73,14 @@ ClientInputHandler::ClientInputHandler() _internalNoiseGenerator = new InternalNoiseGenerator; _whiteNoiseGenerator = new WhiteNoiseGenerator; _sineWaveGenerator = new SineWaveGenerator(250.0, MIC_SAMPLE_RATE); - _selectedAudioFileGenerator = NULL; + _selectedAudioFileGenerator = NULL; // Note that this value can be NULL. + _hardwareMicSampleGenerator = _nullSampleGenerator; + + _avgMicLevel = 0.0f; + _avgMicLevelTotal = 0.0f; + _avgMicLevelsRead = 0.0f; + _isHardwareMicMuted = true; + _isHardwareMicPaused = true; _enableAutohold = false; @@ -83,7 +90,9 @@ ClientInputHandler::ClientInputHandler() _paddleValuePending = _paddleValueProcessing = _paddleValueApplied = 0; _paddleAdjustPending = _paddleAdjustProcessing = _paddleAdjustApplied = 0; - _softwareMicSampleGeneratorPending = _softwareMicSampleGeneratorProcessing = _softwareMicSampleGeneratorApplied = _nullSampleGenerator; + _softwareMicSampleGeneratorPending = _nullSampleGenerator; + _softwareMicSampleGeneratorProcessing = _nullSampleGenerator; + _softwareMicSampleGeneratorApplied = _nullSampleGenerator; memset(_clientInputPending, 0, sizeof(_clientInputPending)); memset(_clientInputProcessing, 0, sizeof(_clientInputProcessing)); @@ -257,6 +266,35 @@ void ClientInputHandler::SetSineWaveFrequency(double freq) this->_sineWaveGenerator->setFrequency(freq); } +float ClientInputHandler::GetAverageMicLevel() +{ + return this->_avgMicLevel; +} + +void ClientInputHandler::AddSampleToAverageMicLevel(uint8_t sampleValue) +{ + this->_avgMicLevelTotal += (float)( (MIC_NULL_SAMPLE_VALUE > sampleValue) ? MIC_NULL_SAMPLE_VALUE - sampleValue : sampleValue - MIC_NULL_SAMPLE_VALUE ); + this->_avgMicLevelsRead += 1.0f; + this->_avgMicLevel = this->_avgMicLevelTotal / this->_avgMicLevelsRead; +} + +void ClientInputHandler::ClearAverageMicLevel() +{ + this->_avgMicLevelTotal = 0.0f; + this->_avgMicLevelsRead = 0.0f; + this->_avgMicLevel = 0.0f; +} + +bool ClientInputHandler::IsMicrophoneIdle() +{ + return (this->_avgMicLevel < MIC_NULL_LEVEL_THRESHOLD); +} + +bool ClientInputHandler::IsMicrophoneClipping() +{ + return (this->_avgMicLevel >= MIC_CLIP_LEVEL_THRESHOLD); +} + AudioGenerator* ClientInputHandler::GetClientSoftwareMicSampleGenerator() { pthread_mutex_lock(&this->_mutexInputsPending); @@ -287,6 +325,23 @@ void ClientInputHandler::SetClientSelectedAudioFileGenerator(AudioSampleBlockGen pthread_mutex_unlock(&this->_mutexInputsPending); } +void ClientInputHandler::SetClientHardwareMicSampleGeneratorApplied(AudioGenerator *hwGenerator) +{ + if (hwGenerator == NULL) + { + this->_hardwareMicSampleGenerator = this->_nullSampleGenerator; + } + else + { + this->_hardwareMicSampleGenerator = hwGenerator; + } +} + +AudioGenerator* ClientInputHandler::GetClientHardwareMicSampleGeneratorApplied() +{ + return this->_hardwareMicSampleGenerator; +} + bool ClientInputHandler::GetClientSoftwareMicState() { pthread_mutex_lock(&this->_mutexInputsPending); @@ -611,3 +666,73 @@ void ClientInputHandler::ApplyInputs() FCEUMOV_HandleRecording(); } } + +bool ClientInputHandler::IsHardwareMicAvailable() +{ + // Do nothing. This is implementation-dependent. + return false; +} + +void ClientInputHandler::ResetHardwareMic() +{ + this->ClearAverageMicLevel(); + this->ReportAverageMicLevel(); + + this->_hardwareMicSampleGenerator->resetSamples(); +} + +uint8_t ClientInputHandler::HandleMicSampleRead() +{ + uint8_t theSample = MIC_NULL_SAMPLE_VALUE; + AudioGenerator *sampleGenerator = (this->GetClientSoftwareMicStateApplied()) ? this->GetClientSoftwareMicSampleGeneratorApplied() : this->_hardwareMicSampleGenerator; + + theSample = sampleGenerator->generateSample(); + + this->AddSampleToAverageMicLevel(theSample); + return theSample; +} + +void ClientInputHandler::ReportAverageMicLevel() +{ + // Do nothing. This is implementation-dependent. + // This method mainly exists for implementations to do some stuff during the + // emulation loop. +} + +bool ClientInputHandler::GetHardwareMicMute() +{ + return this->_isHardwareMicMuted; +} + +void ClientInputHandler::SetHardwareMicMute(bool muteState) +{ + this->_isHardwareMicMuted = muteState; + + if (muteState) + { + this->_hardwareMicSampleGenerator->resetSamples(); + this->ClearAverageMicLevel(); + } +} + +bool ClientInputHandler::GetHardwareMicPause() +{ + return this->_isHardwareMicPaused; +} + +void ClientInputHandler::SetHardwareMicPause(bool pauseState) +{ + this->_isHardwareMicPaused = pauseState; +} + +float ClientInputHandler::GetHardwareMicNormalizedGain() +{ + // Do nothing. This is implementation-dependent. + // The default value is 0.0f, which represents 0.0% of the hardware device's pickup sensitivity. + return 0.0f; +} + +void ClientInputHandler::SetHardwareMicGainAsNormalized(float normalizedGain) +{ + // Do nothing. This is implementation-dependent. +} diff --git a/desmume/src/frontend/cocoa/ClientInputHandler.h b/desmume/src/frontend/cocoa/ClientInputHandler.h index b1775f382..421016dc4 100644 --- a/desmume/src/frontend/cocoa/ClientInputHandler.h +++ b/desmume/src/frontend/cocoa/ClientInputHandler.h @@ -330,6 +330,8 @@ protected: SineWaveGenerator *_sineWaveGenerator; AudioSampleBlockGenerator *_selectedAudioFileGenerator; + AudioGenerator *_hardwareMicSampleGenerator; + ClientInput _clientInputPending[NDSInputID_InputCount]; ClientInput _clientInputProcessing[NDSInputID_InputCount]; ClientInput _clientInputApplied[NDSInputID_InputCount]; @@ -354,6 +356,12 @@ protected: int16_t _paddleValueApplied; int16_t _paddleAdjustApplied; + float _avgMicLevel; + float _avgMicLevelTotal; + float _avgMicLevelsRead; + bool _isHardwareMicMuted; + bool _isHardwareMicPaused; + pthread_mutex_t _mutexInputsPending; public: @@ -374,11 +382,21 @@ public: double GetSineWaveFrequency(); void SetSineWaveFrequency(double freq); + float GetAverageMicLevel(); + void AddSampleToAverageMicLevel(uint8_t sampleValue); + void ClearAverageMicLevel(); + + bool IsMicrophoneIdle(); + bool IsMicrophoneClipping(); + AudioGenerator* GetClientSoftwareMicSampleGenerator(); AudioGenerator* GetClientSoftwareMicSampleGeneratorApplied(); AudioSampleBlockGenerator* GetClientSelectedAudioFileGenerator(); void SetClientSelectedAudioFileGenerator(AudioSampleBlockGenerator *selectedAudioFileGenerator); + void SetClientHardwareMicSampleGeneratorApplied(AudioGenerator *hwGenerator); + AudioGenerator* GetClientHardwareMicSampleGeneratorApplied(); + bool GetClientSoftwareMicState(); bool GetClientSoftwareMicStateApplied(); void SetClientSoftwareMicState(bool pressedState, MicrophoneMode micMode); @@ -395,6 +413,20 @@ public: void ProcessInputs(); void ApplyInputs(); + + virtual bool IsHardwareMicAvailable(); + virtual void ResetHardwareMic(); + virtual uint8_t HandleMicSampleRead(); + virtual void ReportAverageMicLevel(); + + virtual bool GetHardwareMicMute(); + virtual void SetHardwareMicMute(bool muteState); + + virtual bool GetHardwareMicPause(); + virtual void SetHardwareMicPause(bool pauseState); + + virtual float GetHardwareMicNormalizedGain(); + virtual void SetHardwareMicGainAsNormalized(float normalizedGain); }; #endif // _CLIENT_INPUT_HANDLER_H_ diff --git a/desmume/src/frontend/cocoa/audiosamplegenerator.cpp b/desmume/src/frontend/cocoa/audiosamplegenerator.cpp index 89ba09e29..4da6dabff 100644 --- a/desmume/src/frontend/cocoa/audiosamplegenerator.cpp +++ b/desmume/src/frontend/cocoa/audiosamplegenerator.cpp @@ -33,6 +33,21 @@ static const uint8_t noiseSample[NUM_INTERNAL_NOISE_SAMPLES] = 0xF4, 0xE1, 0xBF, 0x9A, 0x71, 0x58, 0x5B, 0x5F, 0x62, 0xC2, 0x25, 0x05, 0x01, 0x01, 0x01, 0x01 }; +size_t AudioGenerator::resetSamples() +{ + // Do nothing. This is implementation-dependent. + // + // The return value represents the number of samples that were dropped. + // By default, return 0 to signify that no samples are dropped. In other + // words, all samples will continue to exist after the reset. + return 0; +} + +uint8_t AudioGenerator::generateSample() +{ + return MIC_NULL_SAMPLE_VALUE; +} + size_t AudioGenerator::generateSampleBlock(size_t sampleCount, uint8_t *outBuffer) { if (outBuffer == NULL) @@ -48,11 +63,6 @@ size_t AudioGenerator::generateSampleBlock(size_t sampleCount, uint8_t *outBuffe return sampleCount; } -uint8_t AudioGenerator::generateSample() -{ - return MIC_NULL_SAMPLE_VALUE; -} - AudioSampleBlockGenerator::AudioSampleBlockGenerator(const uint8_t *audioBuffer, const size_t sampleCount) { _buffer = (uint8_t *)malloc(sampleCount * sizeof(uint8_t)); diff --git a/desmume/src/frontend/cocoa/audiosamplegenerator.h b/desmume/src/frontend/cocoa/audiosamplegenerator.h index 45df90d16..6e5cf60a3 100644 --- a/desmume/src/frontend/cocoa/audiosamplegenerator.h +++ b/desmume/src/frontend/cocoa/audiosamplegenerator.h @@ -28,8 +28,9 @@ public: AudioGenerator() {}; virtual ~AudioGenerator() {}; - virtual size_t generateSampleBlock(size_t sampleCount, uint8_t *outBuffer); + virtual size_t resetSamples(); virtual uint8_t generateSample(); + virtual size_t generateSampleBlock(size_t sampleCount, uint8_t *outBuffer); }; class NullGenerator : public AudioGenerator {}; diff --git a/desmume/src/frontend/cocoa/cocoa_core.mm b/desmume/src/frontend/cocoa/cocoa_core.mm index 7cd74f0ce..bf9a88c49 100644 --- a/desmume/src/frontend/cocoa/cocoa_core.mm +++ b/desmume/src/frontend/cocoa/cocoa_core.mm @@ -870,7 +870,6 @@ volatile bool execute = true; [self setMasterExecute:YES]; [self restoreCoreState]; [[self cdsController] reset]; - [[self cdsController] updateMicLevel]; } - (void) getTimedEmulatorStatistics:(NSTimer *)timer @@ -1159,6 +1158,10 @@ static void* RunCoreThread(void *arg) NDSError ndsError = NDS_GetLastError(); pthread_mutex_unlock(¶m->mutexThreadExecute); + inputHandler->SetHardwareMicPause(true); + inputHandler->ClearAverageMicLevel(); + inputHandler->ReportAverageMicLevel(); + [cdsCore postNDSError:ndsError]; continue; } @@ -1167,9 +1170,8 @@ static void* RunCoreThread(void *arg) // of whether the NDS actually reads the mic or not. if ((ndsFrameInfo.frameIndex & 0x07) == 0x07) { - CocoaDSController *cdsController = [cdsCore cdsController]; - [cdsController updateMicLevel]; - [cdsController clearMicLevelMeasure]; + inputHandler->ReportAverageMicLevel(); + inputHandler->ClearAverageMicLevel(); } const uint8_t framesToSkip = execControl->GetFramesToSkip(); diff --git a/desmume/src/frontend/cocoa/cocoa_input.h b/desmume/src/frontend/cocoa/cocoa_input.h index ecbd1b608..a0fb849d5 100644 --- a/desmume/src/frontend/cocoa/cocoa_input.h +++ b/desmume/src/frontend/cocoa/cocoa_input.h @@ -20,6 +20,8 @@ #include #include +#include "ClientInputHandler.h" + @class CocoaDSController; class ClientInputHandler; class CoreAudioInput; @@ -45,16 +47,7 @@ class AudioSampleBlockGenerator; ClientInputHandler *inputHandler; NSInteger stylusPressure; - - float micLevel; - float _micLevelTotal; - float _micLevelsRead; - - BOOL hardwareMicMute; - size_t _availableMicSamples; - - CoreAudioInput *CAInputDevice; - + NSString *hardwareMicInfoString; NSString *hardwareMicNameString; NSString *hardwareMicManufacturerString; @@ -70,13 +63,9 @@ class AudioSampleBlockGenerator; @property (readonly) BOOL isHardwareMicIdle; @property (readonly) BOOL isHardwareMicInClip; @property (assign) float micLevel; -@property (assign) BOOL hardwareMicEnabled; -@property (readonly) BOOL hardwareMicLocked; @property (assign) float hardwareMicGain; @property (assign) BOOL hardwareMicMute; @property (assign) BOOL hardwareMicPause; -@property (readonly) CoreAudioInput *CAInputDevice; -@property (readonly) AudioGenerator *softwareMicSampleGenerator; @property (assign) AudioSampleBlockGenerator *selectedAudioFileGenerator; @property (retain) NSString *hardwareMicInfoString; @property (retain) NSString *hardwareMicNameString; @@ -91,10 +80,8 @@ class AudioSampleBlockGenerator; - (void) setSineWaveGeneratorFrequency:(const double)freq; - (void) clearAutohold; - (void) reset; +- (void) startHardwareMicDevice; -- (void) clearMicLevelMeasure; -- (void) updateMicLevel; -- (uint8_t) handleMicSampleRead:(CoreAudioInput *)caInput softwareMic:(AudioGenerator *)sampleGenerator; - (void) handleMicHardwareStateChanged:(CoreAudioInputDeviceInfo *)deviceInfo isEnabled:(BOOL)isHardwareEnabled isLocked:(BOOL)isHardwareLocked; @@ -102,6 +89,33 @@ class AudioSampleBlockGenerator; @end +class MacInputHandler : public ClientInputHandler +{ +private: + CocoaDSController *_cdsController; + CoreAudioInput *_CAInputDevice; + +public: + MacInputHandler(); + ~MacInputHandler(); + + CocoaDSController* GetCocoaController(); + void SetCocoaController(CocoaDSController *theController); + + void StartHardwareMicDevice(); + + virtual bool IsHardwareMicAvailable(); + virtual void ReportAverageMicLevel(); + + virtual void SetHardwareMicMute(bool muteState); + + virtual bool GetHardwareMicPause(); + virtual void SetHardwareMicPause(bool pauseState); + + virtual float GetHardwareMicNormalizedGain(); + virtual void SetHardwareMicGainAsNormalized(float normalizedGain); +}; + #ifdef __cplusplus extern "C" { diff --git a/desmume/src/frontend/cocoa/cocoa_input.mm b/desmume/src/frontend/cocoa/cocoa_input.mm index c0cc862a8..b1ba80e29 100644 --- a/desmume/src/frontend/cocoa/cocoa_input.mm +++ b/desmume/src/frontend/cocoa/cocoa_input.mm @@ -26,8 +26,6 @@ #include "../../slot2.h" #undef BOOL -#include "ClientInputHandler.h" - @implementation CocoaDSController @@ -39,14 +37,10 @@ @dynamic isHardwareMicAvailable; @dynamic isHardwareMicIdle; @dynamic isHardwareMicInClip; -@synthesize micLevel; -@dynamic hardwareMicEnabled; -@dynamic hardwareMicLocked; +@dynamic micLevel; @dynamic hardwareMicGain; -@synthesize hardwareMicMute; +@dynamic hardwareMicMute; @dynamic hardwareMicPause; -@synthesize CAInputDevice; -@dynamic softwareMicSampleGenerator; @dynamic selectedAudioFileGenerator; @synthesize hardwareMicInfoString; @synthesize hardwareMicNameString; @@ -62,32 +56,20 @@ } delegate = nil; - inputHandler = new ClientInputHandler; - _availableMicSamples = 0; - - micLevel = 0.0f; - _micLevelTotal = 0.0f; - _micLevelsRead = 0.0f; - - CAInputDevice = new CoreAudioInput; - CAInputDevice->SetCallbackHardwareStateChanged(&CAHardwareStateChangedCallback, self, NULL); - CAInputDevice->SetCallbackHardwareGainChanged(&CAHardwareGainChangedCallback, self, NULL); - + hardwareMicInfoString = @"No hardware input detected."; hardwareMicNameString = @"No hardware input detected."; hardwareMicManufacturerString = @"No hardware input detected."; hardwareMicSampleRateString = @"No hardware input detected."; - Mic_SetResetCallback(&CAResetCallback, self, NULL); - Mic_SetSampleReadCallback(&CASampleReadCallback, self, NULL); + inputHandler = new MacInputHandler; + ((MacInputHandler *)inputHandler)->SetCocoaController(self); return self; } - (void)dealloc { - delete CAInputDevice; - [self setDelegate:nil]; [self setHardwareMicInfoString:nil]; [self setHardwareMicNameString:nil]; @@ -104,62 +86,66 @@ - (BOOL) isHardwareMicAvailable { - return ( CAInputDevice->IsHardwareEnabled() && - !CAInputDevice->IsHardwareLocked() && - !CAInputDevice->GetPauseState() ) ? YES : NO; + return (inputHandler->IsHardwareMicAvailable()) ? YES : NO; } - (BOOL) isHardwareMicIdle { - return (micLevel < MIC_NULL_LEVEL_THRESHOLD); + return (inputHandler->IsMicrophoneIdle()) ? YES : NO; } - (BOOL) isHardwareMicInClip { - return (micLevel >= MIC_CLIP_LEVEL_THRESHOLD); + return (inputHandler->IsMicrophoneClipping()) ? YES : NO; } -- (void) setHardwareMicEnabled:(BOOL)micEnabled +- (void) setMicLevel:(float)micLevelValue { - if (micEnabled) + // This method doesn't set the mic level, since the mic level is always an internally + // calculated value. What this method actually does is trigger updates for any + // KVO-compliant controls that are associated with this property. + + if ( (delegate != nil) && [delegate respondsToSelector:@selector(doMicLevelUpdateFromController:)] ) { - CAInputDevice->Start(); - } - else - { - CAInputDevice->Stop(); + NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init]; + [[self delegate] doMicLevelUpdateFromController:self]; + [tempPool release]; } } -- (BOOL) hardwareMicEnabled +- (float) micLevel { - return (CAInputDevice->IsHardwareEnabled()) ? YES : NO; -} - -- (BOOL) hardwareMicLocked -{ - return (CAInputDevice->IsHardwareLocked()) ? YES : NO; + return inputHandler->GetAverageMicLevel(); } - (void) setHardwareMicGain:(float)micGain { - CAInputDevice->SetGain(micGain); + inputHandler->SetHardwareMicGainAsNormalized(micGain); } - (float) hardwareMicGain { - return CAInputDevice->GetGain(); + return inputHandler->GetHardwareMicNormalizedGain(); +} + +- (void) setHardwareMicMute:(BOOL)isMicMuted +{ + inputHandler->SetHardwareMicMute((isMicMuted) ? true : false); +} + +- (BOOL) hardwareMicMute +{ + return (inputHandler->GetHardwareMicMute()) ? YES : NO; } - (void) setHardwareMicPause:(BOOL)isMicPaused { - bool pauseState = (isMicPaused || [self hardwareMicMute]) ? true : false; - CAInputDevice->SetPauseState(pauseState); + inputHandler->SetHardwareMicPause((isMicPaused) ? true : false); } - (BOOL) hardwareMicPause { - return (CAInputDevice->GetPauseState()) ? YES : NO; + return (inputHandler->GetHardwareMicPause()) ? YES : NO; } - (void) setSoftwareMicState:(BOOL)theState mode:(NSInteger)micMode @@ -172,11 +158,6 @@ return (inputHandler->GetClientSoftwareMicState()) ? YES : NO; } -- (AudioGenerator *) softwareMicSampleGenerator -{ - return inputHandler->GetClientSoftwareMicSampleGenerator(); -} - - (void) setSelectedAudioFileGenerator:(AudioSampleBlockGenerator *)audioGenerator { inputHandler->SetClientSelectedAudioFileGenerator(audioGenerator); @@ -246,63 +227,13 @@ - (void) reset { [self setAutohold:NO]; - [self setMicLevel:0.0f]; - [self clearMicLevelMeasure]; - _availableMicSamples = 0; + inputHandler->ResetHardwareMic(); } -- (void) clearMicLevelMeasure +- (void) startHardwareMicDevice { - _micLevelTotal = 0.0f; - _micLevelsRead = 0.0f; -} - -- (void) updateMicLevel -{ - float avgMicLevel = (_micLevelsRead != 0) ? _micLevelTotal / _micLevelsRead : 0.0f; - - NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init]; - [self setMicLevel:avgMicLevel]; - - if (delegate != nil && [delegate respondsToSelector:@selector(doMicLevelUpdateFromController:)]) - { - [[self delegate] doMicLevelUpdateFromController:self]; - } - - [tempPool release]; -} - -- (uint8_t) handleMicSampleRead:(CoreAudioInput *)caInput softwareMic:(AudioGenerator *)sampleGenerator -{ - uint8_t theSample = MIC_NULL_SAMPLE_VALUE; - - if (!inputHandler->GetClientSoftwareMicStateApplied() && (caInput != NULL)) - { - if (caInput->GetPauseState()) - { - return theSample; - } - else - { - if (_availableMicSamples == 0) - { - _availableMicSamples = CAInputDevice->Pull(); - } - - caInput->_samplesConverted->read(&theSample, 1); - theSample >>= 1; // Samples from CoreAudio are 8-bit, so we need to convert the sample to 7-bit - _availableMicSamples--; - } - } - else - { - theSample = sampleGenerator->generateSample(); - } - - _micLevelTotal += (float)( (MIC_NULL_SAMPLE_VALUE > theSample) ? MIC_NULL_SAMPLE_VALUE - theSample : theSample - MIC_NULL_SAMPLE_VALUE ); - _micLevelsRead += 1.0f; - return theSample; + ((MacInputHandler *)inputHandler)->StartHardwareMicDevice(); } - (void) handleMicHardwareStateChanged:(CoreAudioInputDeviceInfo *)deviceInfo @@ -329,10 +260,10 @@ [self setHardwareMicSampleRateString:[NSString stringWithFormat:@"%1.1f Hz", (double)deviceInfo->sampleRate]]; } - [self clearMicLevelMeasure]; - [self setMicLevel:0.0f]; + inputHandler->ClearAverageMicLevel(); + inputHandler->ReportAverageMicLevel(); - if (delegate != nil && [delegate respondsToSelector:@selector(doMicHardwareStateChangedFromController:isEnabled:isLocked:)]) + if ( (delegate != nil) && [delegate respondsToSelector:@selector(doMicHardwareStateChangedFromController:isEnabled:isLocked:)] ) { [[self delegate] doMicHardwareStateChangedFromController:self isEnabled:isHardwareEnabled @@ -344,7 +275,7 @@ - (void) handleMicHardwareGainChanged:(float)gainValue { - if (delegate != nil && [delegate respondsToSelector:@selector(doMicHardwareGainChangedFromController:gain:)]) + if ( (delegate != nil) && [delegate respondsToSelector:@selector(doMicHardwareGainChangedFromController:gain:)] ) { NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init]; [[self delegate] doMicHardwareGainChangedFromController:self gain:gainValue]; @@ -354,16 +285,105 @@ @end +MacInputHandler::MacInputHandler() +{ + _cdsController = nil; + _CAInputDevice = new CoreAudioInput; + _hardwareMicSampleGenerator = _CAInputDevice; + _isHardwareMicMuted = false; + + _CAInputDevice->SetCallbackHardwareStateChanged(&CAHardwareStateChangedCallback, _cdsController, NULL); + _CAInputDevice->SetCallbackHardwareGainChanged(&CAHardwareGainChangedCallback, _cdsController, NULL); + + Mic_SetResetCallback(&CAResetCallback, _CAInputDevice, NULL); + Mic_SetSampleReadCallback(&CASampleReadCallback, this, NULL); +} + +MacInputHandler::~MacInputHandler() +{ + delete this->_CAInputDevice; +} + +CocoaDSController* MacInputHandler::GetCocoaController() +{ + return this->_cdsController; +} + +void MacInputHandler::SetCocoaController(CocoaDSController *theController) +{ + this->_cdsController = theController; + this->_CAInputDevice->SetCallbackHardwareStateChanged(&CAHardwareStateChangedCallback, theController, NULL); + this->_CAInputDevice->SetCallbackHardwareGainChanged(&CAHardwareGainChangedCallback, theController, NULL); +} + +void MacInputHandler::StartHardwareMicDevice() +{ + this->_CAInputDevice->Start(); +} + +bool MacInputHandler::IsHardwareMicAvailable() +{ + return ( this->_CAInputDevice->IsHardwareEnabled() && !this->_CAInputDevice->IsHardwareLocked() ); +} + +void MacInputHandler::ReportAverageMicLevel() +{ + [this->_cdsController setMicLevel:this->GetAverageMicLevel()]; +} + +void MacInputHandler::SetHardwareMicMute(bool muteState) +{ + const bool needSetMuteState = (this->_isHardwareMicMuted != muteState); + + if (needSetMuteState) + { + if (muteState) + { + this->_hardwareMicSampleGenerator->resetSamples(); + this->ClearAverageMicLevel(); + } + + this->_isHardwareMicMuted = muteState; + this->_CAInputDevice->SetPauseState(this->_isHardwareMicPaused || muteState); + } +} + +bool MacInputHandler::GetHardwareMicPause() +{ + return this->_CAInputDevice->GetPauseState(); +} + +void MacInputHandler::SetHardwareMicPause(bool pauseState) +{ + const bool needSetPauseState = (this->_isHardwareMicPaused != pauseState); + + if (needSetPauseState) + { + this->_isHardwareMicPaused = pauseState; + this->_CAInputDevice->SetPauseState(pauseState || this->_isHardwareMicMuted); + } +} + +float MacInputHandler::GetHardwareMicNormalizedGain() +{ + return this->_CAInputDevice->GetNormalizedGain(); +} + +void MacInputHandler::SetHardwareMicGainAsNormalized(float normalizedGain) +{ + this->_CAInputDevice->SetGainAsNormalized(normalizedGain); +} + void CAResetCallback(void *inParam1, void *inParam2) { - CocoaDSController *cdsController = (CocoaDSController *)inParam1; - [cdsController CAInputDevice]->Start(); + CoreAudioInput *caInputDevice = (CoreAudioInput *)inParam1; + caInputDevice->Start(); } uint8_t CASampleReadCallback(void *inParam1, void *inParam2) { - CocoaDSController *cdsController = (CocoaDSController *)inParam1; - return [cdsController handleMicSampleRead:[cdsController CAInputDevice] softwareMic:[cdsController softwareMicSampleGenerator]]; + ClientInputHandler *inputHandler = (ClientInputHandler *)inParam1; + return inputHandler->HandleMicSampleRead(); } void CAHardwareStateChangedCallback(CoreAudioInputDeviceInfo *deviceInfo, diff --git a/desmume/src/frontend/cocoa/coreaudiosound.cpp b/desmume/src/frontend/cocoa/coreaudiosound.cpp index 1d21ab69a..e428772ee 100644 --- a/desmume/src/frontend/cocoa/coreaudiosound.cpp +++ b/desmume/src/frontend/cocoa/coreaudiosound.cpp @@ -47,7 +47,7 @@ CoreAudioInput::CoreAudioInput() _hwDeviceInfo.deviceUID = CFSTR(""); _hwDeviceInfo.modelUID = CFSTR(""); _hwDeviceInfo.sampleRate = 0.0; - _isPaused = false; + _isPaused = true; _isHardwareEnabled = false; _isHardwareLocked = true; _captureFrames = 0; @@ -602,12 +602,12 @@ void CoreAudioInput::SetPauseState(bool pauseState) this->_isPaused = (this->IsHardwareLocked()) ? true : pauseState; } -float CoreAudioInput::GetGain() const +float CoreAudioInput::GetNormalizedGain() const { return this->_inputGainNormalized; } -void CoreAudioInput::SetGain(float normalizedGain) +void CoreAudioInput::SetGainAsNormalized(float normalizedGain) { Float32 gainValue = normalizedGain; UInt32 gainPropSize = sizeof(gainValue); @@ -698,6 +698,36 @@ void CoreAudioInput::UpdateHardwareLock() this->_hwStateChangedCallbackParam2); } +size_t CoreAudioInput::resetSamples() +{ + size_t samplesToDrop = this->_samplesConverted->getUsedElements(); + this->_samplesConverted->clear(); + + return samplesToDrop; +} + +uint8_t CoreAudioInput::generateSample() +{ + uint8_t theSample = MIC_NULL_SAMPLE_VALUE; + + if (this->_isPaused) + { + return theSample; + } + else + { + if (this->_samplesConverted->getUsedElements() == 0) + { + this->Pull(); + } + + this->_samplesConverted->read(&theSample, 1); + theSample >>= 1; // Samples from CoreAudio are 8-bit, so we need to convert the sample to 7-bit + } + + return theSample; +} + void CoreAudioInput::SetCallbackHardwareStateChanged(CoreAudioInputHardwareStateChangedCallback callbackFunc, void *inParam1, void *inParam2) { this->_hwStateChangedCallbackFunc = callbackFunc; diff --git a/desmume/src/frontend/cocoa/coreaudiosound.h b/desmume/src/frontend/cocoa/coreaudiosound.h index 207a49ab9..e51835e16 100644 --- a/desmume/src/frontend/cocoa/coreaudiosound.h +++ b/desmume/src/frontend/cocoa/coreaudiosound.h @@ -22,8 +22,9 @@ #include #include #include -#include "ringbuffer.h" +#include "ringbuffer.h" +#include "audiosamplegenerator.h" struct CoreAudioInputDeviceInfo { @@ -44,7 +45,7 @@ typedef void (*CoreAudioInputHardwareStateChangedCallback)(CoreAudioInputDeviceI typedef void (*CoreAudioInputHardwareGainChangedCallback)(float normalizedGain, void *inParam1, void *inParam2); -class CoreAudioInput +class CoreAudioInput : public AudioGenerator { private: OSSpinLock *_spinlockAUHAL; @@ -65,6 +66,8 @@ private: AudioBufferList *_convertBufferList; UInt32 _captureFrames; + RingBuffer *_samplesConverted; + float _inputGainNormalized; AudioUnitElement _inputElement; @@ -80,10 +83,9 @@ public: AudioTimeStamp _timeStamp; AudioBufferList *_captureBufferList; RingBuffer *_samplesCaptured; - RingBuffer *_samplesConverted; CoreAudioInput(); - ~CoreAudioInput(); + virtual ~CoreAudioInput(); void Start(); void Stop(); @@ -93,13 +95,16 @@ public: bool IsHardwareLocked() const; bool GetPauseState() const; void SetPauseState(bool pauseState); - float GetGain() const; - void SetGain(float normalizedGain); + float GetNormalizedGain() const; + void SetGainAsNormalized(float normalizedGain); void UpdateHardwareGain(float normalizedGain); void UpdateHardwareLock(); void SetCallbackHardwareStateChanged(CoreAudioInputHardwareStateChangedCallback callbackFunc, void *inParam1, void *inParam2); void SetCallbackHardwareGainChanged(CoreAudioInputHardwareGainChangedCallback callbackFunc, void *inParam1, void *inParam2); + + virtual size_t resetSamples(); + virtual uint8_t generateSample(); }; class CoreAudioOutput diff --git a/desmume/src/frontend/cocoa/userinterface/EmuControllerDelegate.mm b/desmume/src/frontend/cocoa/userinterface/EmuControllerDelegate.mm index f2648b5ba..4a8715738 100644 --- a/desmume/src/frontend/cocoa/userinterface/EmuControllerDelegate.mm +++ b/desmume/src/frontend/cocoa/userinterface/EmuControllerDelegate.mm @@ -810,7 +810,6 @@ const BOOL muteState = [CocoaDSUtil getIBActionSenderButtonStateBool:sender]; CocoaDSCore *cdsCore = (CocoaDSCore *)[cdsCoreController content]; [[cdsCore cdsController] setHardwareMicMute:muteState]; - [[cdsCore cdsController] setHardwareMicPause:([cdsCore coreState] != ExecutionBehavior_Run)]; [self updateMicStatusIcon]; } @@ -1911,7 +1910,6 @@ [cdsCore setSlot1StatusText:NSSTRING_STATUS_EMULATION_NOT_RUNNING]; [[cdsCore cdsController] reset]; - [[cdsCore cdsController] updateMicLevel]; result = YES; @@ -2016,7 +2014,11 @@ { if ([cdsController isHardwareMicAvailable]) { - if ([cdsController isHardwareMicInClip]) + if ([cdsController hardwareMicPause]) + { + micIcon = iconMicDisabled; + } + else if ([cdsController isHardwareMicInClip]) { micIcon = iconMicInClip; } diff --git a/desmume/src/frontend/cocoa/userinterface/appDelegate.mm b/desmume/src/frontend/cocoa/userinterface/appDelegate.mm index 2f751d227..a7475d1b6 100644 --- a/desmume/src/frontend/cocoa/userinterface/appDelegate.mm +++ b/desmume/src/frontend/cocoa/userinterface/appDelegate.mm @@ -186,7 +186,7 @@ // Init the DS controller. [[newCore cdsController] setDelegate:emuControl]; - [[newCore cdsController] setHardwareMicEnabled:YES]; + [[newCore cdsController] startHardwareMicDevice]; // Init the DS speakers. CocoaDSSpeaker *newSpeaker = [[[CocoaDSSpeaker alloc] init] autorelease]; @@ -207,7 +207,6 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - CocoaDSCore *cdsCore = (CocoaDSCore *)[cdsCoreController content]; EmuControllerDelegate *emuControl = (EmuControllerDelegate *)[emuControlController content]; // Determine if the app was run for the first time. @@ -237,11 +236,10 @@ [[inputPrefsView inputPrefOutlineView] expandItem:nil expandChildren:YES]; [[inputPrefsView inputProfileMenu] selectItemAtIndex:0]; - // Make sure that the mic is paused to start with. - [[cdsCore cdsController] setHardwareMicPause:YES]; + // Initialize the microphone status icon. [emuControl updateMicStatusIcon]; - //Bring the application to the front + // Bring the application to the front [NSApp activateIgnoringOtherApps:YES]; [self restoreDisplayWindowStates];