diff --git a/cmake/Xcode_iOS/CMakeLists.txt b/cmake/Xcode_iOS/CMakeLists.txt index 85af251af1..f3b20714cf 100644 --- a/cmake/Xcode_iOS/CMakeLists.txt +++ b/cmake/Xcode_iOS/CMakeLists.txt @@ -32,7 +32,7 @@ else() ADD_SYSTEM(MENGINE_SYSTEM_ALLOCATOR POSIXAllocatorSystem "MENGINE_SYSTEM_ALLOCATOR") endif() -ADD_SYSTEM(MENGINE_SYSTEM_SOUND OpenALSoundSystem "MENGINE_SYSTEM_SOUND") +ADD_SYSTEM(MENGINE_SYSTEM_SOUND IOSAVSoundSystem "MENGINE_SYSTEM_SOUND") ADD_SYSTEM(MENGINE_SYSTEM_FILE AppleFileSystem "MENGINE_SYSTEM_FILE") ADD_SYSTEM(MENGINE_SYSTEM_DATETIME POSIXDateTimeSystem "MENGINE_SYSTEM_DATETIME") ADD_SYSTEM(MENGINE_SYSTEM_UNICODE NativeUnicodeSystem "MENGINE_SYSTEM_UNICODE") diff --git a/cmake/Xcode_iOS_Simulator/CMakeLists.txt b/cmake/Xcode_iOS_Simulator/CMakeLists.txt index 5ba141f9eb..641388bab5 100644 --- a/cmake/Xcode_iOS_Simulator/CMakeLists.txt +++ b/cmake/Xcode_iOS_Simulator/CMakeLists.txt @@ -32,7 +32,7 @@ else() ADD_SYSTEM(MENGINE_SYSTEM_ALLOCATOR POSIXAllocatorSystem "MENGINE_SYSTEM_ALLOCATOR") endif() -ADD_SYSTEM(MENGINE_SYSTEM_SOUND OpenALSoundSystem "MENGINE_SYSTEM_SOUND") +ADD_SYSTEM(MENGINE_SYSTEM_SOUND IOSAVSoundSystem "MENGINE_SYSTEM_SOUND") ADD_SYSTEM(MENGINE_SYSTEM_DATETIME POSIXDateTimeSystem "MENGINE_SYSTEM_DATETIME") ADD_SYSTEM(MENGINE_SYSTEM_UNICODE NativeUnicodeSystem "MENGINE_SYSTEM_UNICODE") ADD_SYSTEM(MENGINE_SYSTEM_THREAD SDL2ThreadSystem "MENGINE_SYSTEM_THREAD") diff --git a/src/Systems/IOSAVSoundSystem/CMakeLists.txt b/src/Systems/IOSAVSoundSystem/CMakeLists.txt new file mode 100644 index 0000000000..7923197af6 --- /dev/null +++ b/src/Systems/IOSAVSoundSystem/CMakeLists.txt @@ -0,0 +1,17 @@ +MENGINE_PROJECT(IOSAVSoundSystem) + +ADD_FILTER( + src + IOSAVSoundSystem.h + IOSAVSoundSystem.mm + IOSAVSoundBuffer.h + IOSAVSoundBuffer.mm + IOSAVSoundSource.h + IOSAVSoundSource.mm +) + +ADD_MENGINE_LIBRARY(Systems) + +IF(MENGINE_TARGET_IOS) + TARGET_LINK_LIBRARIES(${PROJECT_NAME} "-framework AVFoundation" "-framework AudioToolbox" "-framework CoreAudio" ) +ENDIF() diff --git a/src/Systems/IOSAVSoundSystem/IOSAVSoundBuffer.h b/src/Systems/IOSAVSoundSystem/IOSAVSoundBuffer.h new file mode 100644 index 0000000000..2abb19c1ea --- /dev/null +++ b/src/Systems/IOSAVSoundSystem/IOSAVSoundBuffer.h @@ -0,0 +1,52 @@ +#pragma once + +#include "Interface/SoundBufferInterface.h" +#include "Interface/SoundCodecInterface.h" + +#include "Kernel/Factorable.h" +#include "Kernel/MemoryInterfacePtr.h" + +namespace Mengine +{ + class IOSAVSoundSystem; + + class IOSAVSoundBuffer + : public SoundBufferInterface + , public Factorable + { + DECLARE_FACTORABLE( IOSAVSoundBuffer ); + + public: + IOSAVSoundBuffer(); + ~IOSAVSoundBuffer() override; + + public: + void initialize( IOSAVSoundSystem * _soundSystem, const SoundDecoderInterfacePtr & _decoder, bool _streamable ); + + public: + bool acquireSoundBuffer() override; + void releaseSoundBuffer() override; + bool updateSoundBuffer() override; + const SoundDecoderInterfacePtr & getDecoder() const override; + + public: + const SoundCodecDataInfo & getDataInfo() const; + const MemoryInterfacePtr & getMemory() const; + bool isStreamable() const; + + public: + void * createNativePCMBuffer( float _startPositionMs ) const; + + protected: + IOSAVSoundSystem * m_soundSystem; + SoundDecoderInterfacePtr m_soundDecoder; + + MemoryInterfacePtr m_memory; + SoundCodecDataInfo m_dataInfo; + + bool m_streamable; + }; + + typedef IntrusivePtr IOSAVSoundBufferPtr; +} + diff --git a/src/Systems/IOSAVSoundSystem/IOSAVSoundBuffer.mm b/src/Systems/IOSAVSoundSystem/IOSAVSoundBuffer.mm new file mode 100644 index 0000000000..c2990a573f --- /dev/null +++ b/src/Systems/IOSAVSoundSystem/IOSAVSoundBuffer.mm @@ -0,0 +1,192 @@ +#import "IOSAVSoundBuffer.h" +#import "IOSAVSoundSystem.h" + +#import "Kernel/MemoryStreamHelper.h" +#import "Kernel/AssertionMemoryPanic.h" +#import "Kernel/Logger.h" + +#import + +namespace Mengine +{ + ////////////////////////////////////////////////////////////////////////// + IOSAVSoundBuffer::IOSAVSoundBuffer() + : m_soundSystem( nullptr ) + , m_streamable( false ) + { + m_dataInfo = SoundCodecDataInfo(); + } + ////////////////////////////////////////////////////////////////////////// + IOSAVSoundBuffer::~IOSAVSoundBuffer() + { + this->releaseSoundBuffer(); + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundBuffer::initialize( IOSAVSoundSystem * _soundSystem, const SoundDecoderInterfacePtr & _decoder, bool _streamable ) + { + m_soundSystem = _soundSystem; + m_soundDecoder = _decoder; + m_streamable = _streamable; + + if( m_streamable == false ) + { + this->acquireSoundBuffer(); + } + } + ////////////////////////////////////////////////////////////////////////// + bool IOSAVSoundBuffer::acquireSoundBuffer() + { + if( m_memory != nullptr ) + { + return true; + } + + if( m_soundDecoder == nullptr ) + { + return false; + } + + const SoundCodecDataInfo * dataInfo = m_soundDecoder->getCodecDataInfo(); + + if( dataInfo == nullptr ) + { + LOGGER_ERROR( "invalid sound decoder info" ); + + return false; + } + + m_dataInfo = *dataInfo; + + size_t size = dataInfo->size; + + MemoryInterfacePtr memory = Helper::createMemoryCacheBuffer( size, MENGINE_DOCUMENT_FACTORABLE ); + + MENGINE_ASSERTION_MEMORY_PANIC( memory, "invalid sound memory" ); + + void * buffer = memory->getBuffer(); + + SoundDecoderData data; + data.buffer = buffer; + data.size = size; + + size_t decode = m_soundDecoder->decode( &data ); + + if( decode == 0 ) + { + LOGGER_ERROR( "failed decode sound" ); + + return false; + } + + m_memory = memory; + + return true; + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundBuffer::releaseSoundBuffer() + { + m_memory = nullptr; + } + ////////////////////////////////////////////////////////////////////////// + bool IOSAVSoundBuffer::updateSoundBuffer() + { + return false; + } + ////////////////////////////////////////////////////////////////////////// + const SoundDecoderInterfacePtr & IOSAVSoundBuffer::getDecoder() const + { + return m_soundDecoder; + } + ////////////////////////////////////////////////////////////////////////// + const SoundCodecDataInfo & IOSAVSoundBuffer::getDataInfo() const + { + return m_dataInfo; + } + ////////////////////////////////////////////////////////////////////////// + const MemoryInterfacePtr & IOSAVSoundBuffer::getMemory() const + { + return m_memory; + } + ////////////////////////////////////////////////////////////////////////// + bool IOSAVSoundBuffer::isStreamable() const + { + return m_streamable; + } + ////////////////////////////////////////////////////////////////////////// + void * IOSAVSoundBuffer::createNativePCMBuffer( float _startPositionMs ) const + { + if( m_memory == nullptr ) + { + return nullptr; + } + + AVAudioFormat * format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatInt16 sampleRate:m_dataInfo.frequency channels:m_dataInfo.channels interleaved:YES]; + + if( format == nil ) + { + LOGGER_ERROR( "invalid create audio format" ); + + return nullptr; + } + + UInt32 bytesPerFrame = format.streamDescription->mBytesPerFrame; + + if( bytesPerFrame == 0 ) + { + LOGGER_ERROR( "invalid bytes per frame" ); + + return nullptr; + } + + AVAudioFrameCount frameCapacity = (AVAudioFrameCount)(m_dataInfo.size / bytesPerFrame); + + AVAudioPCMBuffer * buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:frameCapacity]; + + if( buffer == nil ) + { + LOGGER_ERROR( "invalid create audio pcm buffer" ); + + return nullptr; + } + + AudioBufferList * audioBufferList = buffer.audioBufferList; + + if( audioBufferList->mNumberBuffers == 0 ) + { + LOGGER_ERROR( "invalid audio buffer list" ); + + return nullptr; + } + + AudioBuffer & audioBuffer = audioBufferList->mBuffers[0]; + + uint8_t * memoryBuffer = static_cast( m_memory->getBuffer() ); + + uint32_t startFrame = (uint32_t)((_startPositionMs / 1000.f) * m_dataInfo.frequency); + uint32_t bytesPerFrame = format.streamDescription->mBytesPerFrame; + uint32_t startOffset = startFrame * bytesPerFrame; + + if( startOffset >= m_dataInfo.size ) + { + startOffset = (uint32_t)m_dataInfo.size; + } + + uint32_t copySize = (uint32_t)m_dataInfo.size - startOffset; + + if( copySize == 0 ) + { + return nullptr; + } + + memcpy( audioBuffer.mData, memoryBuffer + startOffset, copySize ); + + audioBuffer.mDataByteSize = copySize; + + AVAudioFrameCount frameLength = copySize / bytesPerFrame; + + buffer.frameLength = frameLength; + + return (__bridge_retained void *)buffer; + } + ////////////////////////////////////////////////////////////////////////// +} diff --git a/src/Systems/IOSAVSoundSystem/IOSAVSoundSource.h b/src/Systems/IOSAVSoundSystem/IOSAVSoundSource.h new file mode 100644 index 0000000000..f94fcb3a9b --- /dev/null +++ b/src/Systems/IOSAVSoundSystem/IOSAVSoundSource.h @@ -0,0 +1,74 @@ +#pragma once + +#include "Interface/SoundSourceInterface.h" + +#include "Kernel/Factorable.h" + +#include "IOSAVSoundBuffer.h" + +namespace Mengine +{ + class IOSAVSoundSystem; + + class IOSAVSoundSource + : public SoundSourceInterface + , public Factorable + { + DECLARE_FACTORABLE( IOSAVSoundSource ); + + public: + IOSAVSoundSource(); + ~IOSAVSoundSource() override; + + public: + void initialize( IOSAVSoundSystem * _soundSystem, bool _headMode ); + + public: + bool play() override; + void stop() override; + void pause() override; + bool resume() override; + + public: + bool isPlay() const override; + bool isPause() const override; + + public: + void setVolume( float _volume ) override; + float getVolume() const override; + + void setLoop( bool _loop ) override; + bool getLoop() const override; + + float getDuration() const override; + bool setPosition( float _position ) override; + float getPosition() const override; + + public: + void setSoundBuffer( const SoundBufferInterfacePtr & _soundBuffer ) override; + const SoundBufferInterfacePtr & getSoundBuffer() const override; + + protected: + void ensurePlayer_(); + void releasePlayer_(); + void releaseCurrentBuffer_(); + + protected: + IOSAVSoundSystem * m_soundSystem; + IOSAVSoundBufferPtr m_soundBuffer; + + bool m_headMode; + bool m_playing; + bool m_paused; + bool m_loop; + float m_volume; + float m_position; + float m_startPosition; + + void * m_playerNode; + void * m_currentBuffer; + }; + + typedef IntrusivePtr IOSAVSoundSourcePtr; +} + diff --git a/src/Systems/IOSAVSoundSystem/IOSAVSoundSource.mm b/src/Systems/IOSAVSoundSystem/IOSAVSoundSource.mm new file mode 100644 index 0000000000..4b4226d3f2 --- /dev/null +++ b/src/Systems/IOSAVSoundSystem/IOSAVSoundSource.mm @@ -0,0 +1,356 @@ +#import "IOSAVSoundSource.h" +#import "IOSAVSoundSystem.h" + +#import "Kernel/Logger.h" +#import "Config/StdMath.h" + +#import + +namespace Mengine +{ + ////////////////////////////////////////////////////////////////////////// + IOSAVSoundSource::IOSAVSoundSource() + : m_soundSystem( nullptr ) + , m_headMode( true ) + , m_playing( false ) + , m_paused( false ) + , m_loop( false ) + , m_volume( 1.f ) + , m_position( 0.f ) + , m_startPosition( 0.f ) + , m_playerNode( nullptr ) + , m_currentBuffer( nullptr ) + { + } + ////////////////////////////////////////////////////////////////////////// + IOSAVSoundSource::~IOSAVSoundSource() + { + this->stop(); + this->releasePlayer_(); + this->releaseCurrentBuffer_(); + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSource::initialize( IOSAVSoundSystem * _soundSystem, bool _headMode ) + { + m_soundSystem = _soundSystem; + m_headMode = _headMode; + m_volume = 1.f; + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSource::ensurePlayer_() + { + if( m_playerNode != nullptr ) + { + return; + } + + m_soundSystem->startEngine(); + + AVAudioEngine * engine = (__bridge AVAudioEngine *)m_soundSystem->getEngine(); + + if( engine == nil ) + { + LOGGER_ERROR( "invalid audio engine" ); + return; + } + + AVAudioPlayerNode * player = [[AVAudioPlayerNode alloc] init]; + + [engine attachNode:player]; + [engine connect:player to:engine.mainMixerNode format:nil]; + [player setVolume:m_volume]; + + m_playerNode = (__bridge_retained void *)player; + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSource::releasePlayer_() + { + if( m_playerNode == nullptr ) + { + return; + } + + AVAudioEngine * engine = (__bridge AVAudioEngine *)m_soundSystem->getEngine(); + AVAudioPlayerNode * player = (__bridge_transfer AVAudioPlayerNode *)m_playerNode; + + if( engine != nil ) + { + [engine detachNode:player]; + } + + m_playerNode = nullptr; + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSource::releaseCurrentBuffer_() + { + if( m_currentBuffer == nullptr ) + { + return; + } + + CFRelease( (CFTypeRef)m_currentBuffer ); + m_currentBuffer = nullptr; + } + ////////////////////////////////////////////////////////////////////////// + bool IOSAVSoundSource::play() + { + if( m_soundBuffer == nullptr ) + { + LOGGER_ERROR( "sound source invalid buffer" ); + + return false; + } + + if( m_soundBuffer->acquireSoundBuffer() == false ) + { + LOGGER_ERROR( "sound source invalid acquire buffer" ); + + return false; + } + + this->ensurePlayer_(); + + if( m_playerNode == nullptr ) + { + return false; + } + + AVAudioPlayerNode * player = (__bridge AVAudioPlayerNode *)m_playerNode; + + this->releaseCurrentBuffer_(); + + void * nativeBufferPtr = m_soundBuffer->createNativePCMBuffer( m_position ); + + if( nativeBufferPtr == nullptr ) + { + LOGGER_ERROR( "sound source invalid native buffer" ); + + return false; + } + + m_currentBuffer = nativeBufferPtr; + + AVAudioPCMBuffer * nativeBuffer = (__bridge AVAudioPCMBuffer *)nativeBufferPtr; + + AVAudioPlayerNodeBufferOptions options = m_loop ? AVAudioPlayerNodeBufferLoops : 0; + + [player scheduleBuffer:nativeBuffer atTime:nil options:options completionHandler:nil]; + + [player setVolume:m_volume]; + + if( player.isPlaying == false ) + { + [player play]; + } + + m_startPosition = m_position; + m_playing = true; + m_paused = false; + + return true; + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSource::stop() + { + if( m_playerNode != nullptr ) + { + AVAudioPlayerNode * player = (__bridge AVAudioPlayerNode *)m_playerNode; + + if( player.isPlaying == true || m_paused == true ) + { + [player stop]; + } + } + + this->releaseCurrentBuffer_(); + + m_playing = false; + m_paused = false; + m_position = 0.f; + m_startPosition = 0.f; + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSource::pause() + { + if( m_playing == false ) + { + return; + } + + if( m_playerNode == nullptr ) + { + return; + } + + AVAudioPlayerNode * player = (__bridge AVAudioPlayerNode *)m_playerNode; + + float currentPosition = this->getPosition(); + + [player pause]; + + m_position = currentPosition; + m_startPosition = m_position; + m_playing = false; + m_paused = true; + } + ////////////////////////////////////////////////////////////////////////// + bool IOSAVSoundSource::resume() + { + if( m_paused == false ) + { + return false; + } + + if( m_playerNode == nullptr ) + { + return false; + } + + AVAudioPlayerNode * player = (__bridge AVAudioPlayerNode *)m_playerNode; + + [player play]; + + m_playing = true; + m_paused = false; + + return true; + } + ////////////////////////////////////////////////////////////////////////// + bool IOSAVSoundSource::isPlay() const + { + return m_playing; + } + ////////////////////////////////////////////////////////////////////////// + bool IOSAVSoundSource::isPause() const + { + return m_paused; + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSource::setVolume( float _volume ) + { + m_volume = _volume; + + if( m_playerNode == nullptr ) + { + return; + } + + AVAudioPlayerNode * player = (__bridge AVAudioPlayerNode *)m_playerNode; + [player setVolume:_volume]; + } + ////////////////////////////////////////////////////////////////////////// + float IOSAVSoundSource::getVolume() const + { + return m_volume; + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSource::setLoop( bool _loop ) + { + m_loop = _loop; + } + ////////////////////////////////////////////////////////////////////////// + bool IOSAVSoundSource::getLoop() const + { + return m_loop; + } + ////////////////////////////////////////////////////////////////////////// + float IOSAVSoundSource::getDuration() const + { + if( m_soundBuffer == nullptr ) + { + return 0.f; + } + + const SoundCodecDataInfo & dataInfo = m_soundBuffer->getDataInfo(); + + return dataInfo.duration; + } + ////////////////////////////////////////////////////////////////////////// + bool IOSAVSoundSource::setPosition( float _position ) + { + if( mt::equal_f_f( m_position, _position ) == true ) + { + return true; + } + + m_position = _position; + m_startPosition = _position; + + if( m_playing == false && m_paused == false ) + { + return true; + } + + bool wasPaused = m_paused; + + this->stop(); + + m_position = _position; + m_startPosition = _position; + + if( wasPaused == true ) + { + if( this->play() == false ) + { + return false; + } + + this->pause(); + + return true; + } + + return this->play(); + } + ////////////////////////////////////////////////////////////////////////// + float IOSAVSoundSource::getPosition() const + { + if( m_playing == false ) + { + return m_position; + } + + if( m_playerNode == nullptr ) + { + return m_position; + } + + AVAudioPlayerNode * player = (__bridge AVAudioPlayerNode *)m_playerNode; + + AVAudioTime * lastTime = player.lastRenderTime; + + if( lastTime == nil ) + { + return m_position; + } + + AVAudioTime * playerTime = [player playerTimeForNodeTime:lastTime]; + + if( playerTime == nil ) + { + return m_position; + } + + if( playerTime.sampleRate <= 0 ) + { + return m_position; + } + + double seconds = (double)playerTime.sampleTime / playerTime.sampleRate; + + return m_startPosition + (float)(seconds * 1000.0); + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSource::setSoundBuffer( const SoundBufferInterfacePtr & _soundBuffer ) + { + this->releaseCurrentBuffer_(); + + m_soundBuffer = IOSAVSoundBufferPtr::from( _soundBuffer ); + } + ////////////////////////////////////////////////////////////////////////// + const SoundBufferInterfacePtr & IOSAVSoundSource::getSoundBuffer() const + { + return m_soundBuffer; + } + ////////////////////////////////////////////////////////////////////////// +} diff --git a/src/Systems/IOSAVSoundSystem/IOSAVSoundSystem.h b/src/Systems/IOSAVSoundSystem/IOSAVSoundSystem.h new file mode 100644 index 0000000000..72fec7e5b4 --- /dev/null +++ b/src/Systems/IOSAVSoundSystem/IOSAVSoundSystem.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Interface/SoundSystemInterface.h" +#include "Interface/FactoryInterface.h" + +#include "Kernel/ServiceBase.h" +#include "Kernel/Factorable.h" +#include "Kernel/Unknowable.h" + +namespace Mengine +{ + class IOSAVSoundSystem; + class IOSAVSoundSource; + class IOSAVSoundBuffer; + + typedef IntrusivePtr IOSAVSoundBufferPtr; + typedef IntrusivePtr IOSAVSoundSourcePtr; + + class IOSAVSoundSystem + : public ServiceBase + { + DECLARE_UNKNOWABLE(); + + public: + IOSAVSoundSystem(); + ~IOSAVSoundSystem() override; + + public: + bool _initializeService() override; + void _finalizeService() override; + + public: + void update() override; + + public: + bool isSilent() const override; + + public: + void onTurnSound( bool _turn ) override; + + public: + SoundBufferInterfacePtr createSoundBuffer( const SoundDecoderInterfacePtr & _decoder, bool _streamable, const DocumentInterfacePtr & _doc ) override; + SoundSourceInterfacePtr createSoundSource( bool _isHeadMode, const SoundBufferInterfacePtr & _sample, const DocumentInterfacePtr & _doc ) override; + + public: + void startEngine(); + void stopEngine(); + + public: + void setInterrupted( bool _interrupted ); + bool isInterrupted() const; + + public: + void * getEngine() const; + + protected: + bool m_engineStarted; + bool m_interrupted; + + void * m_engine; + + FactoryInterfacePtr m_factorySoundBuffer; + FactoryInterfacePtr m_factorySoundSource; + }; +} + diff --git a/src/Systems/IOSAVSoundSystem/IOSAVSoundSystem.mm b/src/Systems/IOSAVSoundSystem/IOSAVSoundSystem.mm new file mode 100644 index 0000000000..4016cc8368 --- /dev/null +++ b/src/Systems/IOSAVSoundSystem/IOSAVSoundSystem.mm @@ -0,0 +1,192 @@ +#import "IOSAVSoundSystem.h" +#import "IOSAVSoundBuffer.h" +#import "IOSAVSoundSource.h" + +#import "Kernel/EnumeratorHelper.h" +#import "Kernel/FactoryPool.h" +#import "Kernel/AssertionFactory.h" +#import "Kernel/DocumentHelper.h" +#import "Kernel/Logger.h" + +#import + +////////////////////////////////////////////////////////////////////////// +SERVICE_FACTORY( IOSAVSoundSystem, Mengine::IOSAVSoundSystem ); +////////////////////////////////////////////////////////////////////////// + +namespace Mengine +{ + ////////////////////////////////////////////////////////////////////////// + IOSAVSoundSystem::IOSAVSoundSystem() + : m_engineStarted( false ) + , m_interrupted( false ) + , m_engine( nullptr ) + { + } + ////////////////////////////////////////////////////////////////////////// + IOSAVSoundSystem::~IOSAVSoundSystem() + { + } + ////////////////////////////////////////////////////////////////////////// + bool IOSAVSoundSystem::_initializeService() + { + m_factorySoundBuffer = Helper::makeFactoryPoolWithMutex( MENGINE_DOCUMENT_FACTORABLE ); + m_factorySoundSource = Helper::makeFactoryPoolWithMutex( MENGINE_DOCUMENT_FACTORABLE ); + + [AVAudioSession sharedInstance]; + + return true; + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSystem::_finalizeService() + { + MENGINE_ASSERTION_FACTORY_EMPTY( m_factorySoundBuffer ); + MENGINE_ASSERTION_FACTORY_EMPTY( m_factorySoundSource ); + + m_factorySoundBuffer = nullptr; + m_factorySoundSource = nullptr; + + this->stopEngine(); + + if( m_engine != nullptr ) + { + AVAudioEngine * engine = (__bridge_transfer AVAudioEngine *)m_engine; + engine = nil; + } + + m_engine = nullptr; + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSystem::update() + { + //Empty + } + ////////////////////////////////////////////////////////////////////////// + bool IOSAVSoundSystem::isSilent() const + { + return false; + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSystem::onTurnSound( bool _turn ) + { + if( _turn == true ) + { + this->startEngine(); + } + else + { + this->stopEngine(); + } + } + ////////////////////////////////////////////////////////////////////////// + SoundBufferInterfacePtr IOSAVSoundSystem::createSoundBuffer( const SoundDecoderInterfacePtr & _decoder, bool _streamable, const DocumentInterfacePtr & _doc ) + { + IOSAVSoundBufferPtr buffer = m_factorySoundBuffer->createObject( _doc ); + + if( buffer == nullptr ) + { + LOGGER_ERROR( "invalid create buffer" ); + + return nullptr; + } + + buffer->initialize( this, _decoder, _streamable ); + + return buffer; + } + ////////////////////////////////////////////////////////////////////////// + SoundSourceInterfacePtr IOSAVSoundSystem::createSoundSource( bool _isHeadMode, const SoundBufferInterfacePtr & _sample, const DocumentInterfacePtr & _doc ) + { + IOSAVSoundSourcePtr source = m_factorySoundSource->createObject( _doc ); + + if( source == nullptr ) + { + LOGGER_ERROR( "invalid create source" ); + + return nullptr; + } + + source->initialize( this, _isHeadMode ); + source->setSoundBuffer( _sample ); + + return source; + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSystem::startEngine() + { + if( m_engineStarted == true ) + { + return; + } + + if( m_engine == nullptr ) + { + AVAudioEngine * engine = [[AVAudioEngine alloc] init]; + + m_engine = (__bridge_retained void *)engine; + } + + AVAudioSession * session = [AVAudioSession sharedInstance]; + + NSError * sessionError = nil; + if( [session setCategory:AVAudioSessionCategoryPlayback error:&sessionError] == NO ) + { + LOGGER_ERROR( "invalid set audio session category: %s", sessionError.localizedDescription.UTF8String ); + } + + sessionError = nil; + if( [session setActive:YES error:&sessionError] == NO ) + { + LOGGER_ERROR( "invalid active audio session: %s", sessionError.localizedDescription.UTF8String ); + } + + AVAudioEngine * engine = (__bridge AVAudioEngine *)m_engine; + + if( engine.isRunning == NO ) + { + NSError * startError = nil; + if( [engine startAndReturnError:&startError] == NO ) + { + LOGGER_ERROR( "invalid start audio engine: %s", startError.localizedDescription.UTF8String ); + } + else + { + m_engineStarted = true; + } + } + else + { + m_engineStarted = true; + } + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSystem::stopEngine() + { + if( m_engineStarted == false ) + { + return; + } + + AVAudioEngine * engine = (__bridge AVAudioEngine *)m_engine; + + [engine stop]; + + m_engineStarted = false; + } + ////////////////////////////////////////////////////////////////////////// + void IOSAVSoundSystem::setInterrupted( bool _interrupted ) + { + m_interrupted = _interrupted; + } + ////////////////////////////////////////////////////////////////////////// + bool IOSAVSoundSystem::isInterrupted() const + { + return m_interrupted; + } + ////////////////////////////////////////////////////////////////////////// + void * IOSAVSoundSystem::getEngine() const + { + return m_engine; + } + ////////////////////////////////////////////////////////////////////////// +}