axmol/CocosDenshion/bada/CCAudioOut.cpp

703 lines
15 KiB
C++
Raw Normal View History

2011-10-14 18:33:14 +08:00
#include "CCAudioOut.h"
2011-10-14 17:57:44 +08:00
#include <stdio.h>
2011-10-18 09:45:04 +08:00
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <FSystem.h>
2011-10-14 17:57:44 +08:00
2011-10-18 18:10:53 +08:00
#ifdef OGG_SUPPORT
#include <vorbis/vorbisfile.h>
#endif
2011-10-14 17:57:44 +08:00
using namespace Osp::Base;
using namespace Osp::Base::Collection;
using namespace Osp::Media;
2011-10-18 09:45:04 +08:00
using namespace Osp::Io;
2011-10-14 17:57:44 +08:00
2011-10-18 09:45:04 +08:00
namespace CocosDenshion {
#define PRE_BUFFERING_NUM 2
#define DEFAULT_VOLUME_LEVEL 5
2011-10-14 17:57:44 +08:00
2011-10-14 18:33:14 +08:00
typedef struct wave_tag
{
char ChunkID[5];
unsigned long int ChunkSize;
char Format[5];
char SubChunk1ID[5];
unsigned long int SubChunk1Size;
unsigned short int AudioFormat;
unsigned short int NumChannels;
unsigned long int SampleRate;
unsigned long int ByteRate;
unsigned short int BlockAlign;
unsigned short int BitsPerSample;
char SubChunk2ID[5];
unsigned long int SubChunk2Size;
}WAVE;
2011-10-18 09:45:04 +08:00
static void GetWaveHeadInfo(File* pFile, WAVE& outWavHead)
2011-10-14 18:33:14 +08:00
{
char szTmp[100] = {0};
int i = 0;
2011-10-18 09:45:04 +08:00
memset(&outWavHead, 0, sizeof(WAVE));
pFile->Read(outWavHead.ChunkID, 4);
pFile->Read(&(outWavHead.ChunkSize),4);
pFile->Read(outWavHead.Format, 4);
pFile->Read(outWavHead.SubChunk1ID, 4);
pFile->Read(&(outWavHead.SubChunk1Size), 4);
pFile->Read(&(outWavHead.AudioFormat), 2);
pFile->Read(&(outWavHead.NumChannels), 2);
pFile->Read(&(outWavHead.SampleRate), 4);
pFile->Read(&(outWavHead.ByteRate), 4);
pFile->Read(&(outWavHead.BlockAlign), 2);
pFile->Read(&(outWavHead.BitsPerSample), 2);
pFile->Seek(FILESEEKPOSITION_BEGIN, 0);
pFile->Read(szTmp, 64);
2011-10-14 18:33:14 +08:00
for (i = 0; i <= 60; i++)
{
if (szTmp[i] == 'd' && szTmp[i+1] == 'a' && szTmp[i+2] == 't' && szTmp[i+3] == 'a')
{
break;
}
}
2011-10-18 09:45:04 +08:00
pFile->Seek(FILESEEKPOSITION_BEGIN, i);
pFile->Read(outWavHead.SubChunk2ID, 4);
pFile->Read(&(outWavHead.SubChunk2Size), 4);
}
2011-10-14 18:33:14 +08:00
2011-10-18 09:45:04 +08:00
int CCAudioOut::DecodeOgg(const char *infile)
{
2011-10-18 18:10:53 +08:00
#ifdef OGG_SUPPORT
2011-10-18 09:45:04 +08:00
FILE *in=NULL;
OggVorbis_File vf;
int bs = 0;
char buf[8192];
int buflen = 8192;
unsigned int written = 0;
int ret;
ogg_int64_t length = 0;
ogg_int64_t done = 0;
int size;
int seekable = 0;
int percent = 0;
char* pPcmBuffer = NULL;
in = fopen(infile, "rb");
if(!in) {
AppLog("ERROR: Failed to open input file:\n");
return 1;
}
2011-10-14 18:33:14 +08:00
2011-10-18 09:45:04 +08:00
if(ov_open(in, &vf, NULL, 0) < 0) {
AppLog("ERROR: Failed to open input as vorbis\n");
fclose(in);
// fclose(out);
return 1;
}
if(ov_seekable(&vf)) {
seekable = 1;
length = ov_pcm_total(&vf, 0);
size = bits/8 * ov_info(&vf, 0)->channels;
}
if (ov_info(&vf,0)->channels == 2)
{
__sampleChannelType = AUDIO_CHANNEL_TYPE_STEREO;
}
else
{
__sampleChannelType = AUDIO_CHANNEL_TYPE_MONO;
}
__sampleRate = ov_info(&vf,0)->rate;
__sampleBitdepth = AUDIO_TYPE_PCM_S16_LE;
while((ret = ov_read(&vf, buf, buflen, endian, bits/8, sign, &bs)) != 0) {
if(bs != 0) {
AppLog("Only one logical bitstream currently supported\n");
break;
}
if(ret < 0 && !quiet) {
AppLog("Warning: hole in data\n");
continue;
}
if (__pAllPcmBuffer == null)
{
__pAllPcmBuffer = (char*)malloc(ret);
}
else
{
__pAllPcmBuffer = (char*)realloc(__pAllPcmBuffer, written+ret);
}
memcpy(__pAllPcmBuffer+written, buf, ret);
written += ret;
if(!quiet && seekable) {
done += ret/size;
if((double)done/(double)length * 200. > (double)percent) {
percent = (double)done/(double)length *200;
AppLog("\r\t[%5.1f%%]", (double)percent/2.);
}
}
}
__iAllPcmBufferSize = written;
// if(seekable && !quiet)
// AppLog("\n");
// if(!raw)
// rewrite_header(out, written); /* We don't care if it fails, too late */
ov_clear(&vf);
fclose(in);
// fclose(out);
2011-10-18 18:10:53 +08:00
#endif
2011-10-18 09:45:04 +08:00
return 0;
2011-10-14 18:33:14 +08:00
}
2011-10-18 09:45:04 +08:00
CCAudioOut::CCAudioOut()
2011-10-14 17:57:44 +08:00
{
2011-10-18 09:45:04 +08:00
AppLog("Enter");
2011-10-18 18:10:53 +08:00
__volumeLevel = -1;
2011-10-18 09:45:04 +08:00
__pAllPcmBuffer = null;
__iAllPcmBufferSize = 0;
__iAllPcmPos = 0;
quiet = 0;
bits = 16;
endian = 0;
raw = 0;
sign = 1;
__iFileType = 0;
__pFile = null;
2011-10-18 18:10:53 +08:00
__bShortPcmBuffer = false;
__bBufferConstruted = false;
__checkInitFiniPair = false;
__iUsedBufferCount = 0;
2011-10-14 17:57:44 +08:00
}
2011-10-18 09:45:04 +08:00
CCAudioOut::~CCAudioOut()
2011-10-14 17:57:44 +08:00
{
2011-10-18 09:45:04 +08:00
AppLog("Enter");
Finalize();
if(__pAudioOut)
2011-10-14 17:57:44 +08:00
{
delete __pAudioOut;
__pAudioOut = null;
}
2011-10-18 09:45:04 +08:00
if (__pAllPcmBuffer)
2011-10-14 17:57:44 +08:00
{
2011-10-18 09:45:04 +08:00
free(__pAllPcmBuffer);
__pAllPcmBuffer = null;
2011-10-14 17:57:44 +08:00
}
}
2011-10-18 09:45:04 +08:00
result CCAudioOut::Initialize(const char* pszFilePath)
2011-10-14 17:57:44 +08:00
{
2011-10-18 09:45:04 +08:00
AppLog("Enter");
// This is called when AudioOut form is moving on the foreground.
result r = E_SUCCESS;
2011-10-18 18:10:53 +08:00
2011-10-18 09:45:04 +08:00
__pAllPcmBuffer = null;
__iAllPcmBufferSize = 0;
__iAllPcmPos = 0;
2011-10-18 18:10:53 +08:00
__iUsedBufferCount = 0;
2011-10-18 09:45:04 +08:00
if(__checkInitFiniPair == false)
2011-10-14 17:57:44 +08:00
{
2011-10-18 09:45:04 +08:00
// Reset the configure variables
__finishChecker =0;
__bufWrittenCnt = PRE_BUFFERING_NUM;
__buffReadCnt = 0;
__buffWriteCnt = 0;
result r = E_FAILURE;
if (__pAudioOut == NULL)
2011-10-14 17:57:44 +08:00
{
2011-10-18 09:45:04 +08:00
__pAudioOut = new AudioOut();
if (!__pAudioOut)
{
AppLog("[E_OUT_OF_MEMORY] m_pAudio new failed\n");
return r;
}
r = __pAudioOut->Construct(*this);
if (IsFailed(r))
{
AppLog("[Error] m_AudioOut.Construct failed");
return r;
}
2011-10-14 17:57:44 +08:00
}
else
{
2011-10-18 09:45:04 +08:00
AppLog("[Error] __pAudioOut is already existed\n");
2011-10-14 17:57:44 +08:00
}
2011-10-18 09:45:04 +08:00
String strFile(pszFilePath);
if (strFile.EndsWith(".wav"))
{
__iFileType = 0;
// Construct File for feeding buffers
__pFile = new File();
if(!__pFile)
{
AppLog("[Error] __pFile new failed\n");
return E_SYSTEM;
}
2011-10-14 17:57:44 +08:00
2011-10-18 09:45:04 +08:00
r = __pFile->Construct(pszFilePath, L"rb");
if (IsFailed(r)) {
AppLog("[Error] __pFile.Construct failed : %d \n", r);
return r;
}
2011-10-14 17:57:44 +08:00
2011-10-18 09:45:04 +08:00
WAVE wavHead;
GetWaveHeadInfo(__pFile, wavHead);
2011-10-18 18:10:53 +08:00
__iAllPcmBufferSize = wavHead.SubChunk2Size;
2011-10-14 17:57:44 +08:00
2011-10-18 09:45:04 +08:00
if (wavHead.BitsPerSample == 8)
{
__sampleBitdepth = AUDIO_TYPE_PCM_U8;
2011-10-14 17:57:44 +08:00
2011-10-18 09:45:04 +08:00
}
else if (wavHead.BitsPerSample == 16)
{
__sampleBitdepth = AUDIO_TYPE_PCM_S16_LE;
}
else
{
__sampleBitdepth = AUDIO_TYPE_NONE;
}
2011-10-14 17:57:44 +08:00
2011-10-18 09:45:04 +08:00
if (wavHead.NumChannels == 1)
{
__sampleChannelType = AUDIO_CHANNEL_TYPE_MONO;
}
else if (wavHead.NumChannels == 2)
{
__sampleChannelType = AUDIO_CHANNEL_TYPE_STEREO;
}
else
{
__sampleChannelType = AUDIO_CHANNEL_TYPE_NONE;
}
2011-10-14 17:57:44 +08:00
2011-10-18 09:45:04 +08:00
__sampleRate = wavHead.SampleRate;
2011-10-18 18:10:53 +08:00
__pAllPcmBuffer = (char*)malloc(__iAllPcmBufferSize);
if (__pAllPcmBuffer != NULL)
{
__pFile->Read(__pAllPcmBuffer, __iAllPcmBufferSize);
}
else
{
AppLog("not more memory...");
}
2011-10-18 09:45:04 +08:00
}
else if (strFile.EndsWith(".ogg"))
{
__iFileType = 1;
long long curTick, oldTick;
Osp::System::SystemTime::GetTicks(oldTick);
DecodeOgg(pszFilePath);
Osp::System::SystemTime::GetTicks(curTick);
AppLog("decode ogg to pcm waste %ld", (long)(curTick-oldTick));
}
// Prepare AudioOut
r = __pAudioOut->Prepare( __sampleBitdepth, __sampleChannelType, __sampleRate );
if (IsFailed(r))
{
AppLog("[Error] m_AudioOut.Prepare failed");
return r;
}
2011-10-14 17:57:44 +08:00
2011-10-18 09:45:04 +08:00
__bufferSize = __pAudioOut->GetMinBufferSize();
AppLog("[Info] __bufferSize=%d (MaxBuf=%d Min Size %d)\n", __bufferSize, __pAudioOut->GetMaxBufferSize(),__pAudioOut->GetMinBufferSize());
2011-10-14 17:57:44 +08:00
2011-10-18 09:45:04 +08:00
// Reset Volume or keeping a volume level
__volumeLevel = __volumeLevel == -1 ? DEFAULT_VOLUME_LEVEL : __volumeLevel;
r = __pAudioOut->SetVolume(DEFAULT_VOLUME_LEVEL);
if (IsFailed(r))
{
AppLog("[Error] m_AudioOut.SetVolume failed");
return r;
}
2011-10-14 17:57:44 +08:00
2011-10-18 18:10:53 +08:00
if (!__bBufferConstruted)
2011-10-18 09:45:04 +08:00
{
2011-10-18 18:10:53 +08:00
__bBufferConstruted = true;
r = __byteBuffer[0].Construct(__bufferSize);
if (E_SUCCESS != r)
{
AppLog( "[Error] __byteBuffer[0].Construct failed..%d ",r);
return E_OUT_OF_MEMORY;
}
r = __byteBuffer[1].Construct(__bufferSize);
if (E_SUCCESS != r)
{
AppLog( "[Error] __byteBuffer[1].Construct failed..%d ",r);
return E_OUT_OF_MEMORY;
}
r = __byteBuffer[2].Construct(__bufferSize);
if (E_SUCCESS != r)
{
AppLog( "[Error] __byteBuffer[2].Construct failed..%d ",r);
return E_OUT_OF_MEMORY;
}
r = __byteBuffer[3].Construct(__bufferSize);
if (E_SUCCESS != r)
{
AppLog( "[Error] __byteBuffer[3].Construct failed..%d ",r);
return E_OUT_OF_MEMORY;
}
2011-10-18 09:45:04 +08:00
}
2011-10-14 17:57:44 +08:00
2011-10-18 18:10:53 +08:00
if (__iAllPcmBufferSize <= __bufferSize * 4)
2011-10-18 09:45:04 +08:00
{
2011-10-18 18:10:53 +08:00
__bShortPcmBuffer = true;
2011-10-18 09:45:04 +08:00
}
2011-10-18 18:10:53 +08:00
else
2011-10-18 09:45:04 +08:00
{
2011-10-18 18:10:53 +08:00
__bShortPcmBuffer = false;
2011-10-18 09:45:04 +08:00
}
2011-10-18 18:10:53 +08:00
ReWriteBuffer();
2011-10-18 09:45:04 +08:00
__checkInitFiniPair = true;
}else{
AppLog("[WANRNING] The application state is not proper");
2011-10-14 17:57:44 +08:00
}
2011-10-18 09:45:04 +08:00
return r;
}
result CCAudioOut::FeedBuffer (void)
{
2011-10-18 18:10:53 +08:00
result ret = E_FAILURE;
// if (__iFileType == 0)
// {
// /*
// * Read buffer from file
// */
// if(__pFile)
// {
// readSize = __pFile->Read((char *)__byteBuffer[__buffWriteCnt].GetPointer (), __bufferSize);
// if(readSize != 0)
// {
// __buffWriteCnt ++;
// if (4 == __buffWriteCnt)
// {
// __buffWriteCnt = 0;
// }
// }else
// {
// __finishChecker = PRE_BUFFERING_NUM;
// __buffWriteCnt = 0;
// }
// }
// }
// else if (__iFileType == 1)
2011-10-18 09:45:04 +08:00
{// ogg
int iRemainSize = __iAllPcmBufferSize - __iAllPcmPos;
if (iRemainSize < __bufferSize)
{
memcpy((void*)__byteBuffer[__buffWriteCnt].GetPointer (), __pAllPcmBuffer+__iAllPcmPos, iRemainSize);
__iAllPcmPos += iRemainSize;
}
else
{
memcpy((void*)__byteBuffer[__buffWriteCnt].GetPointer (), __pAllPcmBuffer+__iAllPcmPos, __bufferSize);
__iAllPcmPos += __bufferSize;
}
2011-10-14 17:57:44 +08:00
2011-10-19 17:15:21 +08:00
__iUsedBufferCount++;
2011-10-18 09:45:04 +08:00
if (__iAllPcmPos < __iAllPcmBufferSize)
{
__buffWriteCnt ++;
if (4 == __buffWriteCnt)
{
__buffWriteCnt = 0;
}
}
else
{
__finishChecker = PRE_BUFFERING_NUM;
__buffWriteCnt = 0;
__iAllPcmPos = 0;
2011-10-18 18:10:53 +08:00
ret = E_SUCCESS;
2011-10-18 09:45:04 +08:00
}
}
2011-10-14 17:57:44 +08:00
2011-10-18 09:45:04 +08:00
return ret;
2011-10-14 17:57:44 +08:00
}
2011-10-18 18:10:53 +08:00
result CCAudioOut::ReWriteBuffer(void)
{
int i = 0;
result r = E_FAILURE;
__iAllPcmPos = 0;
__iUsedBufferCount = 0;
if (__bShortPcmBuffer)
{
for (i=0; i<4; i++)
{
r = FeedBuffer();
if (!IsFailed(r)) // Feeding buffers(4)
{
break;
}
}
for (i = 0; i < __iUsedBufferCount; i++)
{
r = __pAudioOut->WriteBuffer(__byteBuffer[i]);
if (IsFailed(r))
{
AppLog("[Error] m_AudioOut.WriteBuffer failed : %d\n", r);
return r;
}
}
}
else
{
for (i=0; i<4; i++)
{
FeedBuffer(); // Feeding buffers(4)
}
for (i = 0; i < 2; i++)
{
r = __pAudioOut->WriteBuffer(__byteBuffer[i]);
if (IsFailed(r))
{
AppLog("[Error] m_AudioOut.WriteBuffer failed : %d\n", r);
return r;
}
}
}
return r;
}
2011-10-18 09:45:04 +08:00
void CCAudioOut::ReFeedBuffer(void)
2011-10-14 17:57:44 +08:00
{
2011-10-18 09:45:04 +08:00
result r = E_SUCCESS;
for (int i=0; i<4; i++)
{
FeedBuffer(); // Feeding buffers(4)
}
r = __pAudioOut->WriteBuffer(__byteBuffer[0]);
if (IsFailed(r))
2011-10-14 17:57:44 +08:00
{
2011-10-18 09:45:04 +08:00
AppLog("[Error] m_AudioOut.WriteBuffer failed : %d\n", r);
2011-10-14 17:57:44 +08:00
}
2011-10-18 09:45:04 +08:00
r = __pAudioOut->WriteBuffer(__byteBuffer[1]);
if (IsFailed(r))
2011-10-14 17:57:44 +08:00
{
2011-10-18 09:45:04 +08:00
AppLog("[Error] m_AudioOut.WriteBuffer failed : %d\n", r);
2011-10-14 17:57:44 +08:00
}
}
2011-10-18 18:10:53 +08:00
result CCAudioOut::Play(void)
2011-10-14 17:57:44 +08:00
{
2011-10-18 09:45:04 +08:00
AppLog("Enter");
2011-10-18 18:10:53 +08:00
result ret = E_FAILURE;
2011-10-18 09:45:04 +08:00
AudioOutState state = __pAudioOut->GetState();
2011-10-18 18:10:53 +08:00
if(state == AUDIOOUT_STATE_PREPARED || state == AUDIOOUT_STATE_STOPPED)
2011-10-18 09:45:04 +08:00
{
2011-10-18 18:10:53 +08:00
AppLog("Enter");
2011-10-18 09:45:04 +08:00
ret = __pAudioOut->Start();
if (IsFailed(ret))
{
AppLog("[Error] m_AudioOut.Start failed : %d\n", ret);
}
}
2011-10-18 18:10:53 +08:00
return ret;
2011-10-14 17:57:44 +08:00
}
2011-10-18 18:10:53 +08:00
result CCAudioOut::Stop(void)
2011-10-14 17:57:44 +08:00
{
2011-10-18 09:45:04 +08:00
AppLog("Enter");
2011-10-18 18:10:53 +08:00
result ret = E_FAILURE;
2011-10-18 09:45:04 +08:00
if( __pAudioOut->GetState() == AUDIOOUT_STATE_PLAYING )
{
ret = __pAudioOut->Stop();
if (IsFailed(ret))
{
AppLog("[Error] m_AudioOut.Stop failed : %d\n", ret);
}
}
2011-10-18 18:10:53 +08:00
return ret;
}
result CCAudioOut::Reset(void)
{
AppLog("Enter");
AudioOutState state = __pAudioOut->GetState();
result r = E_SUCCESS;
if(state == AUDIOOUT_STATE_PLAYING || state == AUDIOOUT_STATE_STOPPED)
2011-10-18 09:45:04 +08:00
{
2011-10-18 18:10:53 +08:00
AppLog("reset ...");
r = __pAudioOut->Reset();
if(IsFailed(r))
{
AppLog("[Error] AudioOut Reset is failed");
}
2011-10-18 09:45:04 +08:00
}
2011-10-18 18:10:53 +08:00
ReWriteBuffer();
return r;
2011-10-14 17:57:44 +08:00
}
2011-10-18 09:45:04 +08:00
void CCAudioOut::OnAudioOutBufferEndReached(Osp::Media::AudioOut& src)
2011-10-14 17:57:44 +08:00
{
2011-10-18 18:10:53 +08:00
result r = E_FAILURE;
if (__bShortPcmBuffer)
2011-10-18 09:45:04 +08:00
{
2011-10-18 18:10:53 +08:00
Reset();
AppLog("Reset...");
}
else
2011-10-14 17:57:44 +08:00
{
2011-10-18 18:10:53 +08:00
int ret;
// AppLog("thread name is %S", Thread::GetCurrentThread()->GetName().GetPointer());
AppLog("__buffReadCnt = %d", __buffReadCnt);
__byteBuffer[__buffReadCnt++].Clear ();
if (4 == __buffReadCnt)
__buffReadCnt = 0;
2011-10-18 09:45:04 +08:00
if(__finishChecker == 0)
{
2011-10-18 18:10:53 +08:00
ret = src.WriteBuffer(__byteBuffer[__bufWrittenCnt++]);
if (4 == __bufWrittenCnt)
__bufWrittenCnt = 0;
FeedBuffer();
}
else
{
AppLog("__finishChecker = %d", __finishChecker);
__finishChecker--;
if(__finishChecker == 0)
{
AppLog("Reset...");
Reset();
__bufWrittenCnt = PRE_BUFFERING_NUM;
__buffReadCnt = 0;
__buffWriteCnt = 0;
}
2011-10-18 09:45:04 +08:00
}
2011-10-14 17:57:44 +08:00
}
2011-10-18 18:10:53 +08:00
2011-10-14 17:57:44 +08:00
}
2011-10-18 09:45:04 +08:00
void CCAudioOut::OnAudioOutInterrupted(Osp::Media::AudioOut& src)
{
AppLog("Enter");
Finalize();
}
void CCAudioOut::OnAudioOutReleased(Osp::Media::AudioOut& src)
{
AppLog("Enter");
//cjh Initialize();
}
void CCAudioOut::Finalize(void)
2011-10-14 17:57:44 +08:00
{
2011-10-18 09:45:04 +08:00
AppLog("Enter");
if(__checkInitFiniPair)
2011-10-14 17:57:44 +08:00
{
2011-10-18 09:45:04 +08:00
// Set OnAudioOutBufferEndReached stop.
__finishChecker = PRE_BUFFERING_NUM;
if(__pAudioOut)
{
AudioOutState state = __pAudioOut->GetState();
result r = E_SUCCESS;
if(state == AUDIOOUT_STATE_PLAYING)
{
r = __pAudioOut->Reset();
if(IsFailed(r))
{
AppLog("[Error] AudioOut Reset is failed");
}
}
state = __pAudioOut->GetState();
if(state == AUDIOOUT_STATE_PREPARED || state == AUDIOOUT_STATE_STOPPED)
{
r = __pAudioOut->Unprepare();
if(IsFailed(r))
{
AppLog("[Error] AudioOut UnPrepare is failed");
}
}
}
if(__pFile)
{
delete __pFile;
__pFile = null;
}
__checkInitFiniPair = false;
}else{
AppLog("[WANRNING] This application state is not proper");
2011-10-14 17:57:44 +08:00
}
}
2011-10-18 18:10:53 +08:00
void CCAudioOut::SetVolume(int volume)
{
if (__pAudioOut)
__pAudioOut->SetVolume(volume);
2011-10-14 17:57:44 +08:00
}
2011-10-18 18:10:53 +08:00
int CCAudioOut::GetVolume(void) const
{
int ret = 0;
if (__pAudioOut)
ret = __pAudioOut->GetVolume();
return ret;
}
Osp::Media::AudioOutState CCAudioOut::GetState(void) const
{
AudioOutState state = AUDIOOUT_STATE_ERROR;
if (__pAudioOut)
state = __pAudioOut->GetState();
return state;
}
}