
/*
 * --- GSMP-COPYRIGHT-NOTE-BEGIN ---
 * 
 * This copyright note is auto-generated by ./scripts/Create-CopyPatch.
 * Please add additional copyright information _after_ the line containing
 * the GSMP-COPYRIGHT-NOTE-END tag. Otherwise it might get removed by
 * the ./scripts/Create-CopyPatch script. Do not edit this copyright text!
 * 
 * GSMP: pcm/src/ALSAHelper.cc
 * General Sound Manipulation Program is Copyright (C) 2000 - 2004
 *   Valentin Ziegler and René Rebe
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2. A copy of the GNU General
 * Public License can be found in the file LICENSE.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT-
 * ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 * 
 * --- GSMP-COPYRIGHT-NOTE-END ---
 */

#include "ALSAHelper.hh"

#include <iostream>

using namespace GSMP;

/* ugly C-style macro for the standard ALSA error warning ... */

#define ALSA_WARN_ERROR(text) if (err < 0) \
    std::cout << __PRETTY_FUNCTION__ << " " << text << std::endl

#define ALSA_WARN_ERROR_NENDL(text) if (err < 0) \
    std::cout << __PRETTY_FUNCTION__ << " " << text

/***************
 * ALSAControl *
 ***************/

ALSAControl::ALSAControl ()
{
  opened = false;
}

ALSAControl::~ALSAControl ()
{
  if (opened)
    {
      std::cout << __PRETTY_FUNCTION__
		<< " BUG: Card is opend in dtor!?!?!?! ..." << std::endl
		<< "trying to close ..." << std::endl;
      Close ();
    }
}

std::string ALSAControl::GetName (int card)
{
  char* t_c_name;
  std::string t_name;
  
  if ( (err = snd_card_get_name (card, &t_c_name) ) < 0)
    {
      std::cout << __PRETTY_FUNCTION__
		<< " Could not open Control device!" << std::endl;
      return "";
    }
  
  t_name = t_c_name;
  free (t_c_name);

  return t_name;
}

bool ALSAControl::Open (const std::string& name, int mode)
{
  cname = name;
  
  err = snd_ctl_open (&handle, (char*) cname.c_str(), mode);
  ALSA_WARN_ERROR ("Could not open Control device!");
  
  opened = err >= 0;
  return opened;
}

bool ALSAControl::Close ()
{
  if (!opened)
    return false;
  
  err = snd_ctl_close (handle);
  ALSA_WARN_ERROR ("Could not close Control device!");
  
  if (err)
    return false;
  
  opened = false;
  return true;
}

/************
 * ALSAPCM *
 ************/

ALSAPCM::ALSAPCM ()
{
  err = snd_pcm_hw_params_malloc (&hw_params);
  ALSA_WARN_ERROR ("Could not allocate hw params structure!");
  
  err = snd_pcm_sw_params_malloc (&sw_params);
  ALSA_WARN_ERROR ("Could not allocate sw params structure!");
  
  err = snd_pcm_status_malloc (&status);
  ALSA_WARN_ERROR ("Could not allocate status structure!");
  
  opened = false;
}

ALSAPCM::~ALSAPCM ()
{
  if (opened)
    {
      std::cout << __PRETTY_FUNCTION__
		<< " BUG: Card is opend in dtor!?!?!?! ..." << std::endl
		<< "trying to close ..." << std::endl;
      Close ();
    }
  
  snd_pcm_status_free (status);
  snd_pcm_sw_params_free (sw_params);
  snd_pcm_hw_params_free (hw_params);
}

bool ALSAPCM::Open (const std::string& name, snd_pcm_stream_t stream, int mode)
{
  cname = name;
  
  err = snd_pcm_open (&handle, (char*) cname.c_str(), stream, mode);
  ALSA_WARN_ERROR ("Could not open PCM device!");
  if (err < 0)
    return false;

  err = snd_pcm_hw_params_any (handle, hw_params);
  ALSA_WARN_ERROR ("Could not get hw_params!");
  if (err < 0)
    return false;
  
  opened = true;
  return true;
}

bool ALSAPCM::Close ()
{
  if (!opened)
    return false;
  
  err = snd_pcm_close (handle);
  ALSA_WARN_ERROR ("Could not close PCM device!");
  
  if (err)
      return false;
  
  opened = false;
  return true;
       
}

snd_pcm_type_t ALSAPCM::Type ()
{
  return snd_pcm_type (handle);
}

bool ALSAPCM::InstallParams ()
{
  if (!opened)
    return false;
  
  err = snd_pcm_hw_params (handle, hw_params);
  ALSA_WARN_ERROR ("Could not install hw_params!");
  if (err)
    return false;
  
  // (re)get sw params
  err = snd_pcm_sw_params_current (handle, sw_params);

  ALSA_WARN_ERROR ("Could not get sw_params!");
  if (err)
    return false;
  
  return true;
}

bool ALSAPCM::SetAccess (snd_pcm_access_t access_mode)
{
  if (!opened)
    return false;
  
  err = snd_pcm_hw_params_set_access (handle, hw_params, access_mode);
  ALSA_WARN_ERROR ("Could not set access mode!");
  
  return (err >= 0);
}

int ALSAPCM::GetAccess ()
{
  snd_pcm_access_t access;
  
  err = snd_pcm_hw_params_get_access (hw_params, &access);
  ALSA_WARN_ERROR ("Could not get access!");
  
  if (err < 0)
    return 0;
  
  return access;
}

bool ALSAPCM::SetFormat (snd_pcm_format_t format)
{
  if (!opened)
    return false;
  
  err = snd_pcm_hw_params_set_format (handle, hw_params, format);
  ALSA_WARN_ERROR ("Hardware sample format is not supported!");
  
  if (err < 0)
    return false;
  
  return true;
}

snd_pcm_format_t ALSAPCM::GetFormat ()
{
  snd_pcm_format_t format;
  
  err = snd_pcm_hw_params_get_format (hw_params, &format);
  ALSA_WARN_ERROR ("Could not get format!");
  
  return format;
}

bool ALSAPCM::SetRate (unsigned int rate)
{
  if (!opened)
    return false;
  
  err = snd_pcm_hw_params_set_rate (handle, hw_params, rate,  0);
  ALSA_WARN_ERROR_NENDL ("Could not set sample/period rate to ")
    << rate << "!" << std::endl;
  
  if (err < 0)
    return false;
  
  return true;
}

bool ALSAPCM::SetRateNear (unsigned int rate)
{
  if (!opened)
    return false;
  
  err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &rate,  0);
  ALSA_WARN_ERROR_NENDL ("Could not set sample/period rate near ")
    << rate << "!" << std::endl;
  
  if (err)
    return false;
  
  return true;
}

int ALSAPCM::GetRateMin ()
{
  unsigned int val;
  err = snd_pcm_hw_params_get_rate_min (hw_params, &val, 0);
  ALSA_WARN_ERROR ("Could not get min rate!");
  
  return val;
}

int ALSAPCM::GetRateMax ()
{
  unsigned int val;
  err = snd_pcm_hw_params_get_rate_max (hw_params, &val, 0);
  ALSA_WARN_ERROR ("Could not get max rate!");
  
  return val;
}

int ALSAPCM::GetRate ()
{
  unsigned int val;
  err = snd_pcm_hw_params_get_rate (hw_params, &val, 0);
  ALSA_WARN_ERROR ("Could not get rate!");
  
  return val;
}

bool ALSAPCM::SetChannels (unsigned int channels)
{
  if (!opened)
    return false;
  
  err = snd_pcm_hw_params_set_channels (handle, hw_params,
					channels);
  ALSA_WARN_ERROR_NENDL ("Could not set channels to ")
    << channels << "!" << std::endl;
  
  if (err < 0)
    return false;
  
  return true;
}

int ALSAPCM::GetChannels ()
{
  unsigned int val;
  
  err = snd_pcm_hw_params_get_channels (hw_params, &val);
  ALSA_WARN_ERROR_NENDL ("Could not get channels!")
    << val << " " << err << std::endl;
  
  return val;
}

int ALSAPCM::GetChannelsMin ()
{
  unsigned int val;
  err = snd_pcm_hw_params_get_channels_min (hw_params, &val);
  ALSA_WARN_ERROR_NENDL ("Could not min channels!")
    << val << " " << err << std::endl;
  
  return val;
}

int ALSAPCM::GetChannelsMax ()
{
  unsigned int val;
  err = snd_pcm_hw_params_get_channels_max (hw_params, &val);
  ALSA_WARN_ERROR_NENDL ("Could not get max channels!")
    << val << " " << err << std::endl;
  
  return val;
}

bool ALSAPCM::SetBufferTimeNear (unsigned int time)
{
  if (!opened)
    return false;
  
  err = snd_pcm_hw_params_set_buffer_time_near (handle, hw_params,
						&time, 0);
  ALSA_WARN_ERROR_NENDL ("Could not set buffer time near to ")
    << time << "!" << std::endl;

  if (err < 0)
    return false;
  
  return true;
}

int ALSAPCM::GetBufferTime ()
{
  unsigned int val;
  err = snd_pcm_hw_params_get_buffer_time (hw_params, &val, 0);
  ALSA_WARN_ERROR ("Could not get buffer time!");

  return val;
}

bool ALSAPCM::SetBufferSize (snd_pcm_uframes_t size)
{
  if (!opened)
    return false;
  
  err = snd_pcm_hw_params_set_buffer_size (handle, hw_params,
					   size);
  ALSA_WARN_ERROR_NENDL ("Could not set buffer size to ")
			 << size << "!" << std::endl;
  
  if (err < 0)
    return false;
  
  return true;
}

snd_pcm_uframes_t ALSAPCM::GetBufferSize ()
{
  snd_pcm_uframes_t val;
  err = snd_pcm_hw_params_get_buffer_size (hw_params, &val);
  ALSA_WARN_ERROR ("Could not get buffer size!");
  
  return val;
}

bool ALSAPCM::SetBufferSizeNear (snd_pcm_uframes_t size)
{
  if (!opened)
    return false;
  
  err = snd_pcm_hw_params_set_buffer_size_near (handle, hw_params,
						&size);
  ALSA_WARN_ERROR_NENDL (" Could not set buffer size near to ")
    << size << "!" << std::endl;

  if (err < 0)
    return false;
  
  return true;
}

bool ALSAPCM::SetPeriodTimeNear (unsigned int time)
{
  if (!opened)
    return false;
  
  err = snd_pcm_hw_params_set_period_time_near (handle, hw_params,
						&time, 0);
  ALSA_WARN_ERROR_NENDL ("Could not set periods time near to ")
    << time << "!" << std::endl;
  
  if (err < 0)
    return false;
  
  return true;
}

int ALSAPCM::GetPeriodTime ()
{
  unsigned int val;
  err = snd_pcm_hw_params_get_period_time (hw_params, &val, 0);
  ALSA_WARN_ERROR ("Could not get period time!");
  
  return val;
}

bool ALSAPCM::SetPeriodSizeNear (snd_pcm_uframes_t size)
{
  if (!opened)
    return false;
  
  err =  snd_pcm_hw_params_set_period_size_near (handle,
						 hw_params, &size, 0);
  ALSA_WARN_ERROR_NENDL ("Could not set periods size near to ")
			 << size << "!" << std::endl;
  
  if (err < 0)
    return false;
  
  return true;
}

int ALSAPCM::GetPeriodSize ()
{
  snd_pcm_uframes_t val;
  err = snd_pcm_hw_params_get_period_size (hw_params, &val, 0);
  ALSA_WARN_ERROR ("Could not get period size!");
  
  return val;
}

bool ALSAPCM::SetPeriodSize (snd_pcm_uframes_t size)
{
  if (!opened)
    return false;
  
  err =  snd_pcm_hw_params_set_period_size (handle,
					    hw_params, size, 0);
  ALSA_WARN_ERROR_NENDL ("Could not set periods size to ")
			 << size << "!" << std::endl;
  
  if (err < 0)
    return false;
  
  return true;
}

bool ALSAPCM::SetPeriods (unsigned int periods)
{
  err = snd_pcm_hw_params_set_periods (handle, hw_params, periods, 0);
  ALSA_WARN_ERROR_NENDL ("Could not set periods to ")
    << periods << "!" << std::endl;
  
  return (err >= 0);
}

bool ALSAPCM::SetPeriodsNear (unsigned int periods)
{
  err = snd_pcm_hw_params_set_periods_near (handle, hw_params, &periods, 0);
  
  ALSA_WARN_ERROR_NENDL ("Could not set periods near ")
    << periods << "!" << std::endl;
  
  return (err >= 0);
}

int ALSAPCM::GetPeriodsMin ()
{
  unsigned int val;
  err = snd_pcm_hw_params_get_periods_min (hw_params, &val, 0);
  
  ALSA_WARN_ERROR ("Could not get min periods!");

  return val;
}

int ALSAPCM::GetPeriodsMax ()
{
  unsigned int val;
  err = snd_pcm_hw_params_get_periods_max (hw_params, &val, 0);

  ALSA_WARN_ERROR ("Could not get max periods!");

  return val;
}

int ALSAPCM::GetPeriods ()
{
  unsigned int val;
  err = snd_pcm_hw_params_get_periods (hw_params, &val, 0);

  ALSA_WARN_ERROR ("Could not get periods!");
  
  return val;
}

bool ALSAPCM::InstallSwParams ()
{
  if (!opened)
    return false;
  
  err = snd_pcm_sw_params (handle, sw_params);
  
  ALSA_WARN_ERROR ("Can not install sw-params!");
  
  return true;
}

bool ALSAPCM::SetSwStartThreshold (snd_pcm_uframes_t count)
{
  if (!opened)
    return false;
  
  err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, count);

  ALSA_WARN_ERROR_NENDL ("Could not set periods time near to ")
    << count << "!" << std::endl;

  if (err < 0)
    return false;
  
  return true;
}

snd_pcm_uframes_t ALSAPCM::GetSwStartThreshold ()
{
  snd_pcm_uframes_t val;
  err = snd_pcm_sw_params_get_start_threshold (sw_params, &val);

  ALSA_WARN_ERROR ("Can not get start threshold!");
  
  return val;
}

bool ALSAPCM::SetSwStopThreshold (snd_pcm_uframes_t count)
{
  if (!opened)
    return false;
  
  err = snd_pcm_sw_params_set_stop_threshold (handle, sw_params, count);

  ALSA_WARN_ERROR_NENDL ("Could not set periods time near to ")
    << count << "!" << std::endl;
  
  if (err < 0)
    return false;
  
  return true;
}

snd_pcm_uframes_t ALSAPCM::GetSwStopThreshold ()
{
  snd_pcm_uframes_t val;
  err =  snd_pcm_sw_params_get_stop_threshold (sw_params, &val);

  ALSA_WARN_ERROR ("Can not get stop threshold!");

  return val;
}

bool ALSAPCM::Prepare ()
{
  err = snd_pcm_prepare (handle);
  
  ALSA_WARN_ERROR ("Could not prepare channel!");
  
  if (err < 0)
    return false;
  
  return true;
}

bool ALSAPCM::Drain ()
{
  err = snd_pcm_drain (handle);
  
  ALSA_WARN_ERROR ("Could not drain channel!");

  if (err < 0)
    return false;
  
  return true;
}

bool ALSAPCM::Start ()
{
  err = snd_pcm_start (handle);
  
  ALSA_WARN_ERROR ("Could not start channel!");
  
  if (err < 0)
    return false;
  
  return true;
}

snd_pcm_sframes_t ALSAPCM::WriteI (const void* buffer, snd_pcm_uframes_t size)
{
  return snd_pcm_writei (handle, buffer, size);
}

snd_pcm_sframes_t ALSAPCM::ReadI (void* buffer, snd_pcm_uframes_t size)
{
  return snd_pcm_readi (handle, buffer, size);
}

snd_pcm_sframes_t ALSAPCM::WriteN (void** buffers, snd_pcm_uframes_t size)
{
  return snd_pcm_writen (handle, buffers, size);
}

snd_pcm_sframes_t ALSAPCM::ReadN (void** buffers, snd_pcm_uframes_t size)
{
  return snd_pcm_readn (handle, buffers, size);
}

bool ALSAPCM::UpdateStatus ()
{
  return snd_pcm_status (handle, status);
}

snd_pcm_state_t ALSAPCM::GetStatusState ()
{
  return snd_pcm_status_get_state (status);
}

void ALSAPCM::GetTriggerTStamp (snd_timestamp_t* ptr)
{
  return snd_pcm_status_get_trigger_tstamp (status, ptr);
}

void ALSAPCM::GetTStamp (snd_timestamp_t* ptr)
{
  return snd_pcm_status_get_tstamp (status, ptr);
}

snd_pcm_sframes_t ALSAPCM::GetStatusDelay ()
{
  return snd_pcm_status_get_delay (status);
}

snd_pcm_uframes_t ALSAPCM::GetStatusAvail ()
{
  return snd_pcm_status_get_avail (status);
}

snd_pcm_uframes_t ALSAPCM::GetStatusAvailMax ()
{
  return snd_pcm_status_get_avail_max (status);
}
