//_______________________________________________
//
//
//_______________________________________________
uint8_t coreAudioDevice::play(uint32_t len, float *data)
 {
 	// First put stuff into the buffer
	uint8_t *src;
	uint32_t left;

	dither16(data, len, _channels);

	pthread_mutex_lock(&lock);

	// Check we have room left
	if(wr_ptr>=rd_ptr)
	{
		left=BUFFER_SIZE-(wr_ptr-rd_ptr);
	}
	else
	{
		left=rd_ptr-wr_ptr;
	}
	if(len+1>left)
	{
		printf("AudioCore:Buffer full!\n");
		pthread_mutex_unlock(&lock);
		return 0;
	}

	// We have room left, copy it
	src=(uint8_t *)&audioBuffer[wr_ptr];
	if(wr_ptr+len<BUFFER_SIZE)
	{
		memcpy(src,data,len*2);
		wr_ptr+=len;
	}
	else
	{
		left=BUFFER_SIZE-wr_ptr-1;
		memcpy(src,data,left*2);
		memcpy(audioBuffer,data+left*2,(len-left)*2);
		wr_ptr=len-left;	
	}
	//aprintf("AudioCore: Putting %lu bytes rd:%lu wr:%lu \n",len*2,rd_ptr,wr_ptr);
	pthread_mutex_unlock(&lock);	
 	if(!frameCount)
	{
		_inUse=1;
		verify_noerr(AudioOutputUnitStart(theOutputUnit));
	}
	return 1;
}
//_______________________________________________
//
//
//_______________________________________________
uint8_t ossAudioDevice::play(uint32_t len, float *data)
{
	uint32_t w;

	if (!oss_fd)
		return 0;

	dither16(data, len, _channels);

	w = write(oss_fd, data, len*2);
        if(w!=len*2)
        {
          printf("[OSS] Warning : %u / %u\n",w,len*2); 
        }
	return 1;
}
/**
    \fn play

*/
uint8_t     audioDeviceThreaded::play(uint32_t len, float *data)
{
    // Reorder channels if needed...
    uint32_t samples=len;
    samples/=_channels;
    ADM_audioReorderChannels(_channels,
                    (float *)(data),
                    samples,
                    incomingMapping,
                    (CHANNEL_TYPE*)getWantedChannelMapping(_channels));
    // dither to int16_t
	dither16(data, len, _channels);
    len*=2;
    // Store in buffer
    return writeData((uint8_t *)data,len);

}
uint8_t win32AudioDevice::play(uint32_t len, float *data)
{
	if (len == 0)
		return 1;

	dither16(data, len, _channels);
	len *= 2;
	uint8_t success = 0;

	for (uint32_t i = 0; i < NB_BUCKET; i++)
	{
		if (waveHdr[i].dwFlags & WHDR_DONE)
		{
			waveHdr[i].dwFlags &= ~WHDR_DONE;

			if (len > bucketSize)
				waveHdr[i].dwBufferLength = bucketSize;
			else
				waveHdr[i].dwBufferLength = len;

			memcpy(waveHdr[i].lpData, data, waveHdr[i].dwBufferLength);
			data += waveHdr[i].dwBufferLength;
			len -= waveHdr[i].dwBufferLength;

			if (waveOutWrite(myDevice, &waveHdr[i], sizeof(WAVEHDR)) == MMSYSERR_NOERROR)
				success = 1;
			else
				break;
		}

		if (len == 0)
			break;
	}

	if (len != 0)
	{
		printf("[Win32] No audio buffer available, %u bytes discarded\n", len);
		return 0;
	}

	return success;
}
/**
    \fn send
    \brief Encode a block
*/
int AUDMEncoder_Lame::send(uint32_t nbSample, uint8_t *dest)
{
  int nbout;
  dither16 (&(tmpbuffer[tmphead]), nbSample, wavheader.channels);
  ADM_assert (tmptail >= tmphead);
  int16_t *sample16=(int16_t *)& (tmpbuffer[tmphead]);
  if (wavheader.channels == 1)
    {
      nbout =	lame_encode_buffer (MYFLAGS, 
                sample16,
			    sample16, nbSample, dest,
			    16 * 1024);

    }
  else
    {
      nbout =	lame_encode_buffer_interleaved (MYFLAGS,
					sample16,
					nbSample / 2, dest, 16 * 1024);
    }
    return nbout;
}
uint8_t	AUDMEncoder_PCM::getPacket(uint8_t *dest, uint32_t *len, uint32_t *samples)
{
  uint32_t nbout;
  
  *samples = _chunk; //FIXME
  *len = 0;

  if(!refillBuffer(_chunk ))
  {
    return 0; 
  }
        
  if(tmptail-tmphead<_chunk)
  {
    return 0; 
  }
        // Do in place replace
  dither16(&(tmpbuffer[tmphead]),_chunk,_wavheader->channels);
  if(!revert)
    memcpy(dest,&(tmpbuffer[tmphead]),_chunk*2);
  else
  {
    uint16_t *in,*out,tmp;
    in=(uint16_t*)&(tmpbuffer[tmphead]);
    out=(uint16_t *)dest;
    for(int i=0;i<_chunk;i++)
    {
      tmp=*in++;
      tmp=((tmp&0xff)<<8)+(tmp>>8);
      *out++=tmp;
    }
  }
  tmphead+=_chunk;
  *len=_chunk*2;
  *samples=_chunk/_wavheader->channels;
  return 1;
}
/**
        \fn getPacket
*/
bool 	AUDMEncoder_Twolame::encode(uint8_t *dest, uint32_t *len, uint32_t *samples)
{
  int nbout;
  int channels=wavheader.channels;
  *samples = 1152; //FIXME
  *len = 0;
  ADM_assert(tmptail>=tmphead);
  if(!refillBuffer(_chunk ))
  {
    return false;
  }

  if(tmptail-tmphead<_chunk)
  {
    return false;
  }

  dither16(&(tmpbuffer[tmphead]),_chunk,channels);

  ADM_assert(tmptail>=tmphead);
  if (channels == 1)
  {
    nbout =twolame_encode_buffer(OPTIONS, (int16_t *)&(tmpbuffer[tmphead]),(int16_t *)&(tmpbuffer[tmphead]), _chunk, dest, 16 * 1024);
  }
  else
  {
    nbout = twolame_encode_buffer_interleaved(OPTIONS, (int16_t *)&(tmpbuffer[tmphead]), _chunk/2, dest, 16 * 1024);
  }
  tmphead+=_chunk;
  ADM_assert(tmptail>=tmphead);
  if (nbout < 0) {
    printf("[TwoLame] Error !!! : %d\n", nbout);
    return false;
  }
  *len=nbout;
  return true;
}
uint8_t	AUDMEncoder_Twolame::getPacket(uint8_t *dest, uint32_t *len, uint32_t *samples)
{
  int nbout;
  
  *samples = 1152; //FIXME
  *len = 0;
  ADM_assert(tmptail>=tmphead);
  if(!refillBuffer(_chunk ))
  {
    return 0; 
  }
        
  if(tmptail-tmphead<_chunk)
  {
    return 0; 
  }

  dither16(&(tmpbuffer[tmphead]),_chunk,_wavheader->channels);

  ADM_assert(tmptail>=tmphead);
  if (_wavheader->channels == 1)
  {
    nbout =twolame_encode_buffer(OPTIONS, (int16_t *)&(tmpbuffer[tmphead]),(int16_t *)&(tmpbuffer[tmphead]), _chunk, dest, 16 * 1024);
  }
  else
  {
    nbout = twolame_encode_buffer_interleaved(OPTIONS, (int16_t *)&(tmpbuffer[tmphead]), _chunk/2, dest, 16 * 1024);
  }
  tmphead+=_chunk;
  ADM_assert(tmptail>=tmphead);
  if (nbout < 0) {
    printf("\n Error !!! : %ld\n", nbout);
    return 0;
  }
  *len=nbout;
  return 1;
}
uint8_t	AUDMEncoder_Lame::getPacket(uint8_t *dest, uint32_t *len, uint32_t *samples)
{
  int32_t nbout;
  
        *samples = BLOCK_SIZE; //FIXME
        *len = 0;

        if(!refillBuffer(_chunk ))
        {
          return 0; 
        }
        
        if(tmptail-tmphead<_chunk)
        {
          return 0; 
        }
        dither16(&(tmpbuffer[tmphead]),_chunk,_wavheader->channels);
        ADM_assert(tmptail>=tmphead);
        if (_wavheader->channels == 1)
        {
          nbout = lame_encode_buffer(MYFLAGS, (int16_t *)&(tmpbuffer[tmphead]),(int16_t *)&(tmpbuffer[tmphead]), _chunk, dest, 16 * 1024);
          
        }
        else
        {
          nbout = lame_encode_buffer_interleaved(MYFLAGS, (int16_t *)&(tmpbuffer[tmphead]), _chunk/2, dest, 16 * 1024);
        }
        tmphead+=_chunk;
        if (nbout < 0) {
          printf("\n Error !!! : %ld\n", nbout);
          return 0;
        }
        *len=nbout;
        if(!*len) *samples=0;
        //printf("Audio packet : size %u, sample %u\n",*len,*samples);
        return 1;
}
/**
    \fn encode
*/
bool	AUDMEncoder_Lavcodec::encode(uint8_t *dest, uint32_t *len, uint32_t *samples)
{
  uint32_t nbout;
  int retries=16;
again:
  int channels=wavheader.channels;
  *samples = _chunk/channels; //FIXME
  *len = 0;
  if(AudioEncoderStopped==_state)
        return false;

   refillBuffer (_chunk);
   if(AudioEncoderNoInput==_state)
    {
        int left=tmptail-tmphead;
        if (left < _chunk)
        {
            if(left) // Last block
            {
               if(_useFloat==false)
                    dither16(&(tmpbuffer[tmphead]),left,channels);
               ADM_assert(tmptail>=tmphead);
//#warning buffer overread
               nbout = avcodec_encode_audio(CONTEXT, dest, 5000, (short *) &(tmpbuffer[tmphead]));
               tmphead=tmptail;
               *samples = left/channels;
               *len=nbout;
               ADM_info("[Lav] Last audio block\n");
               goto cnt;
            }
              // Flush
               ADM_info("[Lav] Flush\n");
              _state=AudioEncoderStopped;
              if(CONTEXT->codec->capabilities & CODEC_CAP_DELAY)
              {
                  nbout=avcodec_encode_audio(CONTEXT, dest, 5000,NULL);
                  if(nbout<0)
                  {
                        ADM_warning("Error while flushing lame\n");
                        return false;
                  }

                  *len=nbout;
                  *samples=_chunk/channels;
                  ADM_info("[Lav] Flushing, last block is %d bytes\n",nbout);
                  return true;
              }else
              {
              }
              ADM_info("[Lav] No data to flush\n",nbout);
              return true;
        }
    }

  if(_useFloat==false)
    dither16(&(tmpbuffer[tmphead]),_chunk,channels);

  ADM_assert(tmptail>=tmphead);
  nbout = avcodec_encode_audio(CONTEXT, dest, 5000, (short *) &(tmpbuffer[tmphead]));

  tmphead+=_chunk;
cnt:
  if(!nbout && retries)
  {
    retries--;
    ADM_info("Audio encoder (lav): no packet, retrying\n");
    goto again;
  }
  if (nbout < 0)
  {
    ADM_error("[Lavcodec] Error !!! : %"PRIi32"\n", nbout);
    return 0;
  }
  *len=nbout;
  *samples=_chunk/channels;
  return true;
}
//_______________________________________________
//
//
//_______________________________________________
uint8_t esdAudioDevice::play(uint32_t len, float *data)
{
	dither16(data, len, _channels);
	write(esdDevice, data, len*2);
	return 1;
}