<commit_before>/**
 * @note HEADER-ONLY IMPLEMENTATION FILE
 * @warn Do not include directly
 */

// FFNN
#include <ffnn/assert.h>
#include <ffnn/logging.h>
#include <ffnn/layer/activation.h>

namespace ffnn
{
namespace optimizer
{
template<>
template<typename ValueType,
         template<class> class NeuronType,
         FFNN_SIZE_TYPE SizeAtCompileTime>
class Adam<layer::Activation<ValueType, NeuronType, SizeAtCompileTime>>:
  public GradientDescent<layer::Activation<ValueType, NeuronType, SizeAtCompileTime>>
{
public:
  /// Base type standardization
  typedef typename GradientDescent<layer::Activation<ValueType, NeuronType, SizeAtCompileTime>> Base;

  /// Layer type standardization
  typedef typename layer::Activation<ValueType, NeuronType, SizeAtCompileTime> LayerType;

  /// Scalar type standardization
  typedef typename LayerType::ScalarType ScalarType;

  /// Size type standardization
  typedef typename LayerType::SizeType SizeType;

  /// Matrix type standardization
  typedef typename LayerType::InputVector InputVector;

  /// Matrix type standardization
  typedef typename LayerType::OutputVector OutputVector;

  /// Matrix type standardization
  typedef typename LayerType::BiasVector BiasVector;

  /**
   * @brief Setup constructor
   * @param lr  Learning rate
   */
  explicit
  Adam(ScalarType lr, ScalarType beta1 = 0.9, ScalarType beta2 = 0.999, ScalarType eps = 1e-8) :
    Base(lr),
    beta1_(beta1),
    beta2_(beta2),
    epsilon_(eps)
  {
    Base::setName("Adam[Activation]");
    FFNN_ASSERT_MSG(beta1_ > 0 && beta1_ < 1, "Beta1 should be in the range (0, 1).");
    FFNN_ASSERT_MSG(beta2_ > 0 && beta2_ < 1, "Beta1 should be in the range (0, 1).");
    FFNN_ASSERT_MSG(epsilon_ > 0, "Epsilon should be > 0.");
  }
  virtual ~Adam() {}

  /**
   * @brief Initializes the Optimizer
   * @param[in, out] layer  Layer to optimize
   */
  void initialize(LayerType& layer)
  {
    Base::initialize(layer);

    // Reset moment matrices
    mean_gradient_.setZero(layer.output_dimension_, 1);
    var_gradient_.setZero(layer.output_dimension_, 1);
  }

  /**
   * @brief Applies optimization update
   * @param[in, out] layer  Layer to optimize
   * @retval true  if optimization update was applied successfully
   * @retval false  otherwise
   */
  bool update(LayerType& layer)
  {
    FFNN_ASSERT_MSG(layer.isInitialized(), "Layer to optimize is not initialized.");

    // Update gradient moments
    mean_gradient_ += beta1_ * (gradient_ - mean_gradient_);
    var_gradient_  += beta2_ * (gradient_ - var_gradient_);

    // Compute learning rates for all weights
    BiasVector current_gradient = var_gradient_;
    current_gradient.noalias() /= (1 - beta2_);
    current_gradient.array() += epsilon_;
    current_gradient.noalias() = mean_gradient_.array() / current_gradient.array();
    current_gradient.noalias() /= (1 - beta1_);
    current_gradient.noalias() *= Base::lr_;

    // Update weights
    layer.b_.noalias() -= current_gradient;

    // Reinitialize optimizer
    Base::reset(layer);
    return true;
  }

private:
  /// Mean decay rate
  const ScalarType beta1_;

  /// Variance decay rate
  const ScalarType beta2_;

  /// Variance normalization value
  const ScalarType epsilon_;

  /// Running mean of error gradient
  BiasVector mean_gradient_;

  /// Uncentered variance of error gradient
  BiasVector var_gradient_;
};
}  // namespace optimizer
}  // namespace ffnn
<commit_msg>Updates asserts in Adam[Activation] optimizer<commit_after>/**
 * @note HEADER-ONLY IMPLEMENTATION FILE
 * @warn Do not include directly
 */

// FFNN
#include <ffnn/assert.h>
#include <ffnn/logging.h>
#include <ffnn/layer/activation.h>

namespace ffnn
{
namespace optimizer
{
template<>
template<typename ValueType,
         template<class> class NeuronType,
         FFNN_SIZE_TYPE SizeAtCompileTime>
class Adam<layer::Activation<ValueType, NeuronType, SizeAtCompileTime>>:
  public GradientDescent<layer::Activation<ValueType, NeuronType, SizeAtCompileTime>>
{
public:
  /// Base type standardization
  typedef typename GradientDescent<layer::Activation<ValueType, NeuronType, SizeAtCompileTime>> Base;

  /// Layer type standardization
  typedef typename layer::Activation<ValueType, NeuronType, SizeAtCompileTime> LayerType;

  /// Scalar type standardization
  typedef typename LayerType::ScalarType ScalarType;

  /// Size type standardization
  typedef typename LayerType::SizeType SizeType;

  /// Matrix type standardization
  typedef typename LayerType::InputVector InputVector;

  /// Matrix type standardization
  typedef typename LayerType::OutputVector OutputVector;

  /// Matrix type standardization
  typedef typename LayerType::BiasVector BiasVector;

  /**
   * @brief Setup constructor
   * @param lr  Learning rate
   */
  explicit
  Adam(ScalarType lr, ScalarType beta1 = 0.9, ScalarType beta2 = 0.999, ScalarType eps = 1e-8) :
    Base(lr),
    beta1_(beta1),
    beta2_(beta2),
    epsilon_(eps)
  {
    Base::setName("Adam[Activation]");
    FFNN_ASSERT_MSG(beta1_ > 0 && beta1_ < 1, "'beta1' should be in the range (0, 1).");
    FFNN_ASSERT_MSG(beta2_ > 0 && beta2_ < 1, "'beta2' should be in the range (0, 1).");
    FFNN_ASSERT_MSG(epsilon_ > 0, "Epsilon should be > 0.");
  }
  virtual ~Adam() {}

  /**
   * @brief Initializes the Optimizer
   * @param[in, out] layer  Layer to optimize
   */
  void initialize(LayerType& layer)
  {
    Base::initialize(layer);

    // Reset moment matrices
    mean_gradient_.setZero(layer.output_dimension_, 1);
    var_gradient_.setZero(layer.output_dimension_, 1);
  }

  /**
   * @brief Applies optimization update
   * @param[in, out] layer  Layer to optimize
   * @retval true  if optimization update was applied successfully
   * @retval false  otherwise
   */
  bool update(LayerType& layer)
  {
    FFNN_ASSERT_MSG(layer.isInitialized(), "Layer to optimize is not initialized.");

    // Update gradient moments
    mean_gradient_ += beta1_ * (gradient_ - mean_gradient_);
    var_gradient_  += beta2_ * (gradient_ - var_gradient_);

    // Compute learning rates for all weights
    BiasVector current_gradient = var_gradient_;
    current_gradient.noalias() /= (1 - beta2_);
    current_gradient.array() += epsilon_;
    current_gradient.noalias() = mean_gradient_.array() / current_gradient.array();
    current_gradient.noalias() /= (1 - beta1_);
    current_gradient.noalias() *= Base::lr_;

    // Update weights
    layer.b_.noalias() -= current_gradient;

    // Reinitialize optimizer
    Base::reset(layer);
    return true;
  }

private:
  /// Mean decay rate
  const ScalarType beta1_;

  /// Variance decay rate
  const ScalarType beta2_;

  /// Variance normalization value
  const ScalarType epsilon_;

  /// Running mean of error gradient
  BiasVector mean_gradient_;

  /// Uncentered variance of error gradient
  BiasVector var_gradient_;
};
}  // namespace optimizer
}  // namespace ffnn
<|endoftext|>


<commit_before>/* This file is part of Mapnik (c++ mapping toolkit)
 * Copyright (C) 2006 Artem Pavlenko
 *
 * Mapnik 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 any later version.
 *
 * This program 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 more 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

//$Id$

// stl
#include <iostream>

// qt
#include <QtGui>
#include <QSplitter>
#include <QTreeView>
#include <QListView>
#include <QTabWidget>
#include <QList>
#include <QItemDelegate>
#include <QSlider>

// mapnik
#include <mapnik/config_error.hpp>
#include <mapnik/load_map.hpp>
#include <mapnik/save_map.hpp>

// qt
#include "mainwindow.hpp"
#include "layerlistmodel.hpp"
#include "styles_model.hpp"
#include "layerwidget.hpp"
#include "layerdelegate.hpp"
#include "about_dialog.hpp"

MainWindow::MainWindow()
    : filename_(),
      default_extent_(-20037508.3428,-20037508.3428,20037508.3428,20037508.3428)
{
    mapWidget_ = new MapWidget(this);
    QSplitter *splitter = new QSplitter(this);
    QTabWidget *tabWidget=new QTabWidget;
    layerTab_ = new LayerTab;
    layerTab_->setFocusPolicy(Qt::NoFocus);
    layerTab_->setIconSize(QSize(16,16));

    //LayerDelegate *delegate = new LayerDelegate(this);
    //layerTab_->setItemDelegate(delegate);
    //layerTab_->setItemDelegate(new QItemDelegate(this));
    //layerTab_->setViewMode(QListView::IconMode);

    layerTab_->setFlow(QListView::TopToBottom);
    tabWidget->addTab(layerTab_,tr("Layers"));

    // Styles tab
    styleTab_ = new StyleTab;
    tabWidget->addTab(styleTab_,tr("Styles"));
    splitter->addWidget(tabWidget);
    splitter->addWidget(mapWidget_);
    QList<int> list;
    list.push_back(200);
    list.push_back(600);
    splitter->setSizes(list);

    mapWidget_->setFocusPolicy(Qt::StrongFocus);
    mapWidget_->setFocus();

    //setCentralWidget(mapWidget_);
    setCentralWidget(splitter);
    createActions();
    createMenus();
    createToolBars();
    createContextMenu();

    setWindowTitle(tr("Mapnik Viewer"));
    status=new QStatusBar(this);
    status->showMessage(tr(""));
    setStatusBar(status);
    resize(800,600);

    //connect mapview to layerlist
    connect(mapWidget_, SIGNAL(mapViewChanged()),layerTab_, SLOT(update()));
    // slider
    connect(slider_,SIGNAL(valueChanged(int)),mapWidget_,SLOT(zoomToLevel(int)));
    //
    connect(layerTab_,SIGNAL(update_mapwidget()),mapWidget_,SLOT(updateMap()));
    connect(layerTab_,SIGNAL(layerSelected(int)),
            mapWidget_,SLOT(layerSelected(int)));
}

MainWindow::~MainWindow()
{
    delete mapWidget_;
}

void MainWindow::closeEvent(QCloseEvent* event)
{
    event->accept();
}

void MainWindow::createContextMenu()
{
    layerTab_->setContextMenuPolicy(Qt::ActionsContextMenu);
    layerTab_->addAction(openAct);
    layerTab_->addAction(layerInfo);
}

void MainWindow::open(QString const& path)
{
    if (path.isNull())
    {
        filename_ = QFileDialog::getOpenFileName(this,tr("Open Mapnik file"),
                                                 currentPath,"*.xml");
    }
    else
    {
        filename_ = path;
    }

    if (!filename_.isEmpty())
    {

        load_map_file(filename_);
        setWindowTitle(tr("%1 - Mapnik Viewer").arg(filename_));
    }

}

void MainWindow::reload()
{
    if (!filename_.isEmpty())
    {

        mapnik::box2d<double> bbox = mapWidget_->getMap()->get_current_extent();
        load_map_file(filename_);
        mapWidget_->zoomToBox(bbox);
        setWindowTitle(tr("%1 - *Reloaded*").arg(filename_));
    }
}

void MainWindow::save()
{
    QString initialPath = QDir::currentPath() + "/untitled.xml";
    QString filename = QFileDialog::getSaveFileName(this, tr("Save"),
                                                    initialPath,
                                                    tr("%1 Files (*.xml)")
                                                    .arg(QString("Mapnik definition")));
    if (!filename.isEmpty())
    {
        std::cout<<"saving "<< filename.toStdString() << std::endl;
        mapnik::save_map(*mapWidget_->getMap(),filename.toStdString());
    }
}

void MainWindow::load_map_file(QString const& filename)
{
    std::cout<<"loading "<< filename.toStdString() << std::endl;
    unsigned width = mapWidget_->width();
    unsigned height = mapWidget_->height();
    boost::shared_ptr<mapnik::Map> map(new mapnik::Map(width,height));
    mapWidget_->setMap(map);
    try
    {
        mapnik::load_map(*map,filename.toStdString());
    }
    catch (mapnik::config_error & ex)
    {
        std::cout << ex.what() << "\n";
    }
    catch (...)
    {
        std::cerr << "Exception caught in load_map\n";
    }
    layerTab_->setModel(new LayerListModel(map,this));
    styleTab_->setModel(new StyleModel(map,this));
}

void MainWindow::zoom_to_box()
{
    mapWidget_->setTool(MapWidget::ZoomToBox);
}

void MainWindow::pan()
{
    mapWidget_->setTool(MapWidget::Pan);
}

void MainWindow::info()
{
    mapWidget_->setTool(MapWidget::Info);
}

void MainWindow::pan_left()
{
    mapWidget_->panLeft();
}

void MainWindow::pan_right()
{
    mapWidget_->panRight();
}

void MainWindow::pan_up()
{
    mapWidget_->panUp();
}

void MainWindow::pan_down()
{
    mapWidget_->panDown();
}

void MainWindow::about()
{
    about_dialog dlg;
    dlg.exec();
}

void MainWindow::export_as()
{
    QAction *action = qobject_cast<QAction *>(sender());
    QByteArray fileFormat = action->data().toByteArray();
    QString initialPath = QDir::currentPath() + "/map." + fileFormat;

    QString fileName = QFileDialog::getSaveFileName(this, tr("Export As"),
                                                    initialPath,
                                                    tr("%1 Files (*.%2);;All Files (*)")
                                                    .arg(QString(fileFormat.toUpper()))
                                                    .arg(QString(fileFormat)));
    if (!fileName.isEmpty())
    {
        QPixmap const& pix = mapWidget_->pixmap();
        pix.save(fileName);
    }
}

void MainWindow::print()
{

    //Q_ASSERT(mapWidget_->pixmap());
    //QPrintDialog dialog(&printer, this);
    //if (dialog.exec()) {
    //   QPainter painter(&printer);
    //   QRect rect = painter.viewport();
    //   QSize size = mapWidget_->pixmap()->size();
    //   size.scale(rect.size(), Qt::KeepAspectRatio);
    //   painter.setViewport(rect.x(), rect.y(), size.width(), size.height());
    //   painter.setWindow(mapWidget_->pixmap()->rect());
    //   painter.drawPixmap(0, 0, *mapWidget_->pixmap());
    //}
}

void MainWindow::createActions()
{
    //exportAct = new QAction(tr("&Export as ..."),this);
    //exportAct->setShortcut(tr("Ctrl+E"));
    //connect(exportAct, SIGNAL(triggered()), this, SLOT(export_as()));
    zoomAllAct = new QAction(QIcon(":/images/home.png"),tr("Zoom All"),this);
    connect(zoomAllAct, SIGNAL(triggered()), this, SLOT(zoom_all()));

    zoomBoxAct = new QAction(QIcon(":/images/zoombox.png"),tr("Zoom To Box"),this);
    zoomBoxAct->setCheckable(true);
    connect(zoomBoxAct, SIGNAL(triggered()), this, SLOT(zoom_to_box()));

    panAct = new QAction(QIcon(":/images/pan.png"),tr("Pan"),this);
    panAct->setCheckable(true);
    connect(panAct, SIGNAL(triggered()), this, SLOT(pan()));

    infoAct = new QAction(QIcon(":/images/info.png"),tr("Info"),this);
    infoAct->setCheckable(true);
    connect(infoAct, SIGNAL(triggered()), this, SLOT(info()));

    toolsGroup=new QActionGroup(this);
    toolsGroup->addAction(zoomBoxAct);
    toolsGroup->addAction(panAct);
    toolsGroup->addAction(infoAct);
    zoomBoxAct->setChecked(true);

    openAct=new QAction(tr("Open Map definition"),this);
    connect(openAct,SIGNAL(triggered()),this,SLOT(open()));
    saveAct=new QAction(tr("Save Map definition"),this);
    connect(saveAct,SIGNAL(triggered()),this,SLOT(save()));

    panLeftAct = new QAction(QIcon(":/images/left.png"),tr("&Pan Left"),this);
    connect(panLeftAct, SIGNAL(triggered()), this, SLOT(pan_left()));
    panRightAct = new QAction(QIcon(":/images/right.png"),tr("&Pan Right"),this);
    connect(panRightAct, SIGNAL(triggered()), this, SLOT(pan_right()));
    panUpAct = new QAction(QIcon(":/images/up.png"),tr("&Pan Up"),this);
    connect(panUpAct, SIGNAL(triggered()), this, SLOT(pan_up()));
    panDownAct = new QAction(QIcon(":/images/down.png"),tr("&Pan Down"),this);
    connect(panDownAct, SIGNAL(triggered()), this, SLOT(pan_down()));

    reloadAct = new QAction(QIcon(":/images/reload.png"),tr("Reload"),this);
    connect(reloadAct, SIGNAL(triggered()), this, SLOT(reload()));

    layerInfo = new QAction(QIcon(":/images/info.png"),tr("&Layer info"),layerTab_);
    connect(layerInfo, SIGNAL(triggered()), layerTab_,SLOT(layerInfo()));
    connect(layerTab_, SIGNAL(doubleClicked(QModelIndex const&)), layerTab_,SLOT(layerInfo2(QModelIndex const&)));
    foreach (QByteArray format, QImageWriter::supportedImageFormats())
    {
        QString text = tr("%1...").arg(QString(format).toUpper());

        QAction *action = new QAction(text, this);
        action->setData(format);
        connect(action, SIGNAL(triggered()), this, SLOT(export_as()));
        exportAsActs.append(action);
    }

    printAct = new QAction(QIcon(":/images/print.png"),tr("&Print ..."),this);
    printAct->setShortcut(tr("Ctrl+E"));
    connect(printAct, SIGNAL(triggered()), this, SLOT(print()));

    exitAct = new QAction(tr("E&xit"), this);
    exitAct->setShortcut(tr("Ctrl+Q"));
    connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));

    aboutAct = new QAction(QIcon(":/images/about.png"),tr("&About"), this);
    connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
}

void MainWindow::createMenus()
{
    exportMenu = new QMenu(tr("&Export As"), this);
    foreach (QAction *action, exportAsActs)
        exportMenu->addAction(action);

    fileMenu = new QMenu(tr("&File"),this);
    fileMenu->addAction(openAct);
    fileMenu->addAction(saveAct);
    fileMenu->addMenu(exportMenu);
    fileMenu->addAction(printAct);
    fileMenu->addSeparator();
    fileMenu->addAction(exitAct);
    menuBar()->addMenu(fileMenu);

    helpMenu = new QMenu(tr("&Help"), this);
    helpMenu->addAction(aboutAct);
    menuBar()->addMenu(helpMenu);
}

void MainWindow::createToolBars()
{
    fileToolBar = addToolBar(tr("Actions"));
    fileToolBar->addAction(zoomAllAct);
    fileToolBar->addAction(zoomBoxAct);
    fileToolBar->addAction(panAct);
    fileToolBar->addAction(panLeftAct);
    fileToolBar->addAction(panRightAct);
    fileToolBar->addAction(panUpAct);
    fileToolBar->addAction(panDownAct);
    fileToolBar->addAction(infoAct);
    fileToolBar->addAction(reloadAct);
    fileToolBar->addAction(printAct);
    slider_ = new QSlider(Qt::Horizontal,fileToolBar);
    slider_->setRange(1,18);
    slider_->setTickPosition(QSlider::TicksBelow);
    slider_->setTickInterval(1);
    slider_->setTracking(false);
    fileToolBar->addWidget(slider_);
    fileToolBar->addAction(aboutAct);
}

void MainWindow::set_default_extent(double x0,double y0, double x1, double y1)
{
    try
    {
        boost::shared_ptr<mapnik::Map> map_ptr = mapWidget_->getMap();
        if (map_ptr)
        {
            mapnik::projection prj(map_ptr->srs());
            prj.forward(x0,y0);
            prj.forward(x1,y1);
            default_extent_=mapnik::box2d<double>(x0,y0,x1,y1);
            mapWidget_->zoomToBox(default_extent_);
            std::cout << "SET DEFAULT EXT\n";
        }
    }
    catch (...) {}
}

void MainWindow::set_scaling_factor(double scaling_factor)
{
    mapWidget_->set_scaling_factor(scaling_factor);
}

void MainWindow::zoom_all()
{
    boost::shared_ptr<mapnik::Map> map_ptr = mapWidget_->getMap();
    if (map_ptr)
    {
        map_ptr->zoom_all();
        mapnik::box2d<double> const& ext = map_ptr->get_current_extent();
        mapWidget_->zoomToBox(ext);
    }
}
<commit_msg>viewer: zoom to full extent of map when loading from xml<commit_after>/* This file is part of Mapnik (c++ mapping toolkit)
 * Copyright (C) 2006 Artem Pavlenko
 *
 * Mapnik 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 any later version.
 *
 * This program 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 more 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

//$Id$

// stl
#include <iostream>

// qt
#include <QtGui>
#include <QSplitter>
#include <QTreeView>
#include <QListView>
#include <QTabWidget>
#include <QList>
#include <QItemDelegate>
#include <QSlider>

// mapnik
#include <mapnik/config_error.hpp>
#include <mapnik/load_map.hpp>
#include <mapnik/save_map.hpp>

// qt
#include "mainwindow.hpp"
#include "layerlistmodel.hpp"
#include "styles_model.hpp"
#include "layerwidget.hpp"
#include "layerdelegate.hpp"
#include "about_dialog.hpp"

MainWindow::MainWindow()
    : filename_(),
      default_extent_(-20037508.3428,-20037508.3428,20037508.3428,20037508.3428)
{
    mapWidget_ = new MapWidget(this);
    QSplitter *splitter = new QSplitter(this);
    QTabWidget *tabWidget=new QTabWidget;
    layerTab_ = new LayerTab;
    layerTab_->setFocusPolicy(Qt::NoFocus);
    layerTab_->setIconSize(QSize(16,16));

    //LayerDelegate *delegate = new LayerDelegate(this);
    //layerTab_->setItemDelegate(delegate);
    //layerTab_->setItemDelegate(new QItemDelegate(this));
    //layerTab_->setViewMode(QListView::IconMode);

    layerTab_->setFlow(QListView::TopToBottom);
    tabWidget->addTab(layerTab_,tr("Layers"));

    // Styles tab
    styleTab_ = new StyleTab;
    tabWidget->addTab(styleTab_,tr("Styles"));
    splitter->addWidget(tabWidget);
    splitter->addWidget(mapWidget_);
    QList<int> list;
    list.push_back(200);
    list.push_back(600);
    splitter->setSizes(list);

    mapWidget_->setFocusPolicy(Qt::StrongFocus);
    mapWidget_->setFocus();

    //setCentralWidget(mapWidget_);
    setCentralWidget(splitter);
    createActions();
    createMenus();
    createToolBars();
    createContextMenu();

    setWindowTitle(tr("Mapnik Viewer"));
    status=new QStatusBar(this);
    status->showMessage(tr(""));
    setStatusBar(status);
    resize(800,600);

    //connect mapview to layerlist
    connect(mapWidget_, SIGNAL(mapViewChanged()),layerTab_, SLOT(update()));
    // slider
    connect(slider_,SIGNAL(valueChanged(int)),mapWidget_,SLOT(zoomToLevel(int)));
    //
    connect(layerTab_,SIGNAL(update_mapwidget()),mapWidget_,SLOT(updateMap()));
    connect(layerTab_,SIGNAL(layerSelected(int)),
            mapWidget_,SLOT(layerSelected(int)));
}

MainWindow::~MainWindow()
{
    delete mapWidget_;
}

void MainWindow::closeEvent(QCloseEvent* event)
{
    event->accept();
}

void MainWindow::createContextMenu()
{
    layerTab_->setContextMenuPolicy(Qt::ActionsContextMenu);
    layerTab_->addAction(openAct);
    layerTab_->addAction(layerInfo);
}

void MainWindow::open(QString const& path)
{
    if (path.isNull())
    {
        filename_ = QFileDialog::getOpenFileName(this,tr("Open Mapnik file"),
                                                 currentPath,"*.xml");
    }
    else
    {
        filename_ = path;
    }

    if (!filename_.isEmpty())
    {

        load_map_file(filename_);
        setWindowTitle(tr("%1 - Mapnik Viewer").arg(filename_));
    }

}

void MainWindow::reload()
{
    if (!filename_.isEmpty())
    {

        mapnik::box2d<double> bbox = mapWidget_->getMap()->get_current_extent();
        load_map_file(filename_);
        mapWidget_->zoomToBox(bbox);
        setWindowTitle(tr("%1 - *Reloaded*").arg(filename_));
    }
}

void MainWindow::save()
{
    QString initialPath = QDir::currentPath() + "/untitled.xml";
    QString filename = QFileDialog::getSaveFileName(this, tr("Save"),
                                                    initialPath,
                                                    tr("%1 Files (*.xml)")
                                                    .arg(QString("Mapnik definition")));
    if (!filename.isEmpty())
    {
        std::cout<<"saving "<< filename.toStdString() << std::endl;
        mapnik::save_map(*mapWidget_->getMap(),filename.toStdString());
    }
}

void MainWindow::load_map_file(QString const& filename)
{
    std::cout<<"loading "<< filename.toStdString() << std::endl;
    unsigned width = mapWidget_->width();
    unsigned height = mapWidget_->height();
    boost::shared_ptr<mapnik::Map> map(new mapnik::Map(width,height));
    mapWidget_->setMap(map);
    try
    {
        mapnik::load_map(*map,filename.toStdString());
    }
    catch (mapnik::config_error & ex)
    {
        std::cout << ex.what() << "\n";
    }
    catch (...)
    {
        std::cerr << "Exception caught in load_map\n";
    }
    layerTab_->setModel(new LayerListModel(map,this));
    styleTab_->setModel(new StyleModel(map,this));
    zoom_all();
}

void MainWindow::zoom_to_box()
{
    mapWidget_->setTool(MapWidget::ZoomToBox);
}

void MainWindow::pan()
{
    mapWidget_->setTool(MapWidget::Pan);
}

void MainWindow::info()
{
    mapWidget_->setTool(MapWidget::Info);
}

void MainWindow::pan_left()
{
    mapWidget_->panLeft();
}

void MainWindow::pan_right()
{
    mapWidget_->panRight();
}

void MainWindow::pan_up()
{
    mapWidget_->panUp();
}

void MainWindow::pan_down()
{
    mapWidget_->panDown();
}

void MainWindow::about()
{
    about_dialog dlg;
    dlg.exec();
}

void MainWindow::export_as()
{
    QAction *action = qobject_cast<QAction *>(sender());
    QByteArray fileFormat = action->data().toByteArray();
    QString initialPath = QDir::currentPath() + "/map." + fileFormat;

    QString fileName = QFileDialog::getSaveFileName(this, tr("Export As"),
                                                    initialPath,
                                                    tr("%1 Files (*.%2);;All Files (*)")
                                                    .arg(QString(fileFormat.toUpper()))
                                                    .arg(QString(fileFormat)));
    if (!fileName.isEmpty())
    {
        QPixmap const& pix = mapWidget_->pixmap();
        pix.save(fileName);
    }
}

void MainWindow::print()
{

    //Q_ASSERT(mapWidget_->pixmap());
    //QPrintDialog dialog(&printer, this);
    //if (dialog.exec()) {
    //   QPainter painter(&printer);
    //   QRect rect = painter.viewport();
    //   QSize size = mapWidget_->pixmap()->size();
    //   size.scale(rect.size(), Qt::KeepAspectRatio);
    //   painter.setViewport(rect.x(), rect.y(), size.width(), size.height());
    //   painter.setWindow(mapWidget_->pixmap()->rect());
    //   painter.drawPixmap(0, 0, *mapWidget_->pixmap());
    //}
}

void MainWindow::createActions()
{
    //exportAct = new QAction(tr("&Export as ..."),this);
    //exportAct->setShortcut(tr("Ctrl+E"));
    //connect(exportAct, SIGNAL(triggered()), this, SLOT(export_as()));
    zoomAllAct = new QAction(QIcon(":/images/home.png"),tr("Zoom All"),this);
    connect(zoomAllAct, SIGNAL(triggered()), this, SLOT(zoom_all()));

    zoomBoxAct = new QAction(QIcon(":/images/zoombox.png"),tr("Zoom To Box"),this);
    zoomBoxAct->setCheckable(true);
    connect(zoomBoxAct, SIGNAL(triggered()), this, SLOT(zoom_to_box()));

    panAct = new QAction(QIcon(":/images/pan.png"),tr("Pan"),this);
    panAct->setCheckable(true);
    connect(panAct, SIGNAL(triggered()), this, SLOT(pan()));

    infoAct = new QAction(QIcon(":/images/info.png"),tr("Info"),this);
    infoAct->setCheckable(true);
    connect(infoAct, SIGNAL(triggered()), this, SLOT(info()));

    toolsGroup=new QActionGroup(this);
    toolsGroup->addAction(zoomBoxAct);
    toolsGroup->addAction(panAct);
    toolsGroup->addAction(infoAct);
    zoomBoxAct->setChecked(true);

    openAct=new QAction(tr("Open Map definition"),this);
    connect(openAct,SIGNAL(triggered()),this,SLOT(open()));
    saveAct=new QAction(tr("Save Map definition"),this);
    connect(saveAct,SIGNAL(triggered()),this,SLOT(save()));

    panLeftAct = new QAction(QIcon(":/images/left.png"),tr("&Pan Left"),this);
    connect(panLeftAct, SIGNAL(triggered()), this, SLOT(pan_left()));
    panRightAct = new QAction(QIcon(":/images/right.png"),tr("&Pan Right"),this);
    connect(panRightAct, SIGNAL(triggered()), this, SLOT(pan_right()));
    panUpAct = new QAction(QIcon(":/images/up.png"),tr("&Pan Up"),this);
    connect(panUpAct, SIGNAL(triggered()), this, SLOT(pan_up()));
    panDownAct = new QAction(QIcon(":/images/down.png"),tr("&Pan Down"),this);
    connect(panDownAct, SIGNAL(triggered()), this, SLOT(pan_down()));

    reloadAct = new QAction(QIcon(":/images/reload.png"),tr("Reload"),this);
    connect(reloadAct, SIGNAL(triggered()), this, SLOT(reload()));

    layerInfo = new QAction(QIcon(":/images/info.png"),tr("&Layer info"),layerTab_);
    connect(layerInfo, SIGNAL(triggered()), layerTab_,SLOT(layerInfo()));
    connect(layerTab_, SIGNAL(doubleClicked(QModelIndex const&)), layerTab_,SLOT(layerInfo2(QModelIndex const&)));
    foreach (QByteArray format, QImageWriter::supportedImageFormats())
    {
        QString text = tr("%1...").arg(QString(format).toUpper());

        QAction *action = new QAction(text, this);
        action->setData(format);
        connect(action, SIGNAL(triggered()), this, SLOT(export_as()));
        exportAsActs.append(action);
    }

    printAct = new QAction(QIcon(":/images/print.png"),tr("&Print ..."),this);
    printAct->setShortcut(tr("Ctrl+E"));
    connect(printAct, SIGNAL(triggered()), this, SLOT(print()));

    exitAct = new QAction(tr("E&xit"), this);
    exitAct->setShortcut(tr("Ctrl+Q"));
    connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));

    aboutAct = new QAction(QIcon(":/images/about.png"),tr("&About"), this);
    connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
}

void MainWindow::createMenus()
{
    exportMenu = new QMenu(tr("&Export As"), this);
    foreach (QAction *action, exportAsActs)
        exportMenu->addAction(action);

    fileMenu = new QMenu(tr("&File"),this);
    fileMenu->addAction(openAct);
    fileMenu->addAction(saveAct);
    fileMenu->addMenu(exportMenu);
    fileMenu->addAction(printAct);
    fileMenu->addSeparator();
    fileMenu->addAction(exitAct);
    menuBar()->addMenu(fileMenu);

    helpMenu = new QMenu(tr("&Help"), this);
    helpMenu->addAction(aboutAct);
    menuBar()->addMenu(helpMenu);
}

void MainWindow::createToolBars()
{
    fileToolBar = addToolBar(tr("Actions"));
    fileToolBar->addAction(zoomAllAct);
    fileToolBar->addAction(zoomBoxAct);
    fileToolBar->addAction(panAct);
    fileToolBar->addAction(panLeftAct);
    fileToolBar->addAction(panRightAct);
    fileToolBar->addAction(panUpAct);
    fileToolBar->addAction(panDownAct);
    fileToolBar->addAction(infoAct);
    fileToolBar->addAction(reloadAct);
    fileToolBar->addAction(printAct);
    slider_ = new QSlider(Qt::Horizontal,fileToolBar);
    slider_->setRange(1,18);
    slider_->setTickPosition(QSlider::TicksBelow);
    slider_->setTickInterval(1);
    slider_->setTracking(false);
    fileToolBar->addWidget(slider_);
    fileToolBar->addAction(aboutAct);
}

void MainWindow::set_default_extent(double x0,double y0, double x1, double y1)
{
    try
    {
        boost::shared_ptr<mapnik::Map> map_ptr = mapWidget_->getMap();
        if (map_ptr)
        {
            mapnik::projection prj(map_ptr->srs());
            prj.forward(x0,y0);
            prj.forward(x1,y1);
            default_extent_=mapnik::box2d<double>(x0,y0,x1,y1);
            mapWidget_->zoomToBox(default_extent_);
            std::cout << "SET DEFAULT EXT\n";
        }
    }
    catch (...) {}
}

void MainWindow::set_scaling_factor(double scaling_factor)
{
    mapWidget_->set_scaling_factor(scaling_factor);
}

void MainWindow::zoom_all()
{
    boost::shared_ptr<mapnik::Map> map_ptr = mapWidget_->getMap();
    if (map_ptr)
    {
        map_ptr->zoom_all();
        mapnik::box2d<double> const& ext = map_ptr->get_current_extent();
        mapWidget_->zoomToBox(ext);
    }
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <helper/propertysetcontainer.hxx>
#include <threadhelp/resetableguard.hxx>

#include <vcl/svapp.hxx>

#define WRONG_TYPE_EXCEPTION    "Only XPropertSet allowed!"

using namespace cppu;
using namespace com::sun::star::uno;
using namespace com::sun::star::container;
using namespace com::sun::star::lang;
using namespace com::sun::star::beans;

namespace framework
{

PropertySetContainer::PropertySetContainer()
        :   ThreadHelpBase( &Application::GetSolarMutex() )
        ,   OWeakObject()

{
}

PropertySetContainer::~PropertySetContainer()
{
}

// XInterface
void SAL_CALL PropertySetContainer::acquire() throw ()
{
    OWeakObject::acquire();
}

void SAL_CALL PropertySetContainer::release() throw ()
{
    OWeakObject::release();
}

Any SAL_CALL PropertySetContainer::queryInterface( const Type& rType )
throw ( RuntimeException )
{
    Any a = ::cppu::queryInterface(
                rType ,
                (static_cast< XIndexContainer* >(this)),
                (static_cast< XIndexReplace* >(this)),
                (static_cast< XIndexAccess* >(this)),
                (static_cast< XElementAccess* >(this)) );

    if( a.hasValue() )
    {
        return a;
    }

    return OWeakObject::queryInterface( rType );
}

// XIndexContainer
void SAL_CALL PropertySetContainer::insertByIndex( sal_Int32 Index, const ::com::sun::star::uno::Any& Element )
    throw ( IllegalArgumentException, IndexOutOfBoundsException, WrappedTargetException, RuntimeException )
{
    ResetableGuard aGuard( m_aLock );

    sal_Int32 nSize = m_aPropertySetVector.size();

    if ( nSize >= Index )
    {
        Reference< XPropertySet > aPropertySetElement;

        if ( Element >>= aPropertySetElement )
        {
            if ( nSize == Index )
                m_aPropertySetVector.push_back( aPropertySetElement );
            else
            {
                PropertySetVector::iterator aIter = m_aPropertySetVector.begin();
                aIter += Index;
                m_aPropertySetVector.insert( aIter, aPropertySetElement );
            }
        }
        else
        {
            throw IllegalArgumentException(
                OUString( WRONG_TYPE_EXCEPTION ),
                (OWeakObject *)this, 2 );
        }
    }
    else
        throw IndexOutOfBoundsException( OUString(), (OWeakObject *)this );
}

void SAL_CALL PropertySetContainer::removeByIndex( sal_Int32 Index )
    throw ( IndexOutOfBoundsException, WrappedTargetException, RuntimeException )
{
    ResetableGuard aGuard( m_aLock );

    if ( (sal_Int32)m_aPropertySetVector.size() > Index )
    {
        PropertySetVector::iterator aIter = m_aPropertySetVector.begin();
        aIter += Index;
        m_aPropertySetVector.erase( aIter );
    }
    else
        throw IndexOutOfBoundsException( OUString(), (OWeakObject *)this );
}

// XIndexReplace
void SAL_CALL PropertySetContainer::replaceByIndex( sal_Int32 Index, const ::com::sun::star::uno::Any& Element )
    throw ( IllegalArgumentException, IndexOutOfBoundsException, WrappedTargetException, RuntimeException)
{
    if ( (sal_Int32)m_aPropertySetVector.size() > Index )
    {
        Reference< XPropertySet > aPropertySetElement;

        if ( Element >>= aPropertySetElement )
        {
            m_aPropertySetVector[ Index ] = aPropertySetElement;
        }
        else
        {
            throw IllegalArgumentException(
                OUString( WRONG_TYPE_EXCEPTION ),
                (OWeakObject *)this, 2 );
        }
    }
    else
        throw IndexOutOfBoundsException( OUString(), (OWeakObject *)this );
}

// XIndexAccess
sal_Int32 SAL_CALL PropertySetContainer::getCount()
    throw ( RuntimeException )
{
    ResetableGuard aGuard( m_aLock );

    return m_aPropertySetVector.size();
}

Any SAL_CALL PropertySetContainer::getByIndex( sal_Int32 Index )
    throw ( IndexOutOfBoundsException, WrappedTargetException, RuntimeException )
{
    ResetableGuard aGuard( m_aLock );

    if ( (sal_Int32)m_aPropertySetVector.size() > Index )
    {
        Any a;

        a <<= m_aPropertySetVector[ Index ];
        return a;
    }
    else
        throw IndexOutOfBoundsException( OUString(), (OWeakObject *)this );
}

// XElementAccess
sal_Bool SAL_CALL PropertySetContainer::hasElements()
    throw (::com::sun::star::uno::RuntimeException)
{
    ResetableGuard aGuard( m_aLock );

    return !( m_aPropertySetVector.empty() );
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>Bin intermediate iterator + rename parameter function<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <helper/propertysetcontainer.hxx>
#include <threadhelp/resetableguard.hxx>

#include <vcl/svapp.hxx>

#define WRONG_TYPE_EXCEPTION    "Only XPropertSet allowed!"

using namespace cppu;
using namespace com::sun::star::uno;
using namespace com::sun::star::container;
using namespace com::sun::star::lang;
using namespace com::sun::star::beans;

namespace framework
{

PropertySetContainer::PropertySetContainer()
        :   ThreadHelpBase( &Application::GetSolarMutex() )
        ,   OWeakObject()

{
}

PropertySetContainer::~PropertySetContainer()
{
}

// XInterface
void SAL_CALL PropertySetContainer::acquire() throw ()
{
    OWeakObject::acquire();
}

void SAL_CALL PropertySetContainer::release() throw ()
{
    OWeakObject::release();
}

Any SAL_CALL PropertySetContainer::queryInterface( const Type& rType )
throw ( RuntimeException )
{
    Any a = ::cppu::queryInterface(
                rType ,
                (static_cast< XIndexContainer* >(this)),
                (static_cast< XIndexReplace* >(this)),
                (static_cast< XIndexAccess* >(this)),
                (static_cast< XElementAccess* >(this)) );

    if( a.hasValue() )
    {
        return a;
    }

    return OWeakObject::queryInterface( rType );
}

// XIndexContainer
void SAL_CALL PropertySetContainer::insertByIndex( sal_Int32 Index, const ::com::sun::star::uno::Any& Element )
    throw ( IllegalArgumentException, IndexOutOfBoundsException, WrappedTargetException, RuntimeException )
{
    ResetableGuard aGuard( m_aLock );

    sal_Int32 nSize = m_aPropertySetVector.size();

    if ( nSize >= Index )
    {
        Reference< XPropertySet > aPropertySetElement;

        if ( Element >>= aPropertySetElement )
        {
            if ( nSize == Index )
                m_aPropertySetVector.push_back( aPropertySetElement );
            else
            {
                PropertySetVector::iterator aIter = m_aPropertySetVector.begin();
                aIter += Index;
                m_aPropertySetVector.insert( aIter, aPropertySetElement );
            }
        }
        else
        {
            throw IllegalArgumentException(
                OUString( WRONG_TYPE_EXCEPTION ),
                (OWeakObject *)this, 2 );
        }
    }
    else
        throw IndexOutOfBoundsException( OUString(), (OWeakObject *)this );
}

void SAL_CALL PropertySetContainer::removeByIndex( sal_Int32 nIndex )
    throw ( IndexOutOfBoundsException, WrappedTargetException, RuntimeException )
{
    ResetableGuard aGuard( m_aLock );

    if ( (sal_Int32)m_aPropertySetVector.size() > nIndex )
    {
        m_aPropertySetVector.erase(m_aPropertySetVector.begin() +  nIndex);
    }
    else
        throw IndexOutOfBoundsException( OUString(), (OWeakObject *)this );
}

// XIndexReplace
void SAL_CALL PropertySetContainer::replaceByIndex( sal_Int32 Index, const ::com::sun::star::uno::Any& Element )
    throw ( IllegalArgumentException, IndexOutOfBoundsException, WrappedTargetException, RuntimeException)
{
    if ( (sal_Int32)m_aPropertySetVector.size() > Index )
    {
        Reference< XPropertySet > aPropertySetElement;

        if ( Element >>= aPropertySetElement )
        {
            m_aPropertySetVector[ Index ] = aPropertySetElement;
        }
        else
        {
            throw IllegalArgumentException(
                OUString( WRONG_TYPE_EXCEPTION ),
                (OWeakObject *)this, 2 );
        }
    }
    else
        throw IndexOutOfBoundsException( OUString(), (OWeakObject *)this );
}

// XIndexAccess
sal_Int32 SAL_CALL PropertySetContainer::getCount()
    throw ( RuntimeException )
{
    ResetableGuard aGuard( m_aLock );

    return m_aPropertySetVector.size();
}

Any SAL_CALL PropertySetContainer::getByIndex( sal_Int32 Index )
    throw ( IndexOutOfBoundsException, WrappedTargetException, RuntimeException )
{
    ResetableGuard aGuard( m_aLock );

    if ( (sal_Int32)m_aPropertySetVector.size() > Index )
    {
        Any a;

        a <<= m_aPropertySetVector[ Index ];
        return a;
    }
    else
        throw IndexOutOfBoundsException( OUString(), (OWeakObject *)this );
}

// XElementAccess
sal_Bool SAL_CALL PropertySetContainer::hasElements()
    throw (::com::sun::star::uno::RuntimeException)
{
    ResetableGuard aGuard( m_aLock );

    return !( m_aPropertySetVector.empty() );
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>#include <nan.h>
#include <node.h>
#include <string>
#include <cstring>

#include "../include/str_array_converter.h"
#include "git2/strarray.h"

using namespace v8;
using namespace node;

git_strarray *StrArrayConverter::Convert(Local<v8::Value> val) {
  if (!Nan::To<bool>(val).FromJust()) {
    return NULL;
  }
  else if (val->IsArray()) {
    return ConvertArray(Array::Cast(*val));
  }
  else if (val->IsString() || val->IsStringObject()) {
    return ConvertString(Nan::To<v8::String>(val).ToLocalChecked());
  }
  else {
    return NULL;
  }
}

git_strarray * StrArrayConverter::AllocStrArray(const size_t count) {
  const size_t size = sizeof(git_strarray) + (sizeof(char*) * count);
  uint8_t* memory = reinterpret_cast<uint8_t*>(malloc(size));
  git_strarray *result = reinterpret_cast<git_strarray *>(memory);
  result->count = count;
  result->strings = reinterpret_cast<char**>(memory + sizeof(git_strarray));
  return result;
}

git_strarray *StrArrayConverter::ConvertArray(Array *val) {
  git_strarray *result = AllocStrArray(val->Length());

  for(size_t i = 0; i < result->count; i++) {
     Nan::Utf8String entry(val->Get(i));
    result->strings[i] = strdup(*entry);
  }

  return result;
}

git_strarray* StrArrayConverter::ConvertString(Local<String> val) {
  char *strings[1];
   Nan::Utf8String utf8String(val);

  strings[0] = *utf8String;

  return ConstructStrArray(1, strings);
}

git_strarray *StrArrayConverter::ConstructStrArray(int argc, char** argv) {
  git_strarray *result = AllocStrArray(argc);

  for(size_t i = 0; i < result->count; i++) {
    result->strings[i] = strdup(argv[i]);
  }

  return result;
}
<commit_msg>:art: Final spacing amends for manual str_array_converter<commit_after>#include <nan.h>
#include <node.h>
#include <string>
#include <cstring>

#include "../include/str_array_converter.h"
#include "git2/strarray.h"

using namespace v8;
using namespace node;

git_strarray *StrArrayConverter::Convert(Local<v8::Value> val) {
  if (!Nan::To<bool>(val).FromJust()) {
    return NULL;
  }
  else if (val->IsArray()) {
    return ConvertArray(Array::Cast(*val));
  }
  else if (val->IsString() || val->IsStringObject()) {
    return ConvertString(Nan::To<v8::String>(val).ToLocalChecked());
  }
  else {
    return NULL;
  }
}

git_strarray * StrArrayConverter::AllocStrArray(const size_t count) {
  const size_t size = sizeof(git_strarray) + (sizeof(char*) * count);
  uint8_t* memory = reinterpret_cast<uint8_t*>(malloc(size));
  git_strarray *result = reinterpret_cast<git_strarray *>(memory);
  result->count = count;
  result->strings = reinterpret_cast<char**>(memory + sizeof(git_strarray));
  return result;
}

git_strarray *StrArrayConverter::ConvertArray(Array *val) {
  git_strarray *result = AllocStrArray(val->Length());

  for(size_t i = 0; i < result->count; i++) {
    Nan::Utf8String entry(val->Get(i));
    result->strings[i] = strdup(*entry);
  }

  return result;
}

git_strarray* StrArrayConverter::ConvertString(Local<String> val) {
  char *strings[1];
  Nan::Utf8String utf8String(val);

  strings[0] = *utf8String;

  return ConstructStrArray(1, strings);
}

git_strarray *StrArrayConverter::ConstructStrArray(int argc, char** argv) {
  git_strarray *result = AllocStrArray(argc);

  for(size_t i = 0; i < result->count; i++) {
    result->strings[i] = strdup(argv[i]);
  }

  return result;
}
<|endoftext|>


<commit_before>/******************************************************************************/
/**
@file
@author		Scott Fazackerley
@brief		Wraps the Arduino Serial object and provides a simple printf
			implementation for C.
@copyright	Copyright 2016
				The University of British Columbia,
				IonDB Project Contributors (see AUTHORS.md)
@par
			Licensed under the Apache License, Version 2.0 (the "License");
			you may not use this file except in compliance with the License.
			You may obtain a copy of the License at
					http://www.apache.org/licenses/LICENSE-2.0
@par
			Unless required by applicable law or agreed to in writing,
			software distributed under the License is distributed on an
			"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
			either express or implied. See the License for the specific
			language governing permissions and limitations under the
			License.
*/
/******************************************************************************/

#include "serial_c_iface.h"

int
serial_printf_c(
	const char *format,
	...
) {
	va_list args;

	va_start(args, format);

	int		bufsize = vsnprintf(NULL, 0, format, args);
	char	buf[bufsize];

	va_end(args);

	va_start(args);
	vsnprintf(buf, bufsize, format, args);
	va_end(args);

	return serial_print(buf);
}

int
serial_print(
	const char *buffer
) {
	int num;

	num = Serial.print(buffer);
#if DEBUG
	Serial.flush();
#endif
	return num;
}

void
serial_init(
	int baud_rate
) {
	Serial.begin(baud_rate);
}

void
serial_close(
) {
	Serial.end();
}
<commit_msg>Fix missing param in va_start<commit_after>/******************************************************************************/
/**
@file
@author		Scott Fazackerley
@brief		Wraps the Arduino Serial object and provides a simple printf
			implementation for C.
@copyright	Copyright 2016
				The University of British Columbia,
				IonDB Project Contributors (see AUTHORS.md)
@par
			Licensed under the Apache License, Version 2.0 (the "License");
			you may not use this file except in compliance with the License.
			You may obtain a copy of the License at
					http://www.apache.org/licenses/LICENSE-2.0
@par
			Unless required by applicable law or agreed to in writing,
			software distributed under the License is distributed on an
			"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
			either express or implied. See the License for the specific
			language governing permissions and limitations under the
			License.
*/
/******************************************************************************/

#include "serial_c_iface.h"

int
serial_printf_c(
	const char *format,
	...
) {
	va_list args;

	va_start(args, format);

	int		bufsize = vsnprintf(NULL, 0, format, args);
	char	buf[bufsize];

	va_end(args);

	va_start(args, format);
	vsnprintf(buf, bufsize, format, args);
	va_end(args);

	return serial_print(buf);
}

int
serial_print(
	const char *buffer
) {
	int num;

	num = Serial.print(buffer);
#if DEBUG
	Serial.flush();
#endif
	return num;
}

void
serial_init(
	int baud_rate
) {
	Serial.begin(baud_rate);
}

void
serial_close(
) {
	Serial.end();
}
<|endoftext|>


<commit_before>/*
  This file is part of Ingen.
  Copyright 2007-2015 David Robillard <http://drobilla.net/>

  Ingen is free software: you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free
  Software Foundation, either version 3 of the License, or 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 Affero General Public License for details.

  You should have received a copy of the GNU Affero General Public License
  along with Ingen.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <errno.h>
#include <poll.h>

#include <sstream>
#include <thread>

#include "ingen/Configuration.hpp"
#include "ingen/Module.hpp"
#include "ingen/World.hpp"
#include "raul/Socket.hpp"

#include "../server/Engine.hpp"
#include "../server/EventWriter.hpp"

#include "SocketListener.hpp"
#include "SocketServer.hpp"

namespace Ingen {
namespace Server {

void
SocketListener::ingen_listen(Engine*       engine,
                             Raul::Socket* unix_sock,
                             Raul::Socket* net_sock)
{
	Ingen::World* world = engine->world();

	const std::string unix_path(world->conf().option("socket").ptr<char>());

	// Bind UNIX socket
	const Raul::URI unix_uri(unix_scheme + unix_path);
	if (!unix_sock->bind(unix_uri) || !unix_sock->listen()) {
		world->log().error("Failed to create UNIX socket\n");
		unix_sock->close();
	} else {
		world->log().info(fmt("Listening on socket %1%\n") % unix_uri);
	}

	// Bind TCP socket
	const int port = world->conf().option("engine-port").get<int32_t>();
	std::ostringstream ss;
	ss << "tcp://*:" << port;
	if (!net_sock->bind(Raul::URI(ss.str())) || !net_sock->listen()) {
		world->log().error("Failed to create TCP socket\n");
		net_sock->close();
	} else {
		world->log().info(fmt("Listening on TCP port %1%\n") % port);
	}

	if (unix_sock->fd() == -1 && net_sock->fd() == -1) {
		return;  // No sockets to listen to, exit thread
	}

	struct pollfd pfds[2];
	int           nfds = 0;
	if (unix_sock->fd() != -1) {
		pfds[nfds].fd      = unix_sock->fd();
		pfds[nfds].events  = POLLIN;
		pfds[nfds].revents = 0;
		++nfds;
	}
	if (net_sock->fd() != -1) {
		pfds[nfds].fd      = net_sock->fd();
		pfds[nfds].events  = POLLIN;
		pfds[nfds].revents = 0;
		++nfds;
	}

	while (true) {
		// Wait for input to arrive at a socket
		const int ret = poll(pfds, nfds, -1);
		if (ret == -1) {
			world->log().error(fmt("Poll error: %1%\n") % strerror(errno));
			break;
		} else if (ret == 0) {
			world->log().warn("Poll returned with no data\n");
			continue;
		} else if ((pfds[0].revents & POLLHUP) || pfds[1].revents & POLLHUP) {
			break;
		}

		if (pfds[0].revents & POLLIN) {
			SPtr<Raul::Socket> conn = unix_sock->accept();
			if (conn) {
				new SocketServer(*world, *engine, conn);
			}
		}

		if (pfds[1].revents & POLLIN) {
			SPtr<Raul::Socket> conn = net_sock->accept();
			if (conn) {
				new SocketServer(*world, *engine, conn);
			}
		}
	}
}

} // namespace Server
} // namespace Ingen
<commit_msg>Gracefully handle UNIX socket after a crash<commit_after>/*
  This file is part of Ingen.
  Copyright 2007-2015 David Robillard <http://drobilla.net/>

  Ingen is free software: you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free
  Software Foundation, either version 3 of the License, or 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 Affero General Public License for details.

  You should have received a copy of the GNU Affero General Public License
  along with Ingen.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <sys/stat.h>
#include <unistd.h>

#include <sstream>
#include <string>
#include <thread>

#include "ingen/Configuration.hpp"
#include "ingen/Module.hpp"
#include "ingen/World.hpp"
#include "raul/Socket.hpp"

#include "../server/Engine.hpp"
#include "../server/EventWriter.hpp"

#include "SocketListener.hpp"
#include "SocketServer.hpp"

namespace Ingen {
namespace Server {

static std::string
get_link_target(const char* link_path)
{
	// Stat the link to get the required size for the target path
	struct stat link_stat;
	if (lstat(link_path, &link_stat)) {
		return std::string();
	}

	// Allocate buffer and read link target
	char* target = (char*)calloc(1, link_stat.st_size + 1);
	if (readlink(link_path, target, link_stat.st_size) != -1) {
		const std::string result(target);
		free(target);
		return result;
	}

	return std::string();
}

void
SocketListener::ingen_listen(Engine*       engine,
                             Raul::Socket* unix_sock,
                             Raul::Socket* net_sock)
{
	Ingen::World* world = engine->world();

	const std::string link_path(world->conf().option("socket").ptr<char>());
	const std::string unix_path(link_path + "." + std::to_string(getpid()));

	// Bind UNIX socket and create PID-less symbolic link
	const Raul::URI unix_uri(unix_scheme + unix_path);
	bool            make_link = true;
	if (!unix_sock->bind(unix_uri) || !unix_sock->listen()) {
		world->log().error("Failed to create UNIX socket\n");
		unix_sock->close();
		make_link = false;
	} else {
		const std::string old_path = get_link_target(link_path.c_str());
		if (!old_path.empty()) {
			const std::string suffix = old_path.substr(old_path.find_last_of(".") + 1);
			const pid_t       pid    = std::stoi(suffix);
			if (!kill(pid, 0)) {
				make_link = false;
				world->log().warn(fmt("Another Ingen instance is running at %1% => %2%\n")
				                  % link_path % old_path);
			} else {
				world->log().warn(fmt("Replacing old link %1% => %2%\n")
				                  % link_path % old_path);
				unlink(link_path.c_str());
			}
		}

		if (make_link) {
			if (!symlink(unix_path.c_str(), link_path.c_str())) {
				world->log().info(fmt("Listening on %1%\n") %
				                  (unix_scheme + link_path));
			} else {
				world->log().error(fmt("Failed to link %1% => %2% (%3%)\n")
				                   % link_path % unix_path % strerror(errno));
			}
		} else {
			world->log().info(fmt("Listening on %1%\n") % unix_uri);
		}
	}

	// Bind TCP socket
	const int port = world->conf().option("engine-port").get<int32_t>();
	std::ostringstream ss;
	ss << "tcp://*:" << port;
	if (!net_sock->bind(Raul::URI(ss.str())) || !net_sock->listen()) {
		world->log().error("Failed to create TCP socket\n");
		net_sock->close();
	} else {
		world->log().info(fmt("Listening on TCP port %1%\n") % port);
	}

	if (unix_sock->fd() == -1 && net_sock->fd() == -1) {
		return;  // No sockets to listen to, exit thread
	}

	struct pollfd pfds[2];
	int           nfds = 0;
	if (unix_sock->fd() != -1) {
		pfds[nfds].fd      = unix_sock->fd();
		pfds[nfds].events  = POLLIN;
		pfds[nfds].revents = 0;
		++nfds;
	}
	if (net_sock->fd() != -1) {
		pfds[nfds].fd      = net_sock->fd();
		pfds[nfds].events  = POLLIN;
		pfds[nfds].revents = 0;
		++nfds;
	}

	while (true) {
		// Wait for input to arrive at a socket
		const int ret = poll(pfds, nfds, -1);
		if (ret == -1) {
			world->log().error(fmt("Poll error: %1%\n") % strerror(errno));
			break;
		} else if (ret == 0) {
			world->log().warn("Poll returned with no data\n");
			continue;
		} else if ((pfds[0].revents & POLLHUP) || pfds[1].revents & POLLHUP) {
			break;
		}

		if (pfds[0].revents & POLLIN) {
			SPtr<Raul::Socket> conn = unix_sock->accept();
			if (conn) {
				new SocketServer(*world, *engine, conn);
			}
		}

		if (pfds[1].revents & POLLIN) {
			SPtr<Raul::Socket> conn = net_sock->accept();
			if (conn) {
				new SocketServer(*world, *engine, conn);
			}
		}
	}

	if (make_link) {
		unlink(link_path.c_str());
	}
}

} // namespace Server
} // namespace Ingen
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/workarounds/dp16_workarounds.H $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file workarounds/dp16.H
/// @brief Workarounds for the DP16 logic blocks
/// Workarounds are very deivce specific, so there is no attempt to generalize
/// this code in any way.
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Steven Glancy <sglancy@usi.ibm.com>
// *HWP Team: Memory
// *HWP Level: 2
// *HWP Consumed by: FSP:HB

#ifndef _MSS_WORKAROUNDS_DP16_H_
#define _MSS_WORKAROUNDS_DP16_H_

#include <fapi2.H>

namespace mss
{

namespace workarounds
{

namespace dp16
{

///
/// @brief DQS polarity workaround
/// For Monza DDR port 2, one pair of DQS P/N is swapped polarity.  Not in DDR port 6
/// @param[in] i_target the fapi2 target of the port
/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok
/// @note This function is called during the phy scom init procedure, after the initfile is
/// processed. It is specific to the Monza module, but can be called for all modules as it
/// will enforce its requirements internally
///
fapi2::ReturnCode dqs_polarity( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target );

///
/// @brief DP16 Read Diagnostic Configuration 5 work around
/// Not in the Model 67 spydef, so we scom them. Should be removed when they are
/// added to the spydef.
/// @param[in] i_target the fapi2 target of the port
/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok
///
fapi2::ReturnCode rd_dia_config5( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target );

///
/// @brief DP16 DQSCLK Offset work around
/// Not in the Model 67 spydef, so we scom them. Should be removed when they are
/// added to the spydef.
/// @param[in] i_target the fapi2 target of the port
/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok
///
fapi2::ReturnCode dqsclk_offset( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target );

} // close namespace dp16
} // close namespace workarounds
} // close namespace mss

#endif
<commit_msg>Added WR VREF workaround for error logic<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/workarounds/dp16_workarounds.H $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file workarounds/dp16.H
/// @brief Workarounds for the DP16 logic blocks
/// Workarounds are very deivce specific, so there is no attempt to generalize
/// this code in any way.
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Stephen Glancy <sglancy@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 2
// *HWP Consumed by: FSP:HB

#ifndef _MSS_WORKAROUNDS_DP16_H_
#define _MSS_WORKAROUNDS_DP16_H_

#include <fapi2.H>

namespace mss
{

namespace workarounds
{

namespace dp16
{

///
/// @brief DQS polarity workaround
/// For Monza DDR port 2, one pair of DQS P/N is swapped polarity.  Not in DDR port 6
/// @param[in] i_target the fapi2 target of the port
/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok
/// @note This function is called during the phy scom init procedure, after the initfile is
/// processed. It is specific to the Monza module, but can be called for all modules as it
/// will enforce its requirements internally
///
fapi2::ReturnCode dqs_polarity( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target );

///
/// @brief DP16 Read Diagnostic Configuration 5 work around
/// Not in the Model 67 spydef, so we scom them. Should be removed when they are
/// added to the spydef.
/// @param[in] i_target the fapi2 target of the port
/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok
///
fapi2::ReturnCode rd_dia_config5( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target );

///
/// @brief DP16 DQSCLK Offset work around
/// Not in the Model 67 spydef, so we scom them. Should be removed when they are
/// added to the spydef.
/// @param[in] i_target the fapi2 target of the port
/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok
///
fapi2::ReturnCode dqsclk_offset( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target );

namespace wr_vref
{

///
/// @brief DP16 WR VREF error latching workaround
/// In DD1 Nimbus in the WR VREF algorithm ,DRAM's 2/3 latch over error information from DRAM's 0/1.
/// The workaround is to set the error mask for DRAM's 2/3 to be 0xFFFF (informational but not errors)
/// @param[in] i_target the fapi2 target of the port
/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok
///
fapi2::ReturnCode error_dram23( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target );

} // close namespace wr_vref
} // close namespace dp16
} // close namespace workarounds
} // close namespace mss

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.  Use of this
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.

#include "media/base/video_frame_impl.h"
#include "media/filters/ffmpeg_common.h"
#include "media/filters/ffmpeg_demuxer.h"
#include "media/filters/ffmpeg_video_decoder.h"

namespace media {

FFmpegVideoDecoder::FFmpegVideoDecoder()
    : DecoderBase<VideoDecoder, VideoFrame>(NULL),
      width_(0),
      height_(0) {
}

FFmpegVideoDecoder::~FFmpegVideoDecoder() {
}

// static
bool FFmpegVideoDecoder::IsMediaFormatSupported(const MediaFormat& format) {
  std::string mime_type;
  return format.GetAsString(MediaFormat::kMimeType, &mime_type) &&
      mime_type::kFFmpegVideo == mime_type;
}

bool FFmpegVideoDecoder::OnInitialize(DemuxerStream* demuxer_stream) {
  scoped_refptr<FFmpegDemuxerStream> ffmpeg_demuxer_stream;
  if (!demuxer_stream->QueryInterface(&ffmpeg_demuxer_stream)) {
    return false;
  }

  AVStream* av_stream = ffmpeg_demuxer_stream->av_stream();

  width_ = av_stream->codec->width;
  height_ = av_stream->codec->height;

  media_format_.SetAsString(MediaFormat::kMimeType,
                            mime_type::kUncompressedVideo);
  media_format_.SetAsInteger(MediaFormat::kWidth, width_);
  media_format_.SetAsInteger(MediaFormat::kHeight, height_);

  codec_context_ = ffmpeg_demuxer_stream->av_stream()->codec;
  codec_context_->flags2 |= CODEC_FLAG2_FAST;  // Enable faster H264 decode.
  AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
  if (!codec || avcodec_open(codec_context_, codec) < 0) {
    host_->Error(media::PIPELINE_ERROR_DECODE);
    return false;
  }
  return true;
}

void FFmpegVideoDecoder::OnDecode(Buffer* buffer) {
  // Check for end of stream.
  // TODO(scherkus): check for end of stream.

  // Queue the incoming timestamp.
  TimeTuple times;
  times.timestamp = buffer->GetTimestamp();
  times.duration = buffer->GetDuration();
  time_queue_.push(times);

  // Cast everything to FFmpeg types.
  const uint8_t* data_in = buffer->GetData();
  const size_t size_in = buffer->GetDataSize();

  // We don't allocate AVFrame on the stack since different versions of FFmpeg
  // may change the size of AVFrame, causing stack corruption.  The solution is
  // to let FFmpeg allocate the structure via avcodec_alloc_frame().
  int decoded = 0;
  scoped_ptr_malloc<AVFrame, ScopedPtrAVFree> yuv_frame(avcodec_alloc_frame());
  int result = avcodec_decode_video(codec_context_, yuv_frame.get(), &decoded,
                                    data_in, size_in);

  if (result < 0) {
    host_->Error(PIPELINE_ERROR_DECODE);
    return;
  }

  if (result == 0 || decoded == 0) {
    return;
  }

  // J (Motion JPEG) versions of YUV are full range 0..255.
  // Regular (MPEG) YUV is 16..240.
  // For now we will ignore the distinction and treat them the same.

  VideoSurface::Format surface_format;
  switch (codec_context_->pix_fmt) {
    case PIX_FMT_YUV420P:
    case PIX_FMT_YUVJ420P:
      surface_format = VideoSurface::YV12;
      break;
    case PIX_FMT_YUV422P:
    case PIX_FMT_YUVJ422P:
      surface_format = VideoSurface::YV16;
      break;
    default:
      // TODO(scherkus): More formats here?
      NOTREACHED();
      host_->Error(PIPELINE_ERROR_DECODE);
      return;
  }
  if (!EnqueueVideoFrame(surface_format, yuv_frame.get())) {
    host_->Error(PIPELINE_ERROR_DECODE);
  }
}

bool FFmpegVideoDecoder::EnqueueVideoFrame(VideoSurface::Format surface_format,
                                           const AVFrame* frame) {
  // Dequeue the next time tuple and create a VideoFrame object with
  // that timestamp and duration.
  TimeTuple time = time_queue_.top();
  time_queue_.pop();

  scoped_refptr<VideoFrame> video_frame;
  VideoFrameImpl::CreateFrame(surface_format, width_, height_,
                              time.timestamp, time.duration, &video_frame);
  if (!video_frame) {
    return false;
  }
  // Copy the frame data since FFmpeg reuses internal buffers for AVFrame
  // output, meaning the data is only valid until the next
  // avcodec_decode_video() call.
  // TODO(scherkus): figure out pre-allocation/buffer cycling scheme.
  // TODO(scherkus): is there a cleaner way to figure out the # of planes?
  VideoSurface surface;
  if (!video_frame->Lock(&surface)) {
    return false;
  }
  CopyPlane(VideoSurface::kYPlane, surface, frame);
  CopyPlane(VideoSurface::kUPlane, surface, frame);
  CopyPlane(VideoSurface::kVPlane, surface, frame);
  video_frame->Unlock();
  EnqueueResult(video_frame);
  return true;
}

void FFmpegVideoDecoder::CopyPlane(size_t plane,
                                   const VideoSurface& surface,
                                   const AVFrame* frame) {
  DCHECK(surface.width % 4 == 0);
  DCHECK(surface.height % 2 == 0);
  const uint8* source = frame->data[plane];
  const size_t source_stride = frame->linesize[plane];
  uint8* dest = surface.data[plane];
  const size_t dest_stride = surface.strides[plane];
  size_t bytes_per_line = surface.width;
  size_t copy_lines = surface.height;
  if (plane != VideoSurface::kYPlane) {
    bytes_per_line /= 2;
    if (surface.format == VideoSurface::YV12) {
      copy_lines /= 2;
    }
  }
  DCHECK(bytes_per_line <= source_stride && bytes_per_line <= dest_stride);
  for (size_t i = 0; i < copy_lines; ++i) {
    memcpy(dest, source, bytes_per_line);
    source += source_stride;
    dest += dest_stride;
  }
}

}  // namespace
<commit_msg>Reduce ffmpeg alignment requirement from 4x2 to 2x1. The multiple of 4 restriction on width is unnecessary and prevents some valid movies from playing. Height being any size does not cause any problems for YV12, which is half height on chroma, as long as you round up in any calcs, and in YV16 there should be no height constraint. Use uluh.mov in debug mode to test.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.  Use of this
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.

#include "media/base/video_frame_impl.h"
#include "media/filters/ffmpeg_common.h"
#include "media/filters/ffmpeg_demuxer.h"
#include "media/filters/ffmpeg_video_decoder.h"

namespace media {

FFmpegVideoDecoder::FFmpegVideoDecoder()
    : DecoderBase<VideoDecoder, VideoFrame>(NULL),
      width_(0),
      height_(0) {
}

FFmpegVideoDecoder::~FFmpegVideoDecoder() {
}

// static
bool FFmpegVideoDecoder::IsMediaFormatSupported(const MediaFormat& format) {
  std::string mime_type;
  return format.GetAsString(MediaFormat::kMimeType, &mime_type) &&
      mime_type::kFFmpegVideo == mime_type;
}

bool FFmpegVideoDecoder::OnInitialize(DemuxerStream* demuxer_stream) {
  scoped_refptr<FFmpegDemuxerStream> ffmpeg_demuxer_stream;
  if (!demuxer_stream->QueryInterface(&ffmpeg_demuxer_stream)) {
    return false;
  }

  AVStream* av_stream = ffmpeg_demuxer_stream->av_stream();

  width_ = av_stream->codec->width;
  height_ = av_stream->codec->height;

  media_format_.SetAsString(MediaFormat::kMimeType,
                            mime_type::kUncompressedVideo);
  media_format_.SetAsInteger(MediaFormat::kWidth, width_);
  media_format_.SetAsInteger(MediaFormat::kHeight, height_);

  codec_context_ = ffmpeg_demuxer_stream->av_stream()->codec;
  codec_context_->flags2 |= CODEC_FLAG2_FAST;  // Enable faster H264 decode.
  AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
  if (!codec || avcodec_open(codec_context_, codec) < 0) {
    host_->Error(media::PIPELINE_ERROR_DECODE);
    return false;
  }
  return true;
}

void FFmpegVideoDecoder::OnDecode(Buffer* buffer) {
  // Check for end of stream.
  // TODO(scherkus): check for end of stream.

  // Queue the incoming timestamp.
  TimeTuple times;
  times.timestamp = buffer->GetTimestamp();
  times.duration = buffer->GetDuration();
  time_queue_.push(times);

  // Cast everything to FFmpeg types.
  const uint8_t* data_in = buffer->GetData();
  const size_t size_in = buffer->GetDataSize();

  // We don't allocate AVFrame on the stack since different versions of FFmpeg
  // may change the size of AVFrame, causing stack corruption.  The solution is
  // to let FFmpeg allocate the structure via avcodec_alloc_frame().
  int decoded = 0;
  scoped_ptr_malloc<AVFrame, ScopedPtrAVFree> yuv_frame(avcodec_alloc_frame());
  int result = avcodec_decode_video(codec_context_, yuv_frame.get(), &decoded,
                                    data_in, size_in);

  if (result < 0) {
    host_->Error(PIPELINE_ERROR_DECODE);
    return;
  }

  if (result == 0 || decoded == 0) {
    return;
  }

  // J (Motion JPEG) versions of YUV are full range 0..255.
  // Regular (MPEG) YUV is 16..240.
  // For now we will ignore the distinction and treat them the same.

  VideoSurface::Format surface_format;
  switch (codec_context_->pix_fmt) {
    case PIX_FMT_YUV420P:
    case PIX_FMT_YUVJ420P:
      surface_format = VideoSurface::YV12;
      break;
    case PIX_FMT_YUV422P:
    case PIX_FMT_YUVJ422P:
      surface_format = VideoSurface::YV16;
      break;
    default:
      // TODO(scherkus): More formats here?
      NOTREACHED();
      host_->Error(PIPELINE_ERROR_DECODE);
      return;
  }
  if (!EnqueueVideoFrame(surface_format, yuv_frame.get())) {
    host_->Error(PIPELINE_ERROR_DECODE);
  }
}

bool FFmpegVideoDecoder::EnqueueVideoFrame(VideoSurface::Format surface_format,
                                           const AVFrame* frame) {
  // Dequeue the next time tuple and create a VideoFrame object with
  // that timestamp and duration.
  TimeTuple time = time_queue_.top();
  time_queue_.pop();

  scoped_refptr<VideoFrame> video_frame;
  VideoFrameImpl::CreateFrame(surface_format, width_, height_,
                              time.timestamp, time.duration, &video_frame);
  if (!video_frame) {
    return false;
  }
  // Copy the frame data since FFmpeg reuses internal buffers for AVFrame
  // output, meaning the data is only valid until the next
  // avcodec_decode_video() call.
  // TODO(scherkus): figure out pre-allocation/buffer cycling scheme.
  // TODO(scherkus): is there a cleaner way to figure out the # of planes?
  VideoSurface surface;
  if (!video_frame->Lock(&surface)) {
    return false;
  }
  CopyPlane(VideoSurface::kYPlane, surface, frame);
  CopyPlane(VideoSurface::kUPlane, surface, frame);
  CopyPlane(VideoSurface::kVPlane, surface, frame);
  video_frame->Unlock();
  EnqueueResult(video_frame);
  return true;
}

void FFmpegVideoDecoder::CopyPlane(size_t plane,
                                   const VideoSurface& surface,
                                   const AVFrame* frame) {
  DCHECK(surface.width % 2 == 0);
  const uint8* source = frame->data[plane];
  const size_t source_stride = frame->linesize[plane];
  uint8* dest = surface.data[plane];
  const size_t dest_stride = surface.strides[plane];
  size_t bytes_per_line = surface.width;
  size_t copy_lines = surface.height;
  if (plane != VideoSurface::kYPlane) {
    bytes_per_line /= 2;
    if (surface.format == VideoSurface::YV12) {
      copy_lines = (copy_lines + 1) / 2;
    }
  }
  DCHECK(bytes_per_line <= source_stride && bytes_per_line <= dest_stride);
  for (size_t i = 0; i < copy_lines; ++i) {
    memcpy(dest, source, bytes_per_line);
    source += source_stride;
    dest += dest_stride;
  }
}

}  // namespace
<|endoftext|>


<commit_before>#include <assert.h>
#include <mutex>
#include <chrono>
#include <thread>
#include <mutex>
#include <condition_variable>
#include "core.hpp"
#include "audio.hpp"
#include "dsp/samplerate.hpp"
#include "dsp/ringbuffer.hpp"

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsuggest-override"
#include <RtAudio.h>
#pragma GCC diagnostic pop

#define OUTPUTS 8
#define INPUTS 8

static const auto audioTimeout = std::chrono::milliseconds(100);

using namespace rack;

struct AudioInterfaceIO : AudioIO {
	std::mutex engineMutex;
	std::condition_variable engineCv;
	std::mutex audioMutex;
	std::condition_variable audioCv;
	// Audio thread produces, engine thread consumes
	DoubleRingBuffer<Frame<INPUTS>, (1<<15)> inputBuffer;
	// Audio thread consumes, engine thread produces
	DoubleRingBuffer<Frame<OUTPUTS>, (1<<15)> outputBuffer;

	AudioInterfaceIO() {
	}

	~AudioInterfaceIO() {
		// Close stream here before destructing AudioInterfaceIO, so the mutexes are still valid when waiting to close.
		setDevice(-1, 0);
	}

	void processStream(const float *input, float *output, int length) override {
		if (numInputs > 0) {
			// TODO Do we need to wait on the input to be consumed here? Experimentally, it works fine if we don't.
			for (int i = 0; i < length; i++) {
				if (inputBuffer.full())
					break;
				Frame<INPUTS> f;
				memset(&f, 0, sizeof(f));
				memcpy(&f, &input[numInputs * i], numInputs * sizeof(float));
				inputBuffer.push(f);
			}
		}

		if (numOutputs > 0) {
			std::unique_lock<std::mutex> lock(audioMutex);
			auto cond = [&] {
				return outputBuffer.size() >= length;
			};
			if (audioCv.wait_for(lock, audioTimeout, cond)) {
				// Consume audio block
				for (int i = 0; i < length; i++) {
					Frame<OUTPUTS> f = outputBuffer.shift();
					memcpy(&output[numOutputs * i], &f, numOutputs * sizeof(float));
				}
			}
			else {
				// Timed out, fill output with zeros
				memset(output, 0, length * numOutputs * sizeof(float));
			}
		}

		// Notify engine when finished processing
		engineCv.notify_all();
	}

	void onCloseStream() override {
		inputBuffer.clear();
		outputBuffer.clear();
	}
};

struct AudioInterface : Module {
	enum ParamIds {
		NUM_PARAMS
	};
	enum InputIds {
		ENUMS(AUDIO_INPUT, INPUTS),
		NUM_INPUTS
	};
	enum OutputIds {
		ENUMS(AUDIO_OUTPUT, OUTPUTS),
		NUM_OUTPUTS
	};
	enum LightIds {
		ENUMS(INPUT_LIGHT, INPUTS / 2),
		ENUMS(OUTPUT_LIGHT, OUTPUTS / 2),
		NUM_LIGHTS
	};

	AudioInterfaceIO audioIO;
	int lastSampleRate = 0;

	SampleRateConverter<INPUTS> inputSrc;
	SampleRateConverter<OUTPUTS> outputSrc;

	// in rack's sample rate
	DoubleRingBuffer<Frame<INPUTS>, 16> inputBuffer;
	DoubleRingBuffer<Frame<OUTPUTS>, 16> outputBuffer;

	AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
		onSampleRateChange();
	}

	void step() override;

	json_t *toJson() override {
		json_t *rootJ = json_object();
		json_object_set_new(rootJ, "audio", audioIO.toJson());
		return rootJ;
	}

	void fromJson(json_t *rootJ) override {
		json_t *audioJ = json_object_get(rootJ, "audio");
		audioIO.fromJson(audioJ);
	}

	void onSampleRateChange() override {
		// for (int i = 0; i < INPUTS; i++) {
		// 	inputSrc[i].setRates(audioIO.sampleRate, engineGetSampleRate());
		// }
		// for (int i = 0; i < OUTPUTS; i++) {
		// 	outputSrc[i].setRates(engineGetSampleRate(), audioIO.sampleRate);
		// }
		inputSrc.setRates(audioIO.sampleRate, engineGetSampleRate());
		outputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate);
	}

	void onReset() override {
		audioIO.setDevice(-1, 0);
	}
};

void AudioInterface::step() {
	Frame<INPUTS> inputFrame;
	memset(&inputFrame, 0, sizeof(inputFrame));

	// Update sample rate if changed by audio driver
	if (audioIO.sampleRate != lastSampleRate) {
		onSampleRateChange();
		lastSampleRate = audioIO.sampleRate;
	}

	if (audioIO.numInputs > 0) {
		if (inputBuffer.empty()) {
			int inLen = audioIO.inputBuffer.size();
			int outLen = inputBuffer.capacity();
			inputSrc.process(audioIO.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen);
			audioIO.inputBuffer.startIncr(inLen);
			inputBuffer.endIncr(outLen);
		}
	}

	if (!inputBuffer.empty()) {
		inputFrame = inputBuffer.shift();
	}
	for (int i = 0; i < INPUTS; i++) {
		outputs[AUDIO_OUTPUT + i].value = 10.0 * inputFrame.samples[i];
	}

	if (audioIO.numOutputs > 0) {
		// Get and push output SRC frame
		if (!outputBuffer.full()) {
			Frame<OUTPUTS> f;
			for (int i = 0; i < audioIO.numOutputs; i++) {
				f.samples[i] = inputs[AUDIO_INPUT + i].value / 10.0;
			}
			outputBuffer.push(f);
		}

		if (outputBuffer.full()) {
			// Wait until outputs are needed
			std::unique_lock<std::mutex> lock(audioIO.engineMutex);
			auto cond = [&] {
				return audioIO.outputBuffer.size() < audioIO.blockSize;
			};
			if (audioIO.engineCv.wait_for(lock, audioTimeout, cond)) {
				// Push converted output
				int inLen = outputBuffer.size();
				int outLen = audioIO.outputBuffer.capacity();
				outputSrc.process(outputBuffer.startData(), &inLen, audioIO.outputBuffer.endData(), &outLen);
				outputBuffer.startIncr(inLen);
				audioIO.outputBuffer.endIncr(outLen);
			}
			else {
				// Give up on pushing output
			}
		}
	}

	for (int i = 0; i < INPUTS / 2; i++)
		lights[INPUT_LIGHT + i].value = (audioIO.numInputs >= 2*i+1);
	for (int i = 0; i < OUTPUTS / 2; i++)
		lights[OUTPUT_LIGHT + i].value = (audioIO.numOutputs >= 2*i+1);

	audioIO.audioCv.notify_all();
}

struct AudioInterfaceWidget : ModuleWidget {
	AudioInterfaceWidget(AudioInterface *module) : ModuleWidget(module) {
		setPanel(SVG::load(assetGlobal("res/Core/AudioInterface.svg")));

		addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
		addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
		addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
		addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));

		addInput(Port::create<PJ301MPort>(mm2px(Vec(3.7069211, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 0));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 1));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 2));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 3));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 4));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 5));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 6));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 7));

		addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 0));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 1));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 2));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 3));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 4));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 5));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 6));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.506523, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 7));

		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 54.577202)), module, AudioInterface::INPUT_LIGHT + 0));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 54.577202)), module, AudioInterface::INPUT_LIGHT + 1));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 69.158226)), module, AudioInterface::INPUT_LIGHT + 2));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 69.158226)), module, AudioInterface::INPUT_LIGHT + 3));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 0));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 1));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 2));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 3));

		AudioWidget *audioWidget = Widget::create<AudioWidget>(mm2px(Vec(3.2122073, 14.837339)));
		audioWidget->box.size = mm2px(Vec(44, 28));
		audioWidget->audioIO = &module->audioIO;
		addChild(audioWidget);
	}
};

Model *modelAudioInterface = Model::create<AudioInterface, AudioInterfaceWidget>("Core", "AudioInterface", "Audio", EXTERNAL_TAG);<commit_msg>Swap input/output LEDs in Audio Interface<commit_after>#include <assert.h>
#include <mutex>
#include <chrono>
#include <thread>
#include <mutex>
#include <condition_variable>
#include "core.hpp"
#include "audio.hpp"
#include "dsp/samplerate.hpp"
#include "dsp/ringbuffer.hpp"

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsuggest-override"
#include <RtAudio.h>
#pragma GCC diagnostic pop

#define OUTPUTS 8
#define INPUTS 8

static const auto audioTimeout = std::chrono::milliseconds(100);

using namespace rack;

struct AudioInterfaceIO : AudioIO {
	std::mutex engineMutex;
	std::condition_variable engineCv;
	std::mutex audioMutex;
	std::condition_variable audioCv;
	// Audio thread produces, engine thread consumes
	DoubleRingBuffer<Frame<INPUTS>, (1<<15)> inputBuffer;
	// Audio thread consumes, engine thread produces
	DoubleRingBuffer<Frame<OUTPUTS>, (1<<15)> outputBuffer;

	AudioInterfaceIO() {
	}

	~AudioInterfaceIO() {
		// Close stream here before destructing AudioInterfaceIO, so the mutexes are still valid when waiting to close.
		setDevice(-1, 0);
	}

	void processStream(const float *input, float *output, int length) override {
		if (numInputs > 0) {
			// TODO Do we need to wait on the input to be consumed here? Experimentally, it works fine if we don't.
			for (int i = 0; i < length; i++) {
				if (inputBuffer.full())
					break;
				Frame<INPUTS> f;
				memset(&f, 0, sizeof(f));
				memcpy(&f, &input[numInputs * i], numInputs * sizeof(float));
				inputBuffer.push(f);
			}
		}

		if (numOutputs > 0) {
			std::unique_lock<std::mutex> lock(audioMutex);
			auto cond = [&] {
				return outputBuffer.size() >= length;
			};
			if (audioCv.wait_for(lock, audioTimeout, cond)) {
				// Consume audio block
				for (int i = 0; i < length; i++) {
					Frame<OUTPUTS> f = outputBuffer.shift();
					memcpy(&output[numOutputs * i], &f, numOutputs * sizeof(float));
				}
			}
			else {
				// Timed out, fill output with zeros
				memset(output, 0, length * numOutputs * sizeof(float));
			}
		}

		// Notify engine when finished processing
		engineCv.notify_all();
	}

	void onCloseStream() override {
		inputBuffer.clear();
		outputBuffer.clear();
	}
};

struct AudioInterface : Module {
	enum ParamIds {
		NUM_PARAMS
	};
	enum InputIds {
		ENUMS(AUDIO_INPUT, INPUTS),
		NUM_INPUTS
	};
	enum OutputIds {
		ENUMS(AUDIO_OUTPUT, OUTPUTS),
		NUM_OUTPUTS
	};
	enum LightIds {
		ENUMS(INPUT_LIGHT, INPUTS / 2),
		ENUMS(OUTPUT_LIGHT, OUTPUTS / 2),
		NUM_LIGHTS
	};

	AudioInterfaceIO audioIO;
	int lastSampleRate = 0;

	SampleRateConverter<INPUTS> inputSrc;
	SampleRateConverter<OUTPUTS> outputSrc;

	// in rack's sample rate
	DoubleRingBuffer<Frame<INPUTS>, 16> inputBuffer;
	DoubleRingBuffer<Frame<OUTPUTS>, 16> outputBuffer;

	AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
		onSampleRateChange();
	}

	void step() override;

	json_t *toJson() override {
		json_t *rootJ = json_object();
		json_object_set_new(rootJ, "audio", audioIO.toJson());
		return rootJ;
	}

	void fromJson(json_t *rootJ) override {
		json_t *audioJ = json_object_get(rootJ, "audio");
		audioIO.fromJson(audioJ);
	}

	void onSampleRateChange() override {
		// for (int i = 0; i < INPUTS; i++) {
		// 	inputSrc[i].setRates(audioIO.sampleRate, engineGetSampleRate());
		// }
		// for (int i = 0; i < OUTPUTS; i++) {
		// 	outputSrc[i].setRates(engineGetSampleRate(), audioIO.sampleRate);
		// }
		inputSrc.setRates(audioIO.sampleRate, engineGetSampleRate());
		outputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate);
	}

	void onReset() override {
		audioIO.setDevice(-1, 0);
	}
};

void AudioInterface::step() {
	Frame<INPUTS> inputFrame;
	memset(&inputFrame, 0, sizeof(inputFrame));

	// Update sample rate if changed by audio driver
	if (audioIO.sampleRate != lastSampleRate) {
		onSampleRateChange();
		lastSampleRate = audioIO.sampleRate;
	}

	if (audioIO.numInputs > 0) {
		if (inputBuffer.empty()) {
			int inLen = audioIO.inputBuffer.size();
			int outLen = inputBuffer.capacity();
			inputSrc.process(audioIO.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen);
			audioIO.inputBuffer.startIncr(inLen);
			inputBuffer.endIncr(outLen);
		}
	}

	if (!inputBuffer.empty()) {
		inputFrame = inputBuffer.shift();
	}
	for (int i = 0; i < INPUTS; i++) {
		outputs[AUDIO_OUTPUT + i].value = 10.0 * inputFrame.samples[i];
	}

	if (audioIO.numOutputs > 0) {
		// Get and push output SRC frame
		if (!outputBuffer.full()) {
			Frame<OUTPUTS> f;
			for (int i = 0; i < audioIO.numOutputs; i++) {
				f.samples[i] = inputs[AUDIO_INPUT + i].value / 10.0;
			}
			outputBuffer.push(f);
		}

		if (outputBuffer.full()) {
			// Wait until outputs are needed
			std::unique_lock<std::mutex> lock(audioIO.engineMutex);
			auto cond = [&] {
				return audioIO.outputBuffer.size() < audioIO.blockSize;
			};
			if (audioIO.engineCv.wait_for(lock, audioTimeout, cond)) {
				// Push converted output
				int inLen = outputBuffer.size();
				int outLen = audioIO.outputBuffer.capacity();
				outputSrc.process(outputBuffer.startData(), &inLen, audioIO.outputBuffer.endData(), &outLen);
				outputBuffer.startIncr(inLen);
				audioIO.outputBuffer.endIncr(outLen);
			}
			else {
				// Give up on pushing output
			}
		}
	}

	for (int i = 0; i < INPUTS / 2; i++)
		lights[INPUT_LIGHT + i].value = (audioIO.numOutputs >= 2*i+1);
	for (int i = 0; i < OUTPUTS / 2; i++)
		lights[OUTPUT_LIGHT + i].value = (audioIO.numInputs >= 2*i+1);

	audioIO.audioCv.notify_all();
}

struct AudioInterfaceWidget : ModuleWidget {
	AudioInterfaceWidget(AudioInterface *module) : ModuleWidget(module) {
		setPanel(SVG::load(assetGlobal("res/Core/AudioInterface.svg")));

		addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
		addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
		addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
		addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));

		addInput(Port::create<PJ301MPort>(mm2px(Vec(3.7069211, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 0));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 1));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 2));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 3));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 4));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 5));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 6));
		addInput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 7));

		addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 0));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 1));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 2));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 3));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 4));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 5));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 6));
		addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.506523, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 7));

		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 54.577202)), module, AudioInterface::INPUT_LIGHT + 0));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 54.577202)), module, AudioInterface::INPUT_LIGHT + 1));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 69.158226)), module, AudioInterface::INPUT_LIGHT + 2));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 69.158226)), module, AudioInterface::INPUT_LIGHT + 3));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 0));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 1));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 2));
		addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 3));

		AudioWidget *audioWidget = Widget::create<AudioWidget>(mm2px(Vec(3.2122073, 14.837339)));
		audioWidget->box.size = mm2px(Vec(44, 28));
		audioWidget->audioIO = &module->audioIO;
		addChild(audioWidget);
	}
};

Model *modelAudioInterface = Model::create<AudioInterface, AudioInterfaceWidget>("Core", "AudioInterface", "Audio", EXTERNAL_TAG);<|endoftext|>