<commit_before>/********************************************************************
 * The iTCH project - Copyright (c) 2007-2011 Dustin Graves
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <QtCore/QTime>
#include <QtNetwork/QHostInfo>
#include "PiTCHNetworkDialog.h"
#include "PiTCHWindow.h"
#include "ui_PiTCHWindow.h"

namespace
{
  // Default timer values
  enum
  {
    BUTTON_HELD_DELAY = 500,
    POSITION_INTERVAL = 1000
  };

  // Create formatted time string for time elapsed/remaining display
  // Formats:
  //   Seconds only -> 0:ss
  //   Has minutes  -> m:ss
  //   Has hours    -> H:mm:ss
  // Will not accept more than 24 hours
  QString secondsToTimePositionString(int seconds)
  {
    QTime converter = QTime().addSecs(seconds);

    if (converter.hour() > 0)
    {
      return converter.toString("H:mm:ss");
    }
    else
    {
      return converter.toString("m:ss");
    }
  }

  QString requestTypeToString(iTCH::ClientRequest::Type type)
  {
    switch (type)
    {
    case iTCH::ClientRequest::BACKTRACK:
      return "Back track";
    case iTCH::ClientRequest::FASTFORWARD:
      return "Fast forward";
    case iTCH::ClientRequest::NEXTTRACK:
      return "Next track";
    case iTCH::ClientRequest::PAUSE:
      return "Pause";
    case iTCH::ClientRequest::PLAY:
      return "Play";
    case iTCH::ClientRequest::PLAYPAUSE:
      return "Toggle play-pause";
    case iTCH::ClientRequest::PREVIOUSTRACK:
      return "Previous track";
    case iTCH::ClientRequest::RESUME:
      return "Resume";
    case iTCH::ClientRequest::REWIND:
      return "Rewind";
    case iTCH::ClientRequest::STOP:
      return "Stop";
    case iTCH::ClientRequest::GET_SOUNDVOLUME:
      return "Get volume";
    case iTCH::ClientRequest::PUT_SOUNDVOLUME:
      return "Set volume";
    case iTCH::ClientRequest::GET_MUTE:
      return "Get mute";
    case iTCH::ClientRequest::PUT_MUTE:
      return "Set mute";
    case iTCH::ClientRequest::GET_PLAYERPOSITION:
      return "Get player position";
    case iTCH::ClientRequest::PUT_PLAYERPOSITION:
      return "Set player position";
    case iTCH::ClientRequest::GET_PLAYERSTATE:
      return "Get player state";
    case iTCH::ClientRequest::GET_CURRENTTRACK:
      return "Get current track";
    case iTCH::ClientRequest::GET_CURRENTPLAYLIST:
      return "Get current playlist";
    default:
      return "Unrecognized";
    }
  }
} // End of anonymous namespace

PiTCHWindow::PiTCHWindow(QWidget *parent) :
  QMainWindow(parent),
  ui_(new Ui::PiTCHWindow),
  serverInfo_(QHostInfo::localHostName(), 8049),
  buttonHeld_(false),
  buttonHeldDelay_(BUTTON_HELD_DELAY),
  sequenceId_(0),
  positionInterval_(POSITION_INTERVAL),
  playing_(false)
{
  ui_->setupUi(this);
  ui_->statusBar->showMessage(tr("Unconnected"));

  createStandardIcons();

  connect(&client_, SIGNAL(hostnameResolved()), this, SLOT(resolvedHostname()));
  connect(&client_, SIGNAL(connected()), this, SLOT(connectedToServer()));
  connect(&client_, SIGNAL(disconnected(bool,QString)), this, SLOT(disconnectedFromServer(bool,QString)));
  connect(&client_, SIGNAL(receivedMessage(iTCH::EnvelopePtr)), this, SLOT(processMessage(iTCH::EnvelopePtr)));
  connect(&client_, SIGNAL(protocolError(QString)), this, SLOT(processProtocolError(QString)));
}

PiTCHWindow::~PiTCHWindow()
{
  client_.closeConnection();
  delete ui_;
}

void PiTCHWindow::changeEvent(QEvent *e)
{
  QMainWindow::changeEvent(e);
  switch (e->type())
  {
  case QEvent::LanguageChange:
    ui_->retranslateUi(this);
    break;
  default:
    break;
  }
}

void PiTCHWindow::resolvedHostname()
{
  // Host was found, now QTcpSocket is connecting
  ui_->statusBar->showMessage(tr("Connecting..."));
}

void PiTCHWindow::connectedToServer()
{
  ui_->statusBar->showMessage(tr("Connected"));

  // Request current track, sound volume, mute, player state, and player position
  sendTrackedRequest(iTCH::MessageBuilder::makeGetCurrentTrackRequest(nextSequenceId()));
  sendTrackedRequest(iTCH::MessageBuilder::makeGetSoundVolumeRequest(nextSequenceId()));
  sendTrackedRequest(iTCH::MessageBuilder::makeGetMuteRequest(nextSequenceId()));
  sendTrackedRequest(iTCH::MessageBuilder::makeGetPlayerStateRequest(nextSequenceId()));
  sendTrackedRequest(iTCH::MessageBuilder::makeGetPlayerPositionRequest(nextSequenceId()));
}

void PiTCHWindow::disconnectedFromServer(bool closedByHost, const QString &message)
{
  stopPositionTimer();

  QString status(tr("Unconnected"));

  ui_->networkButton->blockSignals(true);
  ui_->networkButton->setChecked(false);
  ui_->networkButton->blockSignals(false);

  if (closedByHost)
  {
    ui_->statusBar->showMessage(status);
  }
  else
  {
    ui_->statusBar->showMessage(QString("%1: %2").arg(status).arg(message));

    // Start auto-connect
  }
}

void PiTCHWindow::processMessage(const iTCH::EnvelopePtr envelope)
{
  // Determine message type
  switch (envelope->type())
  {
  case iTCH::Envelope::SERVERNOTIFICATION:
    processNotification(envelope);
    break;
  case iTCH::Envelope::SERVERRESPONSE:
    processResponse(envelope);
    break;
  default:
    processProtocolError("Received unrecognized message from server");
  }
}

void PiTCHWindow::processNotification(iTCH::EnvelopePtr envelope)
{
  assert(envelope->has_notification());
  switch (envelope->notification().type())
  {
  case iTCH::ServerNotification::VOLUMECHANGED:
    // Get the volume/mute status
    sendTrackedRequest(iTCH::MessageBuilder::makeGetSoundVolumeRequest(nextSequenceId()));
    sendTrackedRequest(iTCH::MessageBuilder::makeGetMuteRequest(nextSequenceId()));
    break;
  case iTCH::ServerNotification::PLAYINGSTARTED:
    // Get the current track info and initial time slider position
    setPlayerState(true);
    sendTrackedRequest(iTCH::MessageBuilder::makeGetCurrentTrackRequest(nextSequenceId()));
    sendTrackedRequest(iTCH::MessageBuilder::makeGetPlayerPositionRequest(nextSequenceId()));
    break;
  case iTCH::ServerNotification::PLAYINGSTOPPED:
    // Get the current track info and final time slider position
    setPlayerState(false);
    break;
  case iTCH::ServerNotification::TRACKINFOCHANGED:
    sendTrackedRequest(iTCH::MessageBuilder::makeGetCurrentTrackRequest(nextSequenceId()));
    break;
  default:
    processProtocolError("Received unrecognized notification type");
  }
}

void PiTCHWindow::processResponse(iTCH::EnvelopePtr envelope)
{
  assert(envelope->has_response());
  bool isValid = false;
  unsigned int seqid = envelope->response().seqid();

  // Find the original request
  PendingRequests::const_iterator iter = requests_.find(seqid);
  if (iter == requests_.end())
  {
    processProtocolError("Server sent an unrequested response");
  }
  else
  {
    assert(iter.value()->has_request());
    const iTCH::ClientRequest &request = iter.value()->request();

    // Make sure envelope contains a server response message with valid values
    if (iTCH::MessageBuilder::containsValidServerResponse(envelope, request))
    {
      const iTCH::ServerResponse &response = envelope->response();
      if (!response.success())
      {
        // Report failure of a request
        processProtocolError(QString("%1 request failed: %2")
          .arg(requestTypeToString(request.type()))
          .arg(response.error_message().c_str()));
      }
      else
      {
        switch (response.value().type())
        {
        case iTCH::ServerResponse::Value::VOLUME:
          setSoundVolume(response.value().volume());
          break;
        case iTCH::ServerResponse::Value::MUTE:
          setMute(response.value().mute());
          break;
        case iTCH::ServerResponse::Value::POSITION:
          setPlayerPosition(response.value().position());
          break;
        case iTCH::ServerResponse::Value::STATE:
          setPlayerState(response.value().state() == iTCH::PLAYING);
          break;
        case iTCH::ServerResponse::Value::TRACK:
          setCurrentTrack(response.value().track());
          break;
        default:
          processProtocolError("Received unrecognized notification type");
        }
      }
    }

    // Remove the request from the pending request list, it has been processed
    requests_.remove(seqid);
  }
}

void PiTCHWindow::processProtocolError(const QString &message)
{
}

void PiTCHWindow::timeSliderValueChanged(int value)
{
  sendTrackedRequest(iTCH::MessageBuilder::makePutPlayerPositionRequest(nextSequenceId(), value));
}

void PiTCHWindow::backButtonPressed()
{
  connect(&buttonTimer_, SIGNAL(timeout()), this, SLOT(rewindTimeout()));
  buttonTimer_.setSingleShot(true);
  buttonTimer_.start(buttonHeldDelay_);
}

void PiTCHWindow::backButtonReleased()
{
  buttonTimer_.stop();
  buttonTimer_.disconnect();

  if (buttonHeld_)
  {
    buttonHeld_ = false;

    // Restore the player state to playing or paused, sending a play
    // or paused request doesn't seem to stop the fast forward so we
    // send pauseplay twice
    sendTrackedRequest(iTCH::MessageBuilder::makePlayPauseRequest(nextSequenceId()));
    sendTrackedRequest(iTCH::MessageBuilder::makePlayPauseRequest(nextSequenceId()));
  }
  else
  {
    sendTrackedRequest(iTCH::MessageBuilder::makeBackTrackRequest(nextSequenceId()));
  }
}

void PiTCHWindow::rewindTimeout()
{
  buttonHeld_ = true;
  sendTrackedRequest(iTCH::MessageBuilder::makeRewindRequest(nextSequenceId()));
}

void PiTCHWindow::forwardButtonPressed()
{
  connect(&buttonTimer_, SIGNAL(timeout()), this, SLOT(fastForwardTimeout()));
  buttonTimer_.setSingleShot(true);
  buttonTimer_.start(buttonHeldDelay_);
}

void PiTCHWindow::forwardButtonReleased()
{
  buttonTimer_.stop();
  buttonTimer_.disconnect();

  if (buttonHeld_)
  {
    buttonHeld_ = false;

    // Restore the player state to playing or paused, sending a play
    // or paused request doesn't seem to stop the fast forward so we
    // send pauseplay twice
    sendTrackedRequest(iTCH::MessageBuilder::makePlayPauseRequest(nextSequenceId()));
    sendTrackedRequest(iTCH::MessageBuilder::makePlayPauseRequest(nextSequenceId()));
  }
  else
  {
    sendTrackedRequest(iTCH::MessageBuilder::makeNextTrackRequest(nextSequenceId()));
  }
}

void PiTCHWindow::fastForwardTimeout()
{
  buttonHeld_ = true;
  sendTrackedRequest(iTCH::MessageBuilder::makeFastForwardRequest(nextSequenceId()));
}

void PiTCHWindow::playPauseButtonClicked()
{
  sendTrackedRequest(iTCH::MessageBuilder::makePlayPauseRequest(nextSequenceId()));
}

void PiTCHWindow::minVolumeButtonClicked()
{
  ui_->volumeSlider->setValue(0);
}

void PiTCHWindow::maxVolumeButtonClicked()
{
  ui_->volumeSlider->setValue(100);
}

void PiTCHWindow::volumeSliderValueChanged(int value)
{
  sendTrackedRequest(iTCH::MessageBuilder::makePutSoundVolumeRequest(nextSequenceId(), value));
}

void PiTCHWindow::networkButtonToggled(bool isChecked)
{
  if (isChecked)
  {
    PiTCHNetworkDialog dialog(serverInfo_, this);

    if (QDialog::Accepted == dialog.exec())
    {
      // Attempt the server connection
      ui_->statusBar->showMessage(tr("Looking up host..."));
      client_.openConnection(dialog.getNetworkInfo());
    }
  }
  else
  {
    client_.closeConnection();
  }
}

void PiTCHWindow::setSoundVolume(int newVolume)
{
  ui_->volumeSlider->blockSignals(true);
  ui_->volumeSlider->setValue(newVolume);
  ui_->volumeSlider->blockSignals(false);
}

void PiTCHWindow::setMute(bool isMute)
{
  if (isMute)
  {
    // We interpret mute as setting volume to 0
    setSoundVolume(0);
  }
}

void PiTCHWindow::setPlayerPosition(int newPosition)
{
  ui_->timeSlider->blockSignals(true);
  ui_->timeSlider->setValue(newPosition);
  ui_->timeSlider->blockSignals(false);

  ui_->timeElapsed->setText(secondsToTimePositionString(newPosition));
  ui_->timeRemaining->setText(QString("-%1").arg(secondsToTimePositionString(currentTrack_.duration() - newPosition)));
}

void PiTCHWindow::setPlayerState(bool playing)
{
  if (playing_ != playing)
  {
    if (playing)
    {
      startPositionTimer();
      ui_->playPauseToggleButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
    }
    else
    {
      startPositionTimer();
      ui_->playPauseToggleButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
    }

    playing_ = playing;
  }
}

void PiTCHWindow::setCurrentTrack(const iTCH::Track &track)
{
  currentTrack_.CopyFrom(track);
  ui_->songTitle->setText(track.name().c_str());

  if (!track.artist().empty() && !track.album().empty())
  {
    ui_->artist->setText(QString("%1 -- %2")
      .arg(track.artist().c_str())
      .arg(track.album().c_str()));
  }
  else if (!track.artist().empty())
  {
    ui_->artist->setText(track.artist().c_str());
  }
  else if (!track.album().empty())
  {
    ui_->artist->setText(track.album().c_str());
  }
  else
  {
    ui_->artist->setText("");
  }

  ui_->timeSlider->setMaximum(track.duration());
}

void PiTCHWindow::requestPlayerPosition()
{
  sendTrackedRequest(iTCH::MessageBuilder::makeGetPlayerPositionRequest(nextSequenceId()));
}

void PiTCHWindow::createStandardIcons()
{
  ui_->backButton->setIcon(style()->standardIcon(QStyle::SP_MediaSkipBackward));
  ui_->playPauseToggleButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
  ui_->forwardButton->setIcon(style()->standardIcon(QStyle::SP_MediaSkipForward));
  ui_->minVolumeButton->setIcon(style()->standardIcon(QStyle::SP_MediaVolumeMuted));
  ui_->maxVolumeButton->setIcon(style()->standardIcon(QStyle::SP_MediaVolume));
  ui_->networkButton->setIcon(style()->standardIcon(QStyle::SP_BrowserReload));
}

unsigned long PiTCHWindow::nextSequenceId()
{
  return ++sequenceId_;
}

void PiTCHWindow::sendTrackedRequest(iTCH::EnvelopePtr envelope)
{
  assert(envelope->has_request());
  requests_.insert(envelope->request().seqid(), envelope);
  client_.sendMessage(envelope);
}

void PiTCHWindow::startPositionTimer()
{
  // Start the timer to peridoically request time slider position
  if (!positionTimer_.isActive())
  {
    connect(&positionTimer_, SIGNAL(timeout()), this, SLOT(requestPlayerPosition()));
    positionTimer_.start(positionInterval_);
  }
}

void PiTCHWindow::stopPositionTimer()
{
  if (positionTimer_.isActive())
  {
    positionTimer_.stop();
    positionTimer_.disconnect();
  }
}
<commit_msg>Fixed setPlayerState() bug<commit_after>/********************************************************************
 * The iTCH project - Copyright (c) 2007-2011 Dustin Graves
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <QtCore/QTime>
#include <QtNetwork/QHostInfo>
#include "PiTCHNetworkDialog.h"
#include "PiTCHWindow.h"
#include "ui_PiTCHWindow.h"

namespace
{
  // Default timer values
  enum
  {
    BUTTON_HELD_DELAY = 500,
    POSITION_INTERVAL = 1000
  };

  // Create formatted time string for time elapsed/remaining display
  // Formats:
  //   Seconds only -> 0:ss
  //   Has minutes  -> m:ss
  //   Has hours    -> H:mm:ss
  // Will not accept more than 24 hours
  QString secondsToTimePositionString(int seconds)
  {
    QTime converter = QTime().addSecs(seconds);

    if (converter.hour() > 0)
    {
      return converter.toString("H:mm:ss");
    }
    else
    {
      return converter.toString("m:ss");
    }
  }

  QString requestTypeToString(iTCH::ClientRequest::Type type)
  {
    switch (type)
    {
    case iTCH::ClientRequest::BACKTRACK:
      return "Back track";
    case iTCH::ClientRequest::FASTFORWARD:
      return "Fast forward";
    case iTCH::ClientRequest::NEXTTRACK:
      return "Next track";
    case iTCH::ClientRequest::PAUSE:
      return "Pause";
    case iTCH::ClientRequest::PLAY:
      return "Play";
    case iTCH::ClientRequest::PLAYPAUSE:
      return "Toggle play-pause";
    case iTCH::ClientRequest::PREVIOUSTRACK:
      return "Previous track";
    case iTCH::ClientRequest::RESUME:
      return "Resume";
    case iTCH::ClientRequest::REWIND:
      return "Rewind";
    case iTCH::ClientRequest::STOP:
      return "Stop";
    case iTCH::ClientRequest::GET_SOUNDVOLUME:
      return "Get volume";
    case iTCH::ClientRequest::PUT_SOUNDVOLUME:
      return "Set volume";
    case iTCH::ClientRequest::GET_MUTE:
      return "Get mute";
    case iTCH::ClientRequest::PUT_MUTE:
      return "Set mute";
    case iTCH::ClientRequest::GET_PLAYERPOSITION:
      return "Get player position";
    case iTCH::ClientRequest::PUT_PLAYERPOSITION:
      return "Set player position";
    case iTCH::ClientRequest::GET_PLAYERSTATE:
      return "Get player state";
    case iTCH::ClientRequest::GET_CURRENTTRACK:
      return "Get current track";
    case iTCH::ClientRequest::GET_CURRENTPLAYLIST:
      return "Get current playlist";
    default:
      return "Unrecognized";
    }
  }
} // End of anonymous namespace

PiTCHWindow::PiTCHWindow(QWidget *parent) :
  QMainWindow(parent),
  ui_(new Ui::PiTCHWindow),
  serverInfo_(QHostInfo::localHostName(), 8049),
  buttonHeld_(false),
  buttonHeldDelay_(BUTTON_HELD_DELAY),
  sequenceId_(0),
  positionInterval_(POSITION_INTERVAL),
  playing_(false)
{
  ui_->setupUi(this);
  ui_->statusBar->showMessage(tr("Unconnected"));

  createStandardIcons();

  connect(&client_, SIGNAL(hostnameResolved()), this, SLOT(resolvedHostname()));
  connect(&client_, SIGNAL(connected()), this, SLOT(connectedToServer()));
  connect(&client_, SIGNAL(disconnected(bool,QString)), this, SLOT(disconnectedFromServer(bool,QString)));
  connect(&client_, SIGNAL(receivedMessage(iTCH::EnvelopePtr)), this, SLOT(processMessage(iTCH::EnvelopePtr)));
  connect(&client_, SIGNAL(protocolError(QString)), this, SLOT(processProtocolError(QString)));
}

PiTCHWindow::~PiTCHWindow()
{
  client_.closeConnection();
  delete ui_;
}

void PiTCHWindow::changeEvent(QEvent *e)
{
  QMainWindow::changeEvent(e);
  switch (e->type())
  {
  case QEvent::LanguageChange:
    ui_->retranslateUi(this);
    break;
  default:
    break;
  }
}

void PiTCHWindow::resolvedHostname()
{
  // Host was found, now QTcpSocket is connecting
  ui_->statusBar->showMessage(tr("Connecting..."));
}

void PiTCHWindow::connectedToServer()
{
  ui_->statusBar->showMessage(tr("Connected"));

  // Request current track, sound volume, mute, player state, and player position
  sendTrackedRequest(iTCH::MessageBuilder::makeGetCurrentTrackRequest(nextSequenceId()));
  sendTrackedRequest(iTCH::MessageBuilder::makeGetSoundVolumeRequest(nextSequenceId()));
  sendTrackedRequest(iTCH::MessageBuilder::makeGetMuteRequest(nextSequenceId()));
  sendTrackedRequest(iTCH::MessageBuilder::makeGetPlayerStateRequest(nextSequenceId()));
  sendTrackedRequest(iTCH::MessageBuilder::makeGetPlayerPositionRequest(nextSequenceId()));
}

void PiTCHWindow::disconnectedFromServer(bool closedByHost, const QString &message)
{
  stopPositionTimer();

  QString status(tr("Unconnected"));

  ui_->networkButton->blockSignals(true);
  ui_->networkButton->setChecked(false);
  ui_->networkButton->blockSignals(false);

  if (closedByHost)
  {
    ui_->statusBar->showMessage(status);
  }
  else
  {
    ui_->statusBar->showMessage(QString("%1: %2").arg(status).arg(message));

    // Start auto-connect
  }
}

void PiTCHWindow::processMessage(const iTCH::EnvelopePtr envelope)
{
  // Determine message type
  switch (envelope->type())
  {
  case iTCH::Envelope::SERVERNOTIFICATION:
    processNotification(envelope);
    break;
  case iTCH::Envelope::SERVERRESPONSE:
    processResponse(envelope);
    break;
  default:
    processProtocolError("Received unrecognized message from server");
  }
}

void PiTCHWindow::processNotification(iTCH::EnvelopePtr envelope)
{
  assert(envelope->has_notification());
  switch (envelope->notification().type())
  {
  case iTCH::ServerNotification::VOLUMECHANGED:
    // Get the volume/mute status
    sendTrackedRequest(iTCH::MessageBuilder::makeGetSoundVolumeRequest(nextSequenceId()));
    sendTrackedRequest(iTCH::MessageBuilder::makeGetMuteRequest(nextSequenceId()));
    break;
  case iTCH::ServerNotification::PLAYINGSTARTED:
    // Get the current track info and initial time slider position
    setPlayerState(true);
    sendTrackedRequest(iTCH::MessageBuilder::makeGetCurrentTrackRequest(nextSequenceId()));
    sendTrackedRequest(iTCH::MessageBuilder::makeGetPlayerPositionRequest(nextSequenceId()));
    break;
  case iTCH::ServerNotification::PLAYINGSTOPPED:
    // Get the current track info and final time slider position
    setPlayerState(false);
    break;
  case iTCH::ServerNotification::TRACKINFOCHANGED:
    sendTrackedRequest(iTCH::MessageBuilder::makeGetCurrentTrackRequest(nextSequenceId()));
    break;
  default:
    processProtocolError("Received unrecognized notification type");
  }
}

void PiTCHWindow::processResponse(iTCH::EnvelopePtr envelope)
{
  assert(envelope->has_response());
  bool isValid = false;
  unsigned int seqid = envelope->response().seqid();

  // Find the original request
  PendingRequests::const_iterator iter = requests_.find(seqid);
  if (iter == requests_.end())
  {
    processProtocolError("Server sent an unrequested response");
  }
  else
  {
    assert(iter.value()->has_request());
    const iTCH::ClientRequest &request = iter.value()->request();

    // Make sure envelope contains a server response message with valid values
    if (iTCH::MessageBuilder::containsValidServerResponse(envelope, request))
    {
      const iTCH::ServerResponse &response = envelope->response();
      if (!response.success())
      {
        // Report failure of a request
        processProtocolError(QString("%1 request failed: %2")
          .arg(requestTypeToString(request.type()))
          .arg(response.error_message().c_str()));
      }
      else
      {
        switch (response.value().type())
        {
        case iTCH::ServerResponse::Value::VOLUME:
          setSoundVolume(response.value().volume());
          break;
        case iTCH::ServerResponse::Value::MUTE:
          setMute(response.value().mute());
          break;
        case iTCH::ServerResponse::Value::POSITION:
          setPlayerPosition(response.value().position());
          break;
        case iTCH::ServerResponse::Value::STATE:
          setPlayerState(response.value().state() == iTCH::PLAYING);
          break;
        case iTCH::ServerResponse::Value::TRACK:
          setCurrentTrack(response.value().track());
          break;
        default:
          processProtocolError("Received unrecognized notification type");
        }
      }
    }

    // Remove the request from the pending request list, it has been processed
    requests_.remove(seqid);
  }
}

void PiTCHWindow::processProtocolError(const QString &message)
{
}

void PiTCHWindow::timeSliderValueChanged(int value)
{
  sendTrackedRequest(iTCH::MessageBuilder::makePutPlayerPositionRequest(nextSequenceId(), value));
}

void PiTCHWindow::backButtonPressed()
{
  connect(&buttonTimer_, SIGNAL(timeout()), this, SLOT(rewindTimeout()));
  buttonTimer_.setSingleShot(true);
  buttonTimer_.start(buttonHeldDelay_);
}

void PiTCHWindow::backButtonReleased()
{
  buttonTimer_.stop();
  buttonTimer_.disconnect();

  if (buttonHeld_)
  {
    buttonHeld_ = false;

    // Restore the player state to playing or paused, sending a play
    // or paused request doesn't seem to stop the fast forward so we
    // send pauseplay twice
    sendTrackedRequest(iTCH::MessageBuilder::makePlayPauseRequest(nextSequenceId()));
    sendTrackedRequest(iTCH::MessageBuilder::makePlayPauseRequest(nextSequenceId()));
  }
  else
  {
    sendTrackedRequest(iTCH::MessageBuilder::makeBackTrackRequest(nextSequenceId()));
  }
}

void PiTCHWindow::rewindTimeout()
{
  buttonHeld_ = true;
  sendTrackedRequest(iTCH::MessageBuilder::makeRewindRequest(nextSequenceId()));
}

void PiTCHWindow::forwardButtonPressed()
{
  connect(&buttonTimer_, SIGNAL(timeout()), this, SLOT(fastForwardTimeout()));
  buttonTimer_.setSingleShot(true);
  buttonTimer_.start(buttonHeldDelay_);
}

void PiTCHWindow::forwardButtonReleased()
{
  buttonTimer_.stop();
  buttonTimer_.disconnect();

  if (buttonHeld_)
  {
    buttonHeld_ = false;

    // Restore the player state to playing or paused, sending a play
    // or paused request doesn't seem to stop the fast forward so we
    // send pauseplay twice
    sendTrackedRequest(iTCH::MessageBuilder::makePlayPauseRequest(nextSequenceId()));
    sendTrackedRequest(iTCH::MessageBuilder::makePlayPauseRequest(nextSequenceId()));
  }
  else
  {
    sendTrackedRequest(iTCH::MessageBuilder::makeNextTrackRequest(nextSequenceId()));
  }
}

void PiTCHWindow::fastForwardTimeout()
{
  buttonHeld_ = true;
  sendTrackedRequest(iTCH::MessageBuilder::makeFastForwardRequest(nextSequenceId()));
}

void PiTCHWindow::playPauseButtonClicked()
{
  sendTrackedRequest(iTCH::MessageBuilder::makePlayPauseRequest(nextSequenceId()));
}

void PiTCHWindow::minVolumeButtonClicked()
{
  ui_->volumeSlider->setValue(0);
}

void PiTCHWindow::maxVolumeButtonClicked()
{
  ui_->volumeSlider->setValue(100);
}

void PiTCHWindow::volumeSliderValueChanged(int value)
{
  sendTrackedRequest(iTCH::MessageBuilder::makePutSoundVolumeRequest(nextSequenceId(), value));
}

void PiTCHWindow::networkButtonToggled(bool isChecked)
{
  if (isChecked)
  {
    PiTCHNetworkDialog dialog(serverInfo_, this);

    if (QDialog::Accepted == dialog.exec())
    {
      // Attempt the server connection
      ui_->statusBar->showMessage(tr("Looking up host..."));
      client_.openConnection(dialog.getNetworkInfo());
    }
  }
  else
  {
    client_.closeConnection();
  }
}

void PiTCHWindow::setSoundVolume(int newVolume)
{
  ui_->volumeSlider->blockSignals(true);
  ui_->volumeSlider->setValue(newVolume);
  ui_->volumeSlider->blockSignals(false);
}

void PiTCHWindow::setMute(bool isMute)
{
  if (isMute)
  {
    // We interpret mute as setting volume to 0
    setSoundVolume(0);
  }
}

void PiTCHWindow::setPlayerPosition(int newPosition)
{
  ui_->timeSlider->blockSignals(true);
  ui_->timeSlider->setValue(newPosition);
  ui_->timeSlider->blockSignals(false);

  ui_->timeElapsed->setText(secondsToTimePositionString(newPosition));
  ui_->timeRemaining->setText(QString("-%1").arg(secondsToTimePositionString(currentTrack_.duration() - newPosition)));
}

void PiTCHWindow::setPlayerState(bool playing)
{
  if (playing_ != playing)
  {
    if (playing)
    {
      startPositionTimer();
      ui_->playPauseToggleButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
    }
    else
    {
      stopPositionTimer();
      ui_->playPauseToggleButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
    }

    playing_ = playing;
  }
}

void PiTCHWindow::setCurrentTrack(const iTCH::Track &track)
{
  currentTrack_.CopyFrom(track);
  ui_->songTitle->setText(track.name().c_str());

  if (!track.artist().empty() && !track.album().empty())
  {
    ui_->artist->setText(QString("%1 -- %2")
      .arg(track.artist().c_str())
      .arg(track.album().c_str()));
  }
  else if (!track.artist().empty())
  {
    ui_->artist->setText(track.artist().c_str());
  }
  else if (!track.album().empty())
  {
    ui_->artist->setText(track.album().c_str());
  }
  else
  {
    ui_->artist->setText("");
  }

  ui_->timeSlider->setMaximum(track.duration());
}

void PiTCHWindow::requestPlayerPosition()
{
  sendTrackedRequest(iTCH::MessageBuilder::makeGetPlayerPositionRequest(nextSequenceId()));
}

void PiTCHWindow::createStandardIcons()
{
  ui_->backButton->setIcon(style()->standardIcon(QStyle::SP_MediaSkipBackward));
  ui_->playPauseToggleButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
  ui_->forwardButton->setIcon(style()->standardIcon(QStyle::SP_MediaSkipForward));
  ui_->minVolumeButton->setIcon(style()->standardIcon(QStyle::SP_MediaVolumeMuted));
  ui_->maxVolumeButton->setIcon(style()->standardIcon(QStyle::SP_MediaVolume));
  ui_->networkButton->setIcon(style()->standardIcon(QStyle::SP_BrowserReload));
}

unsigned long PiTCHWindow::nextSequenceId()
{
  return ++sequenceId_;
}

void PiTCHWindow::sendTrackedRequest(iTCH::EnvelopePtr envelope)
{
  assert(envelope->has_request());
  requests_.insert(envelope->request().seqid(), envelope);
  client_.sendMessage(envelope);
}

void PiTCHWindow::startPositionTimer()
{
  // Start the timer to peridoically request time slider position
  if (!positionTimer_.isActive())
  {
    connect(&positionTimer_, SIGNAL(timeout()), this, SLOT(requestPlayerPosition()));
    positionTimer_.start(positionInterval_);
  }
}

void PiTCHWindow::stopPositionTimer()
{
  if (positionTimer_.isActive())
  {
    positionTimer_.stop();
    positionTimer_.disconnect();
  }
}
<|endoftext|>


<commit_before>/**
 * @author Carsten Könemann
 */

// This is a ROS project
#include <ros/ros.h>

// Since we use OpenCV to load image files,
// we need to convert them to ROS messages
#include <cv_bridge/cv_bridge.h>
#include <sensor_msgs/image_encodings.h>

// Local headers
#include <thesis/image_loader.h>

// This node provides these services
#include <thesis/DatabaseGetAll.h>
#include <thesis/DatabaseGetByID.h>
#include <thesis/DatabaseList.h>
#include <thesis/DatabaseSetByID.h>

ImageLoader image_loader;

// Store all sample objects mapped to their IDs for quick access
typedef std::map<std::string, thesis::Sample> SampleMap;
SampleMap samples;

bool get_list(thesis::DatabaseList::Request& request,
              thesis::DatabaseList::Response& result)
{
  for(SampleMap::iterator it = samples.begin(); it != samples.end(); it++)
  {
    result.list.push_back(it->first);
  }
  return true;
}

bool get_all(thesis::DatabaseGetAll::Request& request,
             thesis::DatabaseGetAll::Response& result)
{
  for(SampleMap::iterator it = samples.begin(); it != samples.end(); it++)
  {
    result.samples.push_back(it->second);
  }
  return true;
}

bool get_by_type(thesis::DatabaseGetByID::Request& request,
                 thesis::DatabaseGetByID::Response& result)
{
  result.sample = samples[request.id];
  return true;
}

bool set_by_type(thesis::DatabaseSetByID::Request& request,
                 thesis::DatabaseSetByID::Response& result)
{
  thesis::Sample temp = samples[request.sample.id];
  if(temp.accuracy < INT_MAX-1)
  {
    samples[request.sample.id].accuracy++;
  }
  samples[request.sample.id].width  = (temp.width  * temp.accuracy + request.sample.width)  / (temp.accuracy + 1);
  samples[request.sample.id].height = (temp.height * temp.accuracy + request.sample.height) / (temp.accuracy + 1);
  return true;
}

int main(int argc, char** argv)
{
  // Initialize ROS
  ros::init(argc, argv, "thesis_database");
  ros::NodeHandle nh_private("~");
  // Get path to image directory
  std::string image_path;
  nh_private.param("image_path", image_path, std::string("img"));
  // Get image size limits
  cv::Size min_image_size,
           max_image_size;
  nh_private.param("min_image_width",  min_image_size.width,  1);
  nh_private.param("min_image_height", min_image_size.height, 1);
  nh_private.param("max_image_width",  max_image_size.width,  1280);
  nh_private.param("max_image_height", max_image_size.height, 1024);
  // Create sample database
  std::vector<cv::Mat> images;
  std::vector<std::string> filenames;
  image_loader.load_directory(image_path, images, &filenames);
  ROS_ASSERT_MSG(images.size() == filenames.size(), "Number of images should be the same as number of filenames.");
  for(size_t i = 0; i < images.size(); i++)
  {
    thesis::Sample sample;
    sample.id = filenames[i];
    // Check if image is large enough
    if(images[i].cols < min_image_size.width || images[i].rows < min_image_size.height)
    {
      ROS_WARN("Image '%s' is smaller than %i x %i.", filenames[i].c_str(), min_image_size.width, min_image_size.height);
      ROS_WARN("Don't add it to database.");
      continue;
    }
    // Determine if max image size needs to be in portrait or landscape orientation
    // in order to match the sample image orientation
    if(max_image_size.width < max_image_size.height)
    {
      max_image_size = (images[i].cols <  images[i].rows) ? max_image_size : cv::Size(max_image_size.height, max_image_size.width);
    }
    else
    {
      max_image_size = (images[i].cols >= images[i].rows) ? max_image_size : cv::Size(max_image_size.height, max_image_size.width);
    }
    // If the input image is too big...
    cv::Mat resized;
    if(images[i].cols > max_image_size.width || images[i].rows > max_image_size.height)
    {
      // ...determine biggest possible new size with the same aspect ratio
      cv::Size re_size      = cv::Size(images[i].cols, images[i].rows),
               aspect_ratio = cv::Size(max_image_size.width  / images[i].cols,
                                       max_image_size.height / images[i].rows);
      if(aspect_ratio.width < aspect_ratio.height)
      {
        re_size = cv::Size(images[i].cols * aspect_ratio.width,
                           images[i].rows * aspect_ratio.width);
      }
      else
      {
        re_size = cv::Size(images[i].cols * aspect_ratio.height,
                           images[i].rows * aspect_ratio.height);
      }
      // ...and resize it accordingly
      ROS_WARN("Image '%s' is bigger than %i x %i.", filenames[i].c_str(), max_image_size.width, max_image_size.height);
      ROS_WARN("Resizing it to %i x %i.", re_size.width, re_size.height);
      cv::resize(images[i], resized, re_size);
    }
    else
    {
      resized = images[i];
    }
    // Convert OpenCV image to ROS image message
    cv_bridge::CvImage cv_image;
    cv_image.encoding = sensor_msgs::image_encodings::MONO8;
    cv_image.image    = resized;
    cv_image.toImageMsg(sample.image);
    // Add to database
    samples[sample.id] = sample;
  }
  // Advertise services
  ros::ServiceServer srv_list    = nh_private.advertiseService("get_list",    get_list);
  ros::ServiceServer srv_all     = nh_private.advertiseService("get_all",     get_all);
  ros::ServiceServer srv_by_type = nh_private.advertiseService("get_by_type", get_by_type);
  ros::ServiceServer srv_update  = nh_private.advertiseService("set_by_type", set_by_type);
  // Spin
  ros::spin();
  // Exit
  return 0;
}
<commit_msg>Fixed a bug that caused the database node to try to resize big images to (0x0)<commit_after>/**
 * @author Carsten Könemann
 */

// This is a ROS project
#include <ros/ros.h>

// Since we use OpenCV to load image files,
// we need to convert them to ROS messages
#include <cv_bridge/cv_bridge.h>
#include <sensor_msgs/image_encodings.h>

// Local headers
#include <thesis/image_loader.h>

// This node provides these services
#include <thesis/DatabaseGetAll.h>
#include <thesis/DatabaseGetByID.h>
#include <thesis/DatabaseList.h>
#include <thesis/DatabaseSetByID.h>

ImageLoader image_loader;

// Store all sample objects mapped to their IDs for quick access
typedef std::map<std::string, thesis::Sample> SampleMap;
SampleMap samples;

bool get_list(thesis::DatabaseList::Request& request,
              thesis::DatabaseList::Response& result)
{
  for(SampleMap::iterator it = samples.begin(); it != samples.end(); it++)
  {
    result.list.push_back(it->first);
  }
  return true;
}

bool get_all(thesis::DatabaseGetAll::Request& request,
             thesis::DatabaseGetAll::Response& result)
{
  for(SampleMap::iterator it = samples.begin(); it != samples.end(); it++)
  {
    result.samples.push_back(it->second);
  }
  return true;
}

bool get_by_type(thesis::DatabaseGetByID::Request& request,
                 thesis::DatabaseGetByID::Response& result)
{
  result.sample = samples[request.id];
  return true;
}

bool set_by_type(thesis::DatabaseSetByID::Request& request,
                 thesis::DatabaseSetByID::Response& result)
{
  thesis::Sample temp = samples[request.sample.id];
  if(temp.accuracy < INT_MAX-1)
  {
    samples[request.sample.id].accuracy++;
  }
  samples[request.sample.id].width  = (temp.width  * temp.accuracy + request.sample.width)  / (temp.accuracy + 1);
  samples[request.sample.id].height = (temp.height * temp.accuracy + request.sample.height) / (temp.accuracy + 1);
  return true;
}

int main(int argc, char** argv)
{
  // Initialize ROS
  ros::init(argc, argv, "thesis_database");
  ros::NodeHandle nh_private("~");
  // Get path to image directory
  std::string image_path;
  nh_private.param("image_path", image_path, std::string("img"));
  // Get image size limits
  cv::Size min_image_size,
           max_image_size;
  nh_private.param("min_image_width",  min_image_size.width,  1);
  nh_private.param("min_image_height", min_image_size.height, 1);
  nh_private.param("max_image_width",  max_image_size.width,  1280);
  nh_private.param("max_image_height", max_image_size.height, 1024);
  // Create sample database
  std::vector<cv::Mat> images;
  std::vector<std::string> filenames;
  image_loader.load_directory(image_path, images, &filenames);
  ROS_ASSERT_MSG(images.size() == filenames.size(), "Number of images should be the same as number of filenames.");
  for(size_t i = 0; i < images.size(); i++)
  {
    thesis::Sample sample;
    sample.id = filenames[i];
    // Check if image is large enough
    if(images[i].cols < min_image_size.width || images[i].rows < min_image_size.height)
    {
      ROS_WARN("Image '%s' is smaller than %i x %i.", filenames[i].c_str(), min_image_size.width, min_image_size.height);
      ROS_WARN("Don't add it to database.");
      continue;
    }
    // Determine if max image size needs to be in portrait or landscape orientation
    // in order to match the sample image orientation
    if(max_image_size.width < max_image_size.height)
    {
      max_image_size = (images[i].cols <  images[i].rows) ? max_image_size : cv::Size(max_image_size.height, max_image_size.width);
    }
    else
    {
      max_image_size = (images[i].cols >= images[i].rows) ? max_image_size : cv::Size(max_image_size.height, max_image_size.width);
    }
    // If the input image is too big...
    cv::Mat resized;
    if(images[i].cols > max_image_size.width || images[i].rows > max_image_size.height)
    {
      // ...determine biggest possible new size with the same aspect ratio
      cv::Size re_size;
      float aspect_ratio_width  = (float) max_image_size.width  / (float) images[i].cols,
            aspect_ratio_height = (float) max_image_size.height / (float) images[i].rows;
      if(aspect_ratio_width < aspect_ratio_height)
      {
        re_size = cv::Size(images[i].cols * aspect_ratio_width,
                           images[i].rows * aspect_ratio_width);
      }
      else
      {
        re_size = cv::Size(images[i].cols * aspect_ratio_height,
                           images[i].rows * aspect_ratio_height);
      }
      // ...and resize it accordingly
      ROS_WARN("Image '%s' is bigger than %i x %i.", filenames[i].c_str(), max_image_size.width, max_image_size.height);
      ROS_WARN("Resizing it to %i x %i.", re_size.width, re_size.height);
      cv::resize(images[i], resized, re_size);
    }
    else
    {
      resized = images[i];
    }
    // Convert OpenCV image to ROS image message
    cv_bridge::CvImage cv_image;
    cv_image.encoding = sensor_msgs::image_encodings::MONO8;
    cv_image.image    = resized;
    cv_image.toImageMsg(sample.image);
    // Add to database
    samples[sample.id] = sample;
  }
  // Advertise services
  ros::ServiceServer srv_list    = nh_private.advertiseService("get_list",    get_list);
  ros::ServiceServer srv_all     = nh_private.advertiseService("get_all",     get_all);
  ros::ServiceServer srv_by_type = nh_private.advertiseService("get_by_type", get_by_type);
  ros::ServiceServer srv_update  = nh_private.advertiseService("set_by_type", set_by_type);
  // Spin
  ros::spin();
  // Exit
  return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright © 2010 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "ast.h"

void
ast_type_specifier::print(void) const
{
   if (structure) {
      structure->print();
   } else {
      printf("%s ", type_name);
   }

   if (array_specifier) {
      array_specifier->print();
   }
}

bool
ast_fully_specified_type::has_qualifiers() const
{
   return this->qualifier.flags.i != 0;
}

bool ast_type_qualifier::has_interpolation() const
{
   return this->flags.q.smooth
          || this->flags.q.flat
          || this->flags.q.noperspective;
}

bool
ast_type_qualifier::has_layout() const
{
   return this->flags.q.origin_upper_left
          || this->flags.q.pixel_center_integer
          || this->flags.q.depth_any
          || this->flags.q.depth_greater
          || this->flags.q.depth_less
          || this->flags.q.depth_unchanged
          || this->flags.q.std140
          || this->flags.q.shared
          || this->flags.q.column_major
          || this->flags.q.row_major
          || this->flags.q.packed
          || this->flags.q.explicit_location
          || this->flags.q.explicit_index
          || this->flags.q.explicit_binding
          || this->flags.q.explicit_offset;
}

bool
ast_type_qualifier::has_storage() const
{
   return this->flags.q.constant
          || this->flags.q.attribute
          || this->flags.q.varying
          || this->flags.q.in
          || this->flags.q.out
          || this->flags.q.uniform;
}

bool
ast_type_qualifier::has_auxiliary_storage() const
{
   return this->flags.q.centroid
          || this->flags.q.sample;
}

const char*
ast_type_qualifier::interpolation_string() const
{
   if (this->flags.q.smooth)
      return "smooth";
   else if (this->flags.q.flat)
      return "flat";
   else if (this->flags.q.noperspective)
      return "noperspective";
   else
      return NULL;
}

bool
ast_type_qualifier::merge_qualifier(YYLTYPE *loc,
				    _mesa_glsl_parse_state *state,
				    ast_type_qualifier q)
{
   ast_type_qualifier ubo_mat_mask;
   ubo_mat_mask.flags.i = 0;
   ubo_mat_mask.flags.q.row_major = 1;
   ubo_mat_mask.flags.q.column_major = 1;

   ast_type_qualifier ubo_layout_mask;
   ubo_layout_mask.flags.i = 0;
   ubo_layout_mask.flags.q.std140 = 1;
   ubo_layout_mask.flags.q.packed = 1;
   ubo_layout_mask.flags.q.shared = 1;

   ast_type_qualifier ubo_binding_mask;
   ubo_binding_mask.flags.q.explicit_binding = 1;
   ubo_binding_mask.flags.q.explicit_offset = 1;

   /* Uniform block layout qualifiers get to overwrite each
    * other (rightmost having priority), while all other
    * qualifiers currently don't allow duplicates.
    */

   if ((this->flags.i & q.flags.i & ~(ubo_mat_mask.flags.i |
				      ubo_layout_mask.flags.i |
                                      ubo_binding_mask.flags.i)) != 0) {
      _mesa_glsl_error(loc, state,
		       "duplicate layout qualifiers used");
      return false;
   }

   if (q.flags.q.prim_type) {
      if (this->flags.q.prim_type && this->prim_type != q.prim_type) {
	 _mesa_glsl_error(loc, state,
			  "conflicting primitive type qualifiers used");
	 return false;
      }
      this->prim_type = q.prim_type;
   }

   if (q.flags.q.max_vertices) {
      if (this->flags.q.max_vertices && this->max_vertices != q.max_vertices) {
	 _mesa_glsl_error(loc, state,
			  "geometry shader set conflicting max_vertices "
			  "(%d and %d)", this->max_vertices, q.max_vertices);
	 return false;
      }
      this->max_vertices = q.max_vertices;
   }

   if ((q.flags.i & ubo_mat_mask.flags.i) != 0)
      this->flags.i &= ~ubo_mat_mask.flags.i;
   if ((q.flags.i & ubo_layout_mask.flags.i) != 0)
      this->flags.i &= ~ubo_layout_mask.flags.i;

   for (int i = 0; i < 3; i++) {
      if (q.flags.q.local_size & (1 << i)) {
         if ((this->flags.q.local_size & (1 << i)) &&
             this->local_size[i] != q.local_size[i]) {
            _mesa_glsl_error(loc, state,
                             "compute shader set conflicting values for "
                             "local_size_%c (%d and %d)", 'x' + i,
                             this->local_size[i], q.local_size[i]);
            return false;
         }
         this->local_size[i] = q.local_size[i];
      }
   }

   this->flags.i |= q.flags.i;

   if (q.flags.q.explicit_location)
      this->location = q.location;

   if (q.flags.q.explicit_index)
      this->index = q.index;

   if (q.flags.q.explicit_binding)
      this->binding = q.binding;

   if (q.flags.q.explicit_offset)
      this->offset = q.offset;

   if (q.precision != ast_precision_none)
      this->precision = q.precision;

   return true;
}

<commit_msg>glsl: Initialize ubo_binding_mask flags to zero.<commit_after>/*
 * Copyright © 2010 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "ast.h"

void
ast_type_specifier::print(void) const
{
   if (structure) {
      structure->print();
   } else {
      printf("%s ", type_name);
   }

   if (array_specifier) {
      array_specifier->print();
   }
}

bool
ast_fully_specified_type::has_qualifiers() const
{
   return this->qualifier.flags.i != 0;
}

bool ast_type_qualifier::has_interpolation() const
{
   return this->flags.q.smooth
          || this->flags.q.flat
          || this->flags.q.noperspective;
}

bool
ast_type_qualifier::has_layout() const
{
   return this->flags.q.origin_upper_left
          || this->flags.q.pixel_center_integer
          || this->flags.q.depth_any
          || this->flags.q.depth_greater
          || this->flags.q.depth_less
          || this->flags.q.depth_unchanged
          || this->flags.q.std140
          || this->flags.q.shared
          || this->flags.q.column_major
          || this->flags.q.row_major
          || this->flags.q.packed
          || this->flags.q.explicit_location
          || this->flags.q.explicit_index
          || this->flags.q.explicit_binding
          || this->flags.q.explicit_offset;
}

bool
ast_type_qualifier::has_storage() const
{
   return this->flags.q.constant
          || this->flags.q.attribute
          || this->flags.q.varying
          || this->flags.q.in
          || this->flags.q.out
          || this->flags.q.uniform;
}

bool
ast_type_qualifier::has_auxiliary_storage() const
{
   return this->flags.q.centroid
          || this->flags.q.sample;
}

const char*
ast_type_qualifier::interpolation_string() const
{
   if (this->flags.q.smooth)
      return "smooth";
   else if (this->flags.q.flat)
      return "flat";
   else if (this->flags.q.noperspective)
      return "noperspective";
   else
      return NULL;
}

bool
ast_type_qualifier::merge_qualifier(YYLTYPE *loc,
				    _mesa_glsl_parse_state *state,
				    ast_type_qualifier q)
{
   ast_type_qualifier ubo_mat_mask;
   ubo_mat_mask.flags.i = 0;
   ubo_mat_mask.flags.q.row_major = 1;
   ubo_mat_mask.flags.q.column_major = 1;

   ast_type_qualifier ubo_layout_mask;
   ubo_layout_mask.flags.i = 0;
   ubo_layout_mask.flags.q.std140 = 1;
   ubo_layout_mask.flags.q.packed = 1;
   ubo_layout_mask.flags.q.shared = 1;

   ast_type_qualifier ubo_binding_mask;
   ubo_binding_mask.flags.i = 0;
   ubo_binding_mask.flags.q.explicit_binding = 1;
   ubo_binding_mask.flags.q.explicit_offset = 1;

   /* Uniform block layout qualifiers get to overwrite each
    * other (rightmost having priority), while all other
    * qualifiers currently don't allow duplicates.
    */

   if ((this->flags.i & q.flags.i & ~(ubo_mat_mask.flags.i |
				      ubo_layout_mask.flags.i |
                                      ubo_binding_mask.flags.i)) != 0) {
      _mesa_glsl_error(loc, state,
		       "duplicate layout qualifiers used");
      return false;
   }

   if (q.flags.q.prim_type) {
      if (this->flags.q.prim_type && this->prim_type != q.prim_type) {
	 _mesa_glsl_error(loc, state,
			  "conflicting primitive type qualifiers used");
	 return false;
      }
      this->prim_type = q.prim_type;
   }

   if (q.flags.q.max_vertices) {
      if (this->flags.q.max_vertices && this->max_vertices != q.max_vertices) {
	 _mesa_glsl_error(loc, state,
			  "geometry shader set conflicting max_vertices "
			  "(%d and %d)", this->max_vertices, q.max_vertices);
	 return false;
      }
      this->max_vertices = q.max_vertices;
   }

   if ((q.flags.i & ubo_mat_mask.flags.i) != 0)
      this->flags.i &= ~ubo_mat_mask.flags.i;
   if ((q.flags.i & ubo_layout_mask.flags.i) != 0)
      this->flags.i &= ~ubo_layout_mask.flags.i;

   for (int i = 0; i < 3; i++) {
      if (q.flags.q.local_size & (1 << i)) {
         if ((this->flags.q.local_size & (1 << i)) &&
             this->local_size[i] != q.local_size[i]) {
            _mesa_glsl_error(loc, state,
                             "compute shader set conflicting values for "
                             "local_size_%c (%d and %d)", 'x' + i,
                             this->local_size[i], q.local_size[i]);
            return false;
         }
         this->local_size[i] = q.local_size[i];
      }
   }

   this->flags.i |= q.flags.i;

   if (q.flags.q.explicit_location)
      this->location = q.location;

   if (q.flags.q.explicit_index)
      this->index = q.index;

   if (q.flags.q.explicit_binding)
      this->binding = q.binding;

   if (q.flags.q.explicit_offset)
      this->offset = q.offset;

   if (q.precision != ast_precision_none)
      this->precision = q.precision;

   return true;
}

<|endoftext|>


<commit_before>/* This file is part of Ingen.
 * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
 *
 * Ingen 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; either version 2 of the License, or (at your option) any later
 * version.
 *
 * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <raul/Array.hpp>
#include <raul/Maid.hpp>
#include <raul/midi_events.h>
#include <cmath>
#include <iostream>
#include "AudioBuffer.hpp"
#include "AudioDriver.hpp"
#include "InputPort.hpp"
#include "InternalPlugin.hpp"
#include "EventBuffer.hpp"
#include "MidiNoteNode.hpp"
#include "OutputPort.hpp"
#include "PatchImpl.hpp"
#include "ProcessContext.hpp"
#include "util.hpp"

using namespace std;

namespace Ingen {

MidiNoteNode::MidiNoteNode(const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size)
	: NodeBase(new InternalPlugin(NS_INGEN "note_node", "note", "MIDI Note"),
			path, polyphonic, parent, srate, buffer_size)
	, _voices(new Raul::Array<Voice>(_polyphony))
	, _prepared_voices(NULL)
	, _sustain(false)
{
	_ports = new Raul::Array<PortImpl*>(5);

	_midi_in_port = new InputPort(this, "input", 0, 1, DataType::EVENT, Atom(), _buffer_size);
	_ports->at(0) = _midi_in_port;

	_freq_port = new OutputPort(this, "frequency", 1, _polyphony, DataType::AUDIO, 440.0f, _buffer_size);
	_ports->at(1) = _freq_port;

	_vel_port = new OutputPort(this, "velocity", 2, _polyphony, DataType::AUDIO, 0.0f, _buffer_size);
	_vel_port->set_variable("ingen:minimum", 0.0f);
	_vel_port->set_variable("ingen:maximum", 1.0f);
	_ports->at(2) = _vel_port;

	_gate_port = new OutputPort(this, "gate", 3, _polyphony, DataType::AUDIO, 0.0f, _buffer_size);
	_gate_port->set_variable("ingen:toggled", true);
	_ports->at(3) = _gate_port;

	_trig_port = new OutputPort(this, "trigger", 4, _polyphony, DataType::AUDIO, 0.0f, _buffer_size);
	_trig_port->set_variable("ingen:toggled", true);
	_ports->at(4) = _trig_port;
}

MidiNoteNode::~MidiNoteNode()
{
	delete _voices;
}

bool
MidiNoteNode::prepare_poly(uint32_t poly)
{
	if (!_polyphonic)
		return true;

	NodeBase::prepare_poly(poly);

	if (_prepared_voices && poly <= _prepared_voices->size())
		return true;

	_prepared_voices = new Raul::Array<Voice>(poly, *_voices);

	return true;
}

bool
MidiNoteNode::apply_poly(Raul::Maid& maid, uint32_t poly)
{
	if (!_polyphonic)
		return true;

	NodeBase::apply_poly(maid, poly);

	if (_prepared_voices) {
		assert(poly <= _prepared_voices->size());
		maid.push(_voices);
		_voices = _prepared_voices;
		_prepared_voices = NULL;
	}

	_polyphony = poly;
	assert(_voices->size() >= _polyphony);

	return true;
}

void
MidiNoteNode::process(ProcessContext& context)
{
	NodeBase::pre_process(context);

	uint32_t       frames = 0;
	uint32_t       subframes = 0;
	uint16_t       type = 0;
	uint16_t       size = 0;
	unsigned char* buf = NULL;

	EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0);
	assert(midi_in->this_nframes() == context.nframes());

	//cerr << path() << " # input events: " << midi_in->event_count() << endl;

	if (midi_in->event_count() > 0)
	while (midi_in->get_event(&frames, &subframes, &type, &size, &buf)) {

		/*cout << "EVENT TYPE " << type << " @ " << frames << "." << subframes << ": ";
		for (uint16_t i = 0; i < size; ++i)
			cout << (int)((char)buf[i]) << " ";
		cout << endl;*/

		const FrameTime time = context.start() + (FrameTime)frames;

		if (size >= 3) {
			switch (buf[0] & 0xF0) {
			case MIDI_CMD_NOTE_ON:
				if (buf[2] == 0)
					note_off(context, buf[1], time);
				else
					note_on(context, buf[1], buf[2], time);
				break;
			case MIDI_CMD_NOTE_OFF:
				note_off(context, buf[1], time);
				break;
			case MIDI_CMD_CONTROL:
				switch (buf[1]) {
				case MIDI_CTL_ALL_NOTES_OFF:
				case MIDI_CTL_ALL_SOUNDS_OFF:
					all_notes_off(context, time);
					break;
				case MIDI_CTL_SUSTAIN:
					if (buf[2] > 63)
						sustain_on(context, time);
					else
						sustain_off(context, time);
					break;
				case MIDI_CMD_BENDER:
					// ?
					break;
				default:
					//cerr << "Ignored controller " << buf[1] << endl;
					break;
				}
				break;
			default:
				fprintf(stderr, "Unknown (size %d) MIDI event %X\n", size, buf[0]);
				break;
			}
		} else {
			fprintf(stderr, "Unknown (size %d) MIDI event %X\n", size, buf[0]);
		}

		if (midi_in->increment() == midi_in->this_nframes())
			break;
	}

	NodeBase::post_process(context);
}

void
MidiNoteNode::note_on(ProcessContext& context, uchar note_num, uchar velocity, FrameTime time)
{
	assert(time >= context.start() && time <= context.end());
	assert(time - context.start() < _buffer_size);
	assert(note_num <= 127);

	Key*   key         = &_keys[note_num];
	Voice* voice       = NULL;
	uint32_t voice_num = 0;

	if (key->state != Key::OFF) {
		//cerr << "[MidiNoteNode] Double midi note received" << endl;
		return;
	}

	// Look for free voices
	for (uint32_t i=0; i < _polyphony; ++i) {
		if ((*_voices)[i].state == Voice::Voice::FREE) {
			voice = &(*_voices)[i];
			voice_num = i;
			break;
		}
	}

	// If we didn't find a free one, steal the oldest
	if (voice == NULL) {
		voice_num = 0;
		voice = &(*_voices)[0];
		jack_nframes_t oldest_time = (*_voices)[0].time;
		for (uint32_t i=1; i < _polyphony; ++i) {
			if ((*_voices)[i].time < oldest_time) {
				voice = &(*_voices)[i];
				voice_num = i;
				oldest_time = voice->time;
			}
		}
	}
	assert(voice != NULL);
	assert(voice == &(*_voices)[voice_num]);

	/*cerr << "[MidiNoteNode] Note " << (int)note_num << " on @ " << time
		<< ". Voice " << voice_num << " / " << _polyphony << endl;*/

	// Update stolen key, if applicable
	if (voice->state == Voice::Voice::ACTIVE) {
		assert(_keys[voice->note].state == Key::ON_ASSIGNED);
		assert(_keys[voice->note].voice == voice_num);
		_keys[voice->note].state = Key::Key::ON_UNASSIGNED;
		//cerr << "[MidiNoteNode] Stole voice " << voice_num << endl;
	}

	// Store key information for later reallocation on note off
	key->state = Key::Key::ON_ASSIGNED;
	key->voice = voice_num;
	key->time  = time;

	// Trigger voice
	voice->state = Voice::Voice::ACTIVE;
	voice->note  = note_num;
	voice->time  = time;

	assert(_keys[voice->note].state == Key::Key::ON_ASSIGNED);
	assert(_keys[voice->note].voice == voice_num);

	((AudioBuffer*)_freq_port->buffer(voice_num))->set_value(note_to_freq(note_num), context.start(), time);
	((AudioBuffer*)_vel_port->buffer(voice_num))->set_value(velocity/127.0, context.start(), time);
	((AudioBuffer*)_gate_port->buffer(voice_num))->set_value(1.0f, context.start(), time);

	// trigger (one sample)
	((AudioBuffer*)_trig_port->buffer(voice_num))->set_value(1.0f, context.start(), time);
	((AudioBuffer*)_trig_port->buffer(voice_num))->set_value(0.0f, context.start(), time + 1);

	assert(key->state == Key::Key::ON_ASSIGNED);
	assert(voice->state == Voice::Voice::ACTIVE);
	assert(key->voice == voice_num);
	assert((*_voices)[key->voice].note == note_num);
}

void
MidiNoteNode::note_off(ProcessContext& context, uchar note_num, FrameTime time)
{
	assert(time >= context.start() && time <= context.end());
	assert(time - context.start() < _buffer_size);

	Key* key = &_keys[note_num];

	//cerr << "[MidiNoteNode] Note " << (int)note_num << " off @ " << time << endl;

	if (key->state == Key::ON_ASSIGNED) {
		// Assigned key, turn off voice and key
		if ((*_voices)[key->voice].state == Voice::ACTIVE) {
			assert((*_voices)[key->voice].note == note_num);

			if ( ! _sustain) {
				//cerr << "... free voice " << key->voice << endl;
				free_voice(context, key->voice, time);
			} else {
				//cerr << "... hold voice " << key->voice << endl;
				(*_voices)[key->voice].state = Voice::HOLDING;
			}

		} else {
#ifndef NDEBUG
			cerr << "WARNING: Assigned key, but voice not active" << endl;
#endif
		}
	}

	key->state = Key::OFF;
}

void
MidiNoteNode::free_voice(ProcessContext& context, uint32_t voice, FrameTime time)
{
	assert(time >= context.start() && time <= context.end());
	assert(time - context.start() < _buffer_size);

	// Find a key to reassign to the freed voice (the newest, if there is one)
	Key*  replace_key     = NULL;
	uchar replace_key_num = 0;

	for (uchar i = 0; i <= 127; ++i) {
		if (_keys[i].state == Key::ON_UNASSIGNED) {
			if (replace_key == NULL || _keys[i].time > replace_key->time) {
				replace_key = &_keys[i];
				replace_key_num = i;
			}
		}
	}

	if (replace_key != NULL) {  // Found a key to assign to freed voice
		assert(&_keys[replace_key_num] == replace_key);
		assert(replace_key->state == Key::ON_UNASSIGNED);

		// Change the freq but leave the gate high and don't retrigger
		((AudioBuffer*)_freq_port->buffer(voice))->set_value(note_to_freq(replace_key_num), context.start(), time);

		replace_key->state = Key::ON_ASSIGNED;
		replace_key->voice = voice;
		_keys[(*_voices)[voice].note].state = Key::ON_UNASSIGNED;
		(*_voices)[voice].note = replace_key_num;
		(*_voices)[voice].state = Voice::ACTIVE;
	} else {
		// No new note for voice, deactivate (set gate low)
		//cerr << "[MidiNoteNode] Note off. Key " << (int)note_num << ", Voice " << voice << " Killed" << endl;
		((AudioBuffer*)_gate_port->buffer(voice))->set_value(0.0f, context.start(), time);
		(*_voices)[voice].state = Voice::FREE;
	}
}

void
MidiNoteNode::all_notes_off(ProcessContext& context, FrameTime time)
{
	assert(time >= context.start() && time <= context.end());
	assert(time - context.start() < _buffer_size);

	//cerr << "All notes off @ " << offset << endl;

	// FIXME: set all keys to Key::OFF?

	for (uint32_t i = 0; i < _polyphony; ++i) {
		((AudioBuffer*)_gate_port->buffer(i))->set_value(0.0f, context.start(), time);
		(*_voices)[i].state = Voice::FREE;
	}
}

float
MidiNoteNode::note_to_freq(int num)
{
	static const float A4 = 440.0f;
	if (num >= 0 && num <= 119)
		return A4 * powf(2.0f, (float)(num - 57.0f) / 12.0f);
	return 1.0f;  // Some LADSPA plugins don't like freq=0
}

void
MidiNoteNode::sustain_on(ProcessContext& context, FrameTime time)
{
	_sustain = true;
}

void
MidiNoteNode::sustain_off(ProcessContext& context, FrameTime time)
{
	assert(time >= context.start() && time <= context.end());
	assert(time - context.start() < _buffer_size);

	_sustain = false;

	for (uint32_t i=0; i < _polyphony; ++i)
		if ((*_voices)[i].state == Voice::HOLDING)
			free_voice(context, i, time);
}

} // namespace Ingen

<commit_msg>Don't print to the console when unrecognized MIDI is received.<commit_after>/* This file is part of Ingen.
 * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
 *
 * Ingen 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; either version 2 of the License, or (at your option) any later
 * version.
 *
 * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <raul/Array.hpp>
#include <raul/Maid.hpp>
#include <raul/midi_events.h>
#include <cmath>
#include <iostream>
#include "AudioBuffer.hpp"
#include "AudioDriver.hpp"
#include "InputPort.hpp"
#include "InternalPlugin.hpp"
#include "EventBuffer.hpp"
#include "MidiNoteNode.hpp"
#include "OutputPort.hpp"
#include "PatchImpl.hpp"
#include "ProcessContext.hpp"
#include "util.hpp"

using namespace std;

namespace Ingen {

MidiNoteNode::MidiNoteNode(const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size)
	: NodeBase(new InternalPlugin(NS_INGEN "note_node", "note", "MIDI Note"),
			path, polyphonic, parent, srate, buffer_size)
	, _voices(new Raul::Array<Voice>(_polyphony))
	, _prepared_voices(NULL)
	, _sustain(false)
{
	_ports = new Raul::Array<PortImpl*>(5);

	_midi_in_port = new InputPort(this, "input", 0, 1, DataType::EVENT, Atom(), _buffer_size);
	_ports->at(0) = _midi_in_port;

	_freq_port = new OutputPort(this, "frequency", 1, _polyphony, DataType::AUDIO, 440.0f, _buffer_size);
	_ports->at(1) = _freq_port;

	_vel_port = new OutputPort(this, "velocity", 2, _polyphony, DataType::AUDIO, 0.0f, _buffer_size);
	_vel_port->set_variable("ingen:minimum", 0.0f);
	_vel_port->set_variable("ingen:maximum", 1.0f);
	_ports->at(2) = _vel_port;

	_gate_port = new OutputPort(this, "gate", 3, _polyphony, DataType::AUDIO, 0.0f, _buffer_size);
	_gate_port->set_variable("ingen:toggled", true);
	_ports->at(3) = _gate_port;

	_trig_port = new OutputPort(this, "trigger", 4, _polyphony, DataType::AUDIO, 0.0f, _buffer_size);
	_trig_port->set_variable("ingen:toggled", true);
	_ports->at(4) = _trig_port;
}

MidiNoteNode::~MidiNoteNode()
{
	delete _voices;
}

bool
MidiNoteNode::prepare_poly(uint32_t poly)
{
	if (!_polyphonic)
		return true;

	NodeBase::prepare_poly(poly);

	if (_prepared_voices && poly <= _prepared_voices->size())
		return true;

	_prepared_voices = new Raul::Array<Voice>(poly, *_voices);

	return true;
}

bool
MidiNoteNode::apply_poly(Raul::Maid& maid, uint32_t poly)
{
	if (!_polyphonic)
		return true;

	NodeBase::apply_poly(maid, poly);

	if (_prepared_voices) {
		assert(poly <= _prepared_voices->size());
		maid.push(_voices);
		_voices = _prepared_voices;
		_prepared_voices = NULL;
	}

	_polyphony = poly;
	assert(_voices->size() >= _polyphony);

	return true;
}

void
MidiNoteNode::process(ProcessContext& context)
{
	NodeBase::pre_process(context);

	uint32_t       frames = 0;
	uint32_t       subframes = 0;
	uint16_t       type = 0;
	uint16_t       size = 0;
	unsigned char* buf = NULL;

	EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0);
	assert(midi_in->this_nframes() == context.nframes());

	//cerr << path() << " # input events: " << midi_in->event_count() << endl;

	if (midi_in->event_count() > 0)
	while (midi_in->get_event(&frames, &subframes, &type, &size, &buf)) {

		/*cout << "EVENT TYPE " << type << " @ " << frames << "." << subframes << ": ";
		for (uint16_t i = 0; i < size; ++i)
			cout << (int)((char)buf[i]) << " ";
		cout << endl;*/

		const FrameTime time = context.start() + (FrameTime)frames;

		if (size >= 3) {
			switch (buf[0] & 0xF0) {
			case MIDI_CMD_NOTE_ON:
				if (buf[2] == 0)
					note_off(context, buf[1], time);
				else
					note_on(context, buf[1], buf[2], time);
				break;
			case MIDI_CMD_NOTE_OFF:
				note_off(context, buf[1], time);
				break;
			case MIDI_CMD_CONTROL:
				switch (buf[1]) {
				case MIDI_CTL_ALL_NOTES_OFF:
				case MIDI_CTL_ALL_SOUNDS_OFF:
					all_notes_off(context, time);
					break;
				case MIDI_CTL_SUSTAIN:
					if (buf[2] > 63)
						sustain_on(context, time);
					else
						sustain_off(context, time);
					break;
				case MIDI_CMD_BENDER:
					// ?
					break;
				default:
					//cerr << "Ignored controller " << buf[1] << endl;
					break;
				}
				break;
			default:
				//fprintf(stderr, "Unknown (size %d) MIDI event %X\n", size, buf[0]);
				break;
			}
		} else {
			//fprintf(stderr, "Unknown (size %d) MIDI event %X\n", size, buf[0]);
		}

		if (midi_in->increment() == midi_in->this_nframes())
			break;
	}

	NodeBase::post_process(context);
}

void
MidiNoteNode::note_on(ProcessContext& context, uchar note_num, uchar velocity, FrameTime time)
{
	assert(time >= context.start() && time <= context.end());
	assert(time - context.start() < _buffer_size);
	assert(note_num <= 127);

	Key*   key         = &_keys[note_num];
	Voice* voice       = NULL;
	uint32_t voice_num = 0;

	if (key->state != Key::OFF) {
		//cerr << "[MidiNoteNode] Double midi note received" << endl;
		return;
	}

	// Look for free voices
	for (uint32_t i=0; i < _polyphony; ++i) {
		if ((*_voices)[i].state == Voice::Voice::FREE) {
			voice = &(*_voices)[i];
			voice_num = i;
			break;
		}
	}

	// If we didn't find a free one, steal the oldest
	if (voice == NULL) {
		voice_num = 0;
		voice = &(*_voices)[0];
		jack_nframes_t oldest_time = (*_voices)[0].time;
		for (uint32_t i=1; i < _polyphony; ++i) {
			if ((*_voices)[i].time < oldest_time) {
				voice = &(*_voices)[i];
				voice_num = i;
				oldest_time = voice->time;
			}
		}
	}
	assert(voice != NULL);
	assert(voice == &(*_voices)[voice_num]);

	/*cerr << "[MidiNoteNode] Note " << (int)note_num << " on @ " << time
		<< ". Voice " << voice_num << " / " << _polyphony << endl;*/

	// Update stolen key, if applicable
	if (voice->state == Voice::Voice::ACTIVE) {
		assert(_keys[voice->note].state == Key::ON_ASSIGNED);
		assert(_keys[voice->note].voice == voice_num);
		_keys[voice->note].state = Key::Key::ON_UNASSIGNED;
		//cerr << "[MidiNoteNode] Stole voice " << voice_num << endl;
	}

	// Store key information for later reallocation on note off
	key->state = Key::Key::ON_ASSIGNED;
	key->voice = voice_num;
	key->time  = time;

	// Trigger voice
	voice->state = Voice::Voice::ACTIVE;
	voice->note  = note_num;
	voice->time  = time;

	assert(_keys[voice->note].state == Key::Key::ON_ASSIGNED);
	assert(_keys[voice->note].voice == voice_num);

	((AudioBuffer*)_freq_port->buffer(voice_num))->set_value(note_to_freq(note_num), context.start(), time);
	((AudioBuffer*)_vel_port->buffer(voice_num))->set_value(velocity/127.0, context.start(), time);
	((AudioBuffer*)_gate_port->buffer(voice_num))->set_value(1.0f, context.start(), time);

	// trigger (one sample)
	((AudioBuffer*)_trig_port->buffer(voice_num))->set_value(1.0f, context.start(), time);
	((AudioBuffer*)_trig_port->buffer(voice_num))->set_value(0.0f, context.start(), time + 1);

	assert(key->state == Key::Key::ON_ASSIGNED);
	assert(voice->state == Voice::Voice::ACTIVE);
	assert(key->voice == voice_num);
	assert((*_voices)[key->voice].note == note_num);
}

void
MidiNoteNode::note_off(ProcessContext& context, uchar note_num, FrameTime time)
{
	assert(time >= context.start() && time <= context.end());
	assert(time - context.start() < _buffer_size);

	Key* key = &_keys[note_num];

	//cerr << "[MidiNoteNode] Note " << (int)note_num << " off @ " << time << endl;

	if (key->state == Key::ON_ASSIGNED) {
		// Assigned key, turn off voice and key
		if ((*_voices)[key->voice].state == Voice::ACTIVE) {
			assert((*_voices)[key->voice].note == note_num);

			if ( ! _sustain) {
				//cerr << "... free voice " << key->voice << endl;
				free_voice(context, key->voice, time);
			} else {
				//cerr << "... hold voice " << key->voice << endl;
				(*_voices)[key->voice].state = Voice::HOLDING;
			}

		} else {
#ifndef NDEBUG
			cerr << "WARNING: Assigned key, but voice not active" << endl;
#endif
		}
	}

	key->state = Key::OFF;
}

void
MidiNoteNode::free_voice(ProcessContext& context, uint32_t voice, FrameTime time)
{
	assert(time >= context.start() && time <= context.end());
	assert(time - context.start() < _buffer_size);

	// Find a key to reassign to the freed voice (the newest, if there is one)
	Key*  replace_key     = NULL;
	uchar replace_key_num = 0;

	for (uchar i = 0; i <= 127; ++i) {
		if (_keys[i].state == Key::ON_UNASSIGNED) {
			if (replace_key == NULL || _keys[i].time > replace_key->time) {
				replace_key = &_keys[i];
				replace_key_num = i;
			}
		}
	}

	if (replace_key != NULL) {  // Found a key to assign to freed voice
		assert(&_keys[replace_key_num] == replace_key);
		assert(replace_key->state == Key::ON_UNASSIGNED);

		// Change the freq but leave the gate high and don't retrigger
		((AudioBuffer*)_freq_port->buffer(voice))->set_value(note_to_freq(replace_key_num), context.start(), time);

		replace_key->state = Key::ON_ASSIGNED;
		replace_key->voice = voice;
		_keys[(*_voices)[voice].note].state = Key::ON_UNASSIGNED;
		(*_voices)[voice].note = replace_key_num;
		(*_voices)[voice].state = Voice::ACTIVE;
	} else {
		// No new note for voice, deactivate (set gate low)
		//cerr << "[MidiNoteNode] Note off. Key " << (int)note_num << ", Voice " << voice << " Killed" << endl;
		((AudioBuffer*)_gate_port->buffer(voice))->set_value(0.0f, context.start(), time);
		(*_voices)[voice].state = Voice::FREE;
	}
}

void
MidiNoteNode::all_notes_off(ProcessContext& context, FrameTime time)
{
	assert(time >= context.start() && time <= context.end());
	assert(time - context.start() < _buffer_size);

	//cerr << "All notes off @ " << offset << endl;

	// FIXME: set all keys to Key::OFF?

	for (uint32_t i = 0; i < _polyphony; ++i) {
		((AudioBuffer*)_gate_port->buffer(i))->set_value(0.0f, context.start(), time);
		(*_voices)[i].state = Voice::FREE;
	}
}

float
MidiNoteNode::note_to_freq(int num)
{
	static const float A4 = 440.0f;
	if (num >= 0 && num <= 119)
		return A4 * powf(2.0f, (float)(num - 57.0f) / 12.0f);
	return 1.0f;  // Some LADSPA plugins don't like freq=0
}

void
MidiNoteNode::sustain_on(ProcessContext& context, FrameTime time)
{
	_sustain = true;
}

void
MidiNoteNode::sustain_off(ProcessContext& context, FrameTime time)
{
	assert(time >= context.start() && time <= context.end());
	assert(time - context.start() < _buffer_size);

	_sustain = false;

	for (uint32_t i=0; i < _polyphony; ++i)
		if ((*_voices)[i].state == Voice::HOLDING)
			free_voice(context, i, time);
}

} // namespace Ingen

<|endoftext|>


<commit_before>#ifndef DUNE_DETAILED_DISCRETIZATIONS_SPACE_CONTINUOUS_LAGRANGE_FEM_HH
#define DUNE_DETAILED_DISCRETIZATIONS_SPACE_CONTINUOUS_LAGRANGE_FEM_HH

#include <dune/fem/space/common/functionspace.hh>
#include <dune/fem/space/lagrangespace.hh>

#include "../../mapper/fem.hh"
#include "../../basefunctionset/fem.hh"

#include "../interface.hh"
#include "../constraints.hh"

namespace Dune {
namespace Detailed {
namespace Discretizations {
namespace ContinuousLagrangeSpace {

// forward, to be used in the traits and to allow for specialization
template< class GridPartImp, int polynomialOrder, class RangeFieldImp, int rangeDimRows = 1, int rangeDimCols = 1 >
class FemWrapper;

// forward, to allow for specialization
template< class GridPartImp, int polynomialOrder, class RangeFieldImp, int rangeDimRows = 1, int rangeDimCols = 1 >
class FemWrapperTraits;

/**
 *  \brief Traits class for ContinuousLagrangeSpace for dimRange 1x1.
 */
template< class GridPartImp, int polynomialOrder, class RangeFieldImp >
class FemWrapperTraits< GridPartImp, polynomialOrder, RangeFieldImp, 1, 1 >
{
public:
  typedef FemWrapper< GridPartImp, polynomialOrder, RangeFieldImp, 1, 1 > derived_type;
  typedef GridPartImp                   GridPartType;
  static const int                      polOrder = polynomialOrder;
  dune_static_assert((polOrder >= 1), "ERROR: wrong polOrder given!");
private:
  typedef typename GridPartType::ctype  DomainFieldType;
  static const unsigned int             dimDomain = GridPartType::dimension;
public:
  typedef RangeFieldImp                 RangeFieldType;
  static const unsigned int             dimRangeRows = 1;
  static const unsigned int             dimRangeCols = 1;
private:
  typedef Dune::Fem::FunctionSpace< DomainFieldType, RangeFieldType, dimDomain, dimRangeRows > FunctionSpaceType;
public:
  typedef Dune::Fem::LagrangeDiscreteFunctionSpace< FunctionSpaceType, GridPartType, polOrder > BackendType;
  typedef Mapper::FemDofWrapper< typename BackendType::MapperType > MapperType;
  typedef typename GridPartType::template Codim< 0 >::EntityType EntityType;
  typedef BaseFunctionSet::FemWrapper< typename BackendType::BaseFunctionSetType, EntityType > BaseFunctionSetType;
}; // class SpaceWrappedFemContinuousLagrangeTraits< ..., 1, 1 >

template< class GridPartImp, int polynomialOrder, class RangeFieldImp >
class FemWrapper< GridPartImp, polynomialOrder, RangeFieldImp, 1, 1 >
  : public SpaceInterface< FemWrapperTraits< GridPartImp, polynomialOrder, RangeFieldImp, 1, 1 > >
{
public:
  typedef FemWrapperTraits< GridPartImp, polynomialOrder, RangeFieldImp, 1, 1 > Traits;

  typedef typename Traits::GridPartType   GridPartType;
  typedef typename GridPartType::ctype    DomainFieldType;
  static const int                        polOrder = Traits::polOrder;
  static const unsigned int               dimDomain = GridPartType::dimension;
  typedef typename Traits::RangeFieldType RangeFieldType;
  static const unsigned int               dimRangeRows = Traits::dimRangeRows;
  static const unsigned int               dimRangeCols = Traits::dimRangeCols;

  typedef typename Traits::BackendType          BackendType;
  typedef typename Traits::MapperType           MapperType;
  typedef typename Traits::BaseFunctionSetType  BaseFunctionSetType;
  typedef typename Traits::EntityType           EntityType;

public:
  FemWrapper(const GridPartType& gridP)
    : gridPart_(gridP)
    , backend_(const_cast< GridPartType& >(gridPart_))
    , mapper_(backend_.mapper())
    , tmpMappedRows_(mapper_.maxNumDofs())
    , tmpMappedCols_(mapper_.maxNumDofs())
  {}

  const GridPartType& gridPart() const
  {
    return gridPart_;
  }

  const BackendType& backend() const
  {
    return backend_;
  }

  bool continuous() const
  {
    return true;
  }

  const MapperType& mapper() const
  {
    return mapper_;
  }

  BaseFunctionSetType baseFunctionSet(const EntityType& entity) const
  {
    return BaseFunctionSetType(backend_, entity);
  }

  template< class R >
  void localConstraints(const EntityType& /*entity*/,
                        Constraints::LocalDefault< R >& /*ret*/) const
  {
    dune_static_assert(Dune::AlwaysFalse< R >::value, "ERROR: not implemented for arbitrary constraints!");
  }

  void localConstraints(const EntityType& entity,
                        Constraints::Dirichlet< typename GridPartType::GridViewType, RangeFieldType >& ret) const
  {
    const auto& lagrangePointSet = backend_.lagrangePointSet(entity);
    std::set< size_t > localDirichletDofs;
    // loop over all intersections
    const auto& gridBoundary = ret.gridBoundary();
    const auto intersectionEndIt = gridPart_.iend(entity);
    for (auto intersectionIt = gridPart_.ibegin(entity); intersectionIt !=intersectionEndIt; ++intersectionIt) {
      const auto& intersection = *intersectionIt;
      // only work on dirichlet intersections
      if (gridBoundary.dirichlet(intersection)) {
        // get local face number of boundary intersection
        const int intersectionIndex = intersection.indexInInside();
        // iterate over face dofs and set unit row
        const auto faceDofEndIt = lagrangePointSet.template endSubEntity< 1 >(intersectionIndex);
        for (auto faceDofIt = lagrangePointSet.template beginSubEntity< 1 >(intersectionIndex);
             faceDofIt != faceDofEndIt;
             ++faceDofIt) {
          const size_t localDofIndex = *faceDofIt;
          localDirichletDofs.insert(localDofIndex);
        } // iterate over face dofs and set unit row
      } // only work on dirichlet intersections
    } // loop over all intersections
    const BaseFunctionSetType basis = baseFunctionSet(entity);
    const size_t numRows = localDirichletDofs.size();
    if (numRows > 0) {
      const size_t numCols = basis.size();
      ret.setSize(numRows, numCols);
      mapper_.globalIndices(entity, tmpMappedRows_);
      mapper_.globalIndices(entity, tmpMappedCols_);
      size_t localRow = 0;
      const RangeFieldType zero(0);
      const RangeFieldType one(1);
      for (auto localDirichletDofIt = localDirichletDofs.begin();
           localDirichletDofIt != localDirichletDofs.end();
           ++localDirichletDofIt) {
        const size_t& localDirichletDofIndex = * localDirichletDofIt;
        ret.globalRow(localRow) = tmpMappedRows_[localDirichletDofIndex];
        for (size_t jj = 0; jj < ret.cols(); ++jj) {
          ret.globalCol(jj) = tmpMappedCols_[jj];
          ret.value(localRow, jj) = zero;
        }
        ret.value(localRow, localRow) = one;
        ++localRow;
      }
    } else {
      ret.setSize(0, 0);
    }
  } // ... localConstraints(..., Dirichlet)

private:
  const GridPartType& gridPart_;
  const BackendType backend_;
  const MapperType mapper_;
  mutable Dune::DynamicVector< size_t > tmpMappedRows_;
  mutable Dune::DynamicVector< size_t > tmpMappedCols_;
}; // class FemWrapper< ..., 1, 1 >

} // namespace ContinuousLagrangeSpace
} // namespace Discretizations
} // namespace Detailed
} // namespace Dune

#endif // DUNE_DETAILED_DISCRETIZATIONS_SPACE_CONTINUOUS_LAGRANGE_FEM_HH
<commit_msg>[ContinuousLagrangeSpace] fixed dirichlet constraints in FemWrapper<commit_after>#ifndef DUNE_DETAILED_DISCRETIZATIONS_SPACE_CONTINUOUS_LAGRANGE_FEM_HH
#define DUNE_DETAILED_DISCRETIZATIONS_SPACE_CONTINUOUS_LAGRANGE_FEM_HH

#include <dune/fem/space/common/functionspace.hh>
#include <dune/fem/space/lagrangespace.hh>

#include "../../mapper/fem.hh"
#include "../../basefunctionset/fem.hh"

#include "../interface.hh"
#include "../constraints.hh"

namespace Dune {
namespace Detailed {
namespace Discretizations {
namespace ContinuousLagrangeSpace {

// forward, to be used in the traits and to allow for specialization
template< class GridPartImp, int polynomialOrder, class RangeFieldImp, int rangeDimRows = 1, int rangeDimCols = 1 >
class FemWrapper;

// forward, to allow for specialization
template< class GridPartImp, int polynomialOrder, class RangeFieldImp, int rangeDimRows = 1, int rangeDimCols = 1 >
class FemWrapperTraits;

/**
 *  \brief Traits class for ContinuousLagrangeSpace for dimRange 1x1.
 */
template< class GridPartImp, int polynomialOrder, class RangeFieldImp >
class FemWrapperTraits< GridPartImp, polynomialOrder, RangeFieldImp, 1, 1 >
{
public:
  typedef FemWrapper< GridPartImp, polynomialOrder, RangeFieldImp, 1, 1 > derived_type;
  typedef GridPartImp                   GridPartType;
  static const int                      polOrder = polynomialOrder;
  dune_static_assert((polOrder >= 1), "ERROR: wrong polOrder given!");
private:
  typedef typename GridPartType::ctype  DomainFieldType;
  static const unsigned int             dimDomain = GridPartType::dimension;
public:
  typedef RangeFieldImp                 RangeFieldType;
  static const unsigned int             dimRangeRows = 1;
  static const unsigned int             dimRangeCols = 1;
private:
  typedef Dune::Fem::FunctionSpace< DomainFieldType, RangeFieldType, dimDomain, dimRangeRows > FunctionSpaceType;
public:
  typedef Dune::Fem::LagrangeDiscreteFunctionSpace< FunctionSpaceType, GridPartType, polOrder > BackendType;
  typedef Mapper::FemDofWrapper< typename BackendType::MapperType > MapperType;
  typedef typename GridPartType::template Codim< 0 >::EntityType EntityType;
  typedef BaseFunctionSet::FemWrapper< typename BackendType::BaseFunctionSetType, EntityType > BaseFunctionSetType;
}; // class SpaceWrappedFemContinuousLagrangeTraits< ..., 1, 1 >

template< class GridPartImp, int polynomialOrder, class RangeFieldImp >
class FemWrapper< GridPartImp, polynomialOrder, RangeFieldImp, 1, 1 >
  : public SpaceInterface< FemWrapperTraits< GridPartImp, polynomialOrder, RangeFieldImp, 1, 1 > >
{
public:
  typedef FemWrapperTraits< GridPartImp, polynomialOrder, RangeFieldImp, 1, 1 > Traits;

  typedef typename Traits::GridPartType   GridPartType;
  typedef typename GridPartType::ctype    DomainFieldType;
  static const int                        polOrder = Traits::polOrder;
  static const unsigned int               dimDomain = GridPartType::dimension;
  typedef typename Traits::RangeFieldType RangeFieldType;
  static const unsigned int               dimRangeRows = Traits::dimRangeRows;
  static const unsigned int               dimRangeCols = Traits::dimRangeCols;

  typedef typename Traits::BackendType          BackendType;
  typedef typename Traits::MapperType           MapperType;
  typedef typename Traits::BaseFunctionSetType  BaseFunctionSetType;
  typedef typename Traits::EntityType           EntityType;

public:
  FemWrapper(const GridPartType& gridP)
    : gridPart_(gridP)
    , backend_(const_cast< GridPartType& >(gridPart_))
    , mapper_(backend_.mapper())
    , tmpMappedRows_(mapper_.maxNumDofs())
    , tmpMappedCols_(mapper_.maxNumDofs())
  {}

  const GridPartType& gridPart() const
  {
    return gridPart_;
  }

  const BackendType& backend() const
  {
    return backend_;
  }

  bool continuous() const
  {
    return true;
  }

  const MapperType& mapper() const
  {
    return mapper_;
  }

  BaseFunctionSetType baseFunctionSet(const EntityType& entity) const
  {
    return BaseFunctionSetType(backend_, entity);
  }

  template< class R >
  void localConstraints(const EntityType& /*entity*/,
                        Constraints::LocalDefault< R >& /*ret*/) const
  {
    dune_static_assert(Dune::AlwaysFalse< R >::value, "ERROR: not implemented for arbitrary constraints!");
  }

  void localConstraints(const EntityType& entity,
                        Constraints::Dirichlet< typename GridPartType::GridViewType, RangeFieldType >& ret) const
  {
    const auto& lagrangePointSet = backend_.lagrangePointSet(entity);
    std::set< size_t > localDirichletDofs;
    // loop over all intersections
    const auto& gridBoundary = ret.gridBoundary();
    const auto intersectionEndIt = gridPart_.iend(entity);
    for (auto intersectionIt = gridPart_.ibegin(entity); intersectionIt !=intersectionEndIt; ++intersectionIt) {
      const auto& intersection = *intersectionIt;
      // only work on dirichlet intersections
      if (gridBoundary.dirichlet(intersection)) {
        // get local face number of boundary intersection
        const int intersectionIndex = intersection.indexInInside();
        // iterate over face dofs and set unit row
        const auto faceDofEndIt = lagrangePointSet.template endSubEntity< 1 >(intersectionIndex);
        for (auto faceDofIt = lagrangePointSet.template beginSubEntity< 1 >(intersectionIndex);
             faceDofIt != faceDofEndIt;
             ++faceDofIt) {
          const size_t localDofIndex = *faceDofIt;
          localDirichletDofs.insert(localDofIndex);
        } // iterate over face dofs and set unit row
      } // only work on dirichlet intersections
    } // loop over all intersections
    const BaseFunctionSetType basis = baseFunctionSet(entity);
    const size_t numRows = localDirichletDofs.size();
    if (numRows > 0) {
      const size_t numCols = basis.size();
      ret.setSize(numRows, numCols);
      mapper_.globalIndices(entity, tmpMappedRows_);
      mapper_.globalIndices(entity, tmpMappedCols_);
      size_t localRow = 0;
      const RangeFieldType zero(0);
      const RangeFieldType one(1);
      for (auto localDirichletDofIt = localDirichletDofs.begin();
           localDirichletDofIt != localDirichletDofs.end();
           ++localDirichletDofIt) {
        const size_t& localDirichletDofIndex = * localDirichletDofIt;
        ret.globalRow(localRow) = tmpMappedRows_[localDirichletDofIndex];
        for (size_t jj = 0; jj < ret.cols(); ++jj) {
          ret.globalCol(jj) = tmpMappedCols_[jj];
          if (tmpMappedCols_[jj] == tmpMappedRows_[localDirichletDofIndex])
            ret.value(localRow, jj) = one;
          else
            ret.value(localRow, jj) = zero;
        }
        ++localRow;
      }
    } else {
      ret.setSize(0, 0);
    }
  } // ... localConstraints(..., Dirichlet)

private:
  const GridPartType& gridPart_;
  const BackendType backend_;
  const MapperType mapper_;
  mutable Dune::DynamicVector< size_t > tmpMappedRows_;
  mutable Dune::DynamicVector< size_t > tmpMappedCols_;
}; // class FemWrapper< ..., 1, 1 >

} // namespace ContinuousLagrangeSpace
} // namespace Discretizations
} // namespace Detailed
} // namespace Dune

#endif // DUNE_DETAILED_DISCRETIZATIONS_SPACE_CONTINUOUS_LAGRANGE_FEM_HH
<|endoftext|>