symbian-qemu-0.9.1-12/libsdl-trunk/src/audio/symbian/SDL_epocaudio.cpp
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2     SDL - Simple DirectMedia Layer
       
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
       
     4 
       
     5     This library is free software; you can redistribute it and/or
       
     6     modify it under the terms of the GNU Library General Public
       
     7     License as published by the Free Software Foundation; either
       
     8     version 2 of the License, or (at your option) any later version.
       
     9 
       
    10     This library is distributed in the hope that it will be useful,
       
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13     Library General Public License for more details.
       
    14 
       
    15     You should have received a copy of the GNU Library General Public
       
    16     License along with this library; if not, write to the Free
       
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    18 
       
    19     Sam Lantinga
       
    20     slouken@devolution.com
       
    21 */
       
    22 
       
    23 /*
       
    24     SDL_epocaudio.cpp
       
    25     Epoc based SDL audio driver implementation
       
    26     
       
    27     Markus Mertama
       
    28 */
       
    29 
       
    30 #ifdef SAVE_RCSID
       
    31 static char rcsid =
       
    32  "@(#) $Id: SDL_epocaudio.c,v 0.0.0.0 2001/06/19 17:19:56 hercules Exp $";
       
    33 #endif
       
    34 
       
    35 
       
    36 #include <stdlib.h>
       
    37 #include <stdio.h>
       
    38 #include <string.h>
       
    39 #include <errno.h>
       
    40 #include <unistd.h>
       
    41 #include <fcntl.h>
       
    42 #include <signal.h>
       
    43 #include <sys/time.h>
       
    44 #include <sys/ioctl.h>
       
    45 #include <sys/stat.h>
       
    46 
       
    47 #include "epoc_sdl.h"
       
    48 
       
    49 #include <e32hal.h>
       
    50 
       
    51 
       
    52 extern "C" {
       
    53 #include "SDL_audio.h"
       
    54 #include "SDL_error.h"
       
    55 #include "SDL_audiomem.h"
       
    56 #include "SDL_audio_c.h"
       
    57 #include "SDL_timer.h"
       
    58 #include "SDL_audiodev_c.h"
       
    59 }
       
    60 
       
    61 #include "SDL_epocaudio.h"
       
    62 
       
    63 #include "streamplayer.h"
       
    64 
       
    65 
       
    66 //#define DEBUG_AUDIO
       
    67 
       
    68 
       
    69 /* Audio driver functions */
       
    70 
       
    71 static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec);
       
    72 static void EPOC_WaitAudio(SDL_AudioDevice *thisdevice);
       
    73 static void EPOC_PlayAudio(SDL_AudioDevice *thisdevice);
       
    74 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice *thisdevice);
       
    75 static void EPOC_CloseAudio(SDL_AudioDevice *thisdevice);
       
    76 static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice);
       
    77 
       
    78 static int Audio_Available(void);
       
    79 static SDL_AudioDevice *Audio_CreateDevice(int devindex);
       
    80 static void Audio_DeleteDevice(SDL_AudioDevice *device);
       
    81 
       
    82 
       
    83 //void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len);
       
    84 
       
    85 #ifdef __WINS__
       
    86 #define DODUMP
       
    87 #endif
       
    88 
       
    89 #ifdef DODUMP
       
    90 NONSHARABLE_CLASS(TDump)
       
    91 	{
       
    92 	public:
       
    93 	TInt Open();
       
    94 	void Close();
       
    95 	void Dump(const TDesC8& aDes);
       
    96 	private:
       
    97 		RFile iFile;
       
    98     	RFs iFs; 
       
    99 	};
       
   100 	
       
   101 TInt TDump::Open()
       
   102 	{
       
   103 	TInt err = iFs.Connect();
       
   104 	if(err == KErrNone)
       
   105 		{
       
   106 #ifdef __WINS__
       
   107 _LIT(target, "C:\\sdlau.raw");
       
   108 #else
       
   109 _LIT(target, "E:\\sdlau.raw");
       
   110 #endif 
       
   111 		err = iFile.Replace(iFs, target, EFileWrite);
       
   112 		}
       
   113 	return err;
       
   114 	}
       
   115 void TDump::Close()
       
   116 	{
       
   117 	iFile.Close();
       
   118 	iFs.Close();
       
   119 	}
       
   120 void TDump::Dump(const TDesC8& aDes)
       
   121 	{
       
   122 	iFile.Write(aDes);
       
   123 	}
       
   124 #endif
       
   125 
       
   126 
       
   127 NONSHARABLE_CLASS(CSimpleWait) : public CTimer
       
   128 	{
       
   129 	public:
       
   130 		void Wait(TTimeIntervalMicroSeconds32 aWait);
       
   131 		static CSimpleWait* NewL();
       
   132 	private:
       
   133 		CSimpleWait();
       
   134 		void RunL();
       
   135 	};
       
   136 
       
   137 
       
   138 CSimpleWait* CSimpleWait::NewL()
       
   139 	{
       
   140 	CSimpleWait* wait = new (ELeave) CSimpleWait();
       
   141 	CleanupStack::PushL(wait);
       
   142 	wait->ConstructL();
       
   143 	CleanupStack::Pop();
       
   144 	return wait;
       
   145 	}
       
   146 
       
   147 void CSimpleWait::Wait(TTimeIntervalMicroSeconds32 aWait)
       
   148 	{
       
   149 	After(aWait);
       
   150 	CActiveScheduler::Start();
       
   151 	}
       
   152 	
       
   153 CSimpleWait::CSimpleWait() : CTimer(CActive::EPriorityStandard)	
       
   154 	{
       
   155 	CActiveScheduler::Add(this);
       
   156 	}
       
   157 
       
   158 void CSimpleWait::RunL()
       
   159 	{
       
   160 	CActiveScheduler::Stop();
       
   161 	}
       
   162 
       
   163 const TInt KAudioBuffers(2);
       
   164 	
       
   165 
       
   166 NONSHARABLE_CLASS(CEpocAudio) : public CBase, public MStreamObs, public MStreamProvider
       
   167     {
       
   168     public:
       
   169     	static void* NewL(TInt BufferSize, TInt aFill);
       
   170     	inline static CEpocAudio& Current(SDL_AudioDevice* thisdevice);
       
   171     	
       
   172     	static void Free(SDL_AudioDevice* thisdevice);
       
   173  		
       
   174     	void Wait();
       
   175     	void Play();
       
   176     //	void SetBuffer(const TDesC8& aBuffer);
       
   177     	void ThreadInitL(TAny* aDevice);
       
   178     	void Open(TInt iRate, TInt iChannels, TUint32 aType, TInt aBytes);
       
   179     	~CEpocAudio();
       
   180     	TUint8* Buffer();
       
   181     	TBool SetPause(TBool aPause);
       
   182     #ifdef DODUMP
       
   183     	void Dump(const TDesC8& aBuf) {iDump.Dump(aBuf);}
       
   184     #endif
       
   185     private:
       
   186     	CEpocAudio(TInt aBufferSize);
       
   187     	void Complete(TInt aState, TInt aError);
       
   188     	TPtrC8 Data();
       
   189     	void ConstructL(TInt aFill);
       
   190     private:
       
   191     	TInt iBufferSize;
       
   192     	CStreamPlayer* iPlayer;
       
   193     	TInt iBufferRate;
       
   194     	TInt iRate;
       
   195     	TInt iChannels;
       
   196     	TUint32 iType;
       
   197     	TInt iPosition;
       
   198     	TThreadId iTid;
       
   199     	TUint8* iAudioPtr;
       
   200     	TUint8* iBuffer;
       
   201     //	TTimeIntervalMicroSeconds iStart;
       
   202     	TTime iStart;
       
   203     	TInt iTune;
       
   204     	CSimpleWait* iWait;
       
   205     #ifdef DODUMP
       
   206     	TDump iDump;
       
   207     #endif
       
   208     };
       
   209 
       
   210 inline CEpocAudio& CEpocAudio::Current(SDL_AudioDevice* thisdevice)
       
   211 	{
       
   212 	return *static_cast<CEpocAudio*>((void*)thisdevice->hidden);
       
   213 	}
       
   214 	
       
   215 /*
       
   216 
       
   217 TBool EndSc(TAny*)
       
   218 	{	
       
   219 	CActiveScheduler::Stop();
       
   220 	}
       
   221 	
       
   222 LOCAL_C void CleanScL()
       
   223 	{
       
   224 	CIdle* d = CIdle::NewLC(CActive:::EPriorityIdle);
       
   225 	d->Start(TCallBack(EndSc));
       
   226 	CActiveScheduler::Start();
       
   227 	
       
   228 	}
       
   229 */
       
   230 	
       
   231 void CEpocAudio::Free(SDL_AudioDevice* thisdevice)
       
   232 	{
       
   233     CEpocAudio* ea = static_cast<CEpocAudio*>((void*)thisdevice->hidden);
       
   234     if(ea)
       
   235     	{
       
   236 		ASSERT(ea->iTid == RThread().Id());
       
   237     	delete ea;
       
   238     	thisdevice->hidden = NULL;	
       
   239    
       
   240     	CActiveScheduler* as =  CActiveScheduler::Current();
       
   241     	ASSERT(as->StackDepth() == 0);    	
       
   242     	delete as;
       
   243     	CActiveScheduler::Install(NULL);
       
   244     	}
       
   245     ASSERT(thisdevice->hidden == NULL);
       
   246 	}
       
   247 	
       
   248 CEpocAudio::CEpocAudio(TInt aBufferSize) : iBufferSize(aBufferSize), iPosition(-1) 
       
   249 	{
       
   250 	}
       
   251 
       
   252 void* CEpocAudio::NewL(TInt aBufferSize, TInt aFill)
       
   253 	{
       
   254 	CEpocAudio* eAudioLib = new (ELeave) CEpocAudio(aBufferSize);
       
   255 	CleanupStack::PushL(eAudioLib);
       
   256 	eAudioLib->ConstructL(aFill);
       
   257 	CleanupStack::Pop();
       
   258 	return eAudioLib;
       
   259 	}
       
   260 	
       
   261 void CEpocAudio::ConstructL(TInt aFill)
       
   262 	{
       
   263 	iBuffer = (TUint8*) User::AllocL(KAudioBuffers * iBufferSize);
       
   264 	memset(iBuffer, aFill, KAudioBuffers * iBufferSize);
       
   265 	iAudioPtr = iBuffer;
       
   266 	}
       
   267 
       
   268 
       
   269 TBool CEpocAudio::SetPause(TBool aPause)
       
   270 	{
       
   271 	if(aPause && iPosition >= 0)
       
   272 		{
       
   273 		iPosition = -1;
       
   274 		if(iPlayer != NULL)
       
   275 			iPlayer->Stop();
       
   276 		}
       
   277 	if(!aPause && iPosition < 0)
       
   278 		{
       
   279 		iPosition = 0;
       
   280 		if(iPlayer != NULL)
       
   281 			iPlayer->Start();
       
   282 		}
       
   283 	return iPosition < 0;
       
   284 	}
       
   285 	
       
   286 void CEpocAudio::ThreadInitL(TAny* aDevice)
       
   287 	{
       
   288 	iTid = RThread().Id(); 
       
   289 	CActiveScheduler* as =  new (ELeave) CActiveScheduler();
       
   290     CActiveScheduler::Install(as);
       
   291     
       
   292     EpocSdlEnv::AppendCleanupItem(TSdlCleanupItem((TSdlCleanupOperation)EPOC_CloseAudio, aDevice));
       
   293    
       
   294     iWait = CSimpleWait::NewL();
       
   295    
       
   296     iPlayer = new (ELeave) CStreamPlayer(*this, *this);
       
   297     iPlayer->ConstructL();	
       
   298     iPlayer->OpenStream(iRate, iChannels, iType);
       
   299     
       
   300     #ifdef DODUMP
       
   301     User::LeaveIfError(iDump.Open());
       
   302     #endif
       
   303 	}
       
   304 	
       
   305 	
       
   306 	
       
   307 TUint8* CEpocAudio::Buffer()
       
   308 	{
       
   309 	iStart.UniversalTime();
       
   310 //	iStart = iPlayer->Position();		
       
   311 	return iAudioPtr;
       
   312 
       
   313 	}
       
   314 	
       
   315 CEpocAudio::~CEpocAudio()
       
   316 	{
       
   317 	if(iWait != NULL)
       
   318 		iWait->Cancel();
       
   319 	delete iWait; 
       
   320 	if(iPlayer != NULL)
       
   321 		iPlayer->Close();
       
   322 	delete iPlayer;
       
   323 	delete iBuffer;
       
   324 	}
       
   325 	
       
   326 void CEpocAudio::Complete(TInt aState, TInt aError)
       
   327 	{
       
   328 	if(aState == MStreamObs::EClose)
       
   329 		{
       
   330 		}
       
   331 	if(iPlayer->Closed())
       
   332 		return;
       
   333 	switch(aError)
       
   334 		{
       
   335 		case KErrUnderflow:
       
   336 		case KErrInUse:
       
   337 			iPlayer->Start();
       
   338 			break;
       
   339 		case KErrAbort:
       
   340 			iPlayer->Open();
       
   341 		}
       
   342 	}
       
   343 	
       
   344 
       
   345 void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len)
       
   346 	{
       
   347 #ifdef DODUMP
       
   348 	const TPtrC8 buf((TUint8*)data, len);
       
   349 	CEpocAudio::Current(thisdevice).Dump(buf);
       
   350 #endif
       
   351 	}
       
   352 
       
   353 const TInt KClip(256);
       
   354 	
       
   355 TPtrC8 CEpocAudio::Data()
       
   356 	{
       
   357 	if(iPosition < 0)
       
   358 		return KNullDesC8();
       
   359 	
       
   360 	TPtrC8 data(iAudioPtr + iPosition, KClip);
       
   361 	
       
   362 #ifdef DODUMP
       
   363 	iDump.Dump(data);
       
   364 #endif
       
   365 	
       
   366 	iPosition += KClip;
       
   367 	if(iPosition >= iBufferSize) 
       
   368 		{
       
   369 		
       
   370 /*		if(iAudioPtr == iBuffer)
       
   371 			iAudioPtr = iBuffer + iBufferSize;
       
   372 		else
       
   373 			iAudioPtr = iBuffer;
       
   374 */		
       
   375 		iAudioPtr += iBufferSize;
       
   376 		
       
   377 		if((iAudioPtr - iBuffer) >= KAudioBuffers * iBufferSize)
       
   378 			iAudioPtr = iBuffer;
       
   379 		
       
   380 		iPosition = -1;
       
   381 		if(iWait->IsActive())
       
   382 			{
       
   383 			iWait->Cancel();
       
   384 			CActiveScheduler::Stop();
       
   385 			}
       
   386 		}
       
   387 	return data;
       
   388 	}
       
   389 		
       
   390 
       
   391 
       
   392 	
       
   393 void CEpocAudio::Play()
       
   394 	{
       
   395 	iPosition = 0;
       
   396 	}
       
   397 
       
   398 void CEpocAudio::Wait()
       
   399 	{
       
   400 	if(iPosition >= 0 /*&& iPlayer->Playing()*/)
       
   401 		{
       
   402 		const TInt64 bufMs = TInt64(iBufferSize - KClip) * TInt64(1000000);
       
   403 		const TInt64 specTime =  bufMs / TInt64(iRate * iChannels * 2);
       
   404 		iWait->After(specTime);
       
   405 		
       
   406 		CActiveScheduler::Start();
       
   407 		TTime end;
       
   408 		end.UniversalTime();
       
   409 		const TTimeIntervalMicroSeconds delta = end.MicroSecondsFrom(iStart);
       
   410 	
       
   411 	
       
   412 //		const TTimeIntervalMicroSeconds end = iPlayer->Position();
       
   413 		
       
   414 		
       
   415 	
       
   416 		
       
   417 		const TInt diff = specTime - delta.Int64();
       
   418 		
       
   419 		if(diff > 0 && diff < 200000)
       
   420 			{
       
   421 			User::After(diff);
       
   422 			}
       
   423 		
       
   424 		}
       
   425 	else
       
   426 		{
       
   427 	User::After(10000); 
       
   428 //	iWait->Wait(10000); //just give some time...	
       
   429 		}	
       
   430 	}
       
   431 	
       
   432 void CEpocAudio::Open(TInt aRate, TInt aChannels, TUint32 aType, TInt aBytes)	
       
   433 	{
       
   434 	iRate = aRate;
       
   435 	iChannels = aChannels;
       
   436 	iType = aType;
       
   437     iBufferRate = iRate * iChannels * aBytes; //1/x
       
   438 	}
       
   439 	
       
   440 
       
   441 /* Audio driver bootstrap functions */
       
   442 
       
   443 AudioBootStrap EPOCAudio_bootstrap = {
       
   444 	"epoc\0\0\0",
       
   445 	"EPOC streaming audio\0\0\0",
       
   446 	Audio_Available,
       
   447 	Audio_CreateDevice
       
   448 };
       
   449 
       
   450 
       
   451 static SDL_AudioDevice *Audio_CreateDevice(int /*devindex*/)
       
   452 {
       
   453 	SDL_AudioDevice *thisdevice;
       
   454 
       
   455 	/* Initialize all variables that we clean on shutdown */
       
   456 	thisdevice = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
       
   457 	if ( thisdevice ) {
       
   458 		memset(thisdevice, 0, (sizeof *thisdevice));
       
   459 		thisdevice->hidden = NULL; /*(struct SDL_PrivateAudioData *)
       
   460 			 malloc((sizeof thisdevice->hidden)); */
       
   461 	}
       
   462 	if ( (thisdevice == NULL) /*|| (thisdevice->hidden == NULL) */) {
       
   463 		SDL_OutOfMemory();
       
   464 		if ( thisdevice ) {
       
   465 			free(thisdevice);
       
   466 		}
       
   467 		return(0);
       
   468 	}
       
   469 //	memset(thisdevice->hidden, 0, (sizeof *thisdevice->hidden));
       
   470 
       
   471 	/* Set the function pointers */
       
   472 	thisdevice->OpenAudio = EPOC_OpenAudio;
       
   473 	thisdevice->WaitAudio = EPOC_WaitAudio;
       
   474 	thisdevice->PlayAudio = EPOC_PlayAudio;
       
   475 	thisdevice->GetAudioBuf = EPOC_GetAudioBuf;
       
   476 	thisdevice->CloseAudio = EPOC_CloseAudio;
       
   477     thisdevice->ThreadInit = EPOC_ThreadInit;
       
   478 	thisdevice->free = Audio_DeleteDevice;
       
   479 
       
   480 	return thisdevice;
       
   481 }
       
   482 
       
   483 
       
   484 static void Audio_DeleteDevice(SDL_AudioDevice *device)
       
   485     {
       
   486 	//free(device->hidden);
       
   487 	free(device);
       
   488     }
       
   489 
       
   490 static int Audio_Available(void)
       
   491 {
       
   492 	return(1); // Audio stream modules should be always there!
       
   493 }
       
   494 
       
   495 
       
   496 static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec)
       
   497 {
       
   498 	SDL_TRACE("SDL:EPOC_OpenAudio");
       
   499 
       
   500 	
       
   501 	TUint32 type = KMMFFourCCCodePCM16;
       
   502 	TInt bytes = 2;
       
   503 	
       
   504 	switch(spec->format)
       
   505 		{
       
   506 		case AUDIO_U16LSB: 
       
   507 			type = KMMFFourCCCodePCMU16; 
       
   508 			break;
       
   509 		case AUDIO_S16LSB: 
       
   510 			type = KMMFFourCCCodePCM16; 
       
   511 			break;
       
   512 		case AUDIO_U16MSB: 
       
   513 			type = KMMFFourCCCodePCMU16B; 
       
   514 			break;
       
   515 		case AUDIO_S16MSB: 
       
   516 			type = KMMFFourCCCodePCM16B; 
       
   517 			break; 
       
   518 			//8 bit not supported!
       
   519 		case AUDIO_U8: 
       
   520 		case AUDIO_S8:
       
   521 		default:
       
   522 			spec->format = AUDIO_S16LSB;
       
   523 		};
       
   524 	
       
   525 
       
   526 	
       
   527 	if(spec->channels > 2)
       
   528 		spec->channels = 2;
       
   529 	
       
   530 	spec->freq = CStreamPlayer::ClosestSupportedRate(spec->freq);
       
   531 	
       
   532 
       
   533 	/* Allocate mixing buffer */
       
   534 	const TInt buflen = spec->size;// * bytes * spec->channels;
       
   535 //	audiobuf = NULL;
       
   536     
       
   537     TRAPD(err, thisdevice->hidden = static_cast<SDL_PrivateAudioData*>(CEpocAudio::NewL(buflen, spec->silence)));
       
   538     if(err != KErrNone)
       
   539         return -1;
       
   540 
       
   541 	CEpocAudio::Current(thisdevice).Open(spec->freq, spec->channels, type, bytes);
       
   542 	
       
   543 	CEpocAudio::Current(thisdevice).SetPause(ETrue);
       
   544 	
       
   545    // isSDLAudioPaused = 1;
       
   546 
       
   547     thisdevice->enabled = 0; /* enable only after audio engine has been initialized!*/
       
   548 
       
   549 	/* We're ready to rock and roll. :-) */
       
   550 	return(0);
       
   551 }
       
   552 
       
   553 
       
   554 static void EPOC_CloseAudio(SDL_AudioDevice* thisdevice)
       
   555     {
       
   556 #ifdef DEBUG_AUDIO
       
   557     SDL_TRACE("Close audio\n");
       
   558 #endif
       
   559 
       
   560 	CEpocAudio::Free(thisdevice);
       
   561 	}
       
   562 
       
   563 
       
   564 static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice)
       
   565     {
       
   566 	SDL_TRACE("SDL:EPOC_ThreadInit");
       
   567     CEpocAudio::Current(thisdevice).ThreadInitL(thisdevice);
       
   568     RThread().SetPriority(EPriorityMore);
       
   569     thisdevice->enabled = 1;
       
   570     }
       
   571 
       
   572 /* This function waits until it is possible to write a full sound buffer */
       
   573 static void EPOC_WaitAudio(SDL_AudioDevice* thisdevice)
       
   574 {
       
   575 #ifdef DEBUG_AUDIO
       
   576     SDL_TRACE1("wait %d audio\n", CEpocAudio::AudioLib().StreamPlayer(KSfxChannel).SyncTime());
       
   577     TInt tics = User::TickCount();
       
   578 #endif
       
   579 
       
   580 	CEpocAudio::Current(thisdevice).Wait();
       
   581 
       
   582 #ifdef DEBUG_AUDIO
       
   583     TInt ntics =  User::TickCount() - tics;
       
   584     SDL_TRACE1("audio waited %d\n", ntics);
       
   585     SDL_TRACE1("audio at %d\n", tics);
       
   586 #endif
       
   587 }
       
   588 
       
   589 
       
   590  
       
   591 static void EPOC_PlayAudio(SDL_AudioDevice* thisdevice)
       
   592 	{
       
   593  	if(CEpocAudio::Current(thisdevice).SetPause(SDL_GetAudioStatus() == SDL_AUDIO_PAUSED))
       
   594  		SDL_Delay(500); //hold on the busy loop
       
   595  	else
       
   596  		CEpocAudio::Current(thisdevice).Play();
       
   597 
       
   598 #ifdef DEBUG_AUDIO
       
   599     SDL_TRACE("buffer has audio data\n");
       
   600 #endif
       
   601 
       
   602 	
       
   603 #ifdef DEBUG_AUDIO
       
   604 	SDL_TRACE1("Wrote %d bytes of audio data\n", buflen);
       
   605 #endif
       
   606 }
       
   607 
       
   608 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice* thisdevice)
       
   609 	{
       
   610 	return CEpocAudio::Current(thisdevice).Buffer();
       
   611 	}
       
   612 
       
   613 
       
   614