<commit_before>#include "settings.h"
#include "mainwindow.h"

Settings::Settings(MainWindow* mwin, Ui::MainWindow* ui, QObject* parent) :
  QObject(parent)
{
  loginWay = 0;
  ownerComment = true;
  dispNG = true;

  this->mwin = mwin;
  this->ui = ui;
}

void Settings::saveStatus(int num)
{
  QStringList dir = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
  if (dir.empty()) {
    mwin->insLog("save directory is not available");
    return;
  }

  QJsonObject other;
  other["auto_getting_user_name"] = ui->auto_getting_user_name_chk->isChecked();
  other["keep_top_comment"] = ui->keep_top_chk->isChecked();
  other["is184_comment"] = ui->is184_chk->isChecked();
  other["auto_getting_new_waku"] = ui->autoNewWakuChk->isChecked();

  QJsonObject command;
  command["comment_check"] = ui->command_comment_chk->isChecked();
  command["comment"] = ui->command_comment->text();
  command["nextWaku_check"] = ui->command_nextWaku_chk->isChecked();
  command["nextWaku"] = ui->command_nextWaku->text();
  command["command_beforeEnd_check"] = ui->command_beforeEnd_chk->isChecked();
  command["command_beforeEnd_minuts"] = ui->command_beforeEndMinuts_spn->value();
  command["command_beforeEnd"] = ui->command_beforeEnd->text();
  command["newWaku_check"] = ui->command_newWaku_chk->isChecked();
  command["newWaku"] = ui->command_newWaku->text();

  QJsonObject root;
  root["other"] = other;
  root["command"] = command;

  QJsonDocument jsd;
  jsd.setObject(root);

  QFile file(dir[0] + "/status_0" + QString::number(num) + ".json");
  file.open(QIODevice::WriteOnly);

  QTextStream out(&file);

  out << jsd.toJson();

  file.close();
}

void Settings::loadStatus(int num)
{
  QStringList dir = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
  if (dir.empty()) {
    mwin->insLog("save directory is not available");
    return;
  }
  QFile file(dir[0] + "/status_0" + QString::number(num) + ".json");
  if ( !file.exists() ) {
    file.close();
    if (num == 0) {
      oldLoad();
    }
    mwin->insLog("no save file : " + QString::number(num));
    return;
  }

  file.open(QIODevice::ReadOnly | QIODevice::Text);

  QJsonDocument jsd = QJsonDocument::fromJson(file.readAll());

  QJsonObject other = jsd.object()["other"].toObject();
  ui->auto_getting_user_name_chk->setChecked(other["auto_getting_user_name"].toBool());
  ui->keep_top_chk->setChecked(other["keep_top_comment"].toBool());
  ui->is184_chk->setChecked(other["is184_comment"].toBool());
  ui->autoNewWakuChk->setChecked(other["auto_getting_new_waku"].toBool());

  QJsonObject command = jsd.object()["command"].toObject();
  ui->command_comment->setText(command["comment"].toString());
  ui->command_comment_chk->setChecked(command["comment_check"].toBool());
  ui->command_nextWaku->setText(command["nextWaku"].toString());
  ui->command_nextWaku_chk->setChecked(command["nextWaku_check"].toBool());
  ui->command_beforeEnd_chk->setChecked(command["command_beforeEnd_check"].toBool());
  if (command.contains("command_beforeEnd_minuts"))
    ui->command_beforeEndMinuts_spn->setValue(command["command_beforeEnd_minuts"].toInt());
  ui->command_beforeEnd->setText(command["command_beforeEnd"].toString());
  ui->command_newWaku->setText(command["newWaku"].toString());
  ui->command_newWaku_chk->setChecked(command["newWaku_check"].toBool());

  file.close();
}

void Settings::oldLoad()
{
  QStringList dir = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
  if (dir.empty()) {
    mwin->insLog("save directory is not available");
    return;
  }
  QFile file(dir[0] + "/settings.json");
  if ( !file.exists() ) {
    return;
  }

  file.open(QIODevice::ReadOnly | QIODevice::Text);

  QJsonDocument jsd = QJsonDocument::fromJson(file.readAll());

  QJsonObject cookie;
  cookie = jsd.object()["cookie"].toObject();
  loginWay = cookie["browser"].toInt();
  userSession = cookie["user_session"].toString();
  cookieFile = cookie["file_name"].toString();

  QJsonObject other;
  other = jsd.object()["other"].toObject();
  ui->auto_getting_user_name_chk->setChecked(other["auto_getting_user_name"].toBool());
  ui->keep_top_chk->setChecked(other["keep_top_comment"].toBool());

  QJsonObject command;
  command = jsd.object()["command"].toObject();
  ui->command_comment->setText(command["comment"].toString());
  ui->command_comment_chk->setChecked(command["comment_check"].toBool());
  ui->command_nextWaku->setText(command["nextWaku"].toString());
  ui->command_nextWaku_chk->setChecked(command["nextWaku_check"].toBool());

  QJsonObject user_data;
  user_data = jsd.object()["user_data"].toObject();
  userMail = user_data["mail"].toString();
  userPass = user_data["pass"].toString();

  file.close();
}

void Settings::saveSettings()
{
  QStringList dir = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
  if (dir.empty()) {
    mwin->insLog("save directory is not available");
    return;
  }

  QFile files(dir[0] + "/status_01.json");
  if ( !files.exists() ) {
    files.open(QIODevice::WriteOnly);
    files.close();
  }

  QJsonObject login_way;
  login_way["login_way"] = loginWay;
  login_way["user_session"] = userSession;
  login_way["cookie_file_name"] = cookieFile;

  QJsonObject user_data;
  user_data["mail"] = userMail;
  user_data["pass"] = userPass;

  QJsonObject comment;
  comment["owner_comment"] = ownerComment;
  comment["viewNG"] = dispNG;

  QJsonObject root;
  root["user_data"] = user_data;
  root["login_way"] = login_way;
  root["comment"] = comment;

  QJsonDocument jsd;
  jsd.setObject(root);

  QFile file(dir[0] + "/settings.json");
  file.open(QIODevice::WriteOnly);

  QTextStream out(&file);

  out << jsd.toJson().data();

  file.close();
}

void Settings::loadSettings()
{
  QStringList dir = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
  if (dir.empty()) {
    mwin->insLog("save directory is not available");
    return;
  }
  QFile file(dir[0] + "/settings.json");
  if ( !file.exists() ) {
    file.close();
    return;
  }

  file.open(QIODevice::ReadOnly | QIODevice::Text);

  QJsonDocument jsd = QJsonDocument::fromJson(file.readAll());

  QJsonObject login_way = jsd.object()["login_way"].toObject();
  loginWay = login_way["login_way"].toInt();
  userSession = login_way["user_session"].toString();
  cookieFile = login_way["cookie_file_name"].toString();

  QJsonObject user_data = jsd.object()["user_data"].toObject();
  userMail = user_data["mail"].toString();
  userPass = user_data["pass"].toString();

  QJsonObject comment = jsd.object()["comment"].toObject();
  if (comment.contains("owner_comment"))
    ownerComment = comment["owner_comment"].toBool();
  if (comment.contains("viewNG"))
    ownerComment = comment["viewNG"].toBool();

  file.close();
}

bool Settings::getIs184()
{
  return ui->is184_chk->isChecked();
}

bool Settings::isAutoNextWaku()
{
  return ui->auto_next_waku->isChecked();
}

bool Settings::isCommandCommentChecked() {
  return ui->command_comment_chk->isChecked();
}
QString Settings::getCommandComment() {
  return ui->command_comment->text();
}

bool Settings::isCommandNextWakuChecked() {
  return ui->command_nextWaku_chk->isChecked();
}
QString Settings::getCommandNextWaku() {
  return ui->command_nextWaku->text();
}

bool Settings::isCommandNewWakuChecked()
{
  return ui->command_newWaku_chk->isChecked();
}
QString Settings::getCommandNewWaku()
{
  return ui->command_newWaku->text();
}

bool Settings::isAutoNewWaku()
{
  return ui->autoNewWakuChk->isChecked();
}

bool Settings::isAutoNewWakuOpenBrowser()
{
  return ui->autoNewWakuOpenBrowser->isChecked();
}

QString Settings::getUserMail() const
{
  return userMail;
}
void Settings::setUserMail(QString value)
{
  userMail = value;
}

QString Settings::getUserPass() const
{
  return userPass;
}
void Settings::setUserPass(QString value)
{
  userPass = value;
}

int Settings::getLoginWay() const
{
  return loginWay;
}
void Settings::setLoginWay(int value)
{
  loginWay = value;
}

QString Settings::getUserSession() const
{
  return userSession;
}

void Settings::setUserSession(QString value)
{
  userSession = value;
}

QString Settings::getCookieFile() const
{
  return cookieFile;
}

void Settings::setCookieFile(QString value)
{
  cookieFile = value;
}

bool Settings::getOwnerComment() const
{
  return ownerComment;
}
void Settings::setOwnerComment(bool value)
{
  ownerComment = value;
}

bool Settings::getDispNG() const
{
  return dispNG;
}

void Settings::setDispNG(bool value)
{
  dispNG = value;
}
<commit_msg>newWakuOpenBrowser is saved state<commit_after>#include "settings.h"
#include "mainwindow.h"

Settings::Settings(MainWindow* mwin, Ui::MainWindow* ui, QObject* parent) :
  QObject(parent)
{
  loginWay = 0;
  ownerComment = true;
  dispNG = true;

  this->mwin = mwin;
  this->ui = ui;
}

void Settings::saveStatus(int num)
{
  QStringList dir = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
  if (dir.empty()) {
    mwin->insLog("save directory is not available");
    return;
  }

  QJsonObject other;
  other["auto_getting_user_name"] = ui->auto_getting_user_name_chk->isChecked();
  other["keep_top_comment"] = ui->keep_top_chk->isChecked();
  other["is184_comment"] = ui->is184_chk->isChecked();
  other["auto_getting_new_waku"] = ui->autoNewWakuChk->isChecked();
  other["new_waku_open_browser"] = ui->autoNewWakuOpenBrowser->isChecked();

  QJsonObject command;
  command["comment_check"] = ui->command_comment_chk->isChecked();
  command["comment"] = ui->command_comment->text();
  command["nextWaku_check"] = ui->command_nextWaku_chk->isChecked();
  command["nextWaku"] = ui->command_nextWaku->text();
  command["command_beforeEnd_check"] = ui->command_beforeEnd_chk->isChecked();
  command["command_beforeEnd_minuts"] = ui->command_beforeEndMinuts_spn->value();
  command["command_beforeEnd"] = ui->command_beforeEnd->text();
  command["newWaku_check"] = ui->command_newWaku_chk->isChecked();
  command["newWaku"] = ui->command_newWaku->text();

  QJsonObject root;
  root["other"] = other;
  root["command"] = command;

  QJsonDocument jsd;
  jsd.setObject(root);

  QFile file(dir[0] + "/status_0" + QString::number(num) + ".json");
  file.open(QIODevice::WriteOnly);

  QTextStream out(&file);

  out << jsd.toJson();

  file.close();
}

void Settings::loadStatus(int num)
{
  QStringList dir = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
  if (dir.empty()) {
    mwin->insLog("save directory is not available");
    return;
  }
  QFile file(dir[0] + "/status_0" + QString::number(num) + ".json");
  if ( !file.exists() ) {
    file.close();
    if (num == 0) {
      oldLoad();
    }
    mwin->insLog("no save file : " + QString::number(num));
    return;
  }

  file.open(QIODevice::ReadOnly | QIODevice::Text);

  QJsonDocument jsd = QJsonDocument::fromJson(file.readAll());

  QJsonObject other = jsd.object()["other"].toObject();
  ui->auto_getting_user_name_chk->setChecked(other["auto_getting_user_name"].toBool());
  ui->keep_top_chk->setChecked(other["keep_top_comment"].toBool());
  ui->is184_chk->setChecked(other["is184_comment"].toBool());
  ui->autoNewWakuChk->setChecked(other["auto_getting_new_waku"].toBool());
  ui->autoNewWakuOpenBrowser->setChecked(other["new_waku_open_browser"].toBool());

  QJsonObject command = jsd.object()["command"].toObject();
  ui->command_comment->setText(command["comment"].toString());
  ui->command_comment_chk->setChecked(command["comment_check"].toBool());
  ui->command_nextWaku->setText(command["nextWaku"].toString());
  ui->command_nextWaku_chk->setChecked(command["nextWaku_check"].toBool());
  ui->command_beforeEnd_chk->setChecked(command["command_beforeEnd_check"].toBool());
  if (command.contains("command_beforeEnd_minuts"))
    ui->command_beforeEndMinuts_spn->setValue(command["command_beforeEnd_minuts"].toInt());
  ui->command_beforeEnd->setText(command["command_beforeEnd"].toString());
  ui->command_newWaku->setText(command["newWaku"].toString());
  ui->command_newWaku_chk->setChecked(command["newWaku_check"].toBool());

  file.close();
}

void Settings::oldLoad()
{
  QStringList dir = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
  if (dir.empty()) {
    mwin->insLog("save directory is not available");
    return;
  }
  QFile file(dir[0] + "/settings.json");
  if ( !file.exists() ) {
    return;
  }

  file.open(QIODevice::ReadOnly | QIODevice::Text);

  QJsonDocument jsd = QJsonDocument::fromJson(file.readAll());

  QJsonObject cookie;
  cookie = jsd.object()["cookie"].toObject();
  loginWay = cookie["browser"].toInt();
  userSession = cookie["user_session"].toString();
  cookieFile = cookie["file_name"].toString();

  QJsonObject other;
  other = jsd.object()["other"].toObject();
  ui->auto_getting_user_name_chk->setChecked(other["auto_getting_user_name"].toBool());
  ui->keep_top_chk->setChecked(other["keep_top_comment"].toBool());

  QJsonObject command;
  command = jsd.object()["command"].toObject();
  ui->command_comment->setText(command["comment"].toString());
  ui->command_comment_chk->setChecked(command["comment_check"].toBool());
  ui->command_nextWaku->setText(command["nextWaku"].toString());
  ui->command_nextWaku_chk->setChecked(command["nextWaku_check"].toBool());

  QJsonObject user_data;
  user_data = jsd.object()["user_data"].toObject();
  userMail = user_data["mail"].toString();
  userPass = user_data["pass"].toString();

  file.close();
}

void Settings::saveSettings()
{
  QStringList dir = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
  if (dir.empty()) {
    mwin->insLog("save directory is not available");
    return;
  }

  QFile files(dir[0] + "/status_01.json");
  if ( !files.exists() ) {
    files.open(QIODevice::WriteOnly);
    files.close();
  }

  QJsonObject login_way;
  login_way["login_way"] = loginWay;
  login_way["user_session"] = userSession;
  login_way["cookie_file_name"] = cookieFile;

  QJsonObject user_data;
  user_data["mail"] = userMail;
  user_data["pass"] = userPass;

  QJsonObject comment;
  comment["owner_comment"] = ownerComment;
  comment["viewNG"] = dispNG;

  QJsonObject root;
  root["user_data"] = user_data;
  root["login_way"] = login_way;
  root["comment"] = comment;

  QJsonDocument jsd;
  jsd.setObject(root);

  QFile file(dir[0] + "/settings.json");
  file.open(QIODevice::WriteOnly);

  QTextStream out(&file);

  out << jsd.toJson().data();

  file.close();
}

void Settings::loadSettings()
{
  QStringList dir = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
  if (dir.empty()) {
    mwin->insLog("save directory is not available");
    return;
  }
  QFile file(dir[0] + "/settings.json");
  if ( !file.exists() ) {
    file.close();
    return;
  }

  file.open(QIODevice::ReadOnly | QIODevice::Text);

  QJsonDocument jsd = QJsonDocument::fromJson(file.readAll());

  QJsonObject login_way = jsd.object()["login_way"].toObject();
  loginWay = login_way["login_way"].toInt();
  userSession = login_way["user_session"].toString();
  cookieFile = login_way["cookie_file_name"].toString();

  QJsonObject user_data = jsd.object()["user_data"].toObject();
  userMail = user_data["mail"].toString();
  userPass = user_data["pass"].toString();

  QJsonObject comment = jsd.object()["comment"].toObject();
  if (comment.contains("owner_comment"))
    ownerComment = comment["owner_comment"].toBool();
  if (comment.contains("viewNG"))
    ownerComment = comment["viewNG"].toBool();

  file.close();
}

bool Settings::getIs184()
{
  return ui->is184_chk->isChecked();
}

bool Settings::isAutoNextWaku()
{
  return ui->auto_next_waku->isChecked();
}

bool Settings::isCommandCommentChecked() {
  return ui->command_comment_chk->isChecked();
}
QString Settings::getCommandComment() {
  return ui->command_comment->text();
}

bool Settings::isCommandNextWakuChecked() {
  return ui->command_nextWaku_chk->isChecked();
}
QString Settings::getCommandNextWaku() {
  return ui->command_nextWaku->text();
}

bool Settings::isCommandNewWakuChecked()
{
  return ui->command_newWaku_chk->isChecked();
}
QString Settings::getCommandNewWaku()
{
  return ui->command_newWaku->text();
}

bool Settings::isAutoNewWaku()
{
  return ui->autoNewWakuChk->isChecked();
}

bool Settings::isAutoNewWakuOpenBrowser()
{
  return ui->autoNewWakuOpenBrowser->isChecked();
}

QString Settings::getUserMail() const
{
  return userMail;
}
void Settings::setUserMail(QString value)
{
  userMail = value;
}

QString Settings::getUserPass() const
{
  return userPass;
}
void Settings::setUserPass(QString value)
{
  userPass = value;
}

int Settings::getLoginWay() const
{
  return loginWay;
}
void Settings::setLoginWay(int value)
{
  loginWay = value;
}

QString Settings::getUserSession() const
{
  return userSession;
}

void Settings::setUserSession(QString value)
{
  userSession = value;
}

QString Settings::getCookieFile() const
{
  return cookieFile;
}

void Settings::setCookieFile(QString value)
{
  cookieFile = value;
}

bool Settings::getOwnerComment() const
{
  return ownerComment;
}
void Settings::setOwnerComment(bool value)
{
  ownerComment = value;
}

bool Settings::getDispNG() const
{
  return dispNG;
}

void Settings::setDispNG(bool value)
{
  dispNG = value;
}
<|endoftext|>


<commit_before>/**
 * Copyright 2018 The Android Open Source Project
 *
 * 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.
 */

#include <cassert>
#include <logging_macros.h>

#include "LiveEffectEngine.h"

LiveEffectEngine::LiveEffectEngine() {
    assert(mOutputChannelCount == mInputChannelCount);
}

void LiveEffectEngine::setRecordingDeviceId(int32_t deviceId) {
    mRecordingDeviceId = deviceId;
}

void LiveEffectEngine::setPlaybackDeviceId(int32_t deviceId) {
    mPlaybackDeviceId = deviceId;
}

bool LiveEffectEngine::isAAudioSupported() {
    oboe::AudioStreamBuilder builder;
    return builder.isAAudioSupported();
}

bool LiveEffectEngine::setAudioApi(oboe::AudioApi api) {
    if (mIsEffectOn) return false;

    mAudioApi = api;
    return true;
}

bool LiveEffectEngine::setEffectOn(bool isOn) {
    bool success = true;
    if (isOn != mIsEffectOn) {
        if (isOn) {
            success = openStreams() == oboe::Result::OK;
            if (success) {
                mFullDuplexPass.start();
                mIsEffectOn = isOn;
            }
        } else {
            mFullDuplexPass.stop();
            closeStreams();
            mIsEffectOn = isOn;
       }
    }
    return success;
}

void LiveEffectEngine::closeStreams() {
    /*
    * Note: The order of events is important here.
    * The playback stream must be closed before the recording stream. If the
    * recording stream were to be closed first the playback stream's
    * callback may attempt to read from the recording stream
    * which would cause the app to crash since the recording stream would be
    * null.
    */
    closeStream(mPlayStream);
    mFullDuplexPass.setOutputStream(nullptr);

    closeStream(mRecordingStream);
    mFullDuplexPass.setInputStream(nullptr);
}

oboe::Result  LiveEffectEngine::openStreams() {
    // Note: The order of stream creation is important. We create the playback
    // stream first, then use properties from the playback stream
    // (e.g. sample rate) to create the recording stream. By matching the
    // properties we should get the lowest latency path
    oboe::AudioStreamBuilder inBuilder, outBuilder;
    setupPlaybackStreamParameters(&outBuilder);
    oboe::Result result = outBuilder.openStream(mPlayStream);
    if (result != oboe::Result::OK) {
        mSampleRate = oboe::kUnspecified;
        return result;
    } else {
        // The input stream needs to run at the same sample rate as the output.
        mSampleRate = mPlayStream->getSampleRate();
    }
    warnIfNotLowLatency(mPlayStream);

    setupRecordingStreamParameters(&inBuilder, mSampleRate);
    result = inBuilder.openStream(mRecordingStream);
    if (result != oboe::Result::OK) {
        closeStream(mPlayStream);
        return result;
    }
    warnIfNotLowLatency(mRecordingStream);

    mFullDuplexPass.setInputStream(mRecordingStream);
    mFullDuplexPass.setOutputStream(mPlayStream);
    return result;
}

/**
 * Sets the stream parameters which are specific to recording,
 * including the sample rate which is determined from the
 * playback stream.
 *
 * @param builder The recording stream builder
 */
oboe::AudioStreamBuilder *LiveEffectEngine::setupRecordingStreamParameters(
    oboe::AudioStreamBuilder *builder, int32_t sampleRate) {
    // This sample uses blocking read() because we don't specify a callback
    builder->setDeviceId(mRecordingDeviceId)
        ->setDirection(oboe::Direction::Input)
        ->setSampleRate(sampleRate)
        ->setChannelCount(mInputChannelCount);
    return setupCommonStreamParameters(builder);
}

/**
 * Sets the stream parameters which are specific to playback, including device
 * id and the dataCallback function, which must be set for low latency
 * playback.
 * @param builder The playback stream builder
 */
oboe::AudioStreamBuilder *LiveEffectEngine::setupPlaybackStreamParameters(
    oboe::AudioStreamBuilder *builder) {
    builder->setDataCallback(this)
        ->setErrorCallback(this)
        ->setDeviceId(mPlaybackDeviceId)
        ->setDirection(oboe::Direction::Output)
        ->setChannelCount(mOutputChannelCount);

    return setupCommonStreamParameters(builder);
}

/**
 * Set the stream parameters which are common to both recording and playback
 * streams.
 * @param builder The playback or recording stream builder
 */
oboe::AudioStreamBuilder *LiveEffectEngine::setupCommonStreamParameters(
    oboe::AudioStreamBuilder *builder) {
    // We request EXCLUSIVE mode since this will give us the lowest possible
    // latency.
    // If EXCLUSIVE mode isn't available the builder will fall back to SHARED
    // mode.
    builder->setAudioApi(mAudioApi)
        ->setFormat(mFormat)
        ->setSharingMode(oboe::SharingMode::Exclusive)
        ->setPerformanceMode(oboe::PerformanceMode::LowLatency);
    return builder;
}

/**
 * Close the stream. AudioStream::close() is a blocking call so
 * the application does not need to add synchronization between
 * onAudioReady() function and the thread calling close().
 * [the closing thread is the UI thread in this sample].
 * @param stream the stream to close
 */
void LiveEffectEngine::closeStream(std::shared_ptr<oboe::AudioStream> &stream) {
    if (stream) {
        oboe::Result result = stream->stop();
        if (result != oboe::Result::OK) {
            LOGW("Error stopping stream: %s", oboe::convertToText(result));
        }
        result = stream->close();
        if (result != oboe::Result::OK) {
            LOGE("Error closing stream: %s", oboe::convertToText(result));
        } else {
            LOGW("Successfully closed streams");
        }
        stream.reset();
    }
}

/**
 * Warn in logcat if non-low latency stream is created
 * @param stream: newly created stream
 *
 */
void LiveEffectEngine::warnIfNotLowLatency(std::shared_ptr<oboe::AudioStream> &stream) {
    if (stream->getPerformanceMode() != oboe::PerformanceMode::LowLatency) {
        LOGW(
            "Stream is NOT low latency."
            "Check your requested format, sample rate and channel count");
    }
}

/**
 * Handles playback stream's audio request. In this sample, we simply block-read
 * from the record stream for the required samples.
 *
 * @param oboeStream: the playback stream that requesting additional samples
 * @param audioData:  the buffer to load audio samples for playback stream
 * @param numFrames:  number of frames to load to audioData buffer
 * @return: DataCallbackResult::Continue.
 */
oboe::DataCallbackResult LiveEffectEngine::onAudioReady(
    oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) {
    return mFullDuplexPass.onAudioReady(oboeStream, audioData, numFrames);
}

/**
 * Oboe notifies the application for "about to close the stream".
 *
 * @param oboeStream: the stream to close
 * @param error: oboe's reason for closing the stream
 */
void LiveEffectEngine::onErrorBeforeClose(oboe::AudioStream *oboeStream,
                                          oboe::Result error) {
    LOGE("%s stream Error before close: %s",
         oboe::convertToText(oboeStream->getDirection()),
         oboe::convertToText(error));
}

/**
 * Oboe notifies application that "the stream is closed"
 *
 * @param oboeStream
 * @param error
 */
void LiveEffectEngine::onErrorAfterClose(oboe::AudioStream *oboeStream,
                                         oboe::Result error) {
    LOGE("%s stream Error after close: %s",
         oboe::convertToText(oboeStream->getDirection()),
         oboe::convertToText(error));
}
<commit_msg>Add doc for sampleRate param<commit_after>/**
 * Copyright 2018 The Android Open Source Project
 *
 * 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.
 */

#include <cassert>
#include <logging_macros.h>

#include "LiveEffectEngine.h"

LiveEffectEngine::LiveEffectEngine() {
    assert(mOutputChannelCount == mInputChannelCount);
}

void LiveEffectEngine::setRecordingDeviceId(int32_t deviceId) {
    mRecordingDeviceId = deviceId;
}

void LiveEffectEngine::setPlaybackDeviceId(int32_t deviceId) {
    mPlaybackDeviceId = deviceId;
}

bool LiveEffectEngine::isAAudioSupported() {
    oboe::AudioStreamBuilder builder;
    return builder.isAAudioSupported();
}

bool LiveEffectEngine::setAudioApi(oboe::AudioApi api) {
    if (mIsEffectOn) return false;

    mAudioApi = api;
    return true;
}

bool LiveEffectEngine::setEffectOn(bool isOn) {
    bool success = true;
    if (isOn != mIsEffectOn) {
        if (isOn) {
            success = openStreams() == oboe::Result::OK;
            if (success) {
                mFullDuplexPass.start();
                mIsEffectOn = isOn;
            }
        } else {
            mFullDuplexPass.stop();
            closeStreams();
            mIsEffectOn = isOn;
       }
    }
    return success;
}

void LiveEffectEngine::closeStreams() {
    /*
    * Note: The order of events is important here.
    * The playback stream must be closed before the recording stream. If the
    * recording stream were to be closed first the playback stream's
    * callback may attempt to read from the recording stream
    * which would cause the app to crash since the recording stream would be
    * null.
    */
    closeStream(mPlayStream);
    mFullDuplexPass.setOutputStream(nullptr);

    closeStream(mRecordingStream);
    mFullDuplexPass.setInputStream(nullptr);
}

oboe::Result  LiveEffectEngine::openStreams() {
    // Note: The order of stream creation is important. We create the playback
    // stream first, then use properties from the playback stream
    // (e.g. sample rate) to create the recording stream. By matching the
    // properties we should get the lowest latency path
    oboe::AudioStreamBuilder inBuilder, outBuilder;
    setupPlaybackStreamParameters(&outBuilder);
    oboe::Result result = outBuilder.openStream(mPlayStream);
    if (result != oboe::Result::OK) {
        mSampleRate = oboe::kUnspecified;
        return result;
    } else {
        // The input stream needs to run at the same sample rate as the output.
        mSampleRate = mPlayStream->getSampleRate();
    }
    warnIfNotLowLatency(mPlayStream);

    setupRecordingStreamParameters(&inBuilder, mSampleRate);
    result = inBuilder.openStream(mRecordingStream);
    if (result != oboe::Result::OK) {
        closeStream(mPlayStream);
        return result;
    }
    warnIfNotLowLatency(mRecordingStream);

    mFullDuplexPass.setInputStream(mRecordingStream);
    mFullDuplexPass.setOutputStream(mPlayStream);
    return result;
}

/**
 * Sets the stream parameters which are specific to recording,
 * including the sample rate which is determined from the
 * playback stream.
 *
 * @param builder The recording stream builder
 * @param sampleRate The desired sample rate of the recording stream
 */
oboe::AudioStreamBuilder *LiveEffectEngine::setupRecordingStreamParameters(
    oboe::AudioStreamBuilder *builder, int32_t sampleRate) {
    // This sample uses blocking read() because we don't specify a callback
    builder->setDeviceId(mRecordingDeviceId)
        ->setDirection(oboe::Direction::Input)
        ->setSampleRate(sampleRate)
        ->setChannelCount(mInputChannelCount);
    return setupCommonStreamParameters(builder);
}

/**
 * Sets the stream parameters which are specific to playback, including device
 * id and the dataCallback function, which must be set for low latency
 * playback.
 * @param builder The playback stream builder
 */
oboe::AudioStreamBuilder *LiveEffectEngine::setupPlaybackStreamParameters(
    oboe::AudioStreamBuilder *builder) {
    builder->setDataCallback(this)
        ->setErrorCallback(this)
        ->setDeviceId(mPlaybackDeviceId)
        ->setDirection(oboe::Direction::Output)
        ->setChannelCount(mOutputChannelCount);

    return setupCommonStreamParameters(builder);
}

/**
 * Set the stream parameters which are common to both recording and playback
 * streams.
 * @param builder The playback or recording stream builder
 */
oboe::AudioStreamBuilder *LiveEffectEngine::setupCommonStreamParameters(
    oboe::AudioStreamBuilder *builder) {
    // We request EXCLUSIVE mode since this will give us the lowest possible
    // latency.
    // If EXCLUSIVE mode isn't available the builder will fall back to SHARED
    // mode.
    builder->setAudioApi(mAudioApi)
        ->setFormat(mFormat)
        ->setSharingMode(oboe::SharingMode::Exclusive)
        ->setPerformanceMode(oboe::PerformanceMode::LowLatency);
    return builder;
}

/**
 * Close the stream. AudioStream::close() is a blocking call so
 * the application does not need to add synchronization between
 * onAudioReady() function and the thread calling close().
 * [the closing thread is the UI thread in this sample].
 * @param stream the stream to close
 */
void LiveEffectEngine::closeStream(std::shared_ptr<oboe::AudioStream> &stream) {
    if (stream) {
        oboe::Result result = stream->stop();
        if (result != oboe::Result::OK) {
            LOGW("Error stopping stream: %s", oboe::convertToText(result));
        }
        result = stream->close();
        if (result != oboe::Result::OK) {
            LOGE("Error closing stream: %s", oboe::convertToText(result));
        } else {
            LOGW("Successfully closed streams");
        }
        stream.reset();
    }
}

/**
 * Warn in logcat if non-low latency stream is created
 * @param stream: newly created stream
 *
 */
void LiveEffectEngine::warnIfNotLowLatency(std::shared_ptr<oboe::AudioStream> &stream) {
    if (stream->getPerformanceMode() != oboe::PerformanceMode::LowLatency) {
        LOGW(
            "Stream is NOT low latency."
            "Check your requested format, sample rate and channel count");
    }
}

/**
 * Handles playback stream's audio request. In this sample, we simply block-read
 * from the record stream for the required samples.
 *
 * @param oboeStream: the playback stream that requesting additional samples
 * @param audioData:  the buffer to load audio samples for playback stream
 * @param numFrames:  number of frames to load to audioData buffer
 * @return: DataCallbackResult::Continue.
 */
oboe::DataCallbackResult LiveEffectEngine::onAudioReady(
    oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) {
    return mFullDuplexPass.onAudioReady(oboeStream, audioData, numFrames);
}

/**
 * Oboe notifies the application for "about to close the stream".
 *
 * @param oboeStream: the stream to close
 * @param error: oboe's reason for closing the stream
 */
void LiveEffectEngine::onErrorBeforeClose(oboe::AudioStream *oboeStream,
                                          oboe::Result error) {
    LOGE("%s stream Error before close: %s",
         oboe::convertToText(oboeStream->getDirection()),
         oboe::convertToText(error));
}

/**
 * Oboe notifies application that "the stream is closed"
 *
 * @param oboeStream
 * @param error
 */
void LiveEffectEngine::onErrorAfterClose(oboe::AudioStream *oboeStream,
                                         oboe::Result error) {
    LOGE("%s stream Error after close: %s",
         oboe::convertToText(oboeStream->getDirection()),
         oboe::convertToText(error));
}
<|endoftext|>


<commit_before>#include <settings.hpp>
#include <window.hpp>
#include <plugin.hpp>
#include <app/Scene.hpp>
#include <app/ModuleBrowser.hpp>
#include <engine/Engine.hpp>
#include <app.hpp>
#include <patch.hpp>
#include <jansson.h>

namespace rack {
namespace settings {

bool devMode = false;
bool headless = false;
std::string token;
math::Vec windowSize;
math::Vec windowPos;
float zoom = 1.0;
bool invertZoom = false;
float cableOpacity = 0.5;
float cableTension = 0.5;
bool allowCursorLock = true;
bool realTime = false;
float sampleRate = 44100.0;
int threadCount = 1;
bool paramTooltip = false;
bool cpuMeter = false;
bool lockModules = false;
float frameRateLimit = 70.0;
bool frameRateSync = true;
bool skipLoadOnLaunch = false;
std::string patchPath;
std::map<std::tuple<std::string, std::string>, float> favoriteScores;
std::vector<NVGcolor> cableColors = {
	nvgRGB(0xc9, 0xb7, 0x0e), // yellow
	nvgRGB(0x0c, 0x8e, 0x15), // green
	nvgRGB(0xc9, 0x18, 0x47), // red
	nvgRGB(0x09, 0x86, 0xad), // blue
};

json_t *toJson() {
	json_t *rootJ = json_object();

	json_object_set_new(rootJ, "token", json_string(token.c_str()));

	json_t *windowSizeJ = json_pack("[f, f]", windowSize.x, windowSize.y);
	json_object_set_new(rootJ, "windowSize", windowSizeJ);

	json_t *windowPosJ = json_pack("[f, f]", windowPos.x, windowPos.y);
	json_object_set_new(rootJ, "windowPos", windowPosJ);

	json_object_set_new(rootJ, "zoom", json_real(zoom));

	json_object_set_new(rootJ, "invertZoom", json_boolean(invertZoom));

	json_object_set_new(rootJ, "cableOpacity", json_real(cableOpacity));

	json_object_set_new(rootJ, "cableTension", json_real(cableTension));

	json_object_set_new(rootJ, "allowCursorLock", json_boolean(allowCursorLock));

	json_object_set_new(rootJ, "realTime", json_boolean(realTime));

	json_object_set_new(rootJ, "sampleRate", json_real(sampleRate));

	json_object_set_new(rootJ, "threadCount", json_integer(threadCount));

	json_object_set_new(rootJ, "paramTooltip", json_boolean(paramTooltip));

	json_object_set_new(rootJ, "cpuMeter", json_boolean(cpuMeter));

	json_object_set_new(rootJ, "lockModules", json_boolean(lockModules));

	json_object_set_new(rootJ, "frameRateLimit", json_real(frameRateLimit));

	json_object_set_new(rootJ, "frameRateSync", json_boolean(frameRateSync));

	if (skipLoadOnLaunch) {
		json_object_set_new(rootJ, "skipLoadOnLaunch", json_true());
	}

	json_object_set_new(rootJ, "patchPath", json_string(patchPath.c_str()));

	json_t *favoriteModelsJ = json_array();
	for (auto &pair : favoriteScores) {
		const std::string &plugin = std::get<0>(pair.first);
		const std::string &model = std::get<1>(pair.first);
		float score = pair.second;
		json_t *favoriteJ = json_object();
		json_object_set_new(favoriteJ, "plugin", json_string(plugin.c_str()));
		json_object_set_new(favoriteJ, "model", json_string(model.c_str()));
		json_object_set_new(favoriteJ, "score", json_real(score));
		json_array_append_new(favoriteModelsJ, favoriteJ);
	}
	json_object_set_new(rootJ, "favoriteModels", favoriteModelsJ);

	json_t *cableColorsJ = json_array();
	for (NVGcolor cableColor : cableColors) {
		std::string colorStr = color::toHexString(cableColor);
		json_array_append_new(cableColorsJ, json_string(colorStr.c_str()));
	}
	json_object_set_new(rootJ, "cableColors", cableColorsJ);

	return rootJ;
}

void fromJson(json_t *rootJ) {
	json_t *tokenJ = json_object_get(rootJ, "token");
	if (tokenJ)
		token = json_string_value(tokenJ);

	json_t *windowSizeJ = json_object_get(rootJ, "windowSize");
	if (windowSizeJ) {
		double x, y;
		json_unpack(windowSizeJ, "[F, F]", &x, &y);
		windowSize = math::Vec(x, y);
	}

	json_t *windowPosJ = json_object_get(rootJ, "windowPos");
	if (windowPosJ) {
		double x, y;
		json_unpack(windowPosJ, "[F, F]", &x, &y);
		windowPos = math::Vec(x, y);
	}

	json_t *zoomJ = json_object_get(rootJ, "zoom");
	if (zoomJ)
		zoom = json_number_value(zoomJ);

	json_t *invertZoomJ = json_object_get(rootJ, "invertZoom");
	if (invertZoomJ)
		invertZoom = json_boolean_value(invertZoomJ);

	json_t *cableOpacityJ = json_object_get(rootJ, "cableOpacity");
	if (cableOpacityJ)
		cableOpacity = json_number_value(cableOpacityJ);

	json_t *tensionJ = json_object_get(rootJ, "cableTension");
	if (tensionJ)
		cableTension = json_number_value(tensionJ);

	json_t *allowCursorLockJ = json_object_get(rootJ, "allowCursorLock");
	if (allowCursorLockJ)
		allowCursorLock = json_is_true(allowCursorLockJ);

	json_t *realTimeJ = json_object_get(rootJ, "realTime");
	if (realTimeJ)
		realTime = json_boolean_value(realTimeJ);

	json_t *sampleRateJ = json_object_get(rootJ, "sampleRate");
	if (sampleRateJ)
		sampleRate = json_number_value(sampleRateJ);

	json_t *threadCountJ = json_object_get(rootJ, "threadCount");
	if (threadCountJ)
		threadCount = json_integer_value(threadCountJ);

	json_t *paramTooltipJ = json_object_get(rootJ, "paramTooltip");
	if (paramTooltipJ)
		paramTooltip = json_boolean_value(paramTooltipJ);

	json_t *cpuMeterJ = json_object_get(rootJ, "cpuMeter");
	if (cpuMeterJ)
		cpuMeter = json_boolean_value(cpuMeterJ);

	json_t *lockModulesJ = json_object_get(rootJ, "lockModules");
	if (lockModulesJ)
		lockModules = json_boolean_value(lockModulesJ);

	json_t *frameRateLimitJ = json_object_get(rootJ, "frameRateLimit");
	if (frameRateLimitJ)
		frameRateLimit = json_number_value(frameRateLimitJ);

	json_t *frameRateSyncJ = json_object_get(rootJ, "frameRateSync");
	if (frameRateSyncJ)
		frameRateSync = json_boolean_value(frameRateSyncJ);

	json_t *skipLoadOnLaunchJ = json_object_get(rootJ, "skipLoadOnLaunch");
	if (skipLoadOnLaunchJ)
		skipLoadOnLaunch = json_boolean_value(skipLoadOnLaunchJ);

	json_t *patchPathJ = json_object_get(rootJ, "patchPath");
	if (patchPathJ)
		patchPath = json_string_value(patchPathJ);

	json_t *favoriteModelsJ = json_object_get(rootJ, "favoriteModels");
	// Legacy: "favorites" was defined under "moduleBrowser" until 1.0.
	if (!favoriteModelsJ) {
		json_t *moduleBrowserJ = json_object_get(rootJ, "moduleBrowser");
		if (moduleBrowserJ)
			favoriteModelsJ = json_object_get(rootJ, "favorites");
	}
	if (favoriteModelsJ) {
		favoriteScores.clear();
		size_t i;
		json_t *favoriteJ;
		json_array_foreach(favoriteModelsJ, i, favoriteJ) {
			json_t *pluginJ = json_object_get(favoriteJ, "plugin");
			json_t *modelJ = json_object_get(favoriteJ, "model");
			if (!pluginJ || !modelJ)
				continue;
			std::string plugin = json_string_value(pluginJ);
			std::string model = json_string_value(modelJ);
			// Set default score when migrating favorites from v0.6
			float score = 1.f;
			json_t *scoreJ = json_object_get(favoriteJ, "score");
			if (scoreJ)
				score = json_number_value(scoreJ);

			favoriteScores[std::make_tuple(plugin, model)] = score;
		}
	}

	json_t *cableColorsJ = json_object_get(rootJ, "cableColors");
	if (cableColorsJ) {
		cableColors.clear();
		size_t i;
		json_t *cableColorJ;
		json_array_foreach(cableColorsJ, i, cableColorJ) {
			std::string colorStr = json_string_value(cableColorJ);
			cableColors.push_back(color::fromHexString(colorStr));
		}
	}
}

void save(const std::string &path) {
	INFO("Saving settings %s", path.c_str());
	json_t *rootJ = toJson();
	if (!rootJ)
		return;

	FILE *file = fopen(path.c_str(), "w");
	if (!file)
		return;
	DEFER({
		fclose(file);
	});

	json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
	json_decref(rootJ);
}

void load(const std::string &path) {
	INFO("Loading settings %s", path.c_str());
	FILE *file = fopen(path.c_str(), "r");
	if (!file)
		return;
	DEFER({
		fclose(file);
	});

	json_error_t error;
	json_t *rootJ = json_loadf(file, 0, &error);
	if (!rootJ)
		throw UserException(string::f("Settings file has invalid JSON at %d:%d %s", error.line, error.column, error.text));

	fromJson(rootJ);
	json_decref(rootJ);
}

} // namespace settings
} // namespace rack
<commit_msg>Fix default zoom level.<commit_after>#include <settings.hpp>
#include <window.hpp>
#include <plugin.hpp>
#include <app/Scene.hpp>
#include <app/ModuleBrowser.hpp>
#include <engine/Engine.hpp>
#include <app.hpp>
#include <patch.hpp>
#include <jansson.h>

namespace rack {
namespace settings {

bool devMode = false;
bool headless = false;
std::string token;
math::Vec windowSize;
math::Vec windowPos;
float zoom = 0.0;
bool invertZoom = false;
float cableOpacity = 0.5;
float cableTension = 0.5;
bool allowCursorLock = true;
bool realTime = false;
float sampleRate = 44100.0;
int threadCount = 1;
bool paramTooltip = false;
bool cpuMeter = false;
bool lockModules = false;
float frameRateLimit = 70.0;
bool frameRateSync = true;
bool skipLoadOnLaunch = false;
std::string patchPath;
std::map<std::tuple<std::string, std::string>, float> favoriteScores;
std::vector<NVGcolor> cableColors = {
	nvgRGB(0xc9, 0xb7, 0x0e), // yellow
	nvgRGB(0x0c, 0x8e, 0x15), // green
	nvgRGB(0xc9, 0x18, 0x47), // red
	nvgRGB(0x09, 0x86, 0xad), // blue
};

json_t *toJson() {
	json_t *rootJ = json_object();

	json_object_set_new(rootJ, "token", json_string(token.c_str()));

	json_t *windowSizeJ = json_pack("[f, f]", windowSize.x, windowSize.y);
	json_object_set_new(rootJ, "windowSize", windowSizeJ);

	json_t *windowPosJ = json_pack("[f, f]", windowPos.x, windowPos.y);
	json_object_set_new(rootJ, "windowPos", windowPosJ);

	json_object_set_new(rootJ, "zoom", json_real(zoom));

	json_object_set_new(rootJ, "invertZoom", json_boolean(invertZoom));

	json_object_set_new(rootJ, "cableOpacity", json_real(cableOpacity));

	json_object_set_new(rootJ, "cableTension", json_real(cableTension));

	json_object_set_new(rootJ, "allowCursorLock", json_boolean(allowCursorLock));

	json_object_set_new(rootJ, "realTime", json_boolean(realTime));

	json_object_set_new(rootJ, "sampleRate", json_real(sampleRate));

	json_object_set_new(rootJ, "threadCount", json_integer(threadCount));

	json_object_set_new(rootJ, "paramTooltip", json_boolean(paramTooltip));

	json_object_set_new(rootJ, "cpuMeter", json_boolean(cpuMeter));

	json_object_set_new(rootJ, "lockModules", json_boolean(lockModules));

	json_object_set_new(rootJ, "frameRateLimit", json_real(frameRateLimit));

	json_object_set_new(rootJ, "frameRateSync", json_boolean(frameRateSync));

	if (skipLoadOnLaunch) {
		json_object_set_new(rootJ, "skipLoadOnLaunch", json_true());
	}

	json_object_set_new(rootJ, "patchPath", json_string(patchPath.c_str()));

	json_t *favoriteModelsJ = json_array();
	for (auto &pair : favoriteScores) {
		const std::string &plugin = std::get<0>(pair.first);
		const std::string &model = std::get<1>(pair.first);
		float score = pair.second;
		json_t *favoriteJ = json_object();
		json_object_set_new(favoriteJ, "plugin", json_string(plugin.c_str()));
		json_object_set_new(favoriteJ, "model", json_string(model.c_str()));
		json_object_set_new(favoriteJ, "score", json_real(score));
		json_array_append_new(favoriteModelsJ, favoriteJ);
	}
	json_object_set_new(rootJ, "favoriteModels", favoriteModelsJ);

	json_t *cableColorsJ = json_array();
	for (NVGcolor cableColor : cableColors) {
		std::string colorStr = color::toHexString(cableColor);
		json_array_append_new(cableColorsJ, json_string(colorStr.c_str()));
	}
	json_object_set_new(rootJ, "cableColors", cableColorsJ);

	return rootJ;
}

void fromJson(json_t *rootJ) {
	json_t *tokenJ = json_object_get(rootJ, "token");
	if (tokenJ)
		token = json_string_value(tokenJ);

	json_t *windowSizeJ = json_object_get(rootJ, "windowSize");
	if (windowSizeJ) {
		double x, y;
		json_unpack(windowSizeJ, "[F, F]", &x, &y);
		windowSize = math::Vec(x, y);
	}

	json_t *windowPosJ = json_object_get(rootJ, "windowPos");
	if (windowPosJ) {
		double x, y;
		json_unpack(windowPosJ, "[F, F]", &x, &y);
		windowPos = math::Vec(x, y);
	}

	json_t *zoomJ = json_object_get(rootJ, "zoom");
	if (zoomJ)
		zoom = json_number_value(zoomJ);

	json_t *invertZoomJ = json_object_get(rootJ, "invertZoom");
	if (invertZoomJ)
		invertZoom = json_boolean_value(invertZoomJ);

	json_t *cableOpacityJ = json_object_get(rootJ, "cableOpacity");
	if (cableOpacityJ)
		cableOpacity = json_number_value(cableOpacityJ);

	json_t *tensionJ = json_object_get(rootJ, "cableTension");
	if (tensionJ)
		cableTension = json_number_value(tensionJ);

	json_t *allowCursorLockJ = json_object_get(rootJ, "allowCursorLock");
	if (allowCursorLockJ)
		allowCursorLock = json_is_true(allowCursorLockJ);

	json_t *realTimeJ = json_object_get(rootJ, "realTime");
	if (realTimeJ)
		realTime = json_boolean_value(realTimeJ);

	json_t *sampleRateJ = json_object_get(rootJ, "sampleRate");
	if (sampleRateJ)
		sampleRate = json_number_value(sampleRateJ);

	json_t *threadCountJ = json_object_get(rootJ, "threadCount");
	if (threadCountJ)
		threadCount = json_integer_value(threadCountJ);

	json_t *paramTooltipJ = json_object_get(rootJ, "paramTooltip");
	if (paramTooltipJ)
		paramTooltip = json_boolean_value(paramTooltipJ);

	json_t *cpuMeterJ = json_object_get(rootJ, "cpuMeter");
	if (cpuMeterJ)
		cpuMeter = json_boolean_value(cpuMeterJ);

	json_t *lockModulesJ = json_object_get(rootJ, "lockModules");
	if (lockModulesJ)
		lockModules = json_boolean_value(lockModulesJ);

	json_t *frameRateLimitJ = json_object_get(rootJ, "frameRateLimit");
	if (frameRateLimitJ)
		frameRateLimit = json_number_value(frameRateLimitJ);

	json_t *frameRateSyncJ = json_object_get(rootJ, "frameRateSync");
	if (frameRateSyncJ)
		frameRateSync = json_boolean_value(frameRateSyncJ);

	json_t *skipLoadOnLaunchJ = json_object_get(rootJ, "skipLoadOnLaunch");
	if (skipLoadOnLaunchJ)
		skipLoadOnLaunch = json_boolean_value(skipLoadOnLaunchJ);

	json_t *patchPathJ = json_object_get(rootJ, "patchPath");
	if (patchPathJ)
		patchPath = json_string_value(patchPathJ);

	json_t *favoriteModelsJ = json_object_get(rootJ, "favoriteModels");
	// Legacy: "favorites" was defined under "moduleBrowser" until 1.0.
	if (!favoriteModelsJ) {
		json_t *moduleBrowserJ = json_object_get(rootJ, "moduleBrowser");
		if (moduleBrowserJ)
			favoriteModelsJ = json_object_get(rootJ, "favorites");
	}
	if (favoriteModelsJ) {
		favoriteScores.clear();
		size_t i;
		json_t *favoriteJ;
		json_array_foreach(favoriteModelsJ, i, favoriteJ) {
			json_t *pluginJ = json_object_get(favoriteJ, "plugin");
			json_t *modelJ = json_object_get(favoriteJ, "model");
			if (!pluginJ || !modelJ)
				continue;
			std::string plugin = json_string_value(pluginJ);
			std::string model = json_string_value(modelJ);
			// Set default score when migrating favorites from v0.6
			float score = 1.f;
			json_t *scoreJ = json_object_get(favoriteJ, "score");
			if (scoreJ)
				score = json_number_value(scoreJ);

			favoriteScores[std::make_tuple(plugin, model)] = score;
		}
	}

	json_t *cableColorsJ = json_object_get(rootJ, "cableColors");
	if (cableColorsJ) {
		cableColors.clear();
		size_t i;
		json_t *cableColorJ;
		json_array_foreach(cableColorsJ, i, cableColorJ) {
			std::string colorStr = json_string_value(cableColorJ);
			cableColors.push_back(color::fromHexString(colorStr));
		}
	}
}

void save(const std::string &path) {
	INFO("Saving settings %s", path.c_str());
	json_t *rootJ = toJson();
	if (!rootJ)
		return;

	FILE *file = fopen(path.c_str(), "w");
	if (!file)
		return;
	DEFER({
		fclose(file);
	});

	json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
	json_decref(rootJ);
}

void load(const std::string &path) {
	INFO("Loading settings %s", path.c_str());
	FILE *file = fopen(path.c_str(), "r");
	if (!file)
		return;
	DEFER({
		fclose(file);
	});

	json_error_t error;
	json_t *rootJ = json_loadf(file, 0, &error);
	if (!rootJ)
		throw UserException(string::f("Settings file has invalid JSON at %d:%d %s", error.line, error.column, error.text));

	fromJson(rootJ);
	json_decref(rootJ);
}

} // namespace settings
} // namespace rack
<|endoftext|>


<commit_before>// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// ---
// Author: Craig Silverstein
//
// This forks out to pprof to do the actual symbolizing.  We might
// be better off writing our own in C++.

#include "config.h"
#include "symbolize.h"
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>   // for write()
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>   // for socketpair() -- needed by Symbolize
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>   // for wait() -- needed by Symbolize
#endif
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#ifdef __MACH__
#include <mach-o/dyld.h>   // for GetProgramInvocationName()
#include <limits.h>        // for PATH_MAX
#endif
#if defined(__CYGWIN__) || defined(__CYGWIN32__)
#include <io.h>            // for get_osfhandle()
#endif
#include <string>
#include "base/commandlineflags.h"
#include "base/logging.h"
#include "base/sysinfo.h"

using std::string;
using tcmalloc::DumpProcSelfMaps;   // from sysinfo.h

DEFINE_string(symbolize_pprof,
              EnvToString("PPROF_PATH", "pprof"),
              "Path to pprof to call for reporting function names.");

// heap_profile_table_pprof may be referenced after destructors are
// called (since that's when leak-checking is done), so we make
// a more-permanent copy that won't ever get destroyed.
static string* g_pprof_path = new string(FLAGS_symbolize_pprof);

// Returns NULL if we're on an OS where we can't get the invocation name.
// Using a static var is ok because we're not called from a thread.
static const char* GetProgramInvocationName() {
#if defined(HAVE_PROGRAM_INVOCATION_NAME)
#ifdef __UCLIBC__
  extern const char* program_invocation_name; // uclibc provides this
#else
  extern char* program_invocation_name;  // gcc provides this
#endif
  return program_invocation_name;
#elif defined(__MACH__)
  // We don't want to allocate memory for this since we may be
  // calculating it when memory is corrupted.
  static char program_invocation_name[PATH_MAX];
  if (program_invocation_name[0] == '\0') {  // first time calculating
    uint32_t length = sizeof(program_invocation_name);
    if (_NSGetExecutablePath(program_invocation_name, &length))
      return NULL;
  }
  return program_invocation_name;
#else
  return NULL;   // figure out a way to get argv[0]
#endif
}

// Prints an error message when you can't run Symbolize().
static void PrintError(const char* reason) {
  RAW_LOG(ERROR,
          "*** WARNING: Cannot convert addresses to symbols in output below.\n"
          "*** Reason: %s\n"
          "*** If you cannot fix this, try running pprof directly.\n",
          reason);
}

void SymbolTable::Add(const void* addr) {
  symbolization_table_[addr] = "";
}

const char* SymbolTable::GetSymbol(const void* addr) {
  return symbolization_table_[addr];
}

// Updates symbolization_table with the pointers to symbol names corresponding
// to its keys. The symbol names are stored in out, which is allocated and
// freed by the caller of this routine.
// Note that the forking/etc is not thread-safe or re-entrant.  That's
// ok for the purpose we need -- reporting leaks detected by heap-checker
// -- but be careful if you decide to use this routine for other purposes.
// Returns number of symbols read on error.  If can't symbolize, returns 0
// and emits an error message about why.
int SymbolTable::Symbolize() {
#if !defined(HAVE_UNISTD_H)  || !defined(HAVE_SYS_SOCKET_H) || !defined(HAVE_SYS_WAIT_H)
  PrintError("Perftools does not know how to call a sub-process on this O/S");
  return 0;
#else
  const char* argv0 = GetProgramInvocationName();
  if (argv0 == NULL) {  // can't call symbolize if we can't figure out our name
    PrintError("Cannot figure out the name of this executable (argv0)");
    return 0;
  }
  if (access(g_pprof_path->c_str(), R_OK) != 0) {
    PrintError("Cannot find 'pprof' (is PPROF_PATH set correctly?)");
    return 0;
  }

  // All this work is to do two-way communication.  ugh.
  int *child_in = NULL;   // file descriptors
  int *child_out = NULL;  // for now, we don't worry about child_err
  int child_fds[5][2];    // socketpair may be called up to five times below

  // The client program may close its stdin and/or stdout and/or stderr
  // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
  // In this case the communication between the forked processes may be broken
  // if either the parent or the child tries to close or duplicate these
  // descriptors. The loop below produces two pairs of file descriptors, each
  // greater than 2 (stderr).
  for (int i = 0; i < 5; i++) {
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, child_fds[i]) == -1) {
      for (int j = 0; j < i; j++) {
        close(child_fds[j][0]);
        close(child_fds[j][1]);
        PrintError("Cannot create a socket pair");
      }
      return 0;
    } else {
      if ((child_fds[i][0] > 2) && (child_fds[i][1] > 2)) {
        if (child_in == NULL) {
          child_in = child_fds[i];
        } else {
          child_out = child_fds[i];
          for (int j = 0; j < i; j++) {
            if (child_fds[j] == child_in) continue;
            close(child_fds[j][0]);
            close(child_fds[j][1]);
          }
          break;
        }
      }
    }
  }

  switch (fork()) {
    case -1: {  // error
      close(child_in[0]);
      close(child_in[1]);
      close(child_out[0]);
      close(child_out[1]);
      PrintError("Unknown error calling fork()");
      return 0;
    }
    case 0: {  // child
      close(child_in[1]);   // child uses the 0's, parent uses the 1's
      close(child_out[1]);  // child uses the 0's, parent uses the 1's
      close(0);
      close(1);
      if (dup2(child_in[0], 0) == -1) _exit(1);
      if (dup2(child_out[0], 1) == -1) _exit(2);
      // Unset vars that might cause trouble when we fork
      unsetenv("CPUPROFILE");
      unsetenv("HEAPPROFILE");
      unsetenv("HEAPCHECK");
      unsetenv("PERFTOOLS_VERBOSE");
      execlp(g_pprof_path->c_str(), g_pprof_path->c_str(),
             "--symbols", argv0, NULL);
      _exit(3);  // if execvp fails, it's bad news for us
    }
    default: {  // parent
      close(child_in[0]);   // child uses the 0's, parent uses the 1's
      close(child_out[0]);  // child uses the 0's, parent uses the 1's
#ifdef HAVE_POLL_H
      // Waiting for 1ms seems to give the OS time to notice any errors.
      poll(0, 0, 1);
      // For maximum safety, we check to make sure the execlp
      // succeeded before trying to write.  (Otherwise we'll get a
      // SIGPIPE.)  For systems without poll.h, we'll just skip this
      // check, and trust that the user set PPROF_PATH correctly!
      struct pollfd pfd = { child_in[1], POLLOUT, 0 };
      if (!poll(&pfd, 1, 0) || !(pfd.revents & POLLOUT) ||
          (pfd.revents & (POLLHUP|POLLERR))) {
        PrintError("Cannot run 'pprof' (is PPROF_PATH set correctly?)");
        return 0;
      }
#endif
#if defined(__CYGWIN__) || defined(__CYGWIN32__)
      // On cygwin, DumpProcSelfMaps() takes a HANDLE, not an fd.  Convert.
      const HANDLE symbols_handle = (HANDLE) get_osfhandle(child_in[1]);
      DumpProcSelfMaps(symbols_handle);
#else
      DumpProcSelfMaps(child_in[1]);  // what pprof expects on stdin
#endif

      // Allocate 24 bytes = ("0x" + 8 bytes + "\n" + overhead) for each
      // address to feed to pprof.
      const int kOutBufSize = 24 * symbolization_table_.size();
      char *pprof_buffer = new char[kOutBufSize];
      int written = 0;
      for (SymbolMap::const_iterator iter = symbolization_table_.begin();
           iter != symbolization_table_.end(); ++iter) {
        written += snprintf(pprof_buffer + written, kOutBufSize - written,
                 // pprof expects format to be 0xXXXXXX
                 "0x%" PRIxPTR "\n", reinterpret_cast<uintptr_t>(iter->first));
      }
      write(child_in[1], pprof_buffer, strlen(pprof_buffer));
      close(child_in[1]);             // that's all we need to write

      const int kSymbolBufferSize = kSymbolSize * symbolization_table_.size();
      int total_bytes_read = 0;
      delete[] symbol_buffer_;
      symbol_buffer_ = new char[kSymbolBufferSize];
      memset(symbol_buffer_, '\0', kSymbolBufferSize);
      while (1) {
        int bytes_read = read(child_out[1], symbol_buffer_ + total_bytes_read,
                              kSymbolBufferSize - total_bytes_read);
        if (bytes_read < 0) {
          close(child_out[1]);
          PrintError("Cannot read data from pprof");
          return 0;
        } else if (bytes_read == 0) {
          close(child_out[1]);
          wait(NULL);
          break;
        } else {
          total_bytes_read += bytes_read;
        }
      }
      // We have successfully read the output of pprof into out.  Make sure
      // the last symbol is full (we can tell because it ends with a \n).
      if (total_bytes_read == 0 || symbol_buffer_[total_bytes_read - 1] != '\n')
        return 0;
      // make the symbolization_table_ values point to the output vector
      SymbolMap::iterator fill = symbolization_table_.begin();
      int num_symbols = 0;
      const char *current_name = symbol_buffer_;
      for (int i = 0; i < total_bytes_read; i++) {
        if (symbol_buffer_[i] == '\n') {
          fill->second = current_name;
          symbol_buffer_[i] = '\0';
          current_name = symbol_buffer_ + i + 1;
          fill++;
          num_symbols++;
        }
      }
      return num_symbols;
    }
  }
  PrintError("Unkown error (should never occur!)");
  return 0;  // shouldn't be reachable
#endif
}
<commit_msg>Implemented GetProgramInvocationName on FreeBSD<commit_after>// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// ---
// Author: Craig Silverstein
//
// This forks out to pprof to do the actual symbolizing.  We might
// be better off writing our own in C++.

#include "config.h"
#include "symbolize.h"
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>   // for write()
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>   // for socketpair() -- needed by Symbolize
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>   // for wait() -- needed by Symbolize
#endif
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#ifdef __MACH__
#include <mach-o/dyld.h>   // for GetProgramInvocationName()
#include <limits.h>        // for PATH_MAX
#endif
#if defined(__CYGWIN__) || defined(__CYGWIN32__)
#include <io.h>            // for get_osfhandle()
#endif
#include <string>
#include "base/commandlineflags.h"
#include "base/logging.h"
#include "base/sysinfo.h"
#if defined(__FreeBSD__)
#include <sys/sysctl.h>
#endif

using std::string;
using tcmalloc::DumpProcSelfMaps;   // from sysinfo.h

DEFINE_string(symbolize_pprof,
              EnvToString("PPROF_PATH", "pprof"),
              "Path to pprof to call for reporting function names.");

// heap_profile_table_pprof may be referenced after destructors are
// called (since that's when leak-checking is done), so we make
// a more-permanent copy that won't ever get destroyed.
static string* g_pprof_path = new string(FLAGS_symbolize_pprof);

// Returns NULL if we're on an OS where we can't get the invocation name.
// Using a static var is ok because we're not called from a thread.
static const char* GetProgramInvocationName() {
#if defined(HAVE_PROGRAM_INVOCATION_NAME)
#ifdef __UCLIBC__
  extern const char* program_invocation_name; // uclibc provides this
#else
  extern char* program_invocation_name;  // gcc provides this
#endif
  return program_invocation_name;
#elif defined(__MACH__)
  // We don't want to allocate memory for this since we may be
  // calculating it when memory is corrupted.
  static char program_invocation_name[PATH_MAX];
  if (program_invocation_name[0] == '\0') {  // first time calculating
    uint32_t length = sizeof(program_invocation_name);
    if (_NSGetExecutablePath(program_invocation_name, &length))
      return NULL;
  }
  return program_invocation_name;
#elif defined(__FreeBSD__)
  static char program_invocation_name[PATH_MAX];
  size_t len = sizeof(program_invocation_name);
  static const int name[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
  if (!sysctl(name, 4, program_invocation_name, &len, NULL, 0))
    return program_invocation_name;
  return NULL;
#else
  return NULL;   // figure out a way to get argv[0]
#endif
}

// Prints an error message when you can't run Symbolize().
static void PrintError(const char* reason) {
  RAW_LOG(ERROR,
          "*** WARNING: Cannot convert addresses to symbols in output below.\n"
          "*** Reason: %s\n"
          "*** If you cannot fix this, try running pprof directly.\n",
          reason);
}

void SymbolTable::Add(const void* addr) {
  symbolization_table_[addr] = "";
}

const char* SymbolTable::GetSymbol(const void* addr) {
  return symbolization_table_[addr];
}

// Updates symbolization_table with the pointers to symbol names corresponding
// to its keys. The symbol names are stored in out, which is allocated and
// freed by the caller of this routine.
// Note that the forking/etc is not thread-safe or re-entrant.  That's
// ok for the purpose we need -- reporting leaks detected by heap-checker
// -- but be careful if you decide to use this routine for other purposes.
// Returns number of symbols read on error.  If can't symbolize, returns 0
// and emits an error message about why.
int SymbolTable::Symbolize() {
#if !defined(HAVE_UNISTD_H)  || !defined(HAVE_SYS_SOCKET_H) || !defined(HAVE_SYS_WAIT_H)
  PrintError("Perftools does not know how to call a sub-process on this O/S");
  return 0;
#else
  const char* argv0 = GetProgramInvocationName();
  if (argv0 == NULL) {  // can't call symbolize if we can't figure out our name
    PrintError("Cannot figure out the name of this executable (argv0)");
    return 0;
  }
  if (access(g_pprof_path->c_str(), R_OK) != 0) {
    PrintError("Cannot find 'pprof' (is PPROF_PATH set correctly?)");
    return 0;
  }

  // All this work is to do two-way communication.  ugh.
  int *child_in = NULL;   // file descriptors
  int *child_out = NULL;  // for now, we don't worry about child_err
  int child_fds[5][2];    // socketpair may be called up to five times below

  // The client program may close its stdin and/or stdout and/or stderr
  // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
  // In this case the communication between the forked processes may be broken
  // if either the parent or the child tries to close or duplicate these
  // descriptors. The loop below produces two pairs of file descriptors, each
  // greater than 2 (stderr).
  for (int i = 0; i < 5; i++) {
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, child_fds[i]) == -1) {
      for (int j = 0; j < i; j++) {
        close(child_fds[j][0]);
        close(child_fds[j][1]);
        PrintError("Cannot create a socket pair");
      }
      return 0;
    } else {
      if ((child_fds[i][0] > 2) && (child_fds[i][1] > 2)) {
        if (child_in == NULL) {
          child_in = child_fds[i];
        } else {
          child_out = child_fds[i];
          for (int j = 0; j < i; j++) {
            if (child_fds[j] == child_in) continue;
            close(child_fds[j][0]);
            close(child_fds[j][1]);
          }
          break;
        }
      }
    }
  }

  switch (fork()) {
    case -1: {  // error
      close(child_in[0]);
      close(child_in[1]);
      close(child_out[0]);
      close(child_out[1]);
      PrintError("Unknown error calling fork()");
      return 0;
    }
    case 0: {  // child
      close(child_in[1]);   // child uses the 0's, parent uses the 1's
      close(child_out[1]);  // child uses the 0's, parent uses the 1's
      close(0);
      close(1);
      if (dup2(child_in[0], 0) == -1) _exit(1);
      if (dup2(child_out[0], 1) == -1) _exit(2);
      // Unset vars that might cause trouble when we fork
      unsetenv("CPUPROFILE");
      unsetenv("HEAPPROFILE");
      unsetenv("HEAPCHECK");
      unsetenv("PERFTOOLS_VERBOSE");
      execlp(g_pprof_path->c_str(), g_pprof_path->c_str(),
             "--symbols", argv0, NULL);
      _exit(3);  // if execvp fails, it's bad news for us
    }
    default: {  // parent
      close(child_in[0]);   // child uses the 0's, parent uses the 1's
      close(child_out[0]);  // child uses the 0's, parent uses the 1's
#ifdef HAVE_POLL_H
      // Waiting for 1ms seems to give the OS time to notice any errors.
      poll(0, 0, 1);
      // For maximum safety, we check to make sure the execlp
      // succeeded before trying to write.  (Otherwise we'll get a
      // SIGPIPE.)  For systems without poll.h, we'll just skip this
      // check, and trust that the user set PPROF_PATH correctly!
      struct pollfd pfd = { child_in[1], POLLOUT, 0 };
      if (!poll(&pfd, 1, 0) || !(pfd.revents & POLLOUT) ||
          (pfd.revents & (POLLHUP|POLLERR))) {
        PrintError("Cannot run 'pprof' (is PPROF_PATH set correctly?)");
        return 0;
      }
#endif
#if defined(__CYGWIN__) || defined(__CYGWIN32__)
      // On cygwin, DumpProcSelfMaps() takes a HANDLE, not an fd.  Convert.
      const HANDLE symbols_handle = (HANDLE) get_osfhandle(child_in[1]);
      DumpProcSelfMaps(symbols_handle);
#else
      DumpProcSelfMaps(child_in[1]);  // what pprof expects on stdin
#endif

      // Allocate 24 bytes = ("0x" + 8 bytes + "\n" + overhead) for each
      // address to feed to pprof.
      const int kOutBufSize = 24 * symbolization_table_.size();
      char *pprof_buffer = new char[kOutBufSize];
      int written = 0;
      for (SymbolMap::const_iterator iter = symbolization_table_.begin();
           iter != symbolization_table_.end(); ++iter) {
        written += snprintf(pprof_buffer + written, kOutBufSize - written,
                 // pprof expects format to be 0xXXXXXX
                 "0x%" PRIxPTR "\n", reinterpret_cast<uintptr_t>(iter->first));
      }
      write(child_in[1], pprof_buffer, strlen(pprof_buffer));
      close(child_in[1]);             // that's all we need to write

      const int kSymbolBufferSize = kSymbolSize * symbolization_table_.size();
      int total_bytes_read = 0;
      delete[] symbol_buffer_;
      symbol_buffer_ = new char[kSymbolBufferSize];
      memset(symbol_buffer_, '\0', kSymbolBufferSize);
      while (1) {
        int bytes_read = read(child_out[1], symbol_buffer_ + total_bytes_read,
                              kSymbolBufferSize - total_bytes_read);
        if (bytes_read < 0) {
          close(child_out[1]);
          PrintError("Cannot read data from pprof");
          return 0;
        } else if (bytes_read == 0) {
          close(child_out[1]);
          wait(NULL);
          break;
        } else {
          total_bytes_read += bytes_read;
        }
      }
      // We have successfully read the output of pprof into out.  Make sure
      // the last symbol is full (we can tell because it ends with a \n).
      if (total_bytes_read == 0 || symbol_buffer_[total_bytes_read - 1] != '\n')
        return 0;
      // make the symbolization_table_ values point to the output vector
      SymbolMap::iterator fill = symbolization_table_.begin();
      int num_symbols = 0;
      const char *current_name = symbol_buffer_;
      for (int i = 0; i < total_bytes_read; i++) {
        if (symbol_buffer_[i] == '\n') {
          fill->second = current_name;
          symbol_buffer_[i] = '\0';
          current_name = symbol_buffer_ + i + 1;
          fill++;
          num_symbols++;
        }
      }
      return num_symbols;
    }
  }
  PrintError("Unkown error (should never occur!)");
  return 0;  // shouldn't be reachable
#endif
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "third_party/googletest/src/include/gtest/gtest.h"

#include "./vp9_rtcd.h"
#include "./vpx_config.h"
#include "./vpx_dsp_rtcd.h"
#include "test/acm_random.h"
#include "test/clear_system_state.h"
#include "test/register_state_check.h"
#include "vp9/common/vp9_blockd.h"
#include "vpx_mem/vpx_mem.h"

typedef void (*SubtractFunc)(int rows, int cols, int16_t *diff_ptr,
                             ptrdiff_t diff_stride, const uint8_t *src_ptr,
                             ptrdiff_t src_stride, const uint8_t *pred_ptr,
                             ptrdiff_t pred_stride);

namespace vp9 {

class VP9SubtractBlockTest : public ::testing::TestWithParam<SubtractFunc> {
 public:
  virtual void TearDown() { libvpx_test::ClearSystemState(); }
};

using libvpx_test::ACMRandom;

TEST_P(VP9SubtractBlockTest, SimpleSubtract) {
  ACMRandom rnd(ACMRandom::DeterministicSeed());

  // FIXME(rbultje) split in its own file
  for (BLOCK_SIZE bsize = BLOCK_4X4; bsize < BLOCK_SIZES;
       bsize = static_cast<BLOCK_SIZE>(static_cast<int>(bsize) + 1)) {
    const int block_width = 4 * num_4x4_blocks_wide_lookup[bsize];
    const int block_height = 4 * num_4x4_blocks_high_lookup[bsize];
    int16_t *diff = reinterpret_cast<int16_t *>(
        vpx_memalign(16, sizeof(*diff) * block_width * block_height * 2));
    uint8_t *pred = reinterpret_cast<uint8_t *>(
        vpx_memalign(16, block_width * block_height * 2));
    uint8_t *src = reinterpret_cast<uint8_t *>(
        vpx_memalign(16, block_width * block_height * 2));

    for (int n = 0; n < 100; n++) {
      for (int r = 0; r < block_height; ++r) {
        for (int c = 0; c < block_width * 2; ++c) {
          src[r * block_width * 2 + c] = rnd.Rand8();
          pred[r * block_width * 2 + c] = rnd.Rand8();
        }
      }

      GetParam()(block_height, block_width, diff, block_width, src, block_width,
                 pred, block_width);

      for (int r = 0; r < block_height; ++r) {
        for (int c = 0; c < block_width; ++c) {
          EXPECT_EQ(diff[r * block_width + c],
                    (src[r * block_width + c] - pred[r * block_width + c]))
              << "r = " << r << ", c = " << c << ", bs = " << bsize;
        }
      }

      GetParam()(block_height, block_width, diff, block_width * 2, src,
                 block_width * 2, pred, block_width * 2);

      for (int r = 0; r < block_height; ++r) {
        for (int c = 0; c < block_width; ++c) {
          EXPECT_EQ(
              diff[r * block_width * 2 + c],
              (src[r * block_width * 2 + c] - pred[r * block_width * 2 + c]))
              << "r = " << r << ", c = " << c << ", bs = " << bsize;
        }
      }
    }
    vpx_free(diff);
    vpx_free(pred);
    vpx_free(src);
  }
}

INSTANTIATE_TEST_CASE_P(C, VP9SubtractBlockTest,
                        ::testing::Values(vpx_subtract_block_c));

#if HAVE_SSE2
INSTANTIATE_TEST_CASE_P(SSE2, VP9SubtractBlockTest,
                        ::testing::Values(vpx_subtract_block_sse2));
#endif
#if HAVE_NEON
INSTANTIATE_TEST_CASE_P(NEON, VP9SubtractBlockTest,
                        ::testing::Values(vpx_subtract_block_neon));
#endif
#if HAVE_MSA
INSTANTIATE_TEST_CASE_P(MSA, VP9SubtractBlockTest,
                        ::testing::Values(vpx_subtract_block_msa));
#endif

#if HAVE_MMI
INSTANTIATE_TEST_CASE_P(MMI, VP9SubtractBlockTest,
                        ::testing::Values(vpx_subtract_block_mmi));
#endif

}  // namespace vp9
<commit_msg>Speed test for subtract_block<commit_after>/*
 *  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "third_party/googletest/src/include/gtest/gtest.h"

#include "./vp9_rtcd.h"
#include "./vpx_config.h"
#include "./vpx_dsp_rtcd.h"
#include "test/acm_random.h"
#include "test/bench.h"
#include "test/clear_system_state.h"
#include "test/register_state_check.h"
#include "vp9/common/vp9_blockd.h"
#include "vpx_mem/vpx_mem.h"

typedef void (*SubtractFunc)(int rows, int cols, int16_t *diff_ptr,
                             ptrdiff_t diff_stride, const uint8_t *src_ptr,
                             ptrdiff_t src_stride, const uint8_t *pred_ptr,
                             ptrdiff_t pred_stride);

namespace vp9 {

class VP9SubtractBlockTest : public AbstractBench,
                             public ::testing::TestWithParam<SubtractFunc> {
 public:
  virtual void TearDown() { libvpx_test::ClearSystemState(); }

 protected:
  int block_width_;
  int block_height_;
  int16_t *diff_;
  uint8_t *pred_;
  uint8_t *src_;

  virtual void Run() {
    GetParam()(block_height_, block_width_, diff_, block_width_, src_,
               block_width_, pred_, block_width_);
  }

  void SetupBlocks(BLOCK_SIZE bsize) {
    block_width_ = 4 * num_4x4_blocks_wide_lookup[bsize];
    block_height_ = 4 * num_4x4_blocks_high_lookup[bsize];
    diff_ = reinterpret_cast<int16_t *>(
        vpx_memalign(16, sizeof(*diff_) * block_width_ * block_height_ * 2));
    pred_ = reinterpret_cast<uint8_t *>(
        vpx_memalign(16, block_width_ * block_height_ * 2));
    src_ = reinterpret_cast<uint8_t *>(
        vpx_memalign(16, block_width_ * block_height_ * 2));
  }
};

using libvpx_test::ACMRandom;

TEST_P(VP9SubtractBlockTest, DISABLED_Speed) {
  ACMRandom rnd(ACMRandom::DeterministicSeed());

  for (BLOCK_SIZE bsize = BLOCK_4X4; bsize < BLOCK_SIZES;
       bsize = static_cast<BLOCK_SIZE>(static_cast<int>(bsize) + 1)) {
    SetupBlocks(bsize);

    RunNTimes(100000000 / (block_height_ * block_width_));
    char block_size[16];
    snprintf(block_size, sizeof(block_size), "%dx%d", block_height_,
             block_width_);
    char title[100];
    snprintf(title, sizeof(title), "%8s ", block_size);
    PrintMedian(title);

    vpx_free(diff_);
    vpx_free(pred_);
    vpx_free(src_);
  }
}

TEST_P(VP9SubtractBlockTest, SimpleSubtract) {
  ACMRandom rnd(ACMRandom::DeterministicSeed());

  // FIXME(rbultje) split in its own file
  for (BLOCK_SIZE bsize = BLOCK_4X4; bsize < BLOCK_SIZES;
       bsize = static_cast<BLOCK_SIZE>(static_cast<int>(bsize) + 1)) {
    SetupBlocks(bsize);

    for (int n = 0; n < 100; n++) {
      for (int r = 0; r < block_height_; ++r) {
        for (int c = 0; c < block_width_ * 2; ++c) {
          src_[r * block_width_ * 2 + c] = rnd.Rand8();
          pred_[r * block_width_ * 2 + c] = rnd.Rand8();
        }
      }

      GetParam()(block_height_, block_width_, diff_, block_width_, src_,
                 block_width_, pred_, block_width_);

      for (int r = 0; r < block_height_; ++r) {
        for (int c = 0; c < block_width_; ++c) {
          EXPECT_EQ(diff_[r * block_width_ + c],
                    (src_[r * block_width_ + c] - pred_[r * block_width_ + c]))
              << "r = " << r << ", c = " << c << ", bs = " << bsize;
        }
      }

      GetParam()(block_height_, block_width_, diff_, block_width_ * 2, src_,
                 block_width_ * 2, pred_, block_width_ * 2);

      for (int r = 0; r < block_height_; ++r) {
        for (int c = 0; c < block_width_; ++c) {
          EXPECT_EQ(diff_[r * block_width_ * 2 + c],
                    (src_[r * block_width_ * 2 + c] -
                     pred_[r * block_width_ * 2 + c]))
              << "r = " << r << ", c = " << c << ", bs = " << bsize;
        }
      }
    }
    vpx_free(diff_);
    vpx_free(pred_);
    vpx_free(src_);
  }
}

INSTANTIATE_TEST_CASE_P(C, VP9SubtractBlockTest,
                        ::testing::Values(vpx_subtract_block_c));

#if HAVE_SSE2
INSTANTIATE_TEST_CASE_P(SSE2, VP9SubtractBlockTest,
                        ::testing::Values(vpx_subtract_block_sse2));
#endif
#if HAVE_NEON
INSTANTIATE_TEST_CASE_P(NEON, VP9SubtractBlockTest,
                        ::testing::Values(vpx_subtract_block_neon));
#endif
#if HAVE_MSA
INSTANTIATE_TEST_CASE_P(MSA, VP9SubtractBlockTest,
                        ::testing::Values(vpx_subtract_block_msa));
#endif

#if HAVE_MMI
INSTANTIATE_TEST_CASE_P(MMI, VP9SubtractBlockTest,
                        ::testing::Values(vpx_subtract_block_mmi));
#endif

}  // namespace vp9
<|endoftext|>