<commit_before>#include <boost/filesystem.hpp>
#include <boost/ref.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>

#include "Renderer.hpp"
#include "AlloShared/StatsUtils.hpp"
#include "AlloShared/to_human_readable_byte_count.hpp"
#include "AlloShared/CommandHandler.hpp"
#include "AlloShared/Console.hpp"
#include "AlloShared/Config.hpp"
#include "AlloShared/CommandLine.hpp"
#include "AlloReceiver/RTSPCubemapSourceClient.hpp"
#include "AlloReceiver/AlloReceiver.h"
#include "AlloReceiver/Stats.hpp"
#include "AlloReceiver/H264CubemapSource.h"

#define DEG_DIV_RAD 57.29577951308233
#define RAD_DIV_DEG  0.01745329251994

const unsigned int DEFAULT_SINK_BUFFER_SIZE = 2000000000;

static Stats    stats;
static bool     noDisplay;
static Renderer renderer;
static auto     lastStatsTime = boost::chrono::steady_clock::now();
static std::string statsFormat = AlloReceiver::formatStringMaker();

StereoCubemap* onNextCubemap(CubemapSource* source, StereoCubemap* cubemap)
{
    for (int i = 0; i < cubemap->getEye(0)->getFacesCount(); i++)
    {
        stats.store(StatsUtils::CubemapFace(i, StatsUtils::CubemapFace::DISPLAYED));
    }
    stats.store(StatsUtils::Cubemap());
    return cubemap;
}

void onReceivedNALU(CubemapSource* source, u_int8_t type, size_t size, int face)
{
    //stats.store(StatsUtils::NALU(type, size, face, StatsUtils::NALU::RECEIVED));
}

void onReceivedFrame(CubemapSource* source, u_int8_t type, size_t size, int face)
{
    //stats.store(StatsUtils::Frame(type, size, face, StatsUtils::Frame::RECEIVED));
}

void onDecodedFrame(CubemapSource* source, u_int8_t type, size_t size, int face)
{
    //stats.store(StatsUtils::Frame(type, size, face, StatsUtils::Frame::DECODED));
}

void onColorConvertedFrame(CubemapSource* source, u_int8_t type, size_t size, int face)
{
    stats.store(StatsUtils::Frame(type, size, face, StatsUtils::Frame::COLOR_CONVERTED));
}

void onAddedFrameToCubemap(CubemapSource* source, int face)
{
    //stats.store(StatsUtils::CubemapFace(face, StatsUtils::CubemapFace::ADDED));
}

void setOnScheduledFrameInCubemap(CubemapSource* source, int face)
{
    stats.store(StatsUtils::CubemapFace(face, StatsUtils::CubemapFace::SCHEDULED));
}

void onDisplayedCubemapFace(Renderer* renderer, int face)
{
    stats.store(StatsUtils::CubemapFace(face, StatsUtils::CubemapFace::DISPLAYED));
}

void onDisplayedFrame(Renderer* renderer)
{
    stats.store(StatsUtils::Cubemap());
}

void onDidConnect(RTSPCubemapSourceClient* client, CubemapSource* cubemapSource)
{
    H264CubemapSource* h264CubemapSource = dynamic_cast<H264CubemapSource*>(cubemapSource);
    if (h264CubemapSource)
    {
        h264CubemapSource->setOnReceivedNALU           (boost::bind(&onReceivedNALU,               _1, _2, _3, _4));
        h264CubemapSource->setOnReceivedFrame          (boost::bind(&onReceivedFrame,              _1, _2, _3, _4));
        h264CubemapSource->setOnDecodedFrame           (boost::bind(&onDecodedFrame,               _1, _2, _3, _4));
        h264CubemapSource->setOnColorConvertedFrame    (boost::bind(&onColorConvertedFrame,        _1, _2, _3, _4));
        h264CubemapSource->setOnAddedFrameToCubemap    (boost::bind(&onAddedFrameToCubemap,        _1, _2));
        h264CubemapSource->setOnScheduledFrameInCubemap(boost::bind(&setOnScheduledFrameInCubemap, _1, _2));
    }

    if (noDisplay)
    {
        cubemapSource->setOnNextCubemap(boost::bind(&onNextCubemap, _1, _2));
    }
    else
    {
        renderer.setCubemapSource(cubemapSource);
    }
}

int main(int argc, char* argv[])
{
                  noDisplay        = false;
    std::string   url              = "";
    std::string   interfaceAddress = "0.0.0.0";
    unsigned long bufferSize       = DEFAULT_SINK_BUFFER_SIZE;
    bool          matchStereoPairs = false;
    std::string   configFilePath   = "AlloPlayer.config";

    std::initializer_list<CommandHandler::Command> generalCommands =
    {
        {
            "gamma-min",
            {"val"},
            [](const std::vector<std::string>& values)
            {
                renderer.setGammaMin(boost::lexical_cast<float>(values[0]));
            }
        },
        {
            "gamma-max",
            {"val"},
            [](const std::vector<std::string>& values)
            {
                renderer.setGammaMax(boost::lexical_cast<float>(values[0]));
            }
        },
        {
            "gamma-pow",
            {"val"},
            [](const std::vector<std::string>& values)
            {
                renderer.setGammaPow(boost::lexical_cast<float>(values[0]));
            }
        },
        {
            "for-rotation",
            {"deg_alpha", "deg_beta", "deg_gamma"},
            [](const std::vector<std::string>& values)
            {
                renderer.setFORRotation(al::Vec3f(boost::lexical_cast<float>(values[0]) * RAD_DIV_DEG,
                                                  boost::lexical_cast<float>(values[1]) * RAD_DIV_DEG,
                                                  boost::lexical_cast<float>(values[2]) * RAD_DIV_DEG));
            }
        },
        {
            "for-angle",
            {"deg"},
            [](const std::vector<std::string>& values)
            {
                renderer.setFORAngle(boost::lexical_cast<float>(values[0]) * RAD_DIV_DEG);
            }
        },
        {
            "rotation",
            {"deg_alpha", "deg_beta", "deg_gamma"},
            [](const std::vector<std::string>& values)
            {
                renderer.setRotation(al::Vec3f(boost::lexical_cast<float>(values[0]) * RAD_DIV_DEG,
                                               boost::lexical_cast<float>(values[1]) * RAD_DIV_DEG,
                                               boost::lexical_cast<float>(values[2]) * RAD_DIV_DEG));
            }
        }
    };

    std::initializer_list<CommandHandler::Command> commandLineOnlyCommands =
    {
        {
            "config",
            {"file_path"},
            [&configFilePath](const std::vector<std::string>& values)
            {
                configFilePath = values[0];
            }
        }
    };

    std::initializer_list<CommandHandler::Command> configCommandLineOnlyCommands =
    {
        {
            "no-display",
            {},
            [](const std::vector<std::string>& values)
            {
                noDisplay = true;
            }
        },
        {
            "url",
            {"rtsp_url"},
            [&url](const std::vector<std::string>& values)
            {
                url = values[0];
            }
        },
        {
            "interface-address",
            {"ip"},
            [&interfaceAddress](const std::vector<std::string>& values)
            {
                interfaceAddress = values[0];
            }
        },
        {
            "buffer-size",
            {"bytes"},
            [&bufferSize](const std::vector<std::string>& values)
            {
                bufferSize = boost::lexical_cast<unsigned long>(values[0]);
            }
        },
        {
            "match-stereo-pairs",
            {},
            [&matchStereoPairs](const std::vector<std::string>& values)
            {
                matchStereoPairs = true;
            }
        }
    };

    std::initializer_list<CommandHandler::Command> consoleOnlyCommands =
    {
        {
            "stats",
            {},
            [](const std::vector<std::string>& values)
            {
                boost::chrono::steady_clock::time_point now = boost::chrono::steady_clock::now();
                std::cout << stats.summary(boost::chrono::duration_cast<boost::chrono::microseconds>(now - lastStatsTime),
                                           AlloReceiver::statValsMaker,
                                           AlloReceiver::postProcessorMaker,
                                           statsFormat);
                lastStatsTime = now;
            }
        },
        {
            "quit",
            {},
            [](const std::vector<std::string>& values)
            {
                exit(0);
            }
        },
        {
            "info",
            {},
            [&bufferSize, &url, &interfaceAddress, &matchStereoPairs, &configFilePath](const std::vector<std::string>& values)
            {
                al::Vec3f forRotation = renderer.getFORRotation() * DEG_DIV_RAD;
                al::Vec3f rotation    = renderer.getRotation()    * DEG_DIV_RAD;

                std::cout << std::setiosflags(std::ios::fixed) << std::setprecision(1);
                std::cout << "Display:            " << ((noDisplay) ? "no" : "yes") << std::endl;
                std::cout << "RTSP URL:           " << url << std::endl;
                std::cout << "Interface address:  " << interfaceAddress << std::endl;
                std::cout << "Buffer size:        " << to_human_readable_byte_count(bufferSize, false, false) << std::endl;
                std::cout << "Match stereo pairs: " << ((matchStereoPairs) ? "yes" : "no") << std::endl;
                std::cout << "Gamma min:          " << renderer.getGammaMin() << std::endl;
                std::cout << "Gamma max:          " << renderer.getGammaMax() << std::endl;
                std::cout << "Gamma pow:          " << renderer.getGammaPow() << std::endl;
                std::cout << "FOR angle:          " << renderer.getFORAngle() * DEG_DIV_RAD << "°" << std::endl;
                std::cout << "FOR rotation:       " << "α=" << forRotation[0] << "°\t"
                                                    << "β=" << forRotation[1] << "°\t"
                                                    << "γ=" << forRotation[2] << "°" << std::endl;
                std::cout << "Scene rotation:     " << "α=" << rotation[0] << "°\t"
                                                    << "β=" << rotation[1] << "°\t"
                                                    << "γ=" << rotation[2] << "°" << std::endl;
                std::cout << "Config file:        " << configFilePath << std::endl;
            }
        }
    };

    CommandHandler consoleCommandHandler({generalCommands, consoleOnlyCommands});
    CommandHandler configCommandHandler({generalCommands, configCommandLineOnlyCommands});
    CommandHandler commandLineCommandHandler({generalCommands, configCommandLineOnlyCommands, commandLineOnlyCommands});

    // The desired behaviour is that command line parameters override config file settings.
    // So, we would run the config file parser first and then the command line parser.
    // However, then we cannot pass the config file path as a command line parameter.
    // The solution here is to run the command line parser twice:
    // before and after the config file parser.
    auto commandLineParseResult = CommandLine::parseCommandLine(commandLineCommandHandler,
                                                                argc,
                                                                argv);
    if (!commandLineParseResult.first)
    {
        std::cerr << commandLineParseResult.second << std::endl;
        std::cerr << commandLineCommandHandler.getCommandHelpString();
        abort();
    }

    auto configParseResult = Config::parseConfigFile(configCommandHandler,
                                                     configFilePath);
    if (!configParseResult.first)
    {
        std::cerr << configParseResult.second << std::endl;
        std::cerr << configCommandHandler.getCommandHelpString();
        abort();
    }

    commandLineParseResult = CommandLine::parseCommandLine(commandLineCommandHandler,
                                                                argc,
                                                                argv);
    if (!commandLineParseResult.first)
    {
        std::cerr << commandLineParseResult.second << std::endl;
        std::cerr << commandLineCommandHandler.getCommandHelpString();
        abort();
    }

    if (url == "")
    {
        std::cerr << "No URL specified." << std::endl;
    }

    Console console(consoleCommandHandler);
    console.start();

    RTSPCubemapSourceClient* rtspClient = RTSPCubemapSourceClient::create(url.c_str(),
                                                                          bufferSize,
                                                                          AV_PIX_FMT_YUV420P,
                                                                          matchStereoPairs,
                                                                          interfaceAddress.c_str());
    rtspClient->setOnDidConnect(boost::bind(&onDidConnect, _1, _2));
    rtspClient->connect();

    if (noDisplay)
    {
        std::cout << "network only" << std::endl;
        while(true)
        {
            boost::this_thread::yield();
        }
    }
    else
    {
        renderer.setOnDisplayedCubemapFace(boost::bind(&onDisplayedCubemapFace, _1, _2));
        renderer.setOnDisplayedFrame(boost::bind(&onDisplayedFrame, _1));
        renderer.start(); // does not return
    }
}
<commit_msg>AlloPlayer quits with error when no url is specified.<commit_after>#include <boost/filesystem.hpp>
#include <boost/ref.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>

#include "Renderer.hpp"
#include "AlloShared/StatsUtils.hpp"
#include "AlloShared/to_human_readable_byte_count.hpp"
#include "AlloShared/CommandHandler.hpp"
#include "AlloShared/Console.hpp"
#include "AlloShared/Config.hpp"
#include "AlloShared/CommandLine.hpp"
#include "AlloReceiver/RTSPCubemapSourceClient.hpp"
#include "AlloReceiver/AlloReceiver.h"
#include "AlloReceiver/Stats.hpp"
#include "AlloReceiver/H264CubemapSource.h"

#define DEG_DIV_RAD 57.29577951308233
#define RAD_DIV_DEG  0.01745329251994

const unsigned int DEFAULT_SINK_BUFFER_SIZE = 2000000000;

static Stats    stats;
static bool     noDisplay;
static Renderer renderer;
static auto     lastStatsTime = boost::chrono::steady_clock::now();
static std::string statsFormat = AlloReceiver::formatStringMaker();

StereoCubemap* onNextCubemap(CubemapSource* source, StereoCubemap* cubemap)
{
    for (int i = 0; i < cubemap->getEye(0)->getFacesCount(); i++)
    {
        stats.store(StatsUtils::CubemapFace(i, StatsUtils::CubemapFace::DISPLAYED));
    }
    stats.store(StatsUtils::Cubemap());
    return cubemap;
}

void onReceivedNALU(CubemapSource* source, u_int8_t type, size_t size, int face)
{
    //stats.store(StatsUtils::NALU(type, size, face, StatsUtils::NALU::RECEIVED));
}

void onReceivedFrame(CubemapSource* source, u_int8_t type, size_t size, int face)
{
    //stats.store(StatsUtils::Frame(type, size, face, StatsUtils::Frame::RECEIVED));
}

void onDecodedFrame(CubemapSource* source, u_int8_t type, size_t size, int face)
{
    //stats.store(StatsUtils::Frame(type, size, face, StatsUtils::Frame::DECODED));
}

void onColorConvertedFrame(CubemapSource* source, u_int8_t type, size_t size, int face)
{
    stats.store(StatsUtils::Frame(type, size, face, StatsUtils::Frame::COLOR_CONVERTED));
}

void onAddedFrameToCubemap(CubemapSource* source, int face)
{
    //stats.store(StatsUtils::CubemapFace(face, StatsUtils::CubemapFace::ADDED));
}

void setOnScheduledFrameInCubemap(CubemapSource* source, int face)
{
    stats.store(StatsUtils::CubemapFace(face, StatsUtils::CubemapFace::SCHEDULED));
}

void onDisplayedCubemapFace(Renderer* renderer, int face)
{
    stats.store(StatsUtils::CubemapFace(face, StatsUtils::CubemapFace::DISPLAYED));
}

void onDisplayedFrame(Renderer* renderer)
{
    stats.store(StatsUtils::Cubemap());
}

void onDidConnect(RTSPCubemapSourceClient* client, CubemapSource* cubemapSource)
{
    H264CubemapSource* h264CubemapSource = dynamic_cast<H264CubemapSource*>(cubemapSource);
    if (h264CubemapSource)
    {
        h264CubemapSource->setOnReceivedNALU           (boost::bind(&onReceivedNALU,               _1, _2, _3, _4));
        h264CubemapSource->setOnReceivedFrame          (boost::bind(&onReceivedFrame,              _1, _2, _3, _4));
        h264CubemapSource->setOnDecodedFrame           (boost::bind(&onDecodedFrame,               _1, _2, _3, _4));
        h264CubemapSource->setOnColorConvertedFrame    (boost::bind(&onColorConvertedFrame,        _1, _2, _3, _4));
        h264CubemapSource->setOnAddedFrameToCubemap    (boost::bind(&onAddedFrameToCubemap,        _1, _2));
        h264CubemapSource->setOnScheduledFrameInCubemap(boost::bind(&setOnScheduledFrameInCubemap, _1, _2));
    }

    if (noDisplay)
    {
        cubemapSource->setOnNextCubemap(boost::bind(&onNextCubemap, _1, _2));
    }
    else
    {
        renderer.setCubemapSource(cubemapSource);
    }
}

int main(int argc, char* argv[])
{
                  noDisplay        = false;
    std::string   url              = "";
    std::string   interfaceAddress = "0.0.0.0";
    unsigned long bufferSize       = DEFAULT_SINK_BUFFER_SIZE;
    bool          matchStereoPairs = false;
    std::string   configFilePath   = "AlloPlayer.config";

    std::initializer_list<CommandHandler::Command> generalCommands =
    {
        {
            "gamma-min",
            {"val"},
            [](const std::vector<std::string>& values)
            {
                renderer.setGammaMin(boost::lexical_cast<float>(values[0]));
            }
        },
        {
            "gamma-max",
            {"val"},
            [](const std::vector<std::string>& values)
            {
                renderer.setGammaMax(boost::lexical_cast<float>(values[0]));
            }
        },
        {
            "gamma-pow",
            {"val"},
            [](const std::vector<std::string>& values)
            {
                renderer.setGammaPow(boost::lexical_cast<float>(values[0]));
            }
        },
        {
            "for-rotation",
            {"deg_alpha", "deg_beta", "deg_gamma"},
            [](const std::vector<std::string>& values)
            {
                renderer.setFORRotation(al::Vec3f(boost::lexical_cast<float>(values[0]) * RAD_DIV_DEG,
                                                  boost::lexical_cast<float>(values[1]) * RAD_DIV_DEG,
                                                  boost::lexical_cast<float>(values[2]) * RAD_DIV_DEG));
            }
        },
        {
            "for-angle",
            {"deg"},
            [](const std::vector<std::string>& values)
            {
                renderer.setFORAngle(boost::lexical_cast<float>(values[0]) * RAD_DIV_DEG);
            }
        },
        {
            "rotation",
            {"deg_alpha", "deg_beta", "deg_gamma"},
            [](const std::vector<std::string>& values)
            {
                renderer.setRotation(al::Vec3f(boost::lexical_cast<float>(values[0]) * RAD_DIV_DEG,
                                               boost::lexical_cast<float>(values[1]) * RAD_DIV_DEG,
                                               boost::lexical_cast<float>(values[2]) * RAD_DIV_DEG));
            }
        }
    };

    std::initializer_list<CommandHandler::Command> commandLineOnlyCommands =
    {
        {
            "config",
            {"file_path"},
            [&configFilePath](const std::vector<std::string>& values)
            {
                configFilePath = values[0];
            }
        }
    };

    std::initializer_list<CommandHandler::Command> configCommandLineOnlyCommands =
    {
        {
            "no-display",
            {},
            [](const std::vector<std::string>& values)
            {
                noDisplay = true;
            }
        },
        {
            "url",
            {"rtsp_url"},
            [&url](const std::vector<std::string>& values)
            {
                url = values[0];
            }
        },
        {
            "interface-address",
            {"ip"},
            [&interfaceAddress](const std::vector<std::string>& values)
            {
                interfaceAddress = values[0];
            }
        },
        {
            "buffer-size",
            {"bytes"},
            [&bufferSize](const std::vector<std::string>& values)
            {
                bufferSize = boost::lexical_cast<unsigned long>(values[0]);
            }
        },
        {
            "match-stereo-pairs",
            {},
            [&matchStereoPairs](const std::vector<std::string>& values)
            {
                matchStereoPairs = true;
            }
        }
    };

    std::initializer_list<CommandHandler::Command> consoleOnlyCommands =
    {
        {
            "stats",
            {},
            [](const std::vector<std::string>& values)
            {
                boost::chrono::steady_clock::time_point now = boost::chrono::steady_clock::now();
                std::cout << stats.summary(boost::chrono::duration_cast<boost::chrono::microseconds>(now - lastStatsTime),
                                           AlloReceiver::statValsMaker,
                                           AlloReceiver::postProcessorMaker,
                                           statsFormat);
                lastStatsTime = now;
            }
        },
        {
            "quit",
            {},
            [](const std::vector<std::string>& values)
            {
                exit(0);
            }
        },
        {
            "info",
            {},
            [&bufferSize, &url, &interfaceAddress, &matchStereoPairs, &configFilePath](const std::vector<std::string>& values)
            {
                al::Vec3f forRotation = renderer.getFORRotation() * DEG_DIV_RAD;
                al::Vec3f rotation    = renderer.getRotation()    * DEG_DIV_RAD;

                std::cout << std::setiosflags(std::ios::fixed) << std::setprecision(1);
                std::cout << "Display:            " << ((noDisplay) ? "no" : "yes") << std::endl;
                std::cout << "RTSP URL:           " << url << std::endl;
                std::cout << "Interface address:  " << interfaceAddress << std::endl;
                std::cout << "Buffer size:        " << to_human_readable_byte_count(bufferSize, false, false) << std::endl;
                std::cout << "Match stereo pairs: " << ((matchStereoPairs) ? "yes" : "no") << std::endl;
                std::cout << "Gamma min:          " << renderer.getGammaMin() << std::endl;
                std::cout << "Gamma max:          " << renderer.getGammaMax() << std::endl;
                std::cout << "Gamma pow:          " << renderer.getGammaPow() << std::endl;
                std::cout << "FOR angle:          " << renderer.getFORAngle() * DEG_DIV_RAD << "°" << std::endl;
                std::cout << "FOR rotation:       " << "α=" << forRotation[0] << "°\t"
                                                    << "β=" << forRotation[1] << "°\t"
                                                    << "γ=" << forRotation[2] << "°" << std::endl;
                std::cout << "Scene rotation:     " << "α=" << rotation[0] << "°\t"
                                                    << "β=" << rotation[1] << "°\t"
                                                    << "γ=" << rotation[2] << "°" << std::endl;
                std::cout << "Config file:        " << configFilePath << std::endl;
            }
        }
    };

    CommandHandler consoleCommandHandler({generalCommands, consoleOnlyCommands});
    CommandHandler configCommandHandler({generalCommands, configCommandLineOnlyCommands});
    CommandHandler commandLineCommandHandler({generalCommands, configCommandLineOnlyCommands, commandLineOnlyCommands});

    // The desired behaviour is that command line parameters override config file settings.
    // So, we would run the config file parser first and then the command line parser.
    // However, then we cannot pass the config file path as a command line parameter.
    // The solution here is to run the command line parser twice:
    // before and after the config file parser.
    auto commandLineParseResult = CommandLine::parseCommandLine(commandLineCommandHandler,
                                                                argc,
                                                                argv);
    if (!commandLineParseResult.first)
    {
        std::cerr << commandLineParseResult.second << std::endl;
        std::cerr << commandLineCommandHandler.getCommandHelpString();
        abort();
    }

    auto configParseResult = Config::parseConfigFile(configCommandHandler,
                                                     configFilePath);
    if (!configParseResult.first)
    {
        std::cerr << configParseResult.second << std::endl;
        std::cerr << configCommandHandler.getCommandHelpString();
        abort();
    }

    commandLineParseResult = CommandLine::parseCommandLine(commandLineCommandHandler,
                                                                argc,
                                                                argv);
    if (!commandLineParseResult.first)
    {
        std::cerr << commandLineParseResult.second << std::endl;
        std::cerr << commandLineCommandHandler.getCommandHelpString();
        abort();
    }

    if (url == "")
    {
        std::cerr << "No URL specified." << std::endl;
        abort();
    }

    Console console(consoleCommandHandler);
    console.start();

    RTSPCubemapSourceClient* rtspClient = RTSPCubemapSourceClient::create(url.c_str(),
                                                                          bufferSize,
                                                                          AV_PIX_FMT_YUV420P,
                                                                          matchStereoPairs,
                                                                          interfaceAddress.c_str());
    rtspClient->setOnDidConnect(boost::bind(&onDidConnect, _1, _2));
    rtspClient->connect();

    if (noDisplay)
    {
        std::cout << "network only" << std::endl;
        while(true)
        {
            boost::this_thread::yield();
        }
    }
    else
    {
        renderer.setOnDisplayedCubemapFace(boost::bind(&onDisplayedCubemapFace, _1, _2));
        renderer.setOnDisplayedFrame(boost::bind(&onDisplayedFrame, _1));
        renderer.start(); // does not return
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2018 dubalu.com LLC. All rights reserved.
 *
 * 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.
 */
#pragma once

#include <cstddef>
#include <cstdint>
#include <limits>
#include <stdexcept>

namespace pmh {

template <typename T>
auto constexpr log(T v) {
	std::size_t n = 0;
	while (v > 1) {
		n += 1;
		v >>= 1;
	}
	return n;
}

constexpr std::size_t bit_weight(std::size_t n) {
	return (
		(n <= 8 * sizeof(unsigned int)) +
		(n <= 8 * sizeof(unsigned long)) +
		(n <= 8 * sizeof(unsigned long long)) +
		(n <= 128)
	);
}

unsigned int select_uint_least(std::integral_constant<std::size_t, 4>);
unsigned long select_uint_least(std::integral_constant<std::size_t, 3>);
unsigned long long select_uint_least(std::integral_constant<std::size_t, 2>);
template<std::size_t N>
unsigned long long select_uint_least(std::integral_constant<std::size_t, N>) {
	static_assert(N < 2, "unsupported type size");
	return {};
}
template<std::size_t N>
using select_uint_least_t = decltype(select_uint_least(std::integral_constant<std::size_t, bit_weight(N)>()));

template <typename T, T a, T c, T m>
class linear_congruential_engine {
	static_assert(std::is_unsigned<T>::value, "Only supports unsigned integral types");

public:
	using result_type = T;
	static constexpr result_type multiplier = a;
	static constexpr result_type increment = c;
	static constexpr result_type modulus = m;
	static constexpr result_type default_seed = 1u;

	linear_congruential_engine() = default;
	constexpr linear_congruential_engine(result_type s) { seed(s); }

	void seed(result_type s = default_seed) { state_ = s; }
	constexpr result_type operator()() {
		using uint_least_t = select_uint_least_t<log(a) + log(m) + 4>;
		uint_least_t tmp = static_cast<uint_least_t>(multiplier) * state_ + increment;
		if(modulus)
			state_ = tmp % modulus;
		else
			state_ = tmp;
		return state_;
	}
	constexpr void discard(unsigned long long n) {
		while (n--)
			operator()();
	}
	static constexpr result_type min() { return increment == 0u ? 1u : 0u; };
	static constexpr result_type max() { return modulus - 1u; };
	friend constexpr bool operator==(const linear_congruential_engine& self,
									 const linear_congruential_engine& other) {
		return self.state_ == other.state_;
	}
	friend constexpr bool operator!=(const linear_congruential_engine& self,
									 const linear_congruential_engine& other) {
		return !(self == other);
	}

private:
	result_type state_ = default_seed;
};

using minstd_rand0 = linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647>;
using minstd_rand = linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>;
using default_prg_t = minstd_rand;

////////////////////////////////////////////////////////////////////////////////

template <class T>
constexpr void swap(T& a, T& b) {
	auto tmp = a;
	a = b;
	b = tmp;
}

template <typename Iterator>
constexpr Iterator partition(Iterator left, Iterator right) {
	auto pivot = left + (right - left) / 2;
	auto value = *pivot;
	swap(*right, *pivot);
	for (auto it = left; it < right; ++it) {
		if (*it < value) {
			swap(*it, *left);
			++left;
		}
	}
	swap(*right, *left);
	return left;
}

template <typename Iterator>
constexpr void quicksort(Iterator left, Iterator right) {
	if (left < right) {
		auto new_pivot = partition(left, right);
		quicksort(left, new_pivot);
		quicksort(new_pivot + 1, right);
	}
}

#define PMH_RESORT

template <typename T, std::size_t N, typename RNG = default_prg_t>
class pmh {
	static_assert(N > 0, "Must have at least one element");
	static_assert(std::is_unsigned<T>::value, "Only supports unsigned integral types");

public:
	constexpr static int max_first_tries = 100;
	constexpr static int max_second_tries = 100;
	constexpr static int max_clashes = 2 * (1u << (log(N) / 2));
	constexpr static auto npos = std::numeric_limits<std::size_t>::max();

	struct bucket_t {
		T item;
		std::size_t pos;

		constexpr bucket_t() : item(0), pos{npos} { }
	};

private:

	struct hashed_item_t {
		std::size_t cnt;
		T slot1;
		T slot2;
		T hash;
		std::size_t pos;

		constexpr hashed_item_t() : cnt{0}, slot1{0}, slot2{0}, hash{0}, pos{npos} { }

		constexpr hashed_item_t(const hashed_item_t& other) :
			cnt{other.cnt},
			slot1{other.slot1},
			slot2{other.slot2},
			hash{other.hash},
			pos{other.pos} { }

		constexpr hashed_item_t(hashed_item_t&& other) noexcept :
			cnt{std::move(other.cnt)},
			slot1{std::move(other.slot1)},
			slot2{std::move(other.slot2)},
			hash{std::move(other.hash)},
			pos{std::move(other.pos)} { }

		constexpr hashed_item_t& operator=(const hashed_item_t& other) {
			cnt = other.cnt;
			slot1 = other.slot1;
			slot2 = other.slot2;
			hash = other.hash;
			pos = other.pos;
			return *this;
		}

		constexpr hashed_item_t& operator=(hashed_item_t&& other) noexcept {
			cnt = std::move(other.cnt);
			slot1 = std::move(other.slot1);
			slot2 = std::move(other.slot2);
			hash = std::move(other.hash);
			pos = std::move(other.pos);
			return *this;
		}

		constexpr bool operator<(const hashed_item_t& other) const {
			if (cnt == other.cnt) {
				return slot1 < other.slot1;
			}
			return cnt > other.cnt;
		}
	};

	constexpr static std::size_t hash(const T &value, std::size_t seed) {
		std::size_t key = seed ^ value;
		key = (~key) + (key << 21); // key = (key << 21) - key - 1;
		key = key ^ (key >> 24);
		key = (key + (key << 3)) + (key << 8); // key * 265
		key = key ^ (key >> 14);
		key = (key + (key << 2)) + (key << 4); // key * 21
		key = key ^ (key >> 28);
		key = key + (key << 31);
		return key;
	}

	std::size_t _seed;
	const T (&_items)[N];
	bucket_t _first[N];
	bucket_t _second[N];

public:
	constexpr pmh(const T (&items)[N]) : _items{items} {
		RNG prg;
		hashed_item_t hashed_items[N];

		for (int try_first = 0; try_first < max_first_tries; ++try_first) {
			_seed = prg();
			for (std::size_t pos = 0; pos < N; ++pos) {
				auto& item = hashed_items[pos];
				auto hashed = hash(_items[pos], _seed);
				item.hash = hashed;
				item.slot1 = hashed % N;
				item.pos = pos;
				item.cnt = 0;
			}

			quicksort(&hashed_items[0], &hashed_items[N - 1]);

			auto end = &hashed_items[N];

			///

			auto frm = &hashed_items[0];
			auto to = frm;
			bool last = false;

#ifdef PMH_RESORT
			while (!last) {
				++to;
				last = to == end;
				if (last || frm->slot1 != to->slot1) {
					auto cnt = to - frm;
					if (cnt > max_clashes) {
						break;
					}
					for (; frm != to; ++frm) {
						frm->cnt = cnt;
					}
				}
			}
			if (!last) {
				continue;
			}
			quicksort(&hashed_items[0], &hashed_items[N - 1]);

			///
			frm = &hashed_items[0];
			to = frm;
			last = false;
#endif

			while (!last) {
				++to;
				last = to == end;

				if (last || frm->slot1 != to->slot1) {
					auto& first_bucket = _first[frm->slot1];
					auto cnt = to - frm;
					if (cnt > 1) {
						// slot clash
#ifndef PMH_RESORT
						if (cnt > max_clashes) {
							break;
						}
#endif
						int try_second = 0;
						for (; try_second < max_second_tries; ++try_second) {
							auto seed = prg();
							first_bucket.item = seed;

							auto frm_ = frm;
							for (; frm_ != to; ++frm_) {
								auto item = _items[frm_->pos];
								auto hashed = hash(item, seed);
								frm_->slot2 = hashed % N;
								auto& second_bucket = _second[frm_->slot2];
								if (second_bucket.pos != npos) {
									break;
								}
								second_bucket.item = item;
								second_bucket.pos = frm_->pos;
							}
							if (frm_ == to) {
								frm = frm_;
								break;
							}
							// rollback, it failed to place all items in empty slots
							for (auto frm__ = frm; frm__ != frm_; ++frm__) {
								auto& second_bucket = _second[frm__->slot2];
								second_bucket.pos = npos;
							}
						}
						if (try_second == max_second_tries) {
							break;
						}
					} else {
						// no slot clash
						auto item = _items[frm->pos];
						first_bucket.item = item;
						first_bucket.pos = frm->pos;
						++frm;
					}
				}
			}
			if (last) {
				return;
			}
		}
		throw std::invalid_argument("Cannot figure out a suitable PMH table");
	}

	constexpr std::size_t operator[](const T& item) const {
		auto pos = find(item);
		if (pos == npos) {
			throw std::out_of_range("Item not found");
		}
		return pos;
	}

	constexpr auto size() const {
		return N;
	}

	constexpr std::size_t find(const T& item) const {
		auto hashed = hash(item, _seed);
		const auto& first_bucket = _first[hashed % N];
		if (first_bucket.pos != npos) {
			if (first_bucket.item != item) {
				return npos;
			}
			return first_bucket.pos;
		}
		hashed = hash(item, first_bucket.item);
		const auto& second_bucket = _second[hashed % N];
		if (second_bucket.pos != npos) {
			if (hashed)
			if (second_bucket.item != item) {
				return npos;
			}
			return second_bucket.pos;
		}
		return npos;
	}

};

template <typename T, std::size_t N>
constexpr static auto
init(const T (&items)[N]) {
	return pmh<T, N>(items);
}

} // namespace pmh
<commit_msg>PMH: Keep item in the hashed_items<commit_after>/*
 * Copyright (C) 2015-2018 dubalu.com LLC. All rights reserved.
 *
 * 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.
 */
#pragma once

#include <cstddef>
#include <cstdint>
#include <limits>
#include <stdexcept>

namespace pmh {

template <typename T>
auto constexpr log(T v) {
	std::size_t n = 0;
	while (v > 1) {
		n += 1;
		v >>= 1;
	}
	return n;
}

constexpr std::size_t bit_weight(std::size_t n) {
	return (
		(n <= 8 * sizeof(unsigned int)) +
		(n <= 8 * sizeof(unsigned long)) +
		(n <= 8 * sizeof(unsigned long long)) +
		(n <= 128)
	);
}

unsigned int select_uint_least(std::integral_constant<std::size_t, 4>);
unsigned long select_uint_least(std::integral_constant<std::size_t, 3>);
unsigned long long select_uint_least(std::integral_constant<std::size_t, 2>);
template<std::size_t N>
unsigned long long select_uint_least(std::integral_constant<std::size_t, N>) {
	static_assert(N < 2, "unsupported type size");
	return {};
}
template<std::size_t N>
using select_uint_least_t = decltype(select_uint_least(std::integral_constant<std::size_t, bit_weight(N)>()));

template <typename T, T a, T c, T m>
class linear_congruential_engine {
	static_assert(std::is_unsigned<T>::value, "Only supports unsigned integral types");

public:
	using result_type = T;
	static constexpr result_type multiplier = a;
	static constexpr result_type increment = c;
	static constexpr result_type modulus = m;
	static constexpr result_type default_seed = 1u;

	linear_congruential_engine() = default;
	constexpr linear_congruential_engine(result_type s) { seed(s); }

	void seed(result_type s = default_seed) { state_ = s; }
	constexpr result_type operator()() {
		using uint_least_t = select_uint_least_t<log(a) + log(m) + 4>;
		uint_least_t tmp = static_cast<uint_least_t>(multiplier) * state_ + increment;
		if(modulus)
			state_ = tmp % modulus;
		else
			state_ = tmp;
		return state_;
	}
	constexpr void discard(unsigned long long n) {
		while (n--)
			operator()();
	}
	static constexpr result_type min() { return increment == 0u ? 1u : 0u; };
	static constexpr result_type max() { return modulus - 1u; };
	friend constexpr bool operator==(const linear_congruential_engine& self,
									 const linear_congruential_engine& other) {
		return self.state_ == other.state_;
	}
	friend constexpr bool operator!=(const linear_congruential_engine& self,
									 const linear_congruential_engine& other) {
		return !(self == other);
	}

private:
	result_type state_ = default_seed;
};

using minstd_rand0 = linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647>;
using minstd_rand = linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>;
using default_prg_t = minstd_rand;

////////////////////////////////////////////////////////////////////////////////

template <class T>
constexpr void swap(T& a, T& b) {
	auto tmp = a;
	a = b;
	b = tmp;
}

template <typename Iterator>
constexpr Iterator partition(Iterator left, Iterator right) {
	auto pivot = left + (right - left) / 2;
	auto value = *pivot;
	swap(*right, *pivot);
	for (auto it = left; it < right; ++it) {
		if (*it < value) {
			swap(*it, *left);
			++left;
		}
	}
	swap(*right, *left);
	return left;
}

template <typename Iterator>
constexpr void quicksort(Iterator left, Iterator right) {
	if (left < right) {
		auto new_pivot = partition(left, right);
		quicksort(left, new_pivot);
		quicksort(new_pivot + 1, right);
	}
}

#define PMH_RESORT

template <typename T, std::size_t N, typename RNG = default_prg_t>
class pmh {
	static_assert(N > 0, "Must have at least one element");
	static_assert(std::is_unsigned<T>::value, "Only supports unsigned integral types");

public:
	constexpr static int max_first_tries = 100;
	constexpr static int max_second_tries = 100;
	constexpr static int max_clashes = 2 * (1u << (log(N) / 2));
	constexpr static auto npos = std::numeric_limits<std::size_t>::max();

	struct bucket_t {
		T item;
		std::size_t pos;

		constexpr bucket_t() : item(0), pos{npos} { }
	};

private:

	struct hashed_item_t {
		std::size_t cnt;
		T slot1;
		T slot2;
		T hash;
		T item;
		std::size_t pos;

		constexpr hashed_item_t() : cnt{0}, slot1{0}, slot2{0}, hash{0}, item{0}, pos{npos} { }

		constexpr hashed_item_t(const hashed_item_t& other) :
			cnt{other.cnt},
			slot1{other.slot1},
			slot2{other.slot2},
			hash{other.hash},
			item{other.item},
			pos{other.pos} { }

		constexpr hashed_item_t(hashed_item_t&& other) noexcept :
			cnt{std::move(other.cnt)},
			slot1{std::move(other.slot1)},
			slot2{std::move(other.slot2)},
			hash{std::move(other.hash)},
			item{std::move(other.item)},
			pos{std::move(other.pos)} { }

		constexpr hashed_item_t& operator=(const hashed_item_t& other) {
			cnt = other.cnt;
			slot1 = other.slot1;
			slot2 = other.slot2;
			hash = other.hash;
			item = other.item;
			pos = other.pos;
			return *this;
		}

		constexpr hashed_item_t& operator=(hashed_item_t&& other) noexcept {
			cnt = std::move(other.cnt);
			slot1 = std::move(other.slot1);
			slot2 = std::move(other.slot2);
			hash = std::move(other.hash);
			item = std::move(other.item);
			pos = std::move(other.pos);
			return *this;
		}

		constexpr bool operator<(const hashed_item_t& other) const {
			if (cnt == other.cnt) {
				return slot1 < other.slot1;
			}
			return cnt > other.cnt;
		}
	};

	constexpr static std::size_t hash(const T &value, std::size_t seed) {
		std::size_t key = seed ^ value;
		key = (~key) + (key << 21); // key = (key << 21) - key - 1;
		key = key ^ (key >> 24);
		key = (key + (key << 3)) + (key << 8); // key * 265
		key = key ^ (key >> 14);
		key = (key + (key << 2)) + (key << 4); // key * 21
		key = key ^ (key >> 28);
		key = key + (key << 31);
		return key;
	}

	std::size_t _seed;
	const T (&_items)[N];
	bucket_t _first[N];
	bucket_t _second[N];

public:
	constexpr pmh(const T (&items)[N]) : _items{items} {
		RNG prg;
		hashed_item_t hashed_items[N];

		for (int try_first = 0; try_first < max_first_tries; ++try_first) {
			_seed = prg();
			for (std::size_t pos = 0; pos < N; ++pos) {
				auto& item = _items[pos];
				auto& hashed_item = hashed_items[pos];
				auto hashed = hash(item, _seed);
				hashed_item.item = item;
				hashed_item.hash = hashed;
				hashed_item.slot1 = hashed % N;
				hashed_item.pos = pos;
				hashed_item.cnt = 0;
			}

			quicksort(&hashed_items[0], &hashed_items[N - 1]);

			auto end = &hashed_items[N];

			///

			auto frm = &hashed_items[0];
			auto to = frm;
			bool last = false;

#ifdef PMH_RESORT
			while (!last) {
				++to;
				last = to == end;
				if (last || frm->slot1 != to->slot1) {
					auto cnt = to - frm;
					if (cnt > max_clashes) {
						break;
					}
					for (; frm != to; ++frm) {
						frm->cnt = cnt;
					}
				}
			}
			if (!last) {
				continue;
			}
			quicksort(&hashed_items[0], &hashed_items[N - 1]);

			///
			frm = &hashed_items[0];
			to = frm;
			last = false;
#endif

			while (!last) {
				++to;
				last = to == end;

				if (last || frm->slot1 != to->slot1) {
					auto& first_bucket = _first[frm->slot1];
					auto cnt = to - frm;
					if (cnt > 1) {
						// slot clash
#ifndef PMH_RESORT
						if (cnt > max_clashes) {
							break;
						}
#endif
						int try_second = 0;
						for (; try_second < max_second_tries; ++try_second) {
							auto seed = prg();
							first_bucket.item = seed;

							auto frm_ = frm;
							for (; frm_ != to; ++frm_) {
								auto hashed = hash(frm_->item, seed);
								frm_->slot2 = hashed % N;
								auto& second_bucket = _second[frm_->slot2];
								if (second_bucket.pos != npos) {
									break;
								}
								second_bucket.item = frm_->item;
								second_bucket.pos = frm_->pos;
							}
							if (frm_ == to) {
								frm = frm_;
								break;
							}
							// rollback, it failed to place all items in empty slots
							for (auto frm__ = frm; frm__ != frm_; ++frm__) {
								auto& second_bucket = _second[frm__->slot2];
								second_bucket.pos = npos;
							}
						}
						if (try_second == max_second_tries) {
							break;
						}
					} else {
						// no slot clash
						first_bucket.item = frm->item;
						first_bucket.pos = frm->pos;
						++frm;
					}
				}
			}
			if (last) {
				return;
			}
		}
		throw std::invalid_argument("Cannot figure out a suitable PMH table");
	}

	constexpr std::size_t operator[](const T& item) const {
		auto pos = find(item);
		if (pos == npos) {
			throw std::out_of_range("Item not found");
		}
		return pos;
	}

	constexpr auto size() const {
		return N;
	}

	constexpr std::size_t find(const T& item) const {
		auto hashed = hash(item, _seed);
		const auto& first_bucket = _first[hashed % N];
		if (first_bucket.pos != npos) {
			if (first_bucket.item != item) {
				return npos;
			}
			return first_bucket.pos;
		}
		hashed = hash(item, first_bucket.item);
		const auto& second_bucket = _second[hashed % N];
		if (second_bucket.pos != npos) {
			if (hashed)
			if (second_bucket.item != item) {
				return npos;
			}
			return second_bucket.pos;
		}
		return npos;
	}

};

template <typename T, std::size_t N>
constexpr static auto
init(const T (&items)[N]) {
	return pmh<T, N>(items);
}

} // namespace pmh
<|endoftext|>


<commit_before>#include <QtGui>
#include "metadataWindow.h"

QString metadataWindow::base64_encode(QString string){
    QByteArray ba;
    ba.append(string);
    return ba.toBase64();
}

QString metadataWindow::base64_decode(QString string){
    QByteArray ba;
    ba.append(string);
    return QByteArray::fromBase64(ba);
}

metadataWindow::metadataWindow(QWidget *parent) : QWidget(parent)
{
    //initialise_decoding_table();

    // Initialize the Track object with empty strings and the like
    initialise_track_object();

    this->setupUI();

    // Connect to the metadata pipe
    FILE *fd = fopen(metadata_file, "r");
    if (fd==NULL)
    {
        printf("No metadata file found");
    }

    // Use for actual reads
    pipe = new QTextStream(fd);

    // Use for events when the pipe is filled
    streamReader = new QSocketNotifier(fileno(fd), QSocketNotifier::Read, qApp);
    QObject::connect(streamReader, SIGNAL(activated(int)), this, SLOT(onData()));
    streamReader->setEnabled(true);
}

void metadataWindow::setupUI()
{
    // Create Fonts
    standard_font = new QFont("Courier New");
    standard_font->setPixelSize(12);

    em_font = new QFont("Courier New");
    em_font->setPixelSize(12);
    em_font->setItalic(true);

    // Add image
    image = new QPixmap(100,100);
    image->fill(QColor("cyan"));
    QLabel *image_label  = new QLabel(this);
        image_label->setPixmap(*image);

    // Add labels
    title_label = new QLabel("Titre", this);
    title_label->setFont(*standard_font);
    artist_label = new QLabel("Artiste", this);
    artist_label->setFont(*standard_font);
    release_label = new QLabel("Album", this);
    release_label->setFont(*standard_font);
    file_type_label = new QLabel("Fichier MP3", this);
    file_type_label->setFont(*standard_font);
    client_ip_label = new QLabel("En streaming depuis 192.168.1.24", this);
    client_ip_label->setFont(*standard_font);

    QVBoxLayout *main_layout = new QVBoxLayout(this);

    QHBoxLayout *hbl = new QHBoxLayout(this);

    QVBoxLayout *vbl = new QVBoxLayout(this);
    vbl->addWidget(title_label);
    vbl->addWidget(artist_label);
    vbl->addWidget(release_label);
    vbl->addWidget(file_type_label);

    hbl->addWidget(image_label);
    hbl->addLayout(vbl);

    main_layout->addLayout(hbl);
    main_layout->addWidget(client_ip_label);

    // Add "quit" button
    //quit_button = new QPushButton("Quit" , this);
    //QObject::connect(quit_button , SIGNAL(clicked()), qApp , SLOT(quit()));
    //vbl->addWidget(quit_button);
}

void metadataWindow::initialise_track_object()
{
    track.title.clear();
    track.artist.clear();
    track.release.clear();
    client_ip.clear();
    client_name.clear();
    file_type.clear();
}

void metadataWindow::updateUI()
{
    cout << "Updating UI\n";
    title_label->setText(QString::fromStdString(track.title));
    artist_label->setText(QString::fromStdString(track.artist));
    release_label->setText(QString::fromStdString(track.release));

    if (file_type.length() != 0) {
        file_type_label->setText("Fichier " + QString::fromStdString(file_type));
    } else {
        file_type_label->setText("");
    }

    if (client_ip.length() != 0) {
        client_ip_label->setText("En streaming depuis " + QString::fromStdString(client_ip));
        client_ip_label->setFont(*standard_font);
    } else {
        client_ip_label->setText("Pas de streaming en cours");
        client_ip_label->setFont(*em_font);
    }

    image->convertFromImage(track.image);
}

void metadataWindow::onData()
{
    emit dataReceived();
}

void metadataWindow::dataReceived()
{
    cout << "\nProcessing new metadata ...\n";
    QString line = pipe->readLine();
    if (line.size() == 0) {
        cout << " > Skipping empty line.\n";
        return;
    }

    const char * message = line.toStdString().c_str();

    uint32_t type, code, length;
    QImage base64_image;
    bool ret_pic = false;

    cout << "Message: [" << message << "]\n";
    int ret = sscanf(message,"<item><type>%8x</type>%8x<length>%u</length>",&type,&code,&length);

    // Get code and type strings
    char typestring[5];
    *(uint32_t*)typestring = htonl(type);
    typestring[4] = 0;
    char codestring[5];
    *(uint32_t*)codestring = htonl(code);
    codestring[4] = 0;

    cout << " > Found tag: " << typestring << " / " << codestring << " of length " << length << "\n";

    if (type > 0 && code > 0 && ret == 3) {
        // now, think about processing the tag.
        // basically, we need to get hold of the base-64 data, if any
        QString payload;

        if (length > 0) {
            cout << " > Extracting data ..." << "\n";

            // get the next line, which should be a data tag
            QString line = pipe->readLine();

            if (line == "<data encoding=\"base64\">") {
                // now, read in that big (possibly) base64 buffer
                cout << " > Data is base64 buffer" << "\n";

                QString line = pipe->readLine();
                if (line.size() > 14) {
                    line.chop(14); // remove "</data></item>"
                    if (code != 'PICT') {
                        payload = base64_decode(line);
                    } else {
                        QByteArray base64Data = line.toAscii();
                        ret_pic = base64_image.loadFromData(QByteArray::fromBase64(base64Data));
                    }
                } else {
                    cout << " > Looks like a bad payload" << "\n";
                }
            }

            cout << " > Got it decoded. Length of decoded string is " << payload.size() << " bytes.\n";
        }

        // this has more information about tags, which might be relevant:
        // https://code.google.com/p/ytrack/wiki/DMAP
        if (code == 'asal') {
            track.release = payload.toStdString();
            cout << "Album Name: " << payload.toStdString() << "\n";
        } else if (code == 'asar') {
            track.artist = payload.toStdString();
            cout << "Artist: " << payload.toStdString() << "\n";
        } else if (code == 'minm') {
            track.title = payload.toStdString();
            cout << "Title: " << payload.toStdString() << "\n";
        } else if (code == 'asdt') {
            file_type = payload.toStdString();
            cout << "File kind: " << payload.toStdString() << "\n";
        } else if (code == 'PICT' && ret_pic) {
            track.image = base64_image;
            cout << "Picture received, length " << length << " bytes." << "\n";
        } else if (code == 'clip') {
            client_ip = payload.toStdString();
            cout << "Client's IP: " << payload.toStdString() << "\n";
        } else if (code == 'snam') {
            client_name = payload.toStdString();
            cout << "Device Name: " << payload.toStdString() << "\n";
        } else if (code == 'snua') {
            client_name = payload.toStdString();
            cout << "User Agent: " << payload.toStdString() << "\n";
        } else if (code == 'prms' || code =='pbeg') {
            track.playing = true;
            cout << "Started playing" << "\n";
        } else if (code == 'pend') {
            track.playing = false;
            cout << "Stopped playing" << "\n";
        } else if (type=='ssnc') {
            cout << "SSNC Stuff : " << typestring << "/" << codestring << " : " << payload.toStdString() << "\n";
        } else {
            cout << "Other Stuff : " << typestring << "/" << codestring << " : " << payload.toStdString() << "\n";
        }

    } else {
        cout << "Could not decipher the message.\n";;
    }

    pipe->flush();
    updateUI();
}
<commit_msg>A few fixes on readData<commit_after>#include <QtGui>
#include "metadataWindow.h"

QString metadataWindow::base64_encode(QString string){
    QByteArray ba;
    ba.append(string);
    return ba.toBase64();
}

QString metadataWindow::base64_decode(QString string){
    QByteArray ba;
    ba.append(string);
    return QByteArray::fromBase64(ba);
}

metadataWindow::metadataWindow(QWidget *parent) : QWidget(parent)
{
    //initialise_decoding_table();

    // Initialize the Track object with empty strings and the like
    initialise_track_object();

    this->setupUI();

    // Connect to the metadata pipe
    FILE *fd = fopen(metadata_file, "r");
    if (fd==NULL)
    {
        printf("No metadata file found");
    }

    // Use for actual reads
    pipe = new QTextStream(fd);

    // Use for events when the pipe is filled
    streamReader = new QSocketNotifier(fileno(fd), QSocketNotifier::Read, qApp);
    QObject::connect(streamReader, SIGNAL(activated(int)), this, SLOT(onData()));
    streamReader->setEnabled(true);
}

void metadataWindow::setupUI()
{
    // Create Fonts
    standard_font = new QFont("Courier New");
    standard_font->setPixelSize(12);

    em_font = new QFont("Courier New");
    em_font->setPixelSize(12);
    em_font->setItalic(true);

    // Add image
    image = new QPixmap(100,100);
    image->fill(QColor("cyan"));
    QLabel *image_label  = new QLabel(this);
        image_label->setPixmap(*image);

    // Add labels
    title_label = new QLabel("Titre", this);
    title_label->setFont(*standard_font);
    artist_label = new QLabel("Artiste", this);
    artist_label->setFont(*standard_font);
    release_label = new QLabel("Album", this);
    release_label->setFont(*standard_font);
    file_type_label = new QLabel("Fichier MP3", this);
    file_type_label->setFont(*standard_font);
    client_ip_label = new QLabel("En streaming depuis 192.168.1.24", this);
    client_ip_label->setFont(*standard_font);

    QVBoxLayout *main_layout = new QVBoxLayout(this);

    QHBoxLayout *hbl = new QHBoxLayout(this);

    QVBoxLayout *vbl = new QVBoxLayout(this);
    vbl->addWidget(title_label);
    vbl->addWidget(artist_label);
    vbl->addWidget(release_label);
    vbl->addWidget(file_type_label);

    hbl->addWidget(image_label);
    hbl->addLayout(vbl);

    main_layout->addLayout(hbl);
    main_layout->addWidget(client_ip_label);

    // Add "quit" button
    //quit_button = new QPushButton("Quit" , this);
    //QObject::connect(quit_button , SIGNAL(clicked()), qApp , SLOT(quit()));
    //vbl->addWidget(quit_button);
}

void metadataWindow::initialise_track_object()
{
    track.title.clear();
    track.artist.clear();
    track.release.clear();
    client_ip.clear();
    client_name.clear();
    file_type.clear();
}

void metadataWindow::updateUI()
{
    cout << "Updating UI\n";
    title_label->setText(QString::fromStdString(track.title));
    artist_label->setText(QString::fromStdString(track.artist));
    release_label->setText(QString::fromStdString(track.release));

    if (file_type.length() != 0) {
        file_type_label->setText("Fichier " + QString::fromStdString(file_type));
    } else {
        file_type_label->setText("");
    }

    if (client_ip.length() != 0) {
        client_ip_label->setText("En streaming depuis " + QString::fromStdString(client_ip));
        client_ip_label->setFont(*standard_font);
    } else {
        client_ip_label->setText("Pas de streaming en cours");
        client_ip_label->setFont(*em_font);
    }

    image->convertFromImage(track.image);
}

void metadataWindow::onData()
{
    emit dataReceived();
}

void metadataWindow::dataReceived()
{
    cout << "\nProcessing new metadata ...\n";

    if (pipe->atEnd()) {
        cout << " > No more data to read ...\n";
        return;
    }

    QString line = pipe->readLine();
    if (line.isEmpty() || line.isNull()) {
        cout << " > Skipping empty line.\n";
        return;
    }

    if (line == "<data encoding=\"base64\">") {
        cout << "How did we get here ???\n";
    }

    const char * message = line.toStdString().c_str();

    uint32_t type, code, length;
    QImage base64_image;
    bool ret_pic = false;

    cout << "Message: [" << message << "]\n";
    int ret = sscanf(message,"<item><type>%8x</type>%8x<length>%u</length>",&type,&code,&length);

    // Get code and type strings
    char typestring[5];
    *(uint32_t*)typestring = htonl(type);
    typestring[4] = 0;
    char codestring[5];
    *(uint32_t*)codestring = htonl(code);
    codestring[4] = 0;

    cout << " > Found tag: " << typestring << " / " << codestring << " of length " << length << "\n";

    if (type > 0 && code > 0 && ret == 3) {
        // now, think about processing the tag.
        // basically, we need to get hold of the base-64 data, if any
        QString payload;

        if (length > 0) {
            cout << " > Extracting data ..." << "\n";

            // get the next line, which should be a data tag
            QString line = pipe->readLine();

            if (line == "<data encoding=\"base64\">") {
                // now, read in that big (possibly) base64 buffer
                cout << " > Data is base64 buffer" << "\n";

                QString line = pipe->readLine();
                if (line.size() > 14) {
                    line.chop(14); // remove "</data></item>"
                    if (code != 'PICT') {
                        payload = base64_decode(line);
                    } else {
                        QByteArray base64Data = line.toAscii();
                        ret_pic = base64_image.loadFromData(QByteArray::fromBase64(base64Data));
                    }
                } else {
                    cout << " > Looks like a bad payload" << "\n";
                }
            }

            cout << " > Got it decoded. Length of decoded string is " << payload.size() << " bytes.\n";
        }

        // this has more information about tags, which might be relevant:
        // https://code.google.com/p/ytrack/wiki/DMAP
        if (code == 'asal') {
            track.release = payload.toStdString();
            cout << "Album Name: " << payload.toStdString() << "\n";
        } else if (code == 'asar') {
            track.artist = payload.toStdString();
            cout << "Artist: " << payload.toStdString() << "\n";
        } else if (code == 'minm') {
            track.title = payload.toStdString();
            cout << "Title: " << payload.toStdString() << "\n";
        } else if (code == 'asdt') {
            file_type = payload.toStdString();
            cout << "File kind: " << payload.toStdString() << "\n";
        } else if (code == 'PICT' && ret_pic) {
            track.image = base64_image;
            cout << "Picture received, length " << length << " bytes." << "\n";
        } else if (code == 'clip') {
            client_ip = payload.toStdString();
            cout << "Client's IP: " << payload.toStdString() << "\n";
        } else if (code == 'snam') {
            client_name = payload.toStdString();
            cout << "Device Name: " << payload.toStdString() << "\n";
        } else if (code == 'snua') {
            client_name = payload.toStdString();
            cout << "User Agent: " << payload.toStdString() << "\n";
        } else if (code == 'prms' || code =='pbeg') {
            track.playing = true;
            cout << "Started playing" << "\n";
        } else if (code == 'pend') {
            track.playing = false;
            cout << "Stopped playing" << "\n";
        } else if (type=='ssnc') {
            cout << "SSNC Stuff : " << typestring << "/" << codestring << " : " << payload.toStdString() << "\n";
        } else {
            cout << "Other Stuff : " << typestring << "/" << codestring << " : " << payload.toStdString() << "\n";
        }

    } else {
        cout << "Could not decipher the message.\n";;
    }

    pipe->flush();
    updateUI();
}
<|endoftext|>


<commit_before>// Copyright 2015-2019 Elviss Strazdins. All rights reserved.

#include "core/Setup.h"

#if OUZEL_COMPILE_OPENAL

#if defined(__APPLE__)
#  include <TargetConditionals.h>
#endif
#if TARGET_OS_IOS || TARGET_OS_TV
#  include <objc/message.h>
extern "C" id const AVAudioSessionCategoryAmbient;
#endif

#include "OALAudioDevice.hpp"
#include "core/Engine.hpp"
#include "utils/Log.hpp"
#include "utils/Utils.hpp"

#ifndef AL_FORMAT_MONO_FLOAT32
#  define AL_FORMAT_MONO_FLOAT32 0x10010
#endif

#ifndef AL_FORMAT_STEREO_FLOAT32
#  define AL_FORMAT_STEREO_FLOAT32 0x10011
#endif

namespace ouzel
{
    namespace audio
    {
        class ALCErrorCategory final: public std::error_category
        {
        public:
            const char* name() const noexcept override
            {
                return "ALC";
            }

            std::string message(int condition) const override
            {
                switch (condition)
                {
                    case ALC_INVALID_DEVICE: return "ALC_INVALID_DEVICE";
                    case ALC_INVALID_CONTEXT: return "ALC_INVALID_CONTEXT";
                    case ALC_INVALID_ENUM: return "ALC_INVALID_ENUM";
                    case ALC_INVALID_VALUE: return "ALC_INVALID_VALUE";
                    case ALC_OUT_OF_MEMORY: return "ALC_OUT_OF_MEMORY";
                    default: return "Unknown error (" + std::to_string(condition) + ")";
                }
            }
        };

        const ALCErrorCategory alcErrorCategory {};

        class OpenALErrorCategory final: public std::error_category
        {
        public:
            const char* name() const noexcept override
            {
                return "OpenAL";
            }

            std::string message(int condition) const override
            {
                switch (condition)
                {
                    case AL_INVALID_NAME: return "AL_INVALID_NAME";
                    case AL_INVALID_ENUM: return "AL_INVALID_ENUM";
                    case AL_INVALID_VALUE: return "AL_INVALID_VALUE";
                    case AL_INVALID_OPERATION: return "AL_INVALID_OPERATION";
                    case AL_OUT_OF_MEMORY: return "AL_OUT_OF_MEMORY";
                    default: return "Unknown error (" + std::to_string(condition) + ")";
                }
            }
        };

        const OpenALErrorCategory openALErrorCategory{};

        OALAudioDevice::OALAudioDevice(const std::function<void(uint32_t frames, uint16_t channels, uint32_t sampleRate, std::vector<float>& samples)>& initDataGetter):
            AudioDevice(Driver::OPENAL, initDataGetter)
        {
#if TARGET_OS_IOS || TARGET_OS_TV
            id audioSession = reinterpret_cast<id (*)(Class, SEL)>(&objc_msgSend)(objc_getClass("AVAudioSession"), sel_getUid("sharedInstance"));
            reinterpret_cast<BOOL (*)(id, SEL, id, id)>(&objc_msgSend)(audioSession, sel_getUid("setCategory:error:"), AVAudioSessionCategoryAmbient, nil);
#endif

            const ALCchar* deviceName = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);

            engine->log(Log::Level::INFO) << "Using " << reinterpret_cast<const char*>(deviceName) << " for audio";

            device = alcOpenDevice(deviceName);

            if (!device)
                throw std::runtime_error("Failed to create OpenAL device");

            ALCenum alcError;

            if ((alcError = alcGetError(device)) != ALC_NO_ERROR)
                throw std::system_error(alcError, alcErrorCategory, "Failed to create OpenAL device");

            context = alcCreateContext(device, nullptr);

            if ((alcError = alcGetError(device)) != ALC_NO_ERROR)
                throw std::system_error(alcError, alcErrorCategory, "Failed to create OpenAL context");

            if (!context)
                throw std::runtime_error("Failed to create OpenAL context");

            alcMakeContextCurrent(context);

            if ((alcError = alcGetError(device)) != ALC_NO_ERROR)
                throw std::system_error(alcError, alcErrorCategory, "Failed to make OpenAL context current");

            const ALchar* audioRenderer = alGetString(AL_RENDERER);

            ALenum error;

            if ((error = alGetError()) != AL_NO_ERROR || !audioRenderer)
                engine->log(Log::Level::WARN) << "Failed to get OpenAL renderer, error: " + std::to_string(error);
            else
                engine->log(Log::Level::INFO) << "Using " << reinterpret_cast<const char*>(audioRenderer) << " audio renderer";

            std::vector<std::string> extensions;
            const ALchar* extensionsPtr = alGetString(AL_EXTENSIONS);

            if ((error = alGetError()) != AL_NO_ERROR || !extensionsPtr)
                engine->log(Log::Level::WARN) << "Failed to get OpenGL extensions";
            else
                extensions = explodeString(reinterpret_cast<const char*>(extensionsPtr), ' ');

            engine->log(Log::Level::ALL) << "Supported OpenAL extensions: " << extensions;

            bool float32Supported = false;
            for (const std::string& extension : extensions)
            {
                if (extension == "AL_EXT_float32")
                    float32Supported = true;
            }

#if !defined(__EMSCRIPTEN__)
            format40 = alGetEnumValue("AL_FORMAT_QUAD16");
            format51 = alGetEnumValue("AL_FORMAT_51CHN16");
            format61 = alGetEnumValue("AL_FORMAT_61CHN16");
            format71 = alGetEnumValue("AL_FORMAT_71CHN16");

            if ((error = alGetError()) != AL_NO_ERROR)
                engine->log(Log::Level::WARN) << "Failed to get OpenAL enum values";
#endif

            alGenSources(1, &sourceId);

            if ((error = alGetError()) != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL source");

            alGenBuffers(2, bufferIds);

            if ((error = alGetError()) != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL buffers");

            switch (channels)
            {
                case 1:
                {
                    if (float32Supported)
                    {
                        format = AL_FORMAT_MONO_FLOAT32;
                        sampleFormat = SampleFormat::FLOAT32;
                        sampleSize = sizeof(float);
                    }
                    else
                    {
                        format = AL_FORMAT_MONO16;
                        sampleFormat = SampleFormat::SINT16;
                        sampleSize = sizeof(int16_t);
                    }
                    break;
                }
                case 2:
                {
                    if (float32Supported)
                    {
                        format = AL_FORMAT_STEREO_FLOAT32;
                        sampleFormat = SampleFormat::FLOAT32;
                        sampleSize = sizeof(float);
                    }
                    else
                    {
                        format = AL_FORMAT_STEREO16;
                        sampleFormat = SampleFormat::SINT16;
                        sampleSize = sizeof(int16_t);
                    }
                    break;
                }
                case 4:
                {
                    format = format40;
                    sampleFormat = SampleFormat::SINT16;
                    sampleSize = sizeof(int16_t);
                    break;
                }
                case 6:
                {
                    format = format51;
                    sampleFormat = SampleFormat::SINT16;
                    sampleSize = sizeof(int16_t);
                    break;
                }
                case 7:
                {
                    format = format61;
                    sampleFormat = SampleFormat::SINT16;
                    sampleSize = sizeof(int16_t);
                    break;
                }
                case 8:
                {
                    format = format71;
                    sampleFormat = SampleFormat::SINT16;
                    sampleSize = sizeof(int16_t);
                    break;
                }
                default:
                    throw std::runtime_error("Invalid channel count");
            }

            getData(bufferSize / (channels * sampleSize), data);

            alBufferData(bufferIds[0], format,
                         data.data(),
                         static_cast<ALsizei>(data.size()),
                         static_cast<ALsizei>(sampleRate));

            getData(bufferSize / (channels * sampleSize), data);

            alBufferData(bufferIds[1], format,
                         data.data(),
                         static_cast<ALsizei>(data.size()),
                         static_cast<ALsizei>(sampleRate));

            nextBuffer = 0;

            alSourceQueueBuffers(sourceId, 2, bufferIds);

            if ((error = alGetError()) != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to queue OpenAL buffers");

            alSourcePlay(sourceId);

            if ((error = alGetError()) != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to play OpenAL source");

#if !defined(__EMSCRIPTEN__)
            running = true;
            audioThread = std::thread(&OALAudioDevice::run, this);
#endif
        }

        OALAudioDevice::~OALAudioDevice()
        {
#if !defined(__EMSCRIPTEN__)
            running = false;
            if (audioThread.joinable()) audioThread.join();
#endif

            if (sourceId)
            {
                alSourceStop(sourceId);
                alSourcei(sourceId, AL_BUFFER, 0);
                alDeleteSources(1, &sourceId);
                alGetError();
            }

            for (ALuint bufferId : bufferIds)
            {
                if (bufferId)
                {
                    alDeleteBuffers(1, &bufferId);
                    alGetError();
                }
            }

            if (context)
            {
                alcMakeContextCurrent(nullptr);
                alcDestroyContext(context);
            }

            if (device)
                alcCloseDevice(device);
        }

        void OALAudioDevice::process()
        {
            alcMakeContextCurrent(context);

            ALCenum alcError;

            if ((alcError = alcGetError(device)) != ALC_NO_ERROR)
                throw std::system_error(alcError, alcErrorCategory, "Failed to make OpenAL context current");

            ALint buffersProcessed;
            alGetSourcei(sourceId, AL_BUFFERS_PROCESSED, &buffersProcessed);

            ALenum error;

            if ((error = alGetError()) != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to get processed buffer count");

            // requeue all processed buffers
            for (; buffersProcessed > 0; --buffersProcessed)
            {
                alSourceUnqueueBuffers(sourceId, 1, &bufferIds[nextBuffer]);

                if ((error = alGetError()) != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to unqueue OpenAL buffer");

                getData(bufferSize / (channels * sampleSize), data);

                alBufferData(bufferIds[nextBuffer], format,
                             data.data(),
                             static_cast<ALsizei>(data.size()),
                             static_cast<ALsizei>(sampleRate));

                alSourceQueueBuffers(sourceId, 1, &bufferIds[nextBuffer]);

                if ((error = alGetError()) != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to queue OpenAL buffer");

                ALint state;
                alGetSourcei(sourceId, AL_SOURCE_STATE, &state);
                if (state != AL_PLAYING)
                {
                    alSourcePlay(sourceId);

                    if ((error = alGetError()) != AL_NO_ERROR)
                        throw std::system_error(error, openALErrorCategory, "Failed to play OpenAL source");
                }

                // swap the buffer
                nextBuffer = (nextBuffer + 1) % 2;
            }
        }

        void OALAudioDevice::run()
        {
            setCurrentThreadName("Audio");

#if !defined(__EMSCRIPTEN__)
            while (running)
            {
                try
                {
                    process();
                }
                catch (const std::exception& e)
                {
                    ouzel::engine->log(ouzel::Log::Level::ERR) << e.what();
                }
            }
#endif
        }
    } // namespace audio
} // namespace ouzel

#endif
<commit_msg>Handle the result of alcMakeContextCurrent<commit_after>// Copyright 2015-2019 Elviss Strazdins. All rights reserved.

#include "core/Setup.h"

#if OUZEL_COMPILE_OPENAL

#if defined(__APPLE__)
#  include <TargetConditionals.h>
#endif
#if TARGET_OS_IOS || TARGET_OS_TV
#  include <objc/message.h>
extern "C" id const AVAudioSessionCategoryAmbient;
#endif

#include "OALAudioDevice.hpp"
#include "core/Engine.hpp"
#include "utils/Log.hpp"
#include "utils/Utils.hpp"

#ifndef AL_FORMAT_MONO_FLOAT32
#  define AL_FORMAT_MONO_FLOAT32 0x10010
#endif

#ifndef AL_FORMAT_STEREO_FLOAT32
#  define AL_FORMAT_STEREO_FLOAT32 0x10011
#endif

namespace ouzel
{
    namespace audio
    {
        class ALCErrorCategory final: public std::error_category
        {
        public:
            const char* name() const noexcept override
            {
                return "ALC";
            }

            std::string message(int condition) const override
            {
                switch (condition)
                {
                    case ALC_INVALID_DEVICE: return "ALC_INVALID_DEVICE";
                    case ALC_INVALID_CONTEXT: return "ALC_INVALID_CONTEXT";
                    case ALC_INVALID_ENUM: return "ALC_INVALID_ENUM";
                    case ALC_INVALID_VALUE: return "ALC_INVALID_VALUE";
                    case ALC_OUT_OF_MEMORY: return "ALC_OUT_OF_MEMORY";
                    default: return "Unknown error (" + std::to_string(condition) + ")";
                }
            }
        };

        const ALCErrorCategory alcErrorCategory {};

        class OpenALErrorCategory final: public std::error_category
        {
        public:
            const char* name() const noexcept override
            {
                return "OpenAL";
            }

            std::string message(int condition) const override
            {
                switch (condition)
                {
                    case AL_INVALID_NAME: return "AL_INVALID_NAME";
                    case AL_INVALID_ENUM: return "AL_INVALID_ENUM";
                    case AL_INVALID_VALUE: return "AL_INVALID_VALUE";
                    case AL_INVALID_OPERATION: return "AL_INVALID_OPERATION";
                    case AL_OUT_OF_MEMORY: return "AL_OUT_OF_MEMORY";
                    default: return "Unknown error (" + std::to_string(condition) + ")";
                }
            }
        };

        const OpenALErrorCategory openALErrorCategory{};

        OALAudioDevice::OALAudioDevice(const std::function<void(uint32_t frames, uint16_t channels, uint32_t sampleRate, std::vector<float>& samples)>& initDataGetter):
            AudioDevice(Driver::OPENAL, initDataGetter)
        {
#if TARGET_OS_IOS || TARGET_OS_TV
            id audioSession = reinterpret_cast<id (*)(Class, SEL)>(&objc_msgSend)(objc_getClass("AVAudioSession"), sel_getUid("sharedInstance"));
            reinterpret_cast<BOOL (*)(id, SEL, id, id)>(&objc_msgSend)(audioSession, sel_getUid("setCategory:error:"), AVAudioSessionCategoryAmbient, nil);
#endif

            const ALCchar* deviceName = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);

            engine->log(Log::Level::INFO) << "Using " << reinterpret_cast<const char*>(deviceName) << " for audio";

            device = alcOpenDevice(deviceName);

            if (!device)
                throw std::runtime_error("Failed to open ALC device");

            ALCenum alcError;

            if ((alcError = alcGetError(device)) != ALC_NO_ERROR)
                throw std::system_error(alcError, alcErrorCategory, "Failed to create ALC device");

            context = alcCreateContext(device, nullptr);

            if ((alcError = alcGetError(device)) != ALC_NO_ERROR)
                throw std::system_error(alcError, alcErrorCategory, "Failed to create ALC context");

            if (!context)
                throw std::runtime_error("Failed to create ALC context");

            if (!alcMakeContextCurrent(context))
                throw std::runtime_error("Failed to make ALC context current");

            if ((alcError = alcGetError(device)) != ALC_NO_ERROR)
                throw std::system_error(alcError, alcErrorCategory, "Failed to make ALC context current");

            const ALchar* audioRenderer = alGetString(AL_RENDERER);

            ALenum error;

            if ((error = alGetError()) != AL_NO_ERROR || !audioRenderer)
                engine->log(Log::Level::WARN) << "Failed to get OpenAL renderer, error: " + std::to_string(error);
            else
                engine->log(Log::Level::INFO) << "Using " << reinterpret_cast<const char*>(audioRenderer) << " audio renderer";

            std::vector<std::string> extensions;
            const ALchar* extensionsPtr = alGetString(AL_EXTENSIONS);

            if ((error = alGetError()) != AL_NO_ERROR || !extensionsPtr)
                engine->log(Log::Level::WARN) << "Failed to get OpenGL extensions";
            else
                extensions = explodeString(reinterpret_cast<const char*>(extensionsPtr), ' ');

            engine->log(Log::Level::ALL) << "Supported OpenAL extensions: " << extensions;

            bool float32Supported = false;
            for (const std::string& extension : extensions)
            {
                if (extension == "AL_EXT_float32")
                    float32Supported = true;
            }

#if !defined(__EMSCRIPTEN__)
            format40 = alGetEnumValue("AL_FORMAT_QUAD16");
            format51 = alGetEnumValue("AL_FORMAT_51CHN16");
            format61 = alGetEnumValue("AL_FORMAT_61CHN16");
            format71 = alGetEnumValue("AL_FORMAT_71CHN16");

            if ((error = alGetError()) != AL_NO_ERROR)
                engine->log(Log::Level::WARN) << "Failed to get OpenAL enum values";
#endif

            alGenSources(1, &sourceId);

            if ((error = alGetError()) != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL source");

            alGenBuffers(2, bufferIds);

            if ((error = alGetError()) != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL buffers");

            switch (channels)
            {
                case 1:
                {
                    if (float32Supported)
                    {
                        format = AL_FORMAT_MONO_FLOAT32;
                        sampleFormat = SampleFormat::FLOAT32;
                        sampleSize = sizeof(float);
                    }
                    else
                    {
                        format = AL_FORMAT_MONO16;
                        sampleFormat = SampleFormat::SINT16;
                        sampleSize = sizeof(int16_t);
                    }
                    break;
                }
                case 2:
                {
                    if (float32Supported)
                    {
                        format = AL_FORMAT_STEREO_FLOAT32;
                        sampleFormat = SampleFormat::FLOAT32;
                        sampleSize = sizeof(float);
                    }
                    else
                    {
                        format = AL_FORMAT_STEREO16;
                        sampleFormat = SampleFormat::SINT16;
                        sampleSize = sizeof(int16_t);
                    }
                    break;
                }
                case 4:
                {
                    format = format40;
                    sampleFormat = SampleFormat::SINT16;
                    sampleSize = sizeof(int16_t);
                    break;
                }
                case 6:
                {
                    format = format51;
                    sampleFormat = SampleFormat::SINT16;
                    sampleSize = sizeof(int16_t);
                    break;
                }
                case 7:
                {
                    format = format61;
                    sampleFormat = SampleFormat::SINT16;
                    sampleSize = sizeof(int16_t);
                    break;
                }
                case 8:
                {
                    format = format71;
                    sampleFormat = SampleFormat::SINT16;
                    sampleSize = sizeof(int16_t);
                    break;
                }
                default:
                    throw std::runtime_error("Invalid channel count");
            }

            getData(bufferSize / (channels * sampleSize), data);

            alBufferData(bufferIds[0], format,
                         data.data(),
                         static_cast<ALsizei>(data.size()),
                         static_cast<ALsizei>(sampleRate));

            getData(bufferSize / (channels * sampleSize), data);

            alBufferData(bufferIds[1], format,
                         data.data(),
                         static_cast<ALsizei>(data.size()),
                         static_cast<ALsizei>(sampleRate));

            nextBuffer = 0;

            alSourceQueueBuffers(sourceId, 2, bufferIds);

            if ((error = alGetError()) != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to queue OpenAL buffers");

            alSourcePlay(sourceId);

            if ((error = alGetError()) != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to play OpenAL source");

#if !defined(__EMSCRIPTEN__)
            running = true;
            audioThread = std::thread(&OALAudioDevice::run, this);
#endif
        }

        OALAudioDevice::~OALAudioDevice()
        {
#if !defined(__EMSCRIPTEN__)
            running = false;
            if (audioThread.joinable()) audioThread.join();
#endif

            if (sourceId)
            {
                alSourceStop(sourceId);
                alSourcei(sourceId, AL_BUFFER, 0);
                alDeleteSources(1, &sourceId);
                alGetError();
            }

            for (ALuint bufferId : bufferIds)
            {
                if (bufferId)
                {
                    alDeleteBuffers(1, &bufferId);
                    alGetError();
                }
            }

            if (context)
            {
                alcMakeContextCurrent(nullptr);
                alcDestroyContext(context);
            }

            if (device)
                alcCloseDevice(device);
        }

        void OALAudioDevice::process()
        {
            if (!alcMakeContextCurrent(context))
                throw std::runtime_error("Failed to make ALC context current");

            ALCenum alcError;

            if ((alcError = alcGetError(device)) != ALC_NO_ERROR)
                throw std::system_error(alcError, alcErrorCategory, "Failed to make ALC context current");

            ALint buffersProcessed;
            alGetSourcei(sourceId, AL_BUFFERS_PROCESSED, &buffersProcessed);

            ALenum error;

            if ((error = alGetError()) != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to get processed buffer count");

            // requeue all processed buffers
            for (; buffersProcessed > 0; --buffersProcessed)
            {
                alSourceUnqueueBuffers(sourceId, 1, &bufferIds[nextBuffer]);

                if ((error = alGetError()) != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to unqueue OpenAL buffer");

                getData(bufferSize / (channels * sampleSize), data);

                alBufferData(bufferIds[nextBuffer], format,
                             data.data(),
                             static_cast<ALsizei>(data.size()),
                             static_cast<ALsizei>(sampleRate));

                alSourceQueueBuffers(sourceId, 1, &bufferIds[nextBuffer]);

                if ((error = alGetError()) != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to queue OpenAL buffer");

                ALint state;
                alGetSourcei(sourceId, AL_SOURCE_STATE, &state);
                if (state != AL_PLAYING)
                {
                    alSourcePlay(sourceId);

                    if ((error = alGetError()) != AL_NO_ERROR)
                        throw std::system_error(error, openALErrorCategory, "Failed to play OpenAL source");
                }

                // swap the buffer
                nextBuffer = (nextBuffer + 1) % 2;
            }
        }

        void OALAudioDevice::run()
        {
            setCurrentThreadName("Audio");

#if !defined(__EMSCRIPTEN__)
            while (running)
            {
                try
                {
                    process();
                }
                catch (const std::exception& e)
                {
                    ouzel::engine->log(ouzel::Log::Level::ERR) << e.what();
                }
            }
#endif
        }
    } // namespace audio
} // namespace ouzel

#endif
<|endoftext|>


<commit_before><commit_msg>INTEGRATION: CWS impress1 (1.5.226); FILE MERGED 2003/09/17 09:31:50 af 1.5.226.1: #111996# Transition to stacked sub-shells. Introduction of namespace sd.<commit_after><|endoftext|>


<commit_before>#include <stdio.h>

int main(int argv, char *argc) {

  printf("Hello sorting!\n");
  return 0;
}
<commit_msg>removed file<commit_after><|endoftext|>