/*
 * sound_libsndio.cxx
 *
 * Sound driver implementation.
 *
 * Portable Windows Library
 *
 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Portable Windows Library.
 *
 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
 *
 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
 * All Rights Reserved.
 *
 * $Log: sound_libsndio.cxx,v $
 * Revision 1.2  2009/01/19 09:42:21  ajacoutot
 * - s/LIBSNDIO/SNDIO for consistency
 * discussed with naddy@
 *
 * "sure" jakemsr@
 *
 * Revision 1.1  2009/01/17 12:30:08  jakemsr
 * - add sndio backend
 * - remove OSS and esd support
 * ok ajacoutot@ (MAINTAINER)
 *
 */

#pragma implementation "sound_libsndio.h"

#include "sound_libsndio.h"

#include <sys/poll.h>

PCREATE_SOUND_PLUGIN(SNDIO, PSoundChannelLIBSNDIO);

PSoundChannelLIBSNDIO::PSoundChannelLIBSNDIO()
{
  PSoundChannelLIBSNDIO::Construct();
}


PSoundChannelLIBSNDIO::PSoundChannelLIBSNDIO(const PString & device,
                                        Directions dir,
                                          unsigned numChannels,
                                          unsigned sampleRate,
                                          unsigned bitsPerSample)
{
  Construct();
  Open(device, dir, numChannels, sampleRate, bitsPerSample);
}


void PSoundChannelLIBSNDIO::Construct()
{
  os_handle = -1;
  hdl = NULL;
}


PSoundChannelLIBSNDIO::~PSoundChannelLIBSNDIO()
{
  Close();
}


PStringArray PSoundChannelLIBSNDIO::GetDeviceNames(Directions)
{
  static const char * const devices[] = {
    "default",
    "/tmp/aucat.sock",
    "/dev/audio0",
    "/dev/audio1",
    "/dev/audio2"
  };

  return PStringArray(PARRAYSIZE(devices), devices);
}


PString PSoundChannelLIBSNDIO::GetDefaultDevice(Directions dir)
{
  return "default";
}

BOOL PSoundChannelLIBSNDIO::Open(const PString & device,
                              Directions dir,
                                unsigned numChannels,
                                unsigned sampleRate,
                                unsigned bitsPerSample)
{
  uint mode;
  char sio_device[32];

  Close();

  if (dir == Recorder)
    mode = SIO_REC;
  else
    mode = SIO_PLAY;

  snprintf(sio_device, 32, "%s", (const char *)device);

  if (strncmp(sio_device, "default", 7) == 0)
    hdl = sio_open(NULL, mode, 0);
  else
    hdl = sio_open(sio_device, mode, 0);

  if (hdl == NULL) {
    printf("sio_open failed\n");
    return FALSE;
  }

  mDirection     = dir;
  mDevice        = device;
  mSampleRate    = sampleRate;
  mNumChannels   = numChannels;
  mBitsPerSample = bitsPerSample;
  mBytesPerFrame = (bitsPerSample / 8) * numChannels;

  isInitialised = FALSE;

  return TRUE;
}

BOOL PSoundChannelLIBSNDIO::Setup()
{
  if (!hdl) {
    PTRACE(6, "LIBSNDIO\tSkipping setup of " << mDevice << " as not open");
    return FALSE;
  }

  if (isInitialised) {
    PTRACE(6, "LIBSNDIO\tSkipping setup of " << mDevice << " as instance already initialised");
    return TRUE;
  }

  PTRACE(6, "LIBSNDIO\tInitialising " << mDevice);

  sio_initpar(&par);

  int framesPerFrag = mFragSize / mBytesPerFrame;
  par.bufsz = mFragCount * framesPerFrag;
  par.round = framesPerFrag;

  par.bits = mBitsPerSample;
  par.sig = 1;
#if PBYTE_ORDER == PLITTLE_ENDIAN
  par.le = 1;
#else
  par.le = 0;
#endif

  if (mDirection == Recorder)
    par.rchan = mNumChannels;
  else
    par.pchan = mNumChannels;

  par.rate = mSampleRate;

  if (!sio_setpar(hdl, &par)) {
    printf("sio_setpar failed\n");
    return FALSE;
  }

  if (!sio_getpar(hdl, &par)) {
    printf("sio_getpar failed\n");
    return FALSE;
  }

  mFragSize = par.round * mBytesPerFrame;
  mFragCount = par.bufsz / par.round;

  if (!sio_start(hdl)) {
    printf("sio_start failed\n");
    return FALSE;
  }

  isInitialised = TRUE;

  return TRUE;
}

BOOL PSoundChannelLIBSNDIO::Close()
{
  if (!hdl)
    return TRUE;

  sio_close(hdl);
  hdl = NULL;
  return PChannel::Close();
}

BOOL PSoundChannelLIBSNDIO::IsOpen() const
{
  return (hdl != NULL);
}

BOOL PSoundChannelLIBSNDIO::Write(const void * buf, PINDEX len)
{
  lastWriteCount = 0;

  if (!Setup() || !hdl)
    return FALSE;

  int did, tot = 0;

  while (len > 0) {
    did = sio_write(hdl, (void *)buf, len);
    if (did == 0) {
      printf("sio_write failed\n");
      return FALSE;
    }
    len -= did;
    (char *)buf += did;
    tot += did;
  }
  lastWriteCount += tot;

  return TRUE;
}

BOOL PSoundChannelLIBSNDIO::Read(void * buf, PINDEX len)
{
  lastReadCount = 0;

  if (!Setup() || !hdl)
    return FALSE;

  int did, tot = 0;

  while (len > 0) {
    did = sio_read(hdl, buf, len);
    if (did == 0) {
      printf("sio_read failed\n");
      return FALSE;
    }
    len -= did;
    (char *)buf += did;
    tot += did;
  }
  lastReadCount += tot;

  return TRUE;
}


BOOL PSoundChannelLIBSNDIO::SetFormat(unsigned numChannels,
                              unsigned sampleRate,
                              unsigned bitsPerSample)
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  PAssert((bitsPerSample == 8) || (bitsPerSample == 16), PInvalidParameter);
  PAssert(numChannels >= 1 && numChannels <= 2, PInvalidParameter);

  if (isInitialised) {
    if ((numChannels   != mNumChannels) ||
        (sampleRate    != mSampleRate) ||
        (bitsPerSample != mBitsPerSample)) {
      PTRACE(6, "LIBSNDIO\tTried to change read/write format without stopping");
      return FALSE;
    }
    return TRUE;
  }

  mNumChannels   = numChannels;
  mSampleRate    = sampleRate;
  mBitsPerSample = bitsPerSample;
  isInitialised  = FALSE;

  return TRUE;
}


unsigned PSoundChannelLIBSNDIO::GetChannels()   const
{
  return mNumChannels;
}


unsigned PSoundChannelLIBSNDIO::GetSampleRate() const
{
  return mSampleRate;
}


unsigned PSoundChannelLIBSNDIO::GetSampleSize() const
{
  return mBitsPerSample;
}


BOOL PSoundChannelLIBSNDIO::SetBuffers(PINDEX size, PINDEX count)
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  PAssert(size > 0 && count > 0 && count < 65536, PInvalidParameter);

  if (isInitialised) {
    if (mFragSize != (unsigned)size || mFragCount != (unsigned)count) {
      PTRACE(6, "LIBSNDIO\tTried to change buffers without stopping");
      return FALSE;
    }
    return TRUE;
  }

  mFragSize = size;
  mFragCount = count;
  isInitialised = FALSE;

  return TRUE;
}


BOOL PSoundChannelLIBSNDIO::GetBuffers(PINDEX & size, PINDEX & count)
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  count = mFragCount;
  size = mFragSize;

  return TRUE;
}


BOOL PSoundChannelLIBSNDIO::PlaySound(const PSound & sound, BOOL wait)
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  if (!Write((const BYTE *)sound, sound.GetSize()))
    return FALSE;

  if (wait)
    return WaitForPlayCompletion();

  return TRUE;
}


BOOL PSoundChannelLIBSNDIO::PlayFile(const PFilePath & filename, BOOL wait)
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  PFile file(filename, PFile::ReadOnly);
  if (!file.IsOpen())
    return FALSE;

  for (;;) {
    BYTE buffer[256];
    if (!file.Read(buffer, 256))
      break;
    PINDEX len = file.GetLastReadCount();
    if (len == 0)
      break;
    if (!Write(buffer, len))
      break;
  }

  file.Close();

  if (wait)
    return WaitForPlayCompletion();

  return TRUE;
}


BOOL PSoundChannelLIBSNDIO::HasPlayCompleted()
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  return TRUE;
}


BOOL PSoundChannelLIBSNDIO::WaitForPlayCompletion()
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  return TRUE;
}


BOOL PSoundChannelLIBSNDIO::RecordSound(PSound & sound)
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  return FALSE;
}


BOOL PSoundChannelLIBSNDIO::RecordFile(const PFilePath & filename)
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  return FALSE;
}


BOOL PSoundChannelLIBSNDIO::StartRecording()
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  return TRUE;
}


BOOL PSoundChannelLIBSNDIO::IsRecordBufferFull()
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  struct pollfd pfd;
  int events = POLLIN;
  sio_pollfd(hdl, &pfd, events);
  return ConvertOSError(::poll(&pfd, 1, 0));
}


BOOL PSoundChannelLIBSNDIO::AreAllRecordBuffersFull()
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  struct pollfd pfd;
  int events = POLLIN;
  sio_pollfd(hdl, &pfd, events);
  return ConvertOSError(::poll(&pfd, 1, 0));
}


BOOL PSoundChannelLIBSNDIO::WaitForRecordBufferFull()
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  // return PXSetIOBlock(PXReadBlock, readTimeout);

  struct pollfd pfd;
  int events = POLLIN;
  sio_pollfd(hdl, &pfd, events);
  return ConvertOSError(::poll(&pfd, 1, 1000));
}


BOOL PSoundChannelLIBSNDIO::WaitForAllRecordBuffersFull()
{
  if (!hdl)
    return SetErrorValues(NotOpen, EBADF);

  struct pollfd pfd;
  int events = POLLIN;
  sio_pollfd(hdl, &pfd, events);
  return ConvertOSError(::poll(&pfd, 1, 1000));
}


BOOL PSoundChannelLIBSNDIO::Abort()
{
  return TRUE;
}


BOOL PSoundChannelLIBSNDIO::SetVolume(unsigned newVal)
{
  if (!hdl)
    return FALSE;

  return FALSE;
}


BOOL  PSoundChannelLIBSNDIO::GetVolume(unsigned &devVol)
{
  if (!hdl)
    return FALSE;

  devVol = 0;
  return FALSE;
}
  


// End of file
