
/*
 * --- 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/ALSAIOStream.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 <sys/time.h>

#include <iostream>
#include <sstream>

#include "Limits.hh"
#include "Logging.hh"
#include "ALSAIOStream.hh"

using namespace GSMP;

GSMP::Logger <DefaultLogging_ALSA> GSMP::ALSAIOStreamLog;

/********************************
 * The ALSA Node implementation *
 ********************************/

ALSAIOStream::ALSAIOStream ()
{
  cname = "hw:0";
  
  initialized = false;
  io_running = false;
  
  do_playback = true;
  do_capture = false;
  
  pl_period_size = 0;
  ca_period_size = 0;
  
  playback_card = new ALSAPCM;
  capture_card = new ALSAPCM;
}

ALSAIOStream::~ALSAIOStream ()
{
  if (io_running)
    Q_LOG(ALSAIOStreamLog)
		    << "BUG: io is running in dtor!?!?!?! ..." << std::endl;
  
  delete playback_card;
  delete capture_card;
}

bool ALSAIOStream::Init ()
{
  if (initialized) {
    Q_LOG(ALSAIOStreamLog)
	     << "Already Initialized!" << std::endl;
    
    return false;
  }
  
  // TODO: obtain a lightweight list from ALSA with the possible interface names
  
  reget_card_params ();
  initialized = true;
  
  return initialized;
}

const std::vector<std::string>& ALSAIOStream::Interfaces ()
{
  return m_interfaces;
}

const std::string& ALSAIOStream::Interface ()
{
  return cname;
}

bool ALSAIOStream::SetInterface (const std::string& n_interface)
{
  if (!initialized) {
    Q_LOG(ALSAIOStreamLog)
	     << "Not Initialized!" << std::endl;
    return false;
  }
  
  if (io_running) {
    Q_LOG(ALSAIOStreamLog)
	     << "IO running!" << std::endl;
    return false;
  }
  
  std::string old_name = cname;
  cname = n_interface;
  
  pl_period_size = 0;
  ca_period_size = 0;
  
  if (!reget_card_params () ) {
    cname = old_name;
    return false;
  }
  return true;
}

int ALSAIOStream::PlaybackPeriodSize ()
{
  return pl_period_size;
}

int ALSAIOStream::CapturePeriodSize ()
{
  return ca_period_size;
}

int ALSAIOStream::PlaybackPeriods ()
{
  return pl_periods;
}

int ALSAIOStream::CapturePeriods ()
{
  return ca_periods;
}

int ALSAIOStream::PlaybackXRuns ()
{
  return pl_xruns;
}

int ALSAIOStream::CaptureXRuns ()
{
  return ca_xruns;
}

void ALSAIOStream::SetPlayback (bool state)
{
  if (!initialized) {
    Q_LOG(ALSAIOStreamLog)
	     << "Not Initialized!" << std::endl;
    return;
  }
  if (io_running) {
    Q_LOG(ALSAIOStreamLog)
	     << "IO running!" << std::endl;
    return;
  }
  do_playback = state;
}

void ALSAIOStream::SetCapture (bool state)
{
  if (!initialized) {
    Q_LOG(ALSAIOStreamLog)
	     << "Not Initialized!" << std::endl;
    return;
    
  }
  if (io_running) {
    Q_LOG(ALSAIOStreamLog)
	     << "IO running!" << std::endl;
    return;
  }
  
  do_capture = state;
}

bool ALSAIOStream::Playback ()
{
  if (!initialized) {
    Q_LOG(ALSAIOStreamLog)
	     << "Not Initialized!" << std::endl;
    return false;
  }
  
  return do_playback;
}

bool ALSAIOStream::Capture ()
{
  if (!initialized) {
    Q_LOG(ALSAIOStreamLog)
	     << "Not Initialized!" << std::endl;
    return false;
  }
  
  return do_capture;
}

bool ALSAIOStream::StartIO ()
{
  if (!initialized) {
    Q_LOG(ALSAIOStreamLog)
	     << "Not Initialized!" << std::endl;
    return false;
  }
  if (io_running) {
    Q_LOG(ALSAIOStreamLog)
	     << "IO running!" << std::endl;
    return false;
  }
  
  if (do_playback)
    {
      if (!playback_card->Open (cname, SND_PCM_STREAM_PLAYBACK,
				SND_PCM_NONBLOCK) ) {
	Q_LOG(ALSAIOStreamLog)
		 << "Can not open playback device: " << cname << "!" << std::endl;
	return false;
      }
      if (!internal_config_params (playback_card, true, false) ) {
	Q_LOG(ALSAIOStreamLog)
		 << "Can not configure the playback parameters!" << std::endl;
	return false;
      }
      
      bool interleaved = playback_card->GetAccess () ==
	SND_PCM_ACCESS_RW_INTERLEAVED;
      channel_index channels = m_params.cur_pl_channels;
      
      // create the IO handling object
      switch (playback_card->GetFormat () )
	{
	case SND_PCM_FORMAT_S16:
	  if (interleaved)
	    write_converter = new ALSAConverter<int16_t, InterleavedTraits>
	      (channels);
	  else
	    write_converter = new ALSAConverter<int16_t, NonInterleavedTraits>
	      (channels);
	  break;
	case SND_PCM_FORMAT_S32:
	  if (interleaved)
	    write_converter = new ALSAConverter<int32_t, InterleavedTraits>
	      (channels);
	  else
	    write_converter = new ALSAConverter<int32_t, NonInterleavedTraits>
	      (channels);
	  break;
	default:
	  Q_LOG(ALSAIOStreamLog)
		 << "Bug: Format not handled here!" << std::endl;
	}
    }
  
  if (do_capture)
    {
      if (!capture_card->Open (cname, SND_PCM_STREAM_CAPTURE,
			       SND_PCM_NONBLOCK) ) {
	Q_LOG(ALSAIOStreamLog)
		 << "Can not open capture device: " << cname << "!" << std::endl;
	return false;
      }
      
      if (!internal_config_params (capture_card, false, false) ) {
	Q_LOG(ALSAIOStreamLog)
	  << "Can not configure the capture parameters!" << std::endl;
	return false;
      }
       
      cap_running = false;
      
      bool interleaved = capture_card->GetAccess () ==
	SND_PCM_ACCESS_RW_INTERLEAVED;
      channel_index channels = m_params.cur_ca_channels;
      
      // create the IO handling object
      switch (capture_card->GetFormat () )
	{
	case SND_PCM_FORMAT_S16:
	  if (interleaved)
	    read_converter = new ALSAConverter<int16_t, InterleavedTraits>
	      (channels);
	  else
	    read_converter = new ALSAConverter<int16_t, NonInterleavedTraits>
	      (channels);
	  break;
	case SND_PCM_FORMAT_S32:
	  if (interleaved)
	    read_converter = new ALSAConverter<int32_t, InterleavedTraits>
	      (channels);
	  else
	    read_converter = new ALSAConverter<int32_t, NonInterleavedTraits>
	      (channels);
	  break;
	default:
	  Q_LOG(ALSAIOStreamLog)
	    << "Bug: Format not handled here!" << std::endl;
	}
    }
  
  // prepare states
  stop_requested = false;
  
  cur_playback_period = 0;
  cur_capture_period = 0;
  
  pl_xruns = 0;
  ca_xruns = 0;
  
  m_is_first_loop = true;
  m_worked_in_last_loop = true;
  m_using_realtime_sched = false;
  
  io_running = true;
  return true;
}
    
bool ALSAIOStream::StopIO ()
{
  if (!io_running) {
    Q_LOG(ALSAIOStreamLog)
	     << "IO not running!" << std::endl;
    return false;
  }
  
  // TODO: add the real stop code
  stop_requested = true;
  return true;
}

bool ALSAIOStream::IORunning ()
{
  return io_running;
}

global_index ALSAIOStream::CurrentPos ()
{
  if (!io_running) {
    Q_LOG(ALSAIOStreamLog)
	     << "IO not running!" << std::endl;
  }
  
  if (do_playback)
    return cur_playback_period * pl_period_size;
  if (do_capture)
    return cur_capture_period * ca_period_size;
  
  Q_LOG(ALSAIOStreamLog)
	     << "Neither playback nor capture is running?!?" << std::endl;
  
  return 0;
}

local_index ALSAIOStream::Write (BasicStream* src, local_index start_index)
{
  if (!io_running) {
    Q_LOG(ALSAIOStreamLog)
	     << "IO not running!" << std::endl;
  }
  if (!do_playback) {
    Q_LOG(ALSAIOStreamLog)
	     << "playback not enabled!" << std::endl;
  }
  
  write_converter->MixWrite (src, start_index);
  snd_pcm_sframes_t written_size = write_converter->Write(playback_card);
  
  if (written_size > 0) {
    ++ cur_playback_period;
  }
  else {
    if (written_size == -EAGAIN) {
      Q_LOG(ALSAIOStreamLog)
	       << "we should try again ..." << std::endl;
    }
    else if (written_size == -EPIPE) { // xrun
      Q_LOG(ALSAIOStreamLog) << "P: XRUN ..." << std::endl;
      handle_xrun (playback_card);
      ++ pl_xruns;
    }
    written_size = 0;
  }
  
  return written_size;
}
  
local_index ALSAIOStream::Read (BasicStream* dst)
{
  if (!io_running) {
    Q_LOG(ALSAIOStreamLog)
	     << "IO not running!" << std::endl;
  }
  if (!do_capture) {
    Q_LOG(ALSAIOStreamLog)
	     << "capture not enabled!" << std::endl;
  }
  
  // maybe we need to start the capture ?
  // this should be more synchronized with the playback start!
  // and yet another maybe: this should not be in the Read method
  // but triggered seperately (for more exact control)
  if (!cap_running) {
    Q_LOG(ALSAIOStreamLog)
	     << "capture started ..." << std::endl;
    capture_card->Start ();
    cap_running = true;
  }
  
  snd_pcm_sframes_t read_size = read_converter->Read(capture_card,
					             dst->FreeSize() );
  
  if (read_size > 0) {
    read_converter->MixRead (dst, dst->FreeSize() );
    dst->SetUsedSize (dst->UsedSize() + read_size);
    ++ cur_capture_period;
  }
  else {
    if (read_size == -EAGAIN) {
      Q_LOG(ALSAIOStreamLog)
	<< "we should try again ..." << std::endl;
    }
    else if (read_size == -EPIPE) { // xrun
      Q_LOG(ALSAIOStreamLog)
	<< "C: XRUN ..." << std::endl;
      handle_xrun (capture_card);
      ++ ca_xruns;
    }
    read_size = 0;
  }
  
  return read_size;
}

/*
  SOME ALSA DOKU (do not delete it is rare!!! ...)
  
  Only to know the area you want to fill:
  
  while (!done) {
  poll(...)
  nframes = snd_pcm_avail_update(pcm);
  snd_pcm_mmap_begin(pcm, &areas, &offset, &nframes);
  process(..., areas, offset, nframes); // Contiguous frames
  snd_pcm_mmap_commit(pcm, offset, nframes);
  // Here you can choose to cycle or complete the residual nframes
  }
  
  "offset" is the frame offset of the first frame you have to fill and not
  always it's 0 on the first access (as you seems to expect, considered
  you didn't call old snd_pcm_mmap_offset)
  
  snd_pcm_mmap_begin(pcm, &areas, &offset, &frames);
  >snd_pcm_channel_area_t *a = &areas[channel];
  >address = a->addr + (a->first + a->step * offset) / 8;
*/

#ifdef TO_BE_OSTREAM
void ALSAIOStream::Debug ()
{
  Q_LOG(ALSAIOStreamLog) << "ALSAIOStream::Debug:" << std::endl;
  
  Q_LOG(ALSAIOStreamLog) << "  Period size: pl: " << pl_period_size
       << ", ca: " << ca_period_size << std::endl;
  
  Q_LOG(ALSAIOStreamLog) << "  Channels: playback: " << capture_channels
       << ", capture: " << playback_channels << std::endl;
  
  Q_LOG(ALSAIOStreamLog) << "  Initialized: " << initialized
       << ", io_running: " << io_running
       << ", do_playback: " << do_playback
       << ", do_capture: " << do_capture << std::endl;
  
  if (io_running)
    {
      Q_LOG(ALSAIOStreamLog) << "  stop requested: " << stop_requested << std::endl;
      
      Q_LOG(ALSAIOStreamLog) << "  Current period: playback: " << cur_playback_period
	   << ", capture: " << cur_capture_period << std::endl;
      
      Q_LOG(ALSAIOStreamLog) << std::endl;
    }
}
#endif

bool ALSAIOStream::reget_card_params ()
{
  if (!capture_card->Open (cname, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) )
    return false;
  if (!internal_config_params (capture_card, false, true) )
    return false;
  if (!capture_card->Close () )
    return false;
  
  if (!playback_card->Open (cname, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) )
    return false;
  if (!internal_config_params (playback_card, true, false) )
    return false;
  if (!playback_card->Close () )
    return false;
  
  return true;
}

void ALSAIOStream::dump_alsa_params_info (ALSAPCM* card)
{
  Q_LOG(ALSAIOStreamLog) << "format: " << card->GetFormat () << std::endl;
  
  Q_LOG(ALSAIOStreamLog) << "min_ch: " << card->GetChannelsMin () << std::endl;
  Q_LOG(ALSAIOStreamLog) << "max_ch: " << card->GetChannelsMax () << std::endl;

  Q_LOG(ALSAIOStreamLog) << "min_rate: " << card->GetRateMin () << std::endl;
  Q_LOG(ALSAIOStreamLog) << "max_rate: " << card->GetRateMax () << std::endl;
  
  Q_LOG(ALSAIOStreamLog) << "period_size: " << card->GetPeriodSize () << std::endl;
  Q_LOG(ALSAIOStreamLog) << "buffer_size: " << card->GetBufferSize () << std::endl;
  
  Q_LOG(ALSAIOStreamLog) << "periods_min: " << card->GetPeriodsMin () << std::endl;
  Q_LOG(ALSAIOStreamLog) << "periods_max: " << card->GetPeriodsMax () << std::endl;
  
  Q_LOG(ALSAIOStreamLog) << "periods: " << card->GetPeriods () << std::endl;
}

bool ALSAIOStream::internal_config_params (ALSAPCM* card, bool is_pl,
					   bool overwrite_params)
{
  dump_alsa_params_info (card);
  
  // TODO:
  // if the type is not real hardware, the max balues are inf.
  // TODO: Check if the current and min values are correct ...
  // if (card->Type () == SND_PCM_TYPE_HW) {
  
  if (is_pl) {
    m_params.min_pl_channels = card->GetChannelsMin ();
    m_params.max_pl_channels = card->GetChannelsMax ();
    m_params.cur_pl_channels = card->GetChannels ();
  }
  else {
    m_params.min_ca_channels = card->GetChannelsMin ();
    m_params.max_ca_channels = card->GetChannelsMax ();
    m_params.cur_ca_channels = card->GetChannels ();
  }
  
  // prefer non-interleaved data formats
  if (!card->SetAccess (SND_PCM_ACCESS_RW_NONINTERLEAVED) )
    if (!card->SetAccess (SND_PCM_ACCESS_RW_INTERLEAVED) ) {
      Q_LOG(ALSAIOStreamLog)
	       << "Neither non-interleaved nor interleaved formats are available!"
	       << std::endl;
      return false;
    }

  // TODO: store whether interleaved or non-interleaved format is in use
  
  // try the formats we support ...
  if ( !(card->SetFormat (SND_PCM_FORMAT_S16) ||
	 card->SetFormat (SND_PCM_FORMAT_S32)) )
    {
      Q_LOG(ALSAIOStreamLog)
	       << "No supported format available!"
	       << std::endl;
      return false;
    }
  
  if (is_pl) {
    if (!card->SetChannels (m_params.cur_pl_channels) ) {
      Q_LOG(ALSAIOStreamLog)
	       << "Failed to set playback channel count: !"
	       << m_params.cur_pl_channels << std::endl;
      return false;
    }
  }
  else {
    if (!card->SetChannels (m_params.cur_ca_channels) ) {
      Q_LOG(ALSAIOStreamLog)
	       << "Failed to set capture channel count: !"
	       << m_params.cur_ca_channels << std::endl;
      return false;
    }
  }
  
  if (overwrite_params) {
    m_params.min_rate = card->GetRateMin ();
    m_params.max_rate = card->GetRateMax ();
    m_params.cur_rate = 44100;
  }
  
  if (!card->SetRateNear (m_params.cur_rate) )
    return false;
  
  if (overwrite_params) {
    m_params.min_periods = card->GetPeriodsMin ();
    m_params.max_periods = card->GetPeriodsMax ();
    m_params.cur_periods = Utility::limit_min_max (4,
						   m_params.min_periods,
						   m_params.max_periods);
  }
  
  Q_LOG(ALSAIOStreamLog) << "try to configure " << m_params.cur_periods << " periods ..." << std::endl;
  if (!card->SetPeriodsNear (m_params.cur_periods) )
    {
      Q_LOG(ALSAIOStreamLog) << "SetPeriodsNear faild ..." << std::endl;
      return false;
    }
  
  if (overwrite_params) {
    m_params.min_period_time = 1000;
    m_params.max_period_time = 100000;
    m_params.cur_period_time = 100000 / m_params.cur_periods;
  }
  
  int t_period_size;
  if (is_pl)
    t_period_size = pl_period_size;
  else
    t_period_size = ca_period_size;
  
  if (t_period_size == 0)
    {
      Q_LOG(ALSAIOStreamLog) << "t_period_size == 0 -> using time near ..." << std::endl;
      
      if (!card->SetPeriodTimeNear (m_params.cur_period_time) )
	return false;
    }
  else
    {
      Q_LOG(ALSAIOStreamLog) << "period_size == " << t_period_size << " -> using size ..." << std::endl;
       if (!card->SetPeriodSize (t_period_size) )
	return false;
    }
  
  if (!card->InstallParams () )
    return false;
  
  dump_alsa_params_info (card);
  
  if (!card->SetSwStartThreshold (card->GetPeriods () / 2) )
    return false;
  
  if (!card->InstallSwParams () )
    return false;
  
  Q_LOG(ALSAIOStreamLog) << "start_th: " << card->GetSwStartThreshold () << std::endl;
  Q_LOG(ALSAIOStreamLog) << "stop_th: " << card->GetSwStopThreshold () << std::endl;
  
  if (is_pl) {
    pl_period_size = card->GetPeriodSize ();
    Q_LOG(ALSAIOStreamLog) << "Set pl_period_size to: " << pl_period_size << std::endl;
    
    pl_periods = card->GetPeriods ();
    Q_LOG(ALSAIOStreamLog) << "Set pl_periods to: " << pl_periods << std::endl;
  }
  else
  {
    ca_period_size = card->GetPeriodSize ();
    Q_LOG(ALSAIOStreamLog) << "Set ca_period_size to: " << ca_period_size << std::endl;
    
    ca_periods = card->GetPeriods ();
    Q_LOG(ALSAIOStreamLog) << "Set ca_periods to: " << ca_periods << std::endl;
  }
  
  return true;
}

void ALSAIOStream::handle_xrun (ALSAPCM* card)
{
  Q_LOG(ALSAIOStreamLog) << std::endl;
  
  card->UpdateStatus ();
  
  if (card->GetStatusState () != SND_PCM_STATE_XRUN) {
    Q_LOG(ALSAIOStreamLog)
	     << "Called but card not in XRUN state!" << std::endl;
    return;
  }
  
  struct timeval now, diff, tstamp;
  gettimeofday(&now, 0);
  
  card->GetTriggerTStamp (&tstamp);
  timersub(&now, &tstamp, &diff);
  Q_LOG(ALSAIOStreamLog)
	   << "XRUN !!! (at least "
	   << diff.tv_sec * 1000 + diff.tv_usec / 1000.0
	   << " ms long)" << std::endl;
  
  if (!card->Prepare () )
    Q_LOG(ALSAIOStreamLog)
	     << "BUG: can't reprepare during XRun!! " << std::endl;
}

// the second thread ...
#ifdef OLD_CODE
void ALSAIOStream::Loop ()
{
  bool data = true;
  
  if (m_is_first_loop) // do first loop things (real-time scheduling ...)
    {
      //m_using_realtime_sched = ThreadHelper::EnableRealtimeScheduling ();
      //if (!m_using_realtime_sched)
      //  Q_LOG(ALSAIOStreamLog) << "Warning: ALSANode: Can't enable realtime scheduling ..." << std::endl;
      m_is_first_loop = false;
    }
  
#ifdef DEBUG_GSML_IO_ALSA
  Q_LOG(ALSAIOStreamLog) << "ALSAIOStream::Loop:" << std::endl;
#endif
  try {
  if (!stop_requested)
    {
      // start capture ??
      if (do_capture && !cap_running)
	{
	  Q_LOG(ALSAIOStreamLog) << "capture is not running ... -> starting now." << std::endl;
	  if (!capture_card->Start () )
	    Q_LOG(ALSAIOStreamLog) << "can't start capture!" << std::endl;
	  else
	    cap_running = true;
	}
      
      // sleep when not worked in last loop
      if (!m_worked_in_last_loop)
	{
	  // get best delay from ALSA
	  int pl_avail, ca_avail, xx_needed;
	  
	  if (do_playback) {
	    playback_card->UpdateStatus ();
	    pl_avail = playback_card->GetStatusAvail ();
	  }
	  if (do_capture) {
	    capture_card->UpdateStatus ();
	    ca_avail = capture_card->GetStatusAvail ();
	  }
	  
	  if (!do_capture)
	    xx_needed = pl_period_size - pl_avail;
	  else if (!do_playback)
	    xx_needed = ca_period_size - ca_avail;
	  else
	    xx_needed = std::min (pl_period_size - pl_avail,
				  ca_period_size - ca_avail);
	  
	  //	  Q_LOG(ALSAIOStreamLog) << "pl_avail: " << pl_avail << ", ca_avail: " << ca_avail
	  //           << ", xx_needed: " << xx_needed << std::endl;
	  
	  xx_needed = Utility::limit_min (xx_needed, 0);
	  
	  // TODO: hw-samplerate usage ...
	  int xx_delay = (1000000 / 44100) * xx_needed;
	  
	  // Q_LOG(ALSAIOStreamLog) << "xx_delay: " << xx_delay << std::endl;
	  
	  // make sure other tasks get time ...
	  if (m_using_realtime_sched)
	    ThreadHelper::USleep (xx_delay);
	  else
	    usleep (xx_delay);
	}
      
      m_worked_in_last_loop = false;
      
      if (do_playback)
	{
	  
	  data = write_handler->TryMix (m_mixer);
	  
	  int written = write_handler->TryWrite ();
	  
	  if (written > 0) {
	    ++cur_playback_period;
	    m_worked_in_last_loop = true;
	  }
	  else {
	    if (written == -EAGAIN) {
	    }
	    else if (written == -EPIPE) { // xrun
	      Q_LOG(ALSAIOStreamLog) << "P: XRUN ..." << std::endl;
	      handle_xrun (playback_card);
	      ++pl_xruns;
	    }
	    else
	      Q_LOG(ALSAIOStreamLog) << "P: WriteI faild: " << snd_strerror (written)
		       << " " << written << std::endl;
	  }
	}
      if (do_capture)
	{
	  int read = read_handler->TryRead ();
	  
	  if (read > 0) {
	    ++cur_capture_period;
	    m_worked_in_last_loop = true;
	  }
	  else {
	    if (read == -EAGAIN) {
	    }
	    else if (read == -EPIPE) { // xrun
	      Q_LOG(ALSAIOStreamLog) << "C: XRUN ..." << std::endl;
	      handle_xrun (capture_card);
	      ++ca_xruns;
	    }
	    else
	      Q_LOG(ALSAIOStreamLog) << "C: Read faild: " << snd_strerror (read) << " " << read << std::endl;
	  }
	  bool temp = read_handler->TryMix (m_mixer);
	  data = temp && data;
	}
    }
  
  // no else - because it might be changed in the io loop-part ...
  if (!data || stop_requested)
    {
      //#ifdef DEBUG_GSML_IO_ALSA
      Q_LOG(ALSAIOStreamLog) << "  Loop: END OF IO" << std::endl;
      //#endif
      
      /* clear some states and data */
      
      Q_LOG(ALSAIOStreamLog) << "Closind cards ..." << std::endl;
      if (do_playback)
	playback_card->Close ();
      if (do_capture)
	capture_card->Close ();
      
      del_helper2 (read_handler);
      del_helper2 (write_handler);
      
      io_running = false;
      // m_mixer->IOFinishedNotification ();
      
      //Wait ();
    }
  }
  catch (GSML::BasicExcept e)
    {
      Q_LOG(ALSAIOStreamLog) << std::endl 
	   << "!! ALSAIOStream::Loop caught exception: !!" << std::endl
	   << e.info << std::endl;
      throw;
    }
}

#endif
