<commit_before>/*
  MockWire.h - Mock class implementation
  Copyright (c) 2012 Korney Czukowski

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
#include "IWire.h"
#include "TwoWire.h"
#include "MockWire.h"

// Begin method
void MockWire::begin(uint8_t address)
{
	addMethodCall("begin");
	initializedAddress = address;
}
void MockWire::begin(int address)
{
	begin( (uint8_t) address);
}
void MockWire::begin(void)
{
	begin(0);
}

// Begin Transmission method
void MockWire::beginTransmission(uint8_t address)
{
	addMethodCall("beginTransmission");
	transmitting = 1;
	transmitAddress = address;
}
void MockWire::beginTransmission(int address)
{
	beginTransmission( (uint8_t) address);
}

// End Transmission method
uint8_t MockWire::endTransmission(uint8_t sendStop)
{
	transmitting = 0;
	addMethodCall("endTransmission");
	endTransmissionSentStop = sendStop;
	// 0 for 'no twi error'
	return 0;
}
uint8_t MockWire::endTransmission(void)
{
	return endTransmission(true);
}

// Request From method
uint8_t MockWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop)
{
	addMethodCall("requestFrom");
	requestedFromAddress = address;
	if (quantity > BUFFER_LENGTH)
	{
		quantity = BUFFER_LENGTH;
	}
	requestedFromQuantity = quantity;
	requestedFromSentStop = sendStop;
	return rxBufferLength;
}
uint8_t MockWire::requestFrom(uint8_t address, uint8_t quantity)
{
	return requestFrom(address, quantity, (uint8_t) true);
}
uint8_t MockWire::requestFrom(int address, int quantity, int sendStop)
{
	return requestFrom((uint8_t) address, (uint8_t) quantity, (uint8_t) sendStop);
}
uint8_t MockWire::requestFrom(int address, int quantity)
{
	return requestFrom(address, quantity, (int) true);
}

// Write method
size_t MockWire::write(const uint8_t *data, size_t quantity)
{
	if (transmitting)
	{
		addMethodCall("write");
		append(writtenData, writtenQuantity, (const char *) data, quantity);
	}
	return quantity;
}
size_t MockWire::write(uint8_t data)
{
	return write(&data, 1);
}

// Available method
int MockWire::available(void)
{
	addMethodCall("available");
	return rxBufferLength - rxBufferIndex;
}

// Read method (copy from TwoWire)
int MockWire::read(void)
{
	addMethodCall("read");
	int value = -1;
	if (rxBufferIndex < rxBufferLength)
	{
		value = rxBuffer[rxBufferIndex];
		++rxBufferIndex;
	}
	return value;
}

// Peek method (copy from TwoWire)
int MockWire::peek(void)
{
	addMethodCall("peek");
	int value = -1;
	if (rxBufferIndex < rxBufferLength)
	{
		value = rxBuffer[rxBufferIndex];
	}
	return value;
}

// Flush method (not implemented in TwoWire)
void MockWire::flush(void)
{
	addMethodCall("flush");
}

// Initialize the properties in constructor
MockWire::MockWire()
{
	methodCallsIndex = 0;
	initializedAddress = 0;
	transmitAddress = 0;
	transmitting = 0;
	requestedFromAddress = 0;
	requestedFromQuantity = 0;
	requestedFromSentStop = 0;
	endTransmissionSentStop = 0;
	writtenQuantity = 0;
}

// Private helper methods
void MockWire::addMethodCall(const char *methodName)
{
	uint8_t length = strlen(methodName);
	if (methodCallsIndex > 0)
	{
		append(methodCalls, methodCallsIndex, ",", 1);
	}
	append(methodCalls, methodCallsIndex, methodName, strlen(methodName));
}
void MockWire::append(char *buffer, uint8_t& cursor, const char *data, int length)
{
	char *p = buffer;
	for (int i = 0; i < length; i++)
	{
		*p++ = *data++;
		cursor++;
	}
	*p = 0;
};<commit_msg>Removed extra line<commit_after>/*
  MockWire.h - Mock class implementation
  Copyright (c) 2012 Korney Czukowski

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
#include "IWire.h"
#include "TwoWire.h"
#include "MockWire.h"

// Begin method
void MockWire::begin(uint8_t address)
{
	addMethodCall("begin");
	initializedAddress = address;
}
void MockWire::begin(int address)
{
	begin( (uint8_t) address);
}
void MockWire::begin(void)
{
	begin(0);
}

// Begin Transmission method
void MockWire::beginTransmission(uint8_t address)
{
	addMethodCall("beginTransmission");
	transmitting = 1;
	transmitAddress = address;
}
void MockWire::beginTransmission(int address)
{
	beginTransmission( (uint8_t) address);
}

// End Transmission method
uint8_t MockWire::endTransmission(uint8_t sendStop)
{
	transmitting = 0;
	addMethodCall("endTransmission");
	endTransmissionSentStop = sendStop;
	// 0 for 'no twi error'
	return 0;
}
uint8_t MockWire::endTransmission(void)
{
	return endTransmission(true);
}

// Request From method
uint8_t MockWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop)
{
	addMethodCall("requestFrom");
	requestedFromAddress = address;
	if (quantity > BUFFER_LENGTH)
	{
		quantity = BUFFER_LENGTH;
	}
	requestedFromQuantity = quantity;
	requestedFromSentStop = sendStop;
	return rxBufferLength;
}
uint8_t MockWire::requestFrom(uint8_t address, uint8_t quantity)
{
	return requestFrom(address, quantity, (uint8_t) true);
}
uint8_t MockWire::requestFrom(int address, int quantity, int sendStop)
{
	return requestFrom((uint8_t) address, (uint8_t) quantity, (uint8_t) sendStop);
}
uint8_t MockWire::requestFrom(int address, int quantity)
{
	return requestFrom(address, quantity, (int) true);
}

// Write method
size_t MockWire::write(const uint8_t *data, size_t quantity)
{
	if (transmitting)
	{
		addMethodCall("write");
		append(writtenData, writtenQuantity, (const char *) data, quantity);
	}
	return quantity;
}
size_t MockWire::write(uint8_t data)
{
	return write(&data, 1);
}

// Available method
int MockWire::available(void)
{
	addMethodCall("available");
	return rxBufferLength - rxBufferIndex;
}

// Read method (copy from TwoWire)
int MockWire::read(void)
{
	addMethodCall("read");
	int value = -1;
	if (rxBufferIndex < rxBufferLength)
	{
		value = rxBuffer[rxBufferIndex];
		++rxBufferIndex;
	}
	return value;
}

// Peek method (copy from TwoWire)
int MockWire::peek(void)
{
	addMethodCall("peek");
	int value = -1;
	if (rxBufferIndex < rxBufferLength)
	{
		value = rxBuffer[rxBufferIndex];
	}
	return value;
}

// Flush method (not implemented in TwoWire)
void MockWire::flush(void)
{
	addMethodCall("flush");
}

// Initialize the properties in constructor
MockWire::MockWire()
{
	methodCallsIndex = 0;
	initializedAddress = 0;
	transmitAddress = 0;
	transmitting = 0;
	requestedFromAddress = 0;
	requestedFromQuantity = 0;
	requestedFromSentStop = 0;
	endTransmissionSentStop = 0;
	writtenQuantity = 0;
}

// Private helper methods
void MockWire::addMethodCall(const char *methodName)
{
	if (methodCallsIndex > 0)
	{
		append(methodCalls, methodCallsIndex, ",", 1);
	}
	append(methodCalls, methodCallsIndex, methodName, strlen(methodName));
}
void MockWire::append(char *buffer, uint8_t& cursor, const char *data, int length)
{
	char *p = buffer;
	for (int i = 0; i < length; i++)
	{
		*p++ = *data++;
		cursor++;
	}
	*p = 0;
};<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <string>
#include <utility>

#include "os/job_t.h"
#include "os/task_system_t.h"
#include "os/llf_scheduler_event_based.h"
#include "os/llf_scheduler_time_based.h"
#include "os/lcm.h"
#include "lib/pinput.h"
#include "lib/io.h"
#include "lib/exception.h"
#include "lib/ansi.h"

template<typename P, typename O, typename D, typename S>
void parse_parameters(const P& parameters, const O& options, D& d, S& mode, S& file_name, bool& open){

	if(parameters.size() < 1) throw lib::exception("Missing d");

	d = std::stoul(parameters[0]);
	if(parameters.size() >= 2){
		file_name = parameters[1];
		open = true;
	}

	typename O::const_iterator it;

	if(((it = options.find("-m")) != options.end() || (it = options.find("--mode")) != options.end()) && it->second.size() > 0){
		mode = it->second[0];
	}

}

template<typename D, typename S>
void check_parameters(const D d, const S& mode){
	if(d < 1) throw lib::exception("d < 1");
	if(mode != "event" && mode != "time") throw lib::exception("mode not in [event|time]");
}

void help(){
	std::cout << " - flags" << std::endl << std::endl;
	std::cout << "   " << "[-h | --help    ]" << std::endl;
	std::cout << "   " << "[-v | --verbose ]" << std::endl;
	std::cout << "   " << "[-p | --pipe    ]" << std::endl;
	std::cout << "   " << "[--nocolor      ]" << std::endl << std::endl;
	std::cout << " - mandatory parameters" << std::endl << std::endl;
	std::cout << "   " << "#0 (int >= 1)" << std::endl << std::endl;
	std::cout << " - optional parameters" << std::endl << std::endl;
	std::cout << "   " << "#1 (string)" << std::endl << std::endl;
	std::cout << "   " << "[-m | --mode    ] #0 (string)" << std::endl << std::endl;
}

int main(int argc, char* argv[]){

	std::vector<std::string> params;
	std::map<std::string, std::vector<std::string>> options;
	std::set<std::string> flags;
	std::set<std::string> option_set = {
		"-m", "--mode"
	};
	std::set<std::string> flag_set = {
		"-h", "--help",
		"-v", "--verbose",
		"-p", "--pipe",
		"--nocolor"
	};

	pinput::parse(argc, argv, params, options, flags, option_set, flag_set);

	const bool nocolor = flags.count("--nocolor");

	const char* vcolor = (nocolor)? "" : ansi::blue;
	const char* ecolor = (nocolor)? "" : ansi::red;
	const char* rcolor = (nocolor)? "" : ansi::reset;

	const bool show_help = flags.count("-h") || flags.count("--help");
	if(show_help){
		help();
		return 0;
	}

	const bool pipe = flags.count("-p") || flags.count("--pipe");

	try{

		uint d;
		std::string mode = "event";
		std::string file_name;
		bool open = false;

		parse_parameters(params, options, d, mode, file_name, open);

		check_parameters(d, mode);

		//READ INPUT

		std::streambuf* buffer;
		std::ifstream ifstream;

		if(open){
			ifstream.open(file_name);
			buffer = ifstream.rdbuf();
		}
		else{
			buffer = std::cin.rdbuf();
		}
		std::istream istream(buffer);

		os::task_system_t task_system;
		istream >> task_system;
		if(open) ifstream.close();

		uint lcm = os::task_system_period_lcm<uint, os::task_system_t>(task_system);
		uint preempted, idle;
		bool schedulable;

		std::function<void(size_t, size_t, size_t, size_t)> callback = [](size_t, size_t, size_t, size_t){};
		if(pipe){
			std::cout << task_system.size() << ' ' << lcm << std::endl;
			callback = [](size_t event, size_t task, size_t i, size_t j){
				std::cout << event << ' ' << task << ' ' << i << ' ' << j << std::endl;
			};
		}

		if(mode == "event"){
			os::llf_scheduler_event_based<os::task_system_t, os::job_t> scheduler;
			scheduler.reset();
			scheduler.init(task_system);
			scheduler.run(d, lcm, callback);

			preempted = scheduler.preempted;
			idle = scheduler.idle;
			schedulable = scheduler.schedulable;
		}
		else{
			os::llf_scheduler_time_based<os::task_system_t, os::job_t> scheduler;
			scheduler.reset();
			scheduler.init(task_system);
			scheduler.run(d, lcm, callback);

			preempted = scheduler.preempted;
			idle = scheduler.idle;
			schedulable = scheduler.schedulable;
		}

		if(!pipe){
			std::cout << vcolor;
			std::cout << "study interval [" << 0 << ", " << lcm << '[' << std::endl;
			std::cout << "# preemptions : " << preempted << std::endl;
			std::cout << "# idle : " << idle << std::endl;
			std::cout << "b schedulable : " << schedulable << std::endl;
			std::cout << rcolor;
		}

	}
	catch(const std::exception& e){
		std::cout << ecolor << "error -> " << e.what() << rcolor << std::endl;
		return 1;
	}

	return 0;
}<commit_msg>add simLLF ouput file option<commit_after>#include <iostream>
#include <fstream>
#include <string>
#include <utility>

#include "os/job_t.h"
#include "os/task_system_t.h"
#include "os/llf_scheduler_event_based.h"
#include "os/llf_scheduler_time_based.h"
#include "os/lcm.h"
#include "lib/pinput.h"
#include "lib/io.h"
#include "lib/exception.h"
#include "lib/ansi.h"

template<typename P, typename O, typename D, typename S>
void parse_parameters(const P& parameters, const O& options, D& d, S& mode, S& file_name_in, bool& open_in, S& file_name_out, bool& open_out){

	if(parameters.size() < 1) throw lib::exception("Missing d");

	d = std::stoul(parameters[0]);
	if(parameters.size() >= 2){
		file_name_in = parameters[1];
		open_in = true;
	}

	typename O::const_iterator it;

	if(((it = options.find("-m")) != options.end() || (it = options.find("--mode")) != options.end()) && it->second.size() > 0){
		mode = it->second[0];
	}

	if((it = options.find("-o")) != options.end() || (it = options.find("--output")) != options.end()){
		if(it->second.size() > 0){
			file_name_out = it->second[0];
			open_out = true;
		}
	};

}

template<typename D, typename S>
void check_parameters(const D d, const S& mode){
	if(d < 1) throw lib::exception("d < 1");
	if(mode != "event" && mode != "time") throw lib::exception("mode not in [event|time]");
}

void help(){
	std::cout << " - flags" << std::endl << std::endl;
	std::cout << "   " << "[-h | --help    ]" << std::endl;
	std::cout << "   " << "[-v | --verbose ]" << std::endl;
	std::cout << "   " << "[-p | --pipe    ]" << std::endl;
	std::cout << "   " << "[--nocolor      ]" << std::endl << std::endl;
	std::cout << " - mandatory parameters" << std::endl << std::endl;
	std::cout << "   " << "#0 (int >= 1)" << std::endl << std::endl;
	std::cout << " - optional parameters" << std::endl << std::endl;
	std::cout << "   " << "#1 (string)" << std::endl << std::endl;
	std::cout << "   " << "[-m | --mode    ] #0 (string)" << std::endl << std::endl;
	std::cout << "   " << "[-o | --output  ] #0 (string)" << std::endl << std::endl;
}

int main(int argc, char* argv[]){

	std::vector<std::string> params;
	std::map<std::string, std::vector<std::string>> options;
	std::set<std::string> flags;
	std::set<std::string> option_set = {
		"-m", "--mode"
	};
	std::set<std::string> flag_set = {
		"-h", "--help",
		"-v", "--verbose",
		"-p", "--pipe",
		"-o", "--output",
		"--nocolor"
	};

	pinput::parse(argc, argv, params, options, flags, option_set, flag_set);

	const bool nocolor = flags.count("--nocolor");

	const char* vcolor = (nocolor)? "" : ansi::blue;
	const char* ecolor = (nocolor)? "" : ansi::red;
	const char* rcolor = (nocolor)? "" : ansi::reset;

	const bool show_help = flags.count("-h") || flags.count("--help");
	if(show_help){
		help();
		return 0;
	}

	const bool pipe = flags.count("-p") || flags.count("--pipe");

	try{

		uint d;
		std::string mode = "event";
		std::string file_name_in, file_name_out;
		bool open_in = false, open_out = false;

		parse_parameters(params, options, d, mode, file_name_in, open_in, file_name_out, open_out);

		check_parameters(d, mode);

		// INPUT

		std::streambuf* buffer_in;
		std::ifstream ifstream;

		if(open_in){
			ifstream.open(file_name_in);
			buffer_in = ifstream.rdbuf();
		}
		else{
			buffer_in = std::cin.rdbuf();
		}
		std::istream istream(buffer_in);

		os::task_system_t task_system;
		istream >> task_system;
		if(open_in) ifstream.close();

		// OUTPUT
		std::streambuf* buffer_out;
		std::ofstream ofstream;

		if(open_out){
			ofstream.open(file_name_out);
			buffer_out = ofstream.rdbuf();
		}
		else{
			buffer_out = std::cout.rdbuf();
		}

		std::ostream ostream(buffer_out);

		uint lcm = os::task_system_period_lcm<uint, os::task_system_t>(task_system);
		uint preempted, idle;
		bool schedulable;

		std::function<void(size_t, size_t, size_t, size_t)> callback = [](size_t, size_t, size_t, size_t){};
		if(pipe){
			ostream << task_system.size() << ' ' << lcm << std::endl;
			callback = [&](size_t event, size_t task, size_t i, size_t j){
				ostream << event << ' ' << task << ' ' << i << ' ' << j << std::endl;
			};
		}

		if(mode == "event"){
			os::llf_scheduler_event_based<os::task_system_t, os::job_t> scheduler;
			scheduler.reset();
			scheduler.init(task_system);
			scheduler.run(d, lcm, callback);

			preempted = scheduler.preempted;
			idle = scheduler.idle;
			schedulable = scheduler.schedulable;
		}
		else{
			os::llf_scheduler_time_based<os::task_system_t, os::job_t> scheduler;
			scheduler.reset();
			scheduler.init(task_system);
			scheduler.run(d, lcm, callback);

			preempted = scheduler.preempted;
			idle = scheduler.idle;
			schedulable = scheduler.schedulable;
		}

		if(!pipe){
			ostream << vcolor;
			ostream << "study interval [" << 0 << ", " << lcm << '[' << std::endl;
			ostream << "# preemptions : " << preempted << std::endl;
			ostream << "# idle : " << idle << std::endl;
			ostream << "b schedulable : " << schedulable << std::endl;
			ostream << rcolor;
		}

		if(open_out) ofstream.close();

	}
	catch(const std::exception& e){
		std::cout << ecolor << "error -> " << e.what() << rcolor << std::endl;
		return 1;
	}

	return 0;
}<|endoftext|>


<commit_before>#include "tern/runtime/rdtsc.h"

using namespace std;

pthread_spinlock_t rdtsc_lock;
const size_t max_v_size = 300000000;
std::vector<sync_op_entry *> rdtsc_log;
size_t rdtsc_index = (size_t)-1;

void process_rdtsc_log(void) {
  printf("Storing rdtsc log...\n");
  char log_path[1024];
  memset(log_path, 0, 1024);
  snprintf(log_path, 1024, "%s/pself-pid-%u.txt", options::rdtsc_output_dir.c_str(), (unsigned)getpid());
  mkdir(options::rdtsc_output_dir.c_str(), 0777);
  FILE *f = fopen(log_path, "w");
  assert(f);

  const char *opdeps[3] = {"", "----", "--------"};

  for (size_t i = 0; i < rdtsc_index; i++) {
    struct sync_op_entry *entry = rdtsc_log[i];
    assert(entry->op_print_depth < 3);
    fprintf(f, "%u %s%s %s %llu %p\n", entry->tid, opdeps[entry->op_print_depth], entry->op, entry->op_suffix, entry->clock, entry->eip);
    delete entry;
  }
  rdtsc_log.clear();
  fflush(f);
  fclose(f);
}

void record_rdtsc_op(const char *op_name, const char *op_suffix, int print_depth, void *eip) {
  if (options::record_rdtsc) {
    if (rdtsc_index == (size_t)-1) {
      pthread_spin_init(&rdtsc_lock, 0);
      //printf("At exit...\n");
      atexit(process_rdtsc_log);
      rdtsc_log.reserve (max_v_size);
      rdtsc_index = 0;
    }

    struct sync_op_entry *entry = new struct sync_op_entry;
    entry->tid = (unsigned)pthread_self();

    entry->op = op_name;
    entry->op_suffix = op_suffix;
    entry->op_print_depth = (unsigned)print_depth;
    entry->eip = eip;

    pthread_spin_lock(&rdtsc_lock);
    entry->clock = rdtsc(); // Put this into critical section, so the log is sorted.
    assert(rdtsc_index < max_v_size && "Please pre-allocated a bigger vector size for rdtsc log.");
    rdtsc_log[rdtsc_index] = entry;
    rdtsc_index++;
    pthread_spin_unlock(&rdtsc_lock);
  }
  //fprintf(stderr, "%u : %s %s %llu\n", (unsigned)pthread_self(), op_name, op_suffix, rdtsc());
}<commit_msg>Nits to perf script.<commit_after>#include "tern/runtime/rdtsc.h"

using namespace std;

pthread_spinlock_t rdtsc_lock;
const size_t max_v_size = 300000000;
std::vector<sync_op_entry *> rdtsc_log;
size_t rdtsc_index = (size_t)-1;

void process_rdtsc_log(void) {
  printf("Storing rdtsc log...\n");
  char log_path[1024];
  memset(log_path, 0, 1024);
  snprintf(log_path, 1024, "%s/pself-pid-%u.txt", options::rdtsc_output_dir.c_str(), (unsigned)getpid());
  mkdir(options::rdtsc_output_dir.c_str(), 0777);
  FILE *f = fopen(log_path, "a+");
  assert(f);

  const char *opdeps[3] = {"", "----", "--------"};

  for (size_t i = 0; i < rdtsc_index; i++) {
    struct sync_op_entry *entry = rdtsc_log[i];
    assert(entry->op_print_depth < 3);
    fprintf(f, "%u %s%s %s %llu %p\n", entry->tid, opdeps[entry->op_print_depth], entry->op, entry->op_suffix, entry->clock, entry->eip);
    delete entry;
  }
  rdtsc_log.clear();
  fflush(f);
  fclose(f);
  rdtsc_index = 0;
}

void record_rdtsc_op(const char *op_name, const char *op_suffix, int print_depth, void *eip) {
  if (options::record_rdtsc) {
    if (rdtsc_index == (size_t)-1) {
      pthread_spin_init(&rdtsc_lock, 0);
      //printf("At exit...\n");
      atexit(process_rdtsc_log);
      rdtsc_log.reserve (max_v_size);
      rdtsc_index = 0;
    }

    struct sync_op_entry *entry = new struct sync_op_entry;
    entry->tid = (unsigned)pthread_self();

    entry->op = op_name;
    entry->op_suffix = op_suffix;
    entry->op_print_depth = (unsigned)print_depth;
    entry->eip = eip;

    pthread_spin_lock(&rdtsc_lock);
    //assert(rdtsc_index < max_v_size && "Please pre-allocated a bigger vector size for rdtsc log.");
    if (rdtsc_index >= max_v_size) {
      process_rdtsc_log();
    }
    entry->clock = rdtsc(); // Put this into critical section, so the log is sorted.
    rdtsc_log[rdtsc_index] = entry;
    rdtsc_index++;
    pthread_spin_unlock(&rdtsc_lock);
  }
  //fprintf(stderr, "%u : %s %s %llu\n", (unsigned)pthread_self(), op_name, op_suffix, rdtsc());
}<|endoftext|>


<commit_before>/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's 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.
//
//   * The name of the copyright holders may not 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 Intel Corporation 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.
//
//M*/

#include "test_precomp.hpp"
#include "opencv2/highgui/highgui.hpp"

using namespace cv;

#ifdef HAVE_FFMPEG

using namespace std;

class CV_FFmpegWriteBigVideoTest : public cvtest::BaseTest
{
public:
    void run(int)
    {
        const int img_r = 4096;
        const int img_c = 4096;
        const double fps0 = 15;
        const double time_sec = 1;

        const int tags[] = {
            0,
            //CV_FOURCC('D', 'I', 'V', '3'),
            //CV_FOURCC('D', 'I', 'V', 'X'),
            CV_FOURCC('D', 'X', '5', '0'),
            CV_FOURCC('F', 'L', 'V', '1'),
            CV_FOURCC('H', '2', '6', '1'),
            CV_FOURCC('H', '2', '6', '3'),
            CV_FOURCC('I', '4', '2', '0'),
            //CV_FOURCC('j', 'p', 'e', 'g'),
            CV_FOURCC('M', 'J', 'P', 'G'),
            CV_FOURCC('m', 'p', '4', 'v'),
            CV_FOURCC('M', 'P', 'E', 'G'),
            //CV_FOURCC('W', 'M', 'V', '1'),
            //CV_FOURCC('W', 'M', 'V', '2'),
            CV_FOURCC('X', 'V', 'I', 'D'),
            //CV_FOURCC('Y', 'U', 'Y', '2'),
        };

        const size_t n = sizeof(tags)/sizeof(tags[0]);

        bool created = false;

        for (size_t j = 0; j < n; ++j)
        {
            int tag = tags[j];
            stringstream s;
            s << tag;

            const string filename = "output_"+s.str()+".avi";

            try
            {
                double fps = fps0;
                Size frame_s = Size(img_c, img_r);

                if( tag == CV_FOURCC('H', '2', '6', '1') )
                    frame_s = Size(352, 288);
                else if( tag == CV_FOURCC('H', '2', '6', '3') )
                    frame_s = Size(704, 576);
                /*else if( tag == CV_FOURCC('M', 'J', 'P', 'G') ||
                         tag == CV_FOURCC('j', 'p', 'e', 'g') )
                    frame_s = Size(1920, 1080);*/

                if( tag == CV_FOURCC('M', 'P', 'E', 'G') )
                {
                    frame_s = Size(720, 576);
                    fps = 25;
                }

                VideoWriter writer(filename, tag, fps, frame_s);

                if (writer.isOpened() == false)
                {
                    ts->printf(ts->LOG, "\n\nFile name: %s\n", filename.c_str());
                    ts->printf(ts->LOG, "Codec id: %d   Codec tag: %c%c%c%c\n", j,
                               tag & 255, (tag >> 8) & 255, (tag >> 16) & 255, (tag >> 24) & 255);
                    ts->printf(ts->LOG, "Error: cannot create video file.");
                    ts->set_failed_test_info(ts->FAIL_INVALID_OUTPUT);
                }
                else
                {
                    Mat img(frame_s, CV_8UC3, Scalar::all(0));
                    const int coeff = cvRound(min(frame_s.width, frame_s.height)/(fps0 * time_sec));

                    for (int i = 0 ; i < static_cast<int>(fps * time_sec); i++ )
                    {
                        //circle(img, Point2i(img_c / 2, img_r / 2), min(img_r, img_c) / 2 * (i + 1), Scalar(255, 0, 0, 0), 2);
                        rectangle(img, Point2i(coeff * i, coeff * i), Point2i(coeff * (i + 1), coeff * (i + 1)),
                                  Scalar::all(255 * (1.0 - static_cast<double>(i) / (fps * time_sec * 2) )), -1);
                        writer << img;
                    }

                    if (!created) created = true;
                    else remove(filename.c_str());
                }
            }
            catch(...)
            {
                ts->set_failed_test_info(ts->FAIL_INVALID_OUTPUT);
            }
            ts->set_failed_test_info(cvtest::TS::OK);
        }
    }
};

TEST(Highgui_Video, ffmpeg_writebig) { CV_FFmpegWriteBigVideoTest test; test.safe_run(); }

class CV_FFmpegReadImageTest : public cvtest::BaseTest
{
public:
    void run(int)
    {
        try
        {
            string filename = ts->get_data_path() + "../cv/features2d/tsukuba.png";
            VideoCapture cap(filename);
            Mat img0 = imread(filename, 1);
            Mat img, img_next;
            cap >> img;
            cap >> img_next;

            CV_Assert( !img0.empty() && !img.empty() && img_next.empty() );

            double diff = norm(img0, img, CV_C);
            CV_Assert( diff == 0 );
        }
        catch(...)
        {
            ts->set_failed_test_info(ts->FAIL_INVALID_OUTPUT);
        }
        ts->set_failed_test_info(cvtest::TS::OK);
    }
};

TEST(Highgui_Video, ffmpeg_image) { CV_FFmpegReadImageTest test; test.safe_run(); }

#endif

#if defined(HAVE_FFMPEG)

//////////////////////////////// Parallel VideoWriters and VideoCaptures ////////////////////////////////////

class CreateVideoWriterInvoker :
    public ParallelLoopBody
{
public:
    const static Size FrameSize;
    static std::string TmpDirectory;

    CreateVideoWriterInvoker(std::vector<VideoWriter*>& _writers, std::vector<std::string>& _files) :
        ParallelLoopBody(), writers(&_writers), files(&_files)
    {
    }

    virtual void operator() (const Range& range) const
    {
        for (int i = range.start; i != range.end; ++i)
        {
            std::ostringstream stream;
            stream << i << ".avi";
            std::string fileName = tempfile(stream.str().c_str());

            files->operator[](i) = fileName;
            writers->operator[](i) = new VideoWriter(fileName, CV_FOURCC('X','V','I','D'), 25.0f, FrameSize);

            CV_Assert(writers->operator[](i)->isOpened());
        }
    }

private:
    std::vector<VideoWriter*>* writers;
    std::vector<std::string>* files;
};

std::string CreateVideoWriterInvoker::TmpDirectory;
const Size CreateVideoWriterInvoker::FrameSize(1020, 900);

class WriteVideo_Invoker :
    public ParallelLoopBody
{
public:
    enum { FrameCount = 300 };

    static const Scalar ObjectColor;
    static const Point Center;

    WriteVideo_Invoker(const std::vector<VideoWriter*>& _writers) :
        ParallelLoopBody(), writers(&_writers)
    {
    }

    static void GenerateFrame(Mat& frame, unsigned int i)
    {
        frame = Scalar::all(i % 255);

        std::string text = to_string(i);
        putText(frame, text, Point(50, Center.y), FONT_HERSHEY_SIMPLEX, 5.0, ObjectColor, 5, CV_AA);
        circle(frame, Center, i + 2, ObjectColor, 2, CV_AA);
    }

    virtual void operator() (const Range& range) const
    {
        for (int j = range.start; j < range.end; ++j)
        {
            VideoWriter* writer = writers->operator[](j);
            CV_Assert(writer != NULL);
            CV_Assert(writer->isOpened());

            Mat frame(CreateVideoWriterInvoker::FrameSize, CV_8UC3);
            for (unsigned int i = 0; i < FrameCount; ++i)
            {
                GenerateFrame(frame, i);
                writer->operator<< (frame);
            }
        }
    }

protected:
    static std::string to_string(unsigned int i)
    {
        std::stringstream stream(std::ios::out);
        stream << "frame #" << i;
        return stream.str();
    }

private:
    const std::vector<VideoWriter*>* writers;
};

const Scalar WriteVideo_Invoker::ObjectColor(Scalar::all(0));
const Point WriteVideo_Invoker::Center(CreateVideoWriterInvoker::FrameSize.height / 2,
    CreateVideoWriterInvoker::FrameSize.width / 2);

class CreateVideoCaptureInvoker :
    public ParallelLoopBody
{
public:
    CreateVideoCaptureInvoker(std::vector<VideoCapture*>& _readers, const std::vector<std::string>& _files) :
        ParallelLoopBody(), readers(&_readers), files(&_files)
    {
    }

    virtual void operator() (const Range& range) const
    {
        for (int i = range.start; i != range.end; ++i)
        {
            readers->operator[](i) = new VideoCapture(files->operator[](i));
            CV_Assert(readers->operator[](i)->isOpened());
        }
    }
private:
    std::vector<VideoCapture*>* readers;
    const std::vector<std::string>* files;
};

class ReadImageAndTest :
    public ParallelLoopBody
{
public:
    ReadImageAndTest(const std::vector<VideoCapture*>& _readers, cvtest::TS* _ts) :
        ParallelLoopBody(), readers(&_readers), ts(_ts)
    {
    }

    virtual void operator() (const Range& range) const
    {
        for (int j = range.start; j < range.end; ++j)
        {
            VideoCapture* capture = readers->operator[](j);
            CV_Assert(capture != NULL);
            CV_Assert(capture->isOpened());

            const static double eps = 23.0;
            unsigned int frameCount = static_cast<unsigned int>(capture->get(CV_CAP_PROP_FRAME_COUNT));
            CV_Assert(frameCount == WriteVideo_Invoker::FrameCount);
            Mat reference(CreateVideoWriterInvoker::FrameSize, CV_8UC3);

            for (unsigned int i = 0; i < frameCount && next; ++i)
            {
                Mat actual;
                (*capture) >> actual;

                WriteVideo_Invoker::GenerateFrame(reference, i);

                EXPECT_EQ(reference.cols, actual.cols);
                EXPECT_EQ(reference.rows, actual.rows);
                EXPECT_EQ(reference.depth(), actual.depth());
                EXPECT_EQ(reference.channels(), actual.channels());

                double psnr = PSNR(actual, reference);
                if (psnr < eps)
                {
    #define SUM cvtest::TS::SUMMARY
                    ts->printf(SUM, "\nPSNR: %lf\n", psnr);
                    ts->printf(SUM, "Video #: %d\n", range.start);
                    ts->printf(SUM, "Frame #: %d\n", i);
    #undef SUM
                    ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
                    ts->set_gtest_status();

                    Mat diff;
                    absdiff(actual, reference, diff);

                    EXPECT_EQ(countNonZero(diff.reshape(1) > 1), 0);

                    next = false;
                }
            }
        }
    }

    static bool next;

private:
    const std::vector<VideoCapture*>* readers;
    cvtest::TS* ts;
};

bool ReadImageAndTest::next;

TEST(Highgui_Video_parallel_writers_and_readers, accuracy)
{
    const unsigned int threadsCount = 4;
    cvtest::TS* ts = cvtest::TS::ptr();

    // creating VideoWriters
    std::vector<VideoWriter*> writers(threadsCount);
    Range range(0, threadsCount);
    std::vector<std::string> files(threadsCount);
    CreateVideoWriterInvoker invoker1(writers, files);
    parallel_for_(range, invoker1);

    // write a video
    parallel_for_(range, WriteVideo_Invoker(writers));

    // deleting the writers
    for (std::vector<VideoWriter*>::iterator i = writers.begin(), end = writers.end(); i != end; ++i)
        delete *i;
    writers.clear();

    std::vector<VideoCapture*> readers(threadsCount);
    CreateVideoCaptureInvoker invoker2(readers, files);
    parallel_for_(range, invoker2);

    ReadImageAndTest::next = true;

    parallel_for_(range, ReadImageAndTest(readers, ts));

    // deleting tmp video files
    for (std::vector<std::string>::const_iterator i = files.begin(), end = files.end(); i != end; ++i)
    {
        int code = remove(i->c_str());
        if (code == 1)
            std::cerr << "Couldn't delete " << *i << std::endl;
    }
}

#endif
<commit_msg>Replaced the image used in the Highgui_Video.ffmpeg_image test.<commit_after>/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's 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.
//
//   * The name of the copyright holders may not 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 Intel Corporation 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.
//
//M*/

#include "test_precomp.hpp"
#include "opencv2/highgui/highgui.hpp"

using namespace cv;

#ifdef HAVE_FFMPEG

using namespace std;

class CV_FFmpegWriteBigVideoTest : public cvtest::BaseTest
{
public:
    void run(int)
    {
        const int img_r = 4096;
        const int img_c = 4096;
        const double fps0 = 15;
        const double time_sec = 1;

        const int tags[] = {
            0,
            //CV_FOURCC('D', 'I', 'V', '3'),
            //CV_FOURCC('D', 'I', 'V', 'X'),
            CV_FOURCC('D', 'X', '5', '0'),
            CV_FOURCC('F', 'L', 'V', '1'),
            CV_FOURCC('H', '2', '6', '1'),
            CV_FOURCC('H', '2', '6', '3'),
            CV_FOURCC('I', '4', '2', '0'),
            //CV_FOURCC('j', 'p', 'e', 'g'),
            CV_FOURCC('M', 'J', 'P', 'G'),
            CV_FOURCC('m', 'p', '4', 'v'),
            CV_FOURCC('M', 'P', 'E', 'G'),
            //CV_FOURCC('W', 'M', 'V', '1'),
            //CV_FOURCC('W', 'M', 'V', '2'),
            CV_FOURCC('X', 'V', 'I', 'D'),
            //CV_FOURCC('Y', 'U', 'Y', '2'),
        };

        const size_t n = sizeof(tags)/sizeof(tags[0]);

        bool created = false;

        for (size_t j = 0; j < n; ++j)
        {
            int tag = tags[j];
            stringstream s;
            s << tag;

            const string filename = "output_"+s.str()+".avi";

            try
            {
                double fps = fps0;
                Size frame_s = Size(img_c, img_r);

                if( tag == CV_FOURCC('H', '2', '6', '1') )
                    frame_s = Size(352, 288);
                else if( tag == CV_FOURCC('H', '2', '6', '3') )
                    frame_s = Size(704, 576);
                /*else if( tag == CV_FOURCC('M', 'J', 'P', 'G') ||
                         tag == CV_FOURCC('j', 'p', 'e', 'g') )
                    frame_s = Size(1920, 1080);*/

                if( tag == CV_FOURCC('M', 'P', 'E', 'G') )
                {
                    frame_s = Size(720, 576);
                    fps = 25;
                }

                VideoWriter writer(filename, tag, fps, frame_s);

                if (writer.isOpened() == false)
                {
                    ts->printf(ts->LOG, "\n\nFile name: %s\n", filename.c_str());
                    ts->printf(ts->LOG, "Codec id: %d   Codec tag: %c%c%c%c\n", j,
                               tag & 255, (tag >> 8) & 255, (tag >> 16) & 255, (tag >> 24) & 255);
                    ts->printf(ts->LOG, "Error: cannot create video file.");
                    ts->set_failed_test_info(ts->FAIL_INVALID_OUTPUT);
                }
                else
                {
                    Mat img(frame_s, CV_8UC3, Scalar::all(0));
                    const int coeff = cvRound(min(frame_s.width, frame_s.height)/(fps0 * time_sec));

                    for (int i = 0 ; i < static_cast<int>(fps * time_sec); i++ )
                    {
                        //circle(img, Point2i(img_c / 2, img_r / 2), min(img_r, img_c) / 2 * (i + 1), Scalar(255, 0, 0, 0), 2);
                        rectangle(img, Point2i(coeff * i, coeff * i), Point2i(coeff * (i + 1), coeff * (i + 1)),
                                  Scalar::all(255 * (1.0 - static_cast<double>(i) / (fps * time_sec * 2) )), -1);
                        writer << img;
                    }

                    if (!created) created = true;
                    else remove(filename.c_str());
                }
            }
            catch(...)
            {
                ts->set_failed_test_info(ts->FAIL_INVALID_OUTPUT);
            }
            ts->set_failed_test_info(cvtest::TS::OK);
        }
    }
};

TEST(Highgui_Video, ffmpeg_writebig) { CV_FFmpegWriteBigVideoTest test; test.safe_run(); }

class CV_FFmpegReadImageTest : public cvtest::BaseTest
{
public:
    void run(int)
    {
        try
        {
            string filename = ts->get_data_path() + "readwrite/ordinary.bmp";
            VideoCapture cap(filename);
            Mat img0 = imread(filename, 1);
            Mat img, img_next;
            cap >> img;
            cap >> img_next;

            CV_Assert( !img0.empty() && !img.empty() && img_next.empty() );

            double diff = norm(img0, img, CV_C);
            CV_Assert( diff == 0 );
        }
        catch(...)
        {
            ts->set_failed_test_info(ts->FAIL_INVALID_OUTPUT);
        }
        ts->set_failed_test_info(cvtest::TS::OK);
    }
};

TEST(Highgui_Video, ffmpeg_image) { CV_FFmpegReadImageTest test; test.safe_run(); }

#endif

#if defined(HAVE_FFMPEG)

//////////////////////////////// Parallel VideoWriters and VideoCaptures ////////////////////////////////////

class CreateVideoWriterInvoker :
    public ParallelLoopBody
{
public:
    const static Size FrameSize;
    static std::string TmpDirectory;

    CreateVideoWriterInvoker(std::vector<VideoWriter*>& _writers, std::vector<std::string>& _files) :
        ParallelLoopBody(), writers(&_writers), files(&_files)
    {
    }

    virtual void operator() (const Range& range) const
    {
        for (int i = range.start; i != range.end; ++i)
        {
            std::ostringstream stream;
            stream << i << ".avi";
            std::string fileName = tempfile(stream.str().c_str());

            files->operator[](i) = fileName;
            writers->operator[](i) = new VideoWriter(fileName, CV_FOURCC('X','V','I','D'), 25.0f, FrameSize);

            CV_Assert(writers->operator[](i)->isOpened());
        }
    }

private:
    std::vector<VideoWriter*>* writers;
    std::vector<std::string>* files;
};

std::string CreateVideoWriterInvoker::TmpDirectory;
const Size CreateVideoWriterInvoker::FrameSize(1020, 900);

class WriteVideo_Invoker :
    public ParallelLoopBody
{
public:
    enum { FrameCount = 300 };

    static const Scalar ObjectColor;
    static const Point Center;

    WriteVideo_Invoker(const std::vector<VideoWriter*>& _writers) :
        ParallelLoopBody(), writers(&_writers)
    {
    }

    static void GenerateFrame(Mat& frame, unsigned int i)
    {
        frame = Scalar::all(i % 255);

        std::string text = to_string(i);
        putText(frame, text, Point(50, Center.y), FONT_HERSHEY_SIMPLEX, 5.0, ObjectColor, 5, CV_AA);
        circle(frame, Center, i + 2, ObjectColor, 2, CV_AA);
    }

    virtual void operator() (const Range& range) const
    {
        for (int j = range.start; j < range.end; ++j)
        {
            VideoWriter* writer = writers->operator[](j);
            CV_Assert(writer != NULL);
            CV_Assert(writer->isOpened());

            Mat frame(CreateVideoWriterInvoker::FrameSize, CV_8UC3);
            for (unsigned int i = 0; i < FrameCount; ++i)
            {
                GenerateFrame(frame, i);
                writer->operator<< (frame);
            }
        }
    }

protected:
    static std::string to_string(unsigned int i)
    {
        std::stringstream stream(std::ios::out);
        stream << "frame #" << i;
        return stream.str();
    }

private:
    const std::vector<VideoWriter*>* writers;
};

const Scalar WriteVideo_Invoker::ObjectColor(Scalar::all(0));
const Point WriteVideo_Invoker::Center(CreateVideoWriterInvoker::FrameSize.height / 2,
    CreateVideoWriterInvoker::FrameSize.width / 2);

class CreateVideoCaptureInvoker :
    public ParallelLoopBody
{
public:
    CreateVideoCaptureInvoker(std::vector<VideoCapture*>& _readers, const std::vector<std::string>& _files) :
        ParallelLoopBody(), readers(&_readers), files(&_files)
    {
    }

    virtual void operator() (const Range& range) const
    {
        for (int i = range.start; i != range.end; ++i)
        {
            readers->operator[](i) = new VideoCapture(files->operator[](i));
            CV_Assert(readers->operator[](i)->isOpened());
        }
    }
private:
    std::vector<VideoCapture*>* readers;
    const std::vector<std::string>* files;
};

class ReadImageAndTest :
    public ParallelLoopBody
{
public:
    ReadImageAndTest(const std::vector<VideoCapture*>& _readers, cvtest::TS* _ts) :
        ParallelLoopBody(), readers(&_readers), ts(_ts)
    {
    }

    virtual void operator() (const Range& range) const
    {
        for (int j = range.start; j < range.end; ++j)
        {
            VideoCapture* capture = readers->operator[](j);
            CV_Assert(capture != NULL);
            CV_Assert(capture->isOpened());

            const static double eps = 23.0;
            unsigned int frameCount = static_cast<unsigned int>(capture->get(CV_CAP_PROP_FRAME_COUNT));
            CV_Assert(frameCount == WriteVideo_Invoker::FrameCount);
            Mat reference(CreateVideoWriterInvoker::FrameSize, CV_8UC3);

            for (unsigned int i = 0; i < frameCount && next; ++i)
            {
                Mat actual;
                (*capture) >> actual;

                WriteVideo_Invoker::GenerateFrame(reference, i);

                EXPECT_EQ(reference.cols, actual.cols);
                EXPECT_EQ(reference.rows, actual.rows);
                EXPECT_EQ(reference.depth(), actual.depth());
                EXPECT_EQ(reference.channels(), actual.channels());

                double psnr = PSNR(actual, reference);
                if (psnr < eps)
                {
    #define SUM cvtest::TS::SUMMARY
                    ts->printf(SUM, "\nPSNR: %lf\n", psnr);
                    ts->printf(SUM, "Video #: %d\n", range.start);
                    ts->printf(SUM, "Frame #: %d\n", i);
    #undef SUM
                    ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
                    ts->set_gtest_status();

                    Mat diff;
                    absdiff(actual, reference, diff);

                    EXPECT_EQ(countNonZero(diff.reshape(1) > 1), 0);

                    next = false;
                }
            }
        }
    }

    static bool next;

private:
    const std::vector<VideoCapture*>* readers;
    cvtest::TS* ts;
};

bool ReadImageAndTest::next;

TEST(Highgui_Video_parallel_writers_and_readers, accuracy)
{
    const unsigned int threadsCount = 4;
    cvtest::TS* ts = cvtest::TS::ptr();

    // creating VideoWriters
    std::vector<VideoWriter*> writers(threadsCount);
    Range range(0, threadsCount);
    std::vector<std::string> files(threadsCount);
    CreateVideoWriterInvoker invoker1(writers, files);
    parallel_for_(range, invoker1);

    // write a video
    parallel_for_(range, WriteVideo_Invoker(writers));

    // deleting the writers
    for (std::vector<VideoWriter*>::iterator i = writers.begin(), end = writers.end(); i != end; ++i)
        delete *i;
    writers.clear();

    std::vector<VideoCapture*> readers(threadsCount);
    CreateVideoCaptureInvoker invoker2(readers, files);
    parallel_for_(range, invoker2);

    ReadImageAndTest::next = true;

    parallel_for_(range, ReadImageAndTest(readers, ts));

    // deleting tmp video files
    for (std::vector<std::string>::const_iterator i = files.begin(), end = files.end(); i != end; ++i)
    {
        int code = remove(i->c_str());
        if (code == 1)
            std::cerr << "Couldn't delete " << *i << std::endl;
    }
}

#endif
<|endoftext|>


<commit_before>#include "BallBounce.hpp"

#include <cmath>

#include <Constants.hpp>

#include "vision/util/VisionFilterConfig.hpp"

REGISTER_CONFIGURABLE(BallBounce)

ConfigDouble* BallBounce::robot_body_lin_dampen;
ConfigDouble* BallBounce::robot_mouth_lin_dampen;
ConfigDouble* BallBounce::robot_body_angle_dampen;
ConfigDouble* BallBounce::robot_mouth_angle_dampen;

// Note 0 case returns -1 instead of 0
inline int sign(float val) { return (0.0 < val) - (val <= 0.0); }

void BallBounce::createConfiguration(Configuration* cfg) {
    robot_body_lin_dampen = new ConfigDouble(cfg, "VisionFilter/Bounce/robot_body_lin_dampen", .9);
    robot_mouth_lin_dampen = new ConfigDouble(cfg, "VisionFilter/Bounce/robot_mouth_lin_dampen", .3);
    robot_body_angle_dampen = new ConfigDouble(cfg, "VisionFilter/Bounce/robot_body_angle_dampen", 0);
    robot_mouth_angle_dampen = new ConfigDouble(cfg, "VisionFilter/Bounce/robot_mouth_angle_dampen", 0);
}

bool BallBounce::CalcBallBounce(KalmanBall& ball,
                                std::vector<WorldRobot>& yellowRobots,
                                std::vector<WorldRobot>& blueRobots,
                                Geometry2d::Point& outNewVel) {
    std::vector<WorldRobot> robots = yellowRobots;
    robots.insert(robots.end(), blueRobots.begin(), blueRobots.end());

    for (WorldRobot& robot : robots) {
        if (!robot.getIsValid()) {
            continue;
        }

        // Make sure ball is intersecting next frame
        if (!BallInRobot(ball, robot)) {
            continue;
        }

        std::vector<Geometry2d::Point> intersectPts = PossibleBallIntersectionPts(ball, robot);

        // Doesn't intersect
        if (intersectPts.size() == 0) {
            continue;
        }

        // Tangent to robot, assuming no interaction
        if (intersectPts.size() == 1) {
            continue;
        }

        // intersectPts.size() == 2

        //                        _____
        //                       /     \
        //                      | Robot |
        //                       \_____/
        //                          B
        //                         /|\
        //                        / | \
        //                       /  |  \
        //                      /   |   \
        //                     /    D    \
        //                    A           C
        // Ball moves from A->B
        // Bounces off the robot
        // Moves from B->C
        // Line B<->D is the line of reflection
        //
        //
        // Line B<->D goes from the center of the robot through the point of intersection between the robot and the ball
        // Since the robot is round (with a flat mouth), any time it hits the robot, we can assume that the it will reflect as if
        //   it hit a flat surface

        // We want to make sure that the ball is never inside the robot when doing this math, so we go back in time
        // This doesn't affect the results since we are just doing vectors
        Geometry2d::Point ballPosSafePt = ball.getPos() - ball.getVel().normalized();

        // Find the closest point
        // AKA: the first point the ball will hit off the robot
        // May actually be slightly off because we add the ball radius to the calculation circle
        // Does not account for the mouth just yet
        Geometry2d::Point closestIntersectPt = intersectPts.at(0);
        if ((ballPosSafePt - closestIntersectPt).magsq() > (ballPosSafePt - intersectPts.at(1)).magsq()) {
            closestIntersectPt = intersectPts.at(1);
        }

        // This is super easy, just check intersection with the mouth line
        // If the first point in the circle shell intersect is in the mouth angle range
        // Use the line intersect point instead
        // If there is no line intersect point (within that angle range)
        // then the ball is moving across the mouth of the robot

        Geometry2d::Line intersectLine = Geometry2d::Line(intersectPts.at(0), intersectPts.at(1));
        Geometry2d::Point mouthHalfUnitVec = Geometry2d::Point(0, 1).rotate(robot.getTheta());
        Geometry2d::Point mouthCenterPos = Geometry2d::Point(Robot_MouthRadius, 0).rotate(robot.getTheta()) + robot.getPos();
        Geometry2d::Line mouthLine = Geometry2d::Line(mouthCenterPos + mouthHalfUnitVec,
                                                      mouthCenterPos - mouthHalfUnitVec);

        Geometry2d::Point mouthIntersect;
        bool intersects = intersectLine.intersects(mouthLine, &mouthIntersect);

        // The mouth is a chord across the circle.
        // We have the distance of the chord to the center of the circle
        // We also have the radius of the robot
        const double chordHalfLength = pow(Robot_MouthWidth / 2.0, 2);
        bool didHitMouth = false;

        // If the line intersect is inside the mouth chord
        if (intersects && (mouthIntersect - mouthCenterPos).magsq() < chordHalfLength) {
            closestIntersectPt = mouthIntersect;
            didHitMouth = true;
        }

        //                          R
        //                        _____
        //                          B
        //                         /|\
        //                        / | \
        //                       /  |  \
        //                      /   |   \
        //                     /    |    \
        //                    A-----D-----C

        // B->A
        Geometry2d::Point intersectPtBallVector = ballPosSafePt - closestIntersectPt;
        // B->D (Officially R->B, but B->D makes more sense visually)
        Geometry2d::Point robotIntersectPtVector = closestIntersectPt - robot.getPos();
        Geometry2d::Point robotIntersectPtUnitVector = robotIntersectPtVector.normalized();

        // If it hit the mouth, the reflection line is pointing straight out
        if (didHitMouth) {
            robotIntersectPtVector = Geometry2d::Point(1, 0).rotate(robot.getTheta());
            robotIntersectPtUnitVector = robotIntersectPtVector;
        }

        // Project B->A vector onto B->D
        // This is so we can get D->A and D->C later
        double projectionMag = intersectPtBallVector.normalized().dot(robotIntersectPtUnitVector);
        Geometry2d::Point projection = projectionMag * robotIntersectPtUnitVector;

        // A->D, which is the same as D->C
        Geometry2d::Point projectionDiff = projection - intersectPtBallVector;

        Geometry2d::Point intersectPtReflectionVector = projection + projectionDiff;
        Geometry2d::Point intersectPtReflectionUnitVector = intersectPtReflectionVector.normalized();

        // Scale magnitude of velocity by a percentage
        double dampenLinCoeff   = *robot_body_lin_dampen;
        double dampenAngleCoeff = *robot_body_angle_dampen;

        if (didHitMouth) {
            dampenLinCoeff   = *robot_mouth_lin_dampen;
            dampenAngleCoeff = *robot_mouth_angle_dampen;
        }

        //                   C------D
        //                    \     |
        //                F    \    |
        //                  \   \   |
        //                    \  \  |
        //                      \ \ |
        //                        \\|              | y+
        //                          B              |
        //                                         |____ x+
        //
        // Note: Letters correspond to ones above
        //
        // We are trying to increase the angle CBD more when angle CBD is large
        // When angle CBD is 0, we want to keep the same angle

        // We dont want any extra rotation when angle CBD is 0 degrees or 90 degrees
        // Just to simplify implementation, I'm going to do a triangle
        //
        // df*45  -              /  \
        //                    /        \
        //                 /              \
        //  0     -     /                    \
        //
        //             |          |           |
        //            0 deg    45 deg       90 deg
        //
        // df is angle dampen factor
        // y axis represents max angle dampen in terms of degrees
        // x axis is the angle CBD

        // Angle CBD
        double halfReflectAngle = intersectPtReflectionUnitVector.angleBetween(robotIntersectPtUnitVector);
        double direction = robotIntersectPtUnitVector.cross(intersectPtReflectionUnitVector);
        double extraRotationAngle = -sign(direction)*halfReflectAngle;
        extraRotationAngle = std::min(extraRotationAngle, M_PI_2 - extraRotationAngle)*dampenAngleCoeff;

        intersectPtReflectionUnitVector = intersectPtReflectionUnitVector.rotate(extraRotationAngle);

        outNewVel = intersectPtReflectionUnitVector * ball.getVel().mag();

        return true;
    }

    return false;
}

bool BallBounce::BallInRobot(KalmanBall& ball, WorldRobot& robot) {
    Geometry2d::Point nextPos = ball.getPos() + ball.getVel() * *VisionFilterConfig::vision_loop_dt;

    return (robot.getPos() - nextPos).mag() < Robot_Radius + Ball_Radius;
}

std::vector<Geometry2d::Point> BallBounce::PossibleBallIntersectionPts(
        KalmanBall& ball, WorldRobot& robot) {
    // http://mathworld.wolfram.com/Circle-LineIntersection.html

    std::vector<Geometry2d::Point> out;

    // ballPos is the ball pos in robot centered coordinates
    // ballVel is the ball vel vector in robot centered coordinates
    // We really just want a line in the direction of ball vel motion
    Geometry2d::Point ballPos = ball.getPos() - robot.getPos();
    Geometry2d::Point ballVel = ball.getPos() + ball.getVel() - robot.getPos();

    // Magnitude of the line
    Geometry2d::Point d = ballVel - ballPos;
    double dr = d.mag();
    // Determinant
    double D = ballPos.x()*ballVel.y() - ballPos.y()*ballVel.x();
    // Assume that two spheres intersection, is similar to the addition of their radius and a point
    double r = Robot_Radius + Ball_Radius;

    // If the ball really isn't moving, just assume no intersection
    // since the math will go to inf
    if (abs(dr) < 1e-5) {
        return out;
    }

    double x1 = D*d.y() + sign(d.y()) * d.x() * sqrt(r*r*dr*dr - D*D);
    x1 /= dr*dr;
    x1 += robot.getPos().x();

    double y1 = -D*d.x() + abs(d.y()) * d.x() * sqrt(r*r*dr*dr - D*D);
    y1 /= dr*dr;
    y1 += robot.getPos().y();

    double x2 = D*d.y() - sign(d.y()) * d.x() * sqrt(r*r*dr*dr - D*D);
    x2 /= dr*dr;
    x2 += robot.getPos().x();

    double y2 = -D*d.x() - abs(d.y()) * sqrt(r*r*dr*dr - D*D);
    y2 /= dr*dr;
    y2 += robot.getPos().y();

    Geometry2d::Point pt1 = Geometry2d::Point(x1, y1);
    Geometry2d::Point pt2 = Geometry2d::Point(x2, y2);

    double delta = r*r*dr*dr - D*D;

    // No intresection
    if (delta < 0) {
        return out;
    }
    // Tagent
    else if (delta == 0) {
        out.push_back(pt1);
        return out;
    }
    // Two intersection points
    else {
        out.push_back(pt1);
        out.push_back(pt2);
        return out;
    }
}<commit_msg>Fixed rounding errors with modified sign function<commit_after>#include "BallBounce.hpp"

#include <cmath>

#include <Constants.hpp>

#include "vision/util/VisionFilterConfig.hpp"

REGISTER_CONFIGURABLE(BallBounce)

ConfigDouble* BallBounce::robot_body_lin_dampen;
ConfigDouble* BallBounce::robot_mouth_lin_dampen;
ConfigDouble* BallBounce::robot_body_angle_dampen;
ConfigDouble* BallBounce::robot_mouth_angle_dampen;

// Note 0 case returns -1 instead of 0
// Forced to check with small epislon since we actually care about 0 being represented
// correctly as -1
inline int sign(double val) { return (1.0e-10 < val) - (val <= 1.0e-10); }

void BallBounce::createConfiguration(Configuration* cfg) {
    robot_body_lin_dampen = new ConfigDouble(cfg, "VisionFilter/Bounce/robot_body_lin_dampen", .9);
    robot_mouth_lin_dampen = new ConfigDouble(cfg, "VisionFilter/Bounce/robot_mouth_lin_dampen", .3);
    robot_body_angle_dampen = new ConfigDouble(cfg, "VisionFilter/Bounce/robot_body_angle_dampen", 0);
    robot_mouth_angle_dampen = new ConfigDouble(cfg, "VisionFilter/Bounce/robot_mouth_angle_dampen", 0);
}

bool BallBounce::CalcBallBounce(KalmanBall& ball,
                                std::vector<WorldRobot>& yellowRobots,
                                std::vector<WorldRobot>& blueRobots,
                                Geometry2d::Point& outNewVel) {
    std::vector<WorldRobot> robots = yellowRobots;
    robots.insert(robots.end(), blueRobots.begin(), blueRobots.end());

    for (WorldRobot& robot : robots) {
        if (!robot.getIsValid()) {
            continue;
        }

        // Make sure ball is intersecting next frame
        if (!BallInRobot(ball, robot)) {
            continue;
        }

        std::vector<Geometry2d::Point> intersectPts = PossibleBallIntersectionPts(ball, robot);

        // Doesn't intersect
        if (intersectPts.size() == 0) {
            continue;
        }

        // Tangent to robot, assuming no interaction
        if (intersectPts.size() == 1) {
            continue;
        }

        // intersectPts.size() == 2

        //                        _____
        //                       /     \
        //                      | Robot |
        //                       \_____/
        //                          B
        //                         /|\
        //                        / | \
        //                       /  |  \
        //                      /   |   \
        //                     /    D    \
        //                    A           C
        // Ball moves from A->B
        // Bounces off the robot
        // Moves from B->C
        // Line B<->D is the line of reflection
        //
        //
        // Line B<->D goes from the center of the robot through the point of intersection between the robot and the ball
        // Since the robot is round (with a flat mouth), any time it hits the robot, we can assume that the it will reflect as if
        //   it hit a flat surface

        // We want to make sure that the ball is never inside the robot when doing this math, so we go back in time
        // This doesn't affect the results since we are just doing vectors
        Geometry2d::Point ballPosSafePt = ball.getPos() - ball.getVel().normalized();

        // Find the closest point
        // AKA: the first point the ball will hit off the robot
        // May actually be slightly off because we add the ball radius to the calculation circle
        // Does not account for the mouth just yet
        Geometry2d::Point closestIntersectPt = intersectPts.at(0);
        if ((ballPosSafePt - closestIntersectPt).magsq() > (ballPosSafePt - intersectPts.at(1)).magsq()) {
            closestIntersectPt = intersectPts.at(1);
        }

        // This is super easy, just check intersection with the mouth line
        // If the first point in the circle shell intersect is in the mouth angle range
        // Use the line intersect point instead
        // If there is no line intersect point (within that angle range)
        // then the ball is moving across the mouth of the robot

        Geometry2d::Line intersectLine = Geometry2d::Line(intersectPts.at(0), intersectPts.at(1));
        Geometry2d::Point mouthHalfUnitVec = Geometry2d::Point(0, 1).rotate(robot.getTheta());
        Geometry2d::Point mouthCenterPos = Geometry2d::Point(Robot_MouthRadius, 0).rotate(robot.getTheta()) + robot.getPos();
        Geometry2d::Line mouthLine = Geometry2d::Line(mouthCenterPos + mouthHalfUnitVec,
                                                      mouthCenterPos - mouthHalfUnitVec);

        Geometry2d::Point mouthIntersect;
        bool intersects = intersectLine.intersects(mouthLine, &mouthIntersect);

        // The mouth is a chord across the circle.
        // We have the distance of the chord to the center of the circle
        // We also have the radius of the robot
        const double chordHalfLength = pow(Robot_MouthWidth / 2.0, 2);
        bool didHitMouth = false;

        // If the line intersect is inside the mouth chord
        if (intersects && (mouthIntersect - mouthCenterPos).magsq() < chordHalfLength) {
            closestIntersectPt = mouthIntersect;
            didHitMouth = true;
        }

        //                          R
        //                        _____
        //                          B
        //                         /|\
        //                        / | \
        //                       /  |  \
        //                      /   |   \
        //                     /    |    \
        //                    A-----D-----C

        // B->A
        Geometry2d::Point intersectPtBallVector = ballPosSafePt - closestIntersectPt;
        // B->D (Officially R->B, but B->D makes more sense visually)
        Geometry2d::Point robotIntersectPtVector = closestIntersectPt - robot.getPos();
        Geometry2d::Point robotIntersectPtUnitVector = robotIntersectPtVector.normalized();

        // If it hit the mouth, the reflection line is pointing straight out
        if (didHitMouth) {
            robotIntersectPtVector = Geometry2d::Point(1, 0).rotate(robot.getTheta());
            robotIntersectPtUnitVector = robotIntersectPtVector;
        }

        // Project B->A vector onto B->D
        // This is so we can get D->A and D->C later
        double projectionMag = intersectPtBallVector.normalized().dot(robotIntersectPtUnitVector);
        Geometry2d::Point projection = projectionMag * robotIntersectPtUnitVector;

        // A->D, which is the same as D->C
        Geometry2d::Point projectionDiff = projection - intersectPtBallVector;

        Geometry2d::Point intersectPtReflectionVector = projection + projectionDiff;
        Geometry2d::Point intersectPtReflectionUnitVector = intersectPtReflectionVector.normalized();

        // Scale magnitude of velocity by a percentage
        double dampenLinCoeff   = *robot_body_lin_dampen;
        double dampenAngleCoeff = *robot_body_angle_dampen;

        if (didHitMouth) {
            dampenLinCoeff   = *robot_mouth_lin_dampen;
            dampenAngleCoeff = *robot_mouth_angle_dampen;
        }

        //                   C------D
        //                    \     |
        //                F    \    |
        //                  \   \   |
        //                    \  \  |
        //                      \ \ |
        //                        \\|              | y+
        //                          B              |
        //                                         |____ x+
        //
        // Note: Letters correspond to ones above
        //
        // We are trying to increase the angle CBD more when angle CBD is large
        // When angle CBD is 0, we want to keep the same angle

        // We dont want any extra rotation when angle CBD is 0 degrees or 90 degrees
        // Just to simplify implementation, I'm going to do a triangle
        //
        // df*45  -              /  \
        //                    /        \
        //                 /              \
        //  0     -     /                    \
        //
        //             |          |           |
        //            0 deg    45 deg       90 deg
        //
        // df is angle dampen factor
        // y axis represents max angle dampen in terms of degrees
        // x axis is the angle CBD

        // Angle CBD
        double halfReflectAngle = intersectPtReflectionUnitVector.angleBetween(robotIntersectPtUnitVector);
        double direction = robotIntersectPtUnitVector.cross(intersectPtReflectionUnitVector);
        double extraRotationAngle = -sign(direction)*halfReflectAngle;
        extraRotationAngle = std::min(extraRotationAngle, M_PI_2 - extraRotationAngle)*dampenAngleCoeff;

        intersectPtReflectionUnitVector = intersectPtReflectionUnitVector.rotate(extraRotationAngle);

        outNewVel = intersectPtReflectionUnitVector * ball.getVel().mag();

        return true;
    }

    return false;
}

bool BallBounce::BallInRobot(KalmanBall& ball, WorldRobot& robot) {
    Geometry2d::Point nextPos = ball.getPos() + ball.getVel() * *VisionFilterConfig::vision_loop_dt;

    return (robot.getPos() - nextPos).mag() < Robot_Radius + Ball_Radius;
}

std::vector<Geometry2d::Point> BallBounce::PossibleBallIntersectionPts(
        KalmanBall& ball, WorldRobot& robot) {
    // http://mathworld.wolfram.com/Circle-LineIntersection.html

    std::vector<Geometry2d::Point> out;

    // ballPos is the ball pos in robot centered coordinates
    // ballVel is the ball vel vector in robot centered coordinates
    // We really just want a line in the direction of ball vel motion
    Geometry2d::Point ballPos = ball.getPos() - robot.getPos();
    Geometry2d::Point ballVel = ball.getPos() + ball.getVel() - robot.getPos();

    // Magnitude of the line
    Geometry2d::Point d = ballVel - ballPos;
    double dr = d.mag();
    // Determinant
    double D = ballPos.x()*ballVel.y() - ballPos.y()*ballVel.x();
    // Assume that two spheres intersection, is similar to the addition of their radius and a point
    double r = Robot_Radius + Ball_Radius;

    // If the ball really isn't moving, just assume no intersection
    // since the math will go to inf
    if (abs(dr) < 1e-5) {
        return out;
    }

    double x1 = D*d.y() + sign(d.y()) * d.x() * sqrt(r*r*dr*dr - D*D);
    x1 /= dr*dr;
    x1 += robot.getPos().x();

    double y1 = -D*d.x() + abs(d.y()) * d.x() * sqrt(r*r*dr*dr - D*D);
    y1 /= dr*dr;
    y1 += robot.getPos().y();

    double x2 = D*d.y() - sign(d.y()) * d.x() * sqrt(r*r*dr*dr - D*D);
    x2 /= dr*dr;
    x2 += robot.getPos().x();

    double y2 = -D*d.x() - abs(d.y()) * sqrt(r*r*dr*dr - D*D);
    y2 /= dr*dr;
    y2 += robot.getPos().y();

    Geometry2d::Point pt1 = Geometry2d::Point(x1, y1);
    Geometry2d::Point pt2 = Geometry2d::Point(x2, y2);

    double delta = r*r*dr*dr - D*D;

    // No intresection
    if (delta < 0) {
        return out;
    }
    // Tagent
    else if (delta == 0) {
        out.push_back(pt1);
        return out;
    }
    // Two intersection points
    else {
        out.push_back(pt1);
        out.push_back(pt2);
        return out;
    }
}<|endoftext|>


<commit_before>/**
 * @file
 *  Zenderer/CoreGraphics/Texture.hpp - A low-level texture asset wrapper
 *  around OpenGL textures.
 *
 * @author      George Kudrayvtsev (halcyon)
 * @version     1.0
 * @copyright   Apache License v2.0
 *  Licensed under the Apache License, Version 2.0 (the "License").         \n
 *  You may not use this file except in compliance with the License.        \n
 *  You may obtain a copy of the License at:
 *  http://www.apache.org/licenses/LICENSE-2.0                              \n
 *  Unless required by applicable law or agreed to in writing, software     \n
 *  distributed under the License is distributed on an "AS IS" BASIS,       \n
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n
 *  See the License for the specific language governing permissions and     \n
 *  limitations under the License.
 *
 * @addtogroup CoreGraphics
 * @{
 **/

#ifndef ZENDERER__CORE_GRAPHICS__TEXTURE_HPP
#define ZENDERER__CORE_GRAPHICS__TEXTURE_HPP

#include "stb_image.h"

#include "Zenderer/Assets/Asset.hpp"
#include "Zenderer/Utilities/Log.hpp"
#include "OpenGL.hpp"

namespace zen
{
namespace gfxcore
{
    class ZEN_API CTexture : public asset::CAsset
    {
    public:
        ~CTexture() { this->Destroy(); }

        bool LoadFromFile(const string_t& filename)
        {
            if(m_loaded) this->Destroy();

            int32_t w, h, comp;
            unsigned char* raw = stbi_load("sample.png", &w, &h, &comp,
                4 /* force 32-bit image (4 x 8-bit) */);
            stbi_flip_y(w, h, 4, raw);

            if(raw == nullptr) return false;

            ZEN_ASSERT(w > 0 && h > 0);

            if(comp != 4 && comp != 3)
            {
                m_error_str = "Invalid number of components. "
                    "Textures must be 24-bit (RGB) or 32-bit (RGBA) files.";

                m_Log << m_Log.SetMode(util::LogMode::ZEN_ERROR)
                      << m_Log.SetSystem("Texture") << m_error_str
                      << util::CLog::endl;

                return (m_loaded = false);
            }

            GL(glGenTextures(1, &m_texture));
            GL(glBindTexture(GL_TEXTURE_2D, m_texture));

            GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
                w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, raw));

            GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
            GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
            GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
            GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));

            GL(glBindTexture(GL_TEXTURE_2D, 0));

            stbi_image_free(raw);

            m_width = w;
            m_height = h;
            this->SetFilename(filename);
            return (m_loaded = true);
        }

        bool LoadFromExisting(const CAsset* const pCopy)
        {
            if(m_loaded) this->Destroy();
            if(pCopy == nullptr || !pCopy->IsLoaded()) return false;

            const unsigned char* raw =
                reinterpret_cast<const unsigned char*>(pCopy->GetData());

            const CTexture* const pCopyTexture =
                reinterpret_cast<const CTexture* const>(pCopy);

            GL(glGenTextures(1, &m_texture));
            GL(glBindTexture(GL_TEXTURE_2D, m_texture));

            GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
                pCopyTexture->m_width, pCopyTexture->m_height, 0,
                GL_RGBA, GL_UNSIGNED_BYTE, raw));

            GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
            GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
            GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
            GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));

            GL(glBindTexture(GL_TEXTURE_2D, 0));

            // new[] occured in GetData() so we are responsible for cleanup.
            delete[] raw;

            m_width = pCopyTexture->m_width;
            m_height= pCopyTexture->m_height;
            this->SetFilename(pCopyTexture->GetFilename());

            return (m_loaded = true);
        }

        const void* const GetData() const
        {
            this->Bind();

            // Size is width * height * bits per component
            // Since we force RBGA format, use 4.
            unsigned char* raw =
                new unsigned char[m_width * m_height * 4];

            GL(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, raw));
            return raw;
        }

        bool Bind() const
        {
            if(!m_loaded) return false;
            GL(glBindTexture(GL_TEXTURE_2D, m_texture));
            return true;
        }

        bool Unbind() const
        {
            if(!m_loaded) return false;
            GL(glBindTexture(GL_TEXTURE_2D, 0));
            return true;
        }

        uint16_t GetWidth()     const { return m_width; }
        uint16_t GetHeight()    const { return m_height; }

        friend class asset::CAssetManager;

    private:
        CTexture(const void* const owner = nullptr) : CAsset(owner),
            m_width(0), m_height(0) {}

        bool Destroy()
        {
            if(m_loaded)
            {
                glDeleteTextures(1, &m_texture);
                m_width = m_height = 0;
            }

            return !(m_loaded = false);
        }

        GLuint m_texture;
        GLuint m_width, m_height;
    };
}   // namespace gfxcore
}   // namespace zen

#endif // ZENDERER__CORE_GRAPHICS__TEXTURE_HPP

/** @} **/
<commit_msg>Loading texture from raw data<commit_after>/**
 * @file
 *  Zenderer/CoreGraphics/Texture.hpp - A low-level texture asset wrapper
 *  around OpenGL textures.
 *
 * @author      George Kudrayvtsev (halcyon)
 * @version     1.0
 * @copyright   Apache License v2.0
 *  Licensed under the Apache License, Version 2.0 (the "License").         \n
 *  You may not use this file except in compliance with the License.        \n
 *  You may obtain a copy of the License at:
 *  http://www.apache.org/licenses/LICENSE-2.0                              \n
 *  Unless required by applicable law or agreed to in writing, software     \n
 *  distributed under the License is distributed on an "AS IS" BASIS,       \n
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n
 *  See the License for the specific language governing permissions and     \n
 *  limitations under the License.
 *
 * @addtogroup CoreGraphics
 * @{
 **/

#ifndef ZENDERER__CORE_GRAPHICS__TEXTURE_HPP
#define ZENDERER__CORE_GRAPHICS__TEXTURE_HPP

#include "stb_image.h"

#include "Zenderer/Assets/Asset.hpp"
#include "Zenderer/Utilities/Log.hpp"
#include "OpenGL.hpp"

namespace zen
{
namespace gfxcore
{
    class ZEN_API CTexture : public asset::CAsset
    {
    public:
        ~CTexture() { this->Destroy(); }

        bool LoadFromFile(const string_t& filename)
        {
            if(m_loaded) this->Destroy();

            int32_t w, h, comp;
            unsigned char* raw = stbi_load("sample.png", &w, &h, &comp,
                4 /* force 32-bit image (4 x 8-bit) */);
            stbi_flip_y(w, h, 4, raw);

            if(raw == nullptr) return false;

            ZEN_ASSERT(w > 0 && h > 0);

            if(comp != 4 && comp != 3)
            {
                m_error_str = "Invalid number of components. "
                    "Textures must be 24-bit (RGB) or 32-bit (RGBA) files.";

                m_Log << m_Log.SetMode(util::LogMode::ZEN_ERROR)
                      << m_Log.SetSystem("Texture") << m_error_str
                      << util::CLog::endl;

                return (m_loaded = false);
            }

            bool ret = this->LoadFromRaw(GL_RGBA8, GL_RGBA, w, h, raw);

            stbi_image_free(raw);
            this->SetFilename(filename);
            return (m_loaded = true);
        }

        bool LoadFromExisting(const CAsset* const pCopy)
        {
            if(m_loaded) this->Destroy();
            if(pCopy == nullptr || !pCopy->IsLoaded()) return false;

            const unsigned char* raw =
                reinterpret_cast<const unsigned char*>(pCopy->GetData());

            const CTexture* const pCopyTexture =
                reinterpret_cast<const CTexture* const>(pCopy);

            bool ret = this->LoadFromRaw(GL_RGBA8, GL_RGBA,
                pCopyTexture->m_width, pCopyTexture->m_height, raw);

            // new[] occured in GetData() so we are responsible for cleanup.
            delete[] raw;

            m_width = pCopyTexture->m_width;
            m_height= pCopyTexture->m_height;
            this->SetFilename(pCopyTexture->GetFilename());

            return (m_loaded = ret);
        }

        bool LoadFromRaw(const GLenum iformat, const GLenum format,
                         const uint16_t w, const uint16_t h,
                         const unsigned char* data)
        {
            if(m_loaded) this->Destroy();

            GL(glGenTextures(1, &m_texture));
            GL(glBindTexture(GL_TEXTURE_2D, m_texture));

            GL(glTexImage2D(GL_TEXTURE_2D, 0, iformat, w, h, 0, format,
                    GL_UNSIGNED_BYTE, raw));

            GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
            GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
            GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
            GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));

            GL(glBindTexture(GL_TEXTURE_2D, 0));

            m_width = w;
            m_height = h;

            std::stringstream ss;
            ss << "Raw texture data " << ((void*)data) << '.';
            this->SetFilename(ss.str());

            return true;
        }

        const void* const GetData() const
        {
            this->Bind();

            // Size is width * height * bits per component
            // Since we force RBGA format, use 4.
            unsigned char* raw =
                new unsigned char[m_width * m_height * 4];

            GL(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, raw));
            return raw;
        }

        bool Bind() const
        {
            if(!m_loaded) return false;
            GL(glBindTexture(GL_TEXTURE_2D, m_texture));
            return true;
        }

        bool Unbind() const
        {
            if(!m_loaded) return false;
            GL(glBindTexture(GL_TEXTURE_2D, 0));
            return true;
        }

        uint16_t GetWidth()     const { return m_width; }
        uint16_t GetHeight()    const { return m_height; }

        friend class asset::CAssetManager;

    private:
        CTexture(const void* const owner = nullptr) : CAsset(owner),
            m_width(0), m_height(0) {}

        bool Destroy()
        {
            if(m_loaded)
            {
                glDeleteTextures(1, &m_texture);
                m_width = m_height = 0;
            }

            return !(m_loaded = false);
        }

        GLuint m_texture;
        GLuint m_width, m_height;
    };
}   // namespace gfxcore
}   // namespace zen

#endif // ZENDERER__CORE_GRAPHICS__TEXTURE_HPP

/** @} **/
<|endoftext|>


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

#define MINIMP3_ONLY_MP3
#define MINIMP3_FLOAT_OUTPUT
#define MINIMP3_IMPLEMENTATION
#define MINIMP3_NO_STDIO

#include "audio_stream_mp3.h"

#include "core/io/file_access.h"

int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
	ERR_FAIL_COND_V(!active, 0);

	int todo = p_frames;

	int frames_mixed_this_step = p_frames;

	while (todo && active) {
		mp3dec_frame_info_t frame_info;
		mp3d_sample_t *buf_frame = nullptr;

		int samples_mixed = mp3dec_ex_read_frame(mp3d, &buf_frame, &frame_info, mp3_stream->channels);

		if (samples_mixed) {
			p_buffer[p_frames - todo] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]);
			--todo;
			++frames_mixed;
		}

		else {
			//EOF
			if (mp3_stream->loop) {
				seek(mp3_stream->loop_offset);
				loops++;
			} else {
				frames_mixed_this_step = p_frames - todo;
				//fill remainder with silence
				for (int i = p_frames - todo; i < p_frames; i++) {
					p_buffer[i] = AudioFrame(0, 0);
				}
				active = false;
				todo = 0;
			}
		}
	}
	return frames_mixed_this_step;
}

float AudioStreamPlaybackMP3::get_stream_sampling_rate() {
	return mp3_stream->sample_rate;
}

void AudioStreamPlaybackMP3::start(float p_from_pos) {
	active = true;
	seek(p_from_pos);
	loops = 0;
	_begin_resample();
}

void AudioStreamPlaybackMP3::stop() {
	active = false;
}

bool AudioStreamPlaybackMP3::is_playing() const {
	return active;
}

int AudioStreamPlaybackMP3::get_loop_count() const {
	return loops;
}

float AudioStreamPlaybackMP3::get_playback_position() const {
	return float(frames_mixed) / mp3_stream->sample_rate;
}

void AudioStreamPlaybackMP3::seek(float p_time) {
	if (!active) {
		return;
	}

	if (p_time >= mp3_stream->get_length()) {
		p_time = 0;
	}

	frames_mixed = uint32_t(mp3_stream->sample_rate * p_time);
	mp3dec_ex_seek(mp3d, (uint64_t)frames_mixed * mp3_stream->channels);
}

AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
	if (mp3d) {
		mp3dec_ex_close(mp3d);
		memfree(mp3d);
	}
}

Ref<AudioStreamPlayback> AudioStreamMP3::instance_playback() {
	Ref<AudioStreamPlaybackMP3> mp3s;

	ERR_FAIL_COND_V_MSG(data.is_empty(), mp3s,
			"This AudioStreamMP3 does not have an audio file assigned "
			"to it. AudioStreamMP3 should not be created from the "
			"inspector or with `.new()`. Instead, load an audio file.");

	mp3s.instantiate();
	mp3s->mp3_stream = Ref<AudioStreamMP3>(this);
	mp3s->mp3d = (mp3dec_ex_t *)memalloc(sizeof(mp3dec_ex_t));

	int errorcode = mp3dec_ex_open_buf(mp3s->mp3d, data.ptr(), data_len, MP3D_SEEK_TO_SAMPLE);

	mp3s->frames_mixed = 0;
	mp3s->active = false;
	mp3s->loops = 0;

	if (errorcode) {
		ERR_FAIL_COND_V(errorcode, Ref<AudioStreamPlaybackMP3>());
	}

	return mp3s;
}

String AudioStreamMP3::get_stream_name() const {
	return ""; //return stream_name;
}

void AudioStreamMP3::clear_data() {
	data.clear();
}

void AudioStreamMP3::set_data(const Vector<uint8_t> &p_data) {
	int src_data_len = p_data.size();
	const uint8_t *src_datar = p_data.ptr();

	mp3dec_ex_t mp3d;
	int err = mp3dec_ex_open_buf(&mp3d, src_datar, src_data_len, MP3D_SEEK_TO_SAMPLE);
	ERR_FAIL_COND_MSG(err || mp3d.info.hz == 0, "Failed to decode mp3 file. Make sure it is a valid mp3 audio file.");

	channels = mp3d.info.channels;
	sample_rate = mp3d.info.hz;
	length = float(mp3d.samples) / (sample_rate * float(channels));

	mp3dec_ex_close(&mp3d);

	clear_data();

	data.resize(src_data_len);
	memcpy(data.ptrw(), src_datar, src_data_len);
	data_len = src_data_len;
}

Vector<uint8_t> AudioStreamMP3::get_data() const {
	return data;
}

void AudioStreamMP3::set_loop(bool p_enable) {
	loop = p_enable;
}

bool AudioStreamMP3::has_loop() const {
	return loop;
}

void AudioStreamMP3::set_loop_offset(float p_seconds) {
	loop_offset = p_seconds;
}

float AudioStreamMP3::get_loop_offset() const {
	return loop_offset;
}

float AudioStreamMP3::get_length() const {
	return length;
}

bool AudioStreamMP3::is_monophonic() const {
	return false;
}

void AudioStreamMP3::_bind_methods() {
	ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMP3::set_data);
	ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMP3::get_data);

	ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamMP3::set_loop);
	ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamMP3::has_loop);

	ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamMP3::set_loop_offset);
	ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamMP3::get_loop_offset);

	ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_loop", "has_loop");
	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_loop_offset", "get_loop_offset");
}

AudioStreamMP3::AudioStreamMP3() {
}

AudioStreamMP3::~AudioStreamMP3() {
	clear_data();
}
<commit_msg>AudioStreamMP3: expose loop properties to the editor<commit_after>/*************************************************************************/
/*  audio_stream_mp3.cpp                                                 */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#define MINIMP3_ONLY_MP3
#define MINIMP3_FLOAT_OUTPUT
#define MINIMP3_IMPLEMENTATION
#define MINIMP3_NO_STDIO

#include "audio_stream_mp3.h"

#include "core/io/file_access.h"

int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
	ERR_FAIL_COND_V(!active, 0);

	int todo = p_frames;

	int frames_mixed_this_step = p_frames;

	while (todo && active) {
		mp3dec_frame_info_t frame_info;
		mp3d_sample_t *buf_frame = nullptr;

		int samples_mixed = mp3dec_ex_read_frame(mp3d, &buf_frame, &frame_info, mp3_stream->channels);

		if (samples_mixed) {
			p_buffer[p_frames - todo] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]);
			--todo;
			++frames_mixed;
		}

		else {
			//EOF
			if (mp3_stream->loop) {
				seek(mp3_stream->loop_offset);
				loops++;
			} else {
				frames_mixed_this_step = p_frames - todo;
				//fill remainder with silence
				for (int i = p_frames - todo; i < p_frames; i++) {
					p_buffer[i] = AudioFrame(0, 0);
				}
				active = false;
				todo = 0;
			}
		}
	}
	return frames_mixed_this_step;
}

float AudioStreamPlaybackMP3::get_stream_sampling_rate() {
	return mp3_stream->sample_rate;
}

void AudioStreamPlaybackMP3::start(float p_from_pos) {
	active = true;
	seek(p_from_pos);
	loops = 0;
	_begin_resample();
}

void AudioStreamPlaybackMP3::stop() {
	active = false;
}

bool AudioStreamPlaybackMP3::is_playing() const {
	return active;
}

int AudioStreamPlaybackMP3::get_loop_count() const {
	return loops;
}

float AudioStreamPlaybackMP3::get_playback_position() const {
	return float(frames_mixed) / mp3_stream->sample_rate;
}

void AudioStreamPlaybackMP3::seek(float p_time) {
	if (!active) {
		return;
	}

	if (p_time >= mp3_stream->get_length()) {
		p_time = 0;
	}

	frames_mixed = uint32_t(mp3_stream->sample_rate * p_time);
	mp3dec_ex_seek(mp3d, (uint64_t)frames_mixed * mp3_stream->channels);
}

AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
	if (mp3d) {
		mp3dec_ex_close(mp3d);
		memfree(mp3d);
	}
}

Ref<AudioStreamPlayback> AudioStreamMP3::instance_playback() {
	Ref<AudioStreamPlaybackMP3> mp3s;

	ERR_FAIL_COND_V_MSG(data.is_empty(), mp3s,
			"This AudioStreamMP3 does not have an audio file assigned "
			"to it. AudioStreamMP3 should not be created from the "
			"inspector or with `.new()`. Instead, load an audio file.");

	mp3s.instantiate();
	mp3s->mp3_stream = Ref<AudioStreamMP3>(this);
	mp3s->mp3d = (mp3dec_ex_t *)memalloc(sizeof(mp3dec_ex_t));

	int errorcode = mp3dec_ex_open_buf(mp3s->mp3d, data.ptr(), data_len, MP3D_SEEK_TO_SAMPLE);

	mp3s->frames_mixed = 0;
	mp3s->active = false;
	mp3s->loops = 0;

	if (errorcode) {
		ERR_FAIL_COND_V(errorcode, Ref<AudioStreamPlaybackMP3>());
	}

	return mp3s;
}

String AudioStreamMP3::get_stream_name() const {
	return ""; //return stream_name;
}

void AudioStreamMP3::clear_data() {
	data.clear();
}

void AudioStreamMP3::set_data(const Vector<uint8_t> &p_data) {
	int src_data_len = p_data.size();
	const uint8_t *src_datar = p_data.ptr();

	mp3dec_ex_t mp3d;
	int err = mp3dec_ex_open_buf(&mp3d, src_datar, src_data_len, MP3D_SEEK_TO_SAMPLE);
	ERR_FAIL_COND_MSG(err || mp3d.info.hz == 0, "Failed to decode mp3 file. Make sure it is a valid mp3 audio file.");

	channels = mp3d.info.channels;
	sample_rate = mp3d.info.hz;
	length = float(mp3d.samples) / (sample_rate * float(channels));

	mp3dec_ex_close(&mp3d);

	clear_data();

	data.resize(src_data_len);
	memcpy(data.ptrw(), src_datar, src_data_len);
	data_len = src_data_len;
}

Vector<uint8_t> AudioStreamMP3::get_data() const {
	return data;
}

void AudioStreamMP3::set_loop(bool p_enable) {
	loop = p_enable;
}

bool AudioStreamMP3::has_loop() const {
	return loop;
}

void AudioStreamMP3::set_loop_offset(float p_seconds) {
	loop_offset = p_seconds;
}

float AudioStreamMP3::get_loop_offset() const {
	return loop_offset;
}

float AudioStreamMP3::get_length() const {
	return length;
}

bool AudioStreamMP3::is_monophonic() const {
	return false;
}

void AudioStreamMP3::_bind_methods() {
	ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMP3::set_data);
	ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMP3::get_data);

	ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamMP3::set_loop);
	ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamMP3::has_loop);

	ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamMP3::set_loop_offset);
	ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamMP3::get_loop_offset);

	ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset");
}

AudioStreamMP3::AudioStreamMP3() {
}

AudioStreamMP3::~AudioStreamMP3() {
	clear_data();
}
<|endoftext|>


<commit_before><commit_msg>framework: Add protect for croutine<commit_after><|endoftext|>