diff --git a/src/engine/media/MT_SevanaMos.cpp b/src/engine/media/MT_SevanaMos.cpp index 3cc3bd54..b3055840 100644 --- a/src/engine/media/MT_SevanaMos.cpp +++ b/src/engine/media/MT_SevanaMos.cpp @@ -62,70 +62,70 @@ void SevanaMosUtility::run(const std::string& pcmPath, const std::string& interv std::string& estimation, std::string& intervals) { #if defined(PVQA_SERVER) - path sevana = current_path() / "sevana"; + path sevana = current_path() / "sevana"; #if defined(TARGET_LINUX) || defined(TARGET_OSX) - path exec = sevana / "pvqa"; + path exec = sevana / "pvqa"; #else - path exec = sevana / "pvqa.exe"; + path exec = sevana / "pvqa.exe"; #endif - path lic = sevana / "pvqa.lic"; - path cfg = sevana / "settings.cfg"; + path lic = sevana / "pvqa.lic"; + path cfg = sevana / "settings.cfg"; - estimation.clear(); - char cmdbuffer[1024]; - sprintf(cmdbuffer, "%s %s analysis %s %s %s 0.799", exec.string().c_str(), lic.string().c_str(), - intervalPath.c_str(), cfg.string().c_str(), pcmPath.c_str()); - std::string output = execCommand(cmdbuffer); + estimation.clear(); + char cmdbuffer[1024]; + sprintf(cmdbuffer, "%s %s analysis %s %s %s 0.799", exec.string().c_str(), lic.string().c_str(), + intervalPath.c_str(), cfg.string().c_str(), pcmPath.c_str()); + std::string output = execCommand(cmdbuffer); - //ICELogDebug(<< "Got PVQA analyzer output: " << output); + //ICELogDebug(<< "Got PVQA analyzer output: " << output); - std::string line; - std::istringstream is(output); - while (std::getline(is, line)) - { - std::string::size_type mosPosition = line.find("MOS = "); - if ( mosPosition != std::string::npos) + std::string line; + std::istringstream is(output); + while (std::getline(is, line)) { - estimation = line.substr(mosPosition + 6); - boost::algorithm::trim(estimation); + std::string::size_type mosPosition = line.find("MOS = "); + if ( mosPosition != std::string::npos) + { + estimation = line.substr(mosPosition + 6); + boost::algorithm::trim(estimation); + } } - } - if (!estimation.size()) - { - // Dump utility output if estimation failed - ICELogCritical(<< "PVQA failed with message: " << output); - return; - } + if (!estimation.size()) + { + // Dump utility output if estimation failed + ICELogCritical(<< "PVQA failed with message: " << output); + return; + } - // Read intervals report file - if (boost::filesystem::exists(intervalPath) && !estimation.empty()) - { - std::ifstream t(intervalPath); - std::string str((std::istreambuf_iterator(t)), - std::istreambuf_iterator()); - intervals = str; - } + // Read intervals report file + if (boost::filesystem::exists(intervalPath) && !estimation.empty()) + { + std::ifstream t(intervalPath); + std::string str((std::istreambuf_iterator(t)), + std::istreambuf_iterator()); + intervals = str; + } #endif } float getSevanaMos(const std::string& audioPath, const std::string& intervalReportPath, std::string& intervalReport) { - // Find Sevana MOS estimation - ICELogDebug( << "Running MOS utitlity on resulted PCM file " << audioPath ); - try - { - std::string buffer; - SevanaMosUtility::run(audioPath, intervalReportPath, buffer, intervalReport); - ICELogDebug( << "MOS utility is finished on PCM file " << audioPath ); - return (float)atof(buffer.c_str()); - } - catch(std::exception& e) - { - ICELogCritical( << "MOS utility failed on PCM file " << audioPath << ". Error msg: " << e.what() ); - return 0.0; - } + // Find Sevana MOS estimation + ICELogDebug( << "Running MOS utitlity on resulted PCM file " << audioPath ); + try + { + std::string buffer; + SevanaMosUtility::run(audioPath, intervalReportPath, buffer, intervalReport); + ICELogDebug( << "MOS utility is finished on PCM file " << audioPath ); + return (float)atof(buffer.c_str()); + } + catch(std::exception& e) + { + ICELogCritical( << "MOS utility failed on PCM file " << audioPath << ". Error msg: " << e.what() ); + return 0.0; + } } // ------------------- SevanaPVQA ------------------- @@ -137,13 +137,13 @@ bool SevanaPVQA::mPvqaLoaded = false; std::string SevanaPVQA::getVersion() { - return PVQA_GetVersion(); + return PVQA_GetVersion(); } #if defined(TARGET_ANDROID) void SevanaPVQA::setupAndroidEnvironment(void *environment, void *appcontext) { - PVQA_SetupAndroidEnvironment(environment, appcontext); + PVQA_SetupAndroidEnvironment(environment, appcontext); } #endif @@ -153,44 +153,44 @@ bool SevanaPVQA::initializeLibrary(const std::string& pathToLicenseFile, const s ICELogInfo(<< "Sevana PVQA is about to be initialized."); - // Initialize PVQA library - if (!mLibraryConfiguration) - { - mInstanceCounter = 0; - mLibraryErrorCode = PVQA_InitLib(const_cast(pathToLicenseFile.c_str())); - if (mLibraryErrorCode) - { - ICELogCritical(<< "Problem when initializing PVQA library. Error code: " << mLibraryErrorCode - << ". Path to license file is " << pathToLicenseFile - << ". Path to config file is " << pathToConfigFile); - return false; - } - - mLibraryConfiguration = PVQA_LoadCFGFile(const_cast(pathToConfigFile.c_str()), &mLibraryErrorCode); + // Initialize PVQA library if (!mLibraryConfiguration) { - PVQA_ReleaseLib(); - ICELogCritical(<< "Problem with PVQA configuration file."); - return false; + mInstanceCounter = 0; + mLibraryErrorCode = PVQA_InitLib(const_cast(pathToLicenseFile.c_str())); + if (mLibraryErrorCode) + { + ICELogCritical(<< "Problem when initializing PVQA library. Error code: " << mLibraryErrorCode + << ". Path to license file is " << pathToLicenseFile + << ". Path to config file is " << pathToConfigFile); + return false; + } + + mLibraryConfiguration = PVQA_LoadCFGFile(const_cast(pathToConfigFile.c_str()), &mLibraryErrorCode); + if (!mLibraryConfiguration) + { + PVQA_ReleaseLib(); + ICELogCritical(<< "Problem with PVQA configuration file."); + return false; + } + mPvqaLoaded = true; } - mPvqaLoaded = true; - } - return true; + return true; } bool SevanaPVQA::isInitialized() { - return mPvqaLoaded; + return mPvqaLoaded; } int SevanaPVQA::getLibraryError() { - return mLibraryErrorCode; + return mLibraryErrorCode; } void SevanaPVQA::releaseLibrary() { - PVQA_ReleaseLib(); + PVQA_ReleaseLib(); } SevanaPVQA::SevanaPVQA() @@ -199,7 +199,7 @@ SevanaPVQA::SevanaPVQA() SevanaPVQA::~SevanaPVQA() { - close(); + close(); } void SevanaPVQA::open(double interval, Model model) @@ -222,177 +222,188 @@ void SevanaPVQA::open(double interval, Model model) return; } - ICELogDebug(<<"Attempt to create PVQA instance."); - mProcessedSamples = 0; - mModel = model; - mIntervalLength = interval; - mAudioLineInitialized = false; + ICELogDebug(<<"Attempt to create PVQA instance."); + mProcessedSamples = 0; + mModel = model; + mIntervalLength = interval; + mAudioLineInitialized = false; - mContext = PVQA_CreateAudioQualityAnalyzer(mLibraryConfiguration); - if (!mContext) - { - ICELogCritical(<< "Failed to create PVQA instance. Instance counter: " << mInstanceCounter); - mOpenFailed = true; - return; - } + mContext = PVQA_CreateAudioQualityAnalyzer(mLibraryConfiguration); + if (!mContext) + { + ICELogCritical(<< "Failed to create PVQA instance. Instance counter: " << mInstanceCounter); + mOpenFailed = true; + return; + } - mInstanceCounter++; + mInstanceCounter++; - int rescode = 0; - rescode = PVQA_AudioQualityAnalyzerSetIntervalLength(mContext, interval); - if (rescode) - { - ICELogCritical(<< "Failed to set interval length on PVQA instance. Result code: " << rescode); - close(); - mOpenFailed = true; - return; - } - - if (mModel == Model::Stream) - { - rescode = PVQA_OnStartStreamData(mContext); + int rescode = 0; + rescode = PVQA_AudioQualityAnalyzerSetIntervalLength(mContext, interval); if (rescode) { - ICELogCritical(<< "Failed to start streaming analysis on PVQA instance. Result code: " << rescode); - close(); - mOpenFailed = true; - return; + ICELogCritical(<< "Failed to set interval length on PVQA instance. Result code: " << rescode); + close(); + mOpenFailed = true; + return; } - } - ICELogDebug(<<"PVQA instance is created. Instance counter: " << mInstanceCounter); + + if (mModel == Model::Stream) + { + rescode = PVQA_OnStartStreamData(mContext); + if (rescode) + { + ICELogCritical(<< "Failed to start streaming analysis on PVQA instance. Result code: " << rescode); + close(); + mOpenFailed = true; + return; + } + } + ICELogDebug(<<"PVQA instance is created. Instance counter: " << mInstanceCounter); } void SevanaPVQA::close() { - if (mContext) - { - ICELogDebug(<< "Attempt to destroy PVQA instance."); - PVQA_ReleaseAudioQualityAnalyzer(mContext); - mInstanceCounter--; - ICELogDebug(<< "PVQA instance destroyed. Current instance counter: " << mInstanceCounter); - mContext = nullptr; - mOpenFailed = false; - } + if (mContext) + { + ICELogDebug(<< "Attempt to destroy PVQA instance."); + PVQA_ReleaseAudioQualityAnalyzer(mContext); + mInstanceCounter--; + ICELogDebug(<< "PVQA instance destroyed. Current instance counter: " << mInstanceCounter); + mContext = nullptr; + mOpenFailed = false; + } } bool SevanaPVQA::isOpen() const { - return mContext != nullptr; + return mContext != nullptr; } void SevanaPVQA::update(int samplerate, int channels, const void *pcmBuffer, int pcmLength) { - if (!mContext) - { - ICELogCritical(<< "No PVQA context."); - return; - } - // Model is assert here as it can be any if context is not created. - assert (mModel == Model::Stream); + if (!mContext) + { + ICELogCritical(<< "No PVQA context."); + return; + } + // Model is assert here as it can be any if context is not created. + assert (mModel == Model::Stream); - TPVQA_AudioItem item; - item.dNChannels = channels; - item.dSampleRate = samplerate; - item.dNSamples = pcmLength / 2 / channels; - item.pSamples = (short*)pcmBuffer; - int rescode = PVQA_OnAddStreamAudioData(mContext, &item); - if (rescode) - { - ICELogCritical(<< "Failed to stream data to PVQA instance. Result code: " << rescode); - } - int milliseconds = pcmLength / 2 / channels / (samplerate / 1000); - mProcessedMilliseconds += milliseconds; - mAllProcessedMilliseconds += milliseconds; + TPVQA_AudioItem item; + item.dNChannels = channels; + item.dSampleRate = samplerate; + item.dNSamples = pcmLength / 2 / channels; + item.pSamples = (short*)pcmBuffer; + int rescode = PVQA_OnAddStreamAudioData(mContext, &item); + if (rescode) + { + ICELogCritical(<< "Failed to stream data to PVQA instance. Result code: " << rescode); + } + int milliseconds = pcmLength / 2 / channels / (samplerate / 1000); + mProcessedMilliseconds += milliseconds; + mAllProcessedMilliseconds += milliseconds; } SevanaPVQA::DetectorsList SevanaPVQA::getDetectorsNames(const std::string& report) { - DetectorsList result; + DetectorsList result; - if (!report.empty()) - { - std::istringstream iss(report); - CsvReader reader(iss); - reader.readLine(result.mNames); - result.mStartIndex = 2; - - // Remove first columns - if (result.mStartIndex < (int)result.mNames.size() - 1) + if (!report.empty()) { - result.mNames.erase(result.mNames.begin(), result.mNames.begin() + result.mStartIndex); + std::istringstream iss(report); + CsvReader reader(iss); + reader.readLine(result.mNames); + result.mStartIndex = 2; - // Remove last column - result.mNames.erase(result.mNames.begin() + result.mNames.size() - 1); + // Remove first columns + if (result.mStartIndex < (int)result.mNames.size() - 1) + { + result.mNames.erase(result.mNames.begin(), result.mNames.begin() + result.mStartIndex); - for (auto& name: result.mNames) - name = StringHelper::trim(name); + // Remove last column + result.mNames.erase(result.mNames.begin() + result.mNames.size() - 1); + + for (auto& name: result.mNames) + name = StringHelper::trim(name); + } } - } - return result; + return result; } -float SevanaPVQA::getResults(std::string& report, const EchoData** echo, int samplerate, Codec codec) +float SevanaPVQA::getResults(std::string& report, EchoData** echo, int samplerate, Codec codec) { - if (!mContext) - { - ICELogCritical(<< "No PVQA context."); - return 0.0; - } - - if (mModel == Model::Stream) - { - if (mProcessedMilliseconds == 0) + if (!mContext) { - ICELogCritical(<< "No audio in PVQA."); - return -1; + ICELogCritical(<< "No PVQA context."); + return 0.0; } - if (PVQA_OnFinalizeStream(mContext, (long)samplerate)) + if (mModel == Model::Stream) { - ICELogCritical(<< "Failed to finalize results from PVQA."); - return -1; + if (mProcessedMilliseconds == 0) + { + ICELogCritical(<< "No audio in PVQA."); + return -1; + } + + if (PVQA_OnFinalizeStream(mContext, (long)samplerate)) + { + ICELogCritical(<< "Failed to finalize results from PVQA."); + return -1; + } + ICELogInfo(<< "Processed " << mProcessedMilliseconds << " milliseconds."); } - ICELogInfo(<< "Processed " << mProcessedMilliseconds << " milliseconds."); - } - TPVQA_Results results; - if (PVQA_FillQualityResultsStruct(mContext, &results)) - { - ICELogCritical(<< "Failed to get results from PVQA."); - return -1; - } - - int reportLength = PVQA_GetQualityStringSize(mContext); - if (reportLength) - { - char* buffer = (char*)alloca(reportLength + 1); - if (PVQA_FillQualityString(mContext, buffer)) + TPVQA_Results results; + if (PVQA_FillQualityResultsStruct(mContext, &results)) { - ICELogCritical(<< "Failed to fill intervals report."); + ICELogCritical(<< "Failed to get results from PVQA."); + return -1; + } + + int reportLength = PVQA_GetQualityStringSize(mContext); + if (reportLength) + { + char* buffer = (char*)alloca(reportLength + 1); + if (PVQA_FillQualityString(mContext, buffer)) + { + ICELogCritical(<< "Failed to fill intervals report."); + } + else + report = buffer; } - else - report = buffer; - } #if defined(TARGET_LINUX) && defined(PVQA_WITH_ECHO_DATA) - if (mModel == SevanaPVQA::Model::Stream && echo) - { - // Return echo detector counters - // Get list of names for echo detector - for debugging only - std::vector names; - int errCode = 0; - const char** iNames = (const char **)PVQA_GetProcessorValuesNamesList(mContext, PVQA_ECHO_DETECTOR_NAME, &errCode); - if (!errCode && iNames) + if (mModel == SevanaPVQA::Model::Stream && echo) { - int nameIndex = 0; - for(const char * locName = iNames[nameIndex]; locName; locName = iNames[++nameIndex]) - names.push_back(locName); + // Return echo detector counters + // Get list of names for echo detector - for debugging only + std::vector names; + int errCode = 0; + const char** iNames = (const char **)PVQA_GetProcessorValuesNamesList(mContext, PVQA_ECHO_DETECTOR_NAME, &errCode); + if (!errCode && iNames) + { + int nameIndex = 0; + for(const char * locName = iNames[nameIndex]; locName; locName = iNames[++nameIndex]) + names.push_back(locName); - // Get values for echo detector - *echo = PVQA_GetProcessorValuesList(mContext, PVQA_ECHO_DETECTOR_NAME, 0, mProcessedMilliseconds, "values", &errCode); - - // For debugging only - /*if (*echo) + // Get values for echo detector + PVQA_Array2D* array = PVQA_GetProcessorValuesList(mContext, PVQA_ECHO_DETECTOR_NAME, 0, mProcessedMilliseconds, "values", &errCode); + if (array) + { + *echo = new std::vector>(); + for (int r = 0; r < array->rows; r++) + { + std::vector row; + for (int c = 0; c < array->columns; c++) + row.push_back(array->data[r * array->columns + c]); + (*echo)->push_back(row); + } + PVQA_ReleaseArray2D(array); array = nullptr; + } + // For debugging only + /*if (*echo) { for (const auto& row: **echo) { @@ -402,53 +413,53 @@ float SevanaPVQA::getResults(std::string& report, const EchoData** echo, int sam std::cout << ">" << std::endl; } }*/ - // No need to delete maxValues - it will be deleted on PVQA analyzer context freeing. + // No need to delete maxValues - it will be deleted on PVQA analyzer context freeing. + } } - } #endif - // Limit maximal value of MOS depending on codec - float result = (float)results.dMOSLike; - float mv = 5.0; - switch (codec) - { - case Codec::G711: mv = 4.1f; break; - case Codec::G729: mv = 3.92f; break; - default: - mv = 5.0; - } + // Limit maximal value of MOS depending on codec + float result = (float)results.dMOSLike; + float mv = 5.0; + switch (codec) + { + case Codec::G711: mv = 4.1f; break; + case Codec::G729: mv = 3.92f; break; + default: + mv = 5.0; + } - return std::min(result, mv); + return std::min(result, mv); } void SevanaPVQA::setPathToDumpFile(const std::string& path) { - mDumpWavPath = path; + mDumpWavPath = path; } float SevanaPVQA::process(int samplerate, int channels, const void *pcmBuffer, int pcmLength, std::string &report, Codec codec) { - //std::cout << "Sent " << pcmLength << " bytes of audio to analyzer." << std::endl; - assert (mModel == Model::Interval); - if (!mContext) - return 0.0; + //std::cout << "Sent " << pcmLength << " bytes of audio to analyzer." << std::endl; + assert (mModel == Model::Interval); + if (!mContext) + return 0.0; - /*if (!mAudioLineInitialized) + /*if (!mAudioLineInitialized) { mAudioLineInitialized = true; if (PVQA_AudioQualityAnalyzerCreateDelayLine(mContext, samplerate, channels, 20)) ICELogCritical(<< "Failed to create delay line."); }*/ - TPVQA_AudioItem item; - item.dNChannels = channels; - item.dSampleRate = samplerate; - item.dNSamples = pcmLength / 2 / channels; - item.pSamples = (short*)pcmBuffer; + TPVQA_AudioItem item; + item.dNChannels = channels; + item.dSampleRate = samplerate; + item.dNSamples = pcmLength / 2 / channels; + item.pSamples = (short*)pcmBuffer; - //std::cout << "Sending chunk of audio with rate = " << samplerate << ", channels = " << channels << ", number of samples " << item.dNSamples << std::endl; + //std::cout << "Sending chunk of audio with rate = " << samplerate << ", channels = " << channels << ", number of samples " << item.dNSamples << std::endl; - /* + /* if (!mDumpWavPath.empty()) { WavFileWriter writer; @@ -458,81 +469,81 @@ float SevanaPVQA::process(int samplerate, int channels, const void *pcmBuffer, i ICELogCritical(<< "Sending chunk of audio with rate = " << samplerate << ", channels = " << channels << ", number of samples " << item.dNSamples); } */ - int code = PVQA_OnTestAudioData(mContext, &item); - if (code) - { - ICELogCritical(<< "Failed to run PVQA on audio buffer with code " << code); - return 0.0; - } + int code = PVQA_OnTestAudioData(mContext, &item); + if (code) + { + ICELogCritical(<< "Failed to run PVQA on audio buffer with code " << code); + return 0.0; + } - /* + /* if (item.pSamples != pcmBuffer || item.dNSamples != pcmLength / 2 / channels || item.dSampleRate != samplerate || item.dNChannels != channels) { ICELogCritical(<< "PVQA changed input parameters!!!!"); } */ - // Increase counter of processed samples - mProcessedSamples += pcmLength / channels / 2; + // Increase counter of processed samples + mProcessedSamples += pcmLength / channels / 2; - int milliseconds = pcmLength / channels / 2 / (samplerate / 1000); - mProcessedMilliseconds += milliseconds; + int milliseconds = pcmLength / channels / 2 / (samplerate / 1000); + mProcessedMilliseconds += milliseconds; - // Overall counter - mAllProcessedMilliseconds += milliseconds; + // Overall counter + mAllProcessedMilliseconds += milliseconds; - // Get results - return getResults(report, nullptr, samplerate, codec); + // Get results + return getResults(report, nullptr, samplerate, codec); } struct RgbColor { - uint8_t mRed = 0; - uint8_t mGreen = 0; - uint8_t mBlue = 0; + uint8_t mRed = 0; + uint8_t mGreen = 0; + uint8_t mBlue = 0; - static RgbColor parse(uint32_t rgb) - { - RgbColor result; - result.mBlue = (uint8_t)(rgb & 0xff); - result.mGreen = (uint8_t)((rgb >> 8) & 0xff); - result.mRed = (uint8_t)((rgb >> 16) & 0xff); - return result; - } + static RgbColor parse(uint32_t rgb) + { + RgbColor result; + result.mBlue = (uint8_t)(rgb & 0xff); + result.mGreen = (uint8_t)((rgb >> 8) & 0xff); + result.mRed = (uint8_t)((rgb >> 16) & 0xff); + return result; + } - std::string toHex() const - { - char result[7]; - sprintf(result, "%02x%02x%02x", int(mRed), int(mGreen), int(mBlue)); - return std::string(result); - } + std::string toHex() const + { + char result[7]; + sprintf(result, "%02x%02x%02x", int(mRed), int(mGreen), int(mBlue)); + return std::string(result); + } }; int SevanaPVQA::getSize() const { - int result = 0; - result += sizeof(*this); + int result = 0; + result += sizeof(*this); - // TODO: add PVQA analyzer size - return result; + // TODO: add PVQA analyzer size + return result; } std::string SevanaPVQA::mosToColor(float mos) { - // Limit MOS value by 5.0 - mos = mos > 5.0f ? 5.0f : mos; - mos = mos < 1.0f ? 1.0f : mos; + // Limit MOS value by 5.0 + mos = mos > 5.0f ? 5.0f : mos; + mos = mos < 1.0f ? 1.0f : mos; - // Split to components - RgbColor start = RgbColor::parse(MOS_BEST_COLOR), end = RgbColor::parse(MOS_BAD_COLOR); + // Split to components + RgbColor start = RgbColor::parse(MOS_BEST_COLOR), end = RgbColor::parse(MOS_BAD_COLOR); - float mosFraction = (mos - 1.0f) / 4.0f; + float mosFraction = (mos - 1.0f) / 4.0f; - end.mBlue += (uint8_t)((start.mBlue - end.mBlue) * mosFraction); - end.mGreen += (uint8_t)((start.mGreen - end.mGreen) * mosFraction); - end.mRed += (uint8_t)((start.mRed - end.mRed) * mosFraction); + end.mBlue += (uint8_t)((start.mBlue - end.mBlue) * mosFraction); + end.mGreen += (uint8_t)((start.mGreen - end.mGreen) * mosFraction); + end.mRed += (uint8_t)((start.mRed - end.mRed) * mosFraction); - return end.toHex(); + return end.toHex(); } } // end of namespace MT @@ -550,343 +561,343 @@ namespace MT int SevanaAqua::initializeLibrary(const std::string& pathToLicenseFile) { - //char buffer[pathToLicenseFile.length() + 1]; - //strcpy(buffer, pathToLicenseFile.c_str()); - return SSA_InitLib(const_cast(pathToLicenseFile.data())); + //char buffer[pathToLicenseFile.length() + 1]; + //strcpy(buffer, pathToLicenseFile.c_str()); + return SSA_InitLib(const_cast(pathToLicenseFile.data())); } void SevanaAqua::releaseLibrary() { - SSA_ReleaseLib(); + SSA_ReleaseLib(); } std::string SevanaAqua::FaultsReport::toText() const { - std::ostringstream oss; + std::ostringstream oss; - if (mSignalAdvancedInMilliseconds > -4999.0) - oss << "Signal advanced in milliseconds: " << mSignalAdvancedInMilliseconds << std::endl; - if (mMistimingInPercents > -4999.0) - oss << "Mistiming in percents: " << mMistimingInPercents << std::endl; + if (mSignalAdvancedInMilliseconds > -4999.0) + oss << "Signal advanced in milliseconds: " << mSignalAdvancedInMilliseconds << std::endl; + if (mMistimingInPercents > -4999.0) + oss << "Mistiming in percents: " << mMistimingInPercents << std::endl; - for (ResultMap::const_iterator resultIter = mResultMap.begin(); resultIter != mResultMap.end(); resultIter++) - { - oss << resultIter->first << ":\t\t\t" << resultIter->second.mSource << " : \t" << resultIter->second.mDegrated << " \t" << resultIter->second.mUnit << std::endl; - } + for (ResultMap::const_iterator resultIter = mResultMap.begin(); resultIter != mResultMap.end(); resultIter++) + { + oss << resultIter->first << ":\t\t\t" << resultIter->second.mSource << " : \t" << resultIter->second.mDegrated << " \t" << resultIter->second.mUnit << std::endl; + } - return oss.str(); + return oss.str(); } Json::Value SevanaAqua::FaultsReport::toJson() const { - std::ostringstream oss; - Json::Value result; + std::ostringstream oss; + Json::Value result; - result["Mistiming"] = mMistimingInPercents; - result["SignalAdvanced"] = mSignalAdvancedInMilliseconds; - Json::Value items; - for (ResultMap::const_iterator resultIter = mResultMap.begin(); resultIter != mResultMap.end(); resultIter++) - { - Json::Value item; - item["name"] = resultIter->first; - item["source"] = resultIter->second.mSource; - item["degrated"] = resultIter->second.mDegrated; - item["unit"] = resultIter->second.mUnit; + result["Mistiming"] = mMistimingInPercents; + result["SignalAdvanced"] = mSignalAdvancedInMilliseconds; + Json::Value items; + for (ResultMap::const_iterator resultIter = mResultMap.begin(); resultIter != mResultMap.end(); resultIter++) + { + Json::Value item; + item["name"] = resultIter->first; + item["source"] = resultIter->second.mSource; + item["degrated"] = resultIter->second.mDegrated; + item["unit"] = resultIter->second.mUnit; - items.append(item); - } + items.append(item); + } - result["items"] = items; + result["items"] = items; - return result; + return result; } std::string SevanaAqua::getVersion() { - TSSA_AQuA_Info* info = SSA_GetPAQuAInfo(); - if (info) - return info->dVersionString; + TSSA_AQuA_Info* info = SSA_GetPAQuAInfo(); + if (info) + return info->dVersionString; - return ""; + return ""; } SevanaAqua::SevanaAqua() { - open(); + open(); } SevanaAqua::~SevanaAqua() { - close(); + close(); } void SevanaAqua::open() { - std::unique_lock l(mMutex); - if (mContext) - return; + std::unique_lock l(mMutex); + if (mContext) + return; - mContext = SSA_CreateAudioQualityAnalyzer(); - if (!mContext) - ; + mContext = SSA_CreateAudioQualityAnalyzer(); + if (!mContext) + ; - //setParam("OutputFormats", "json"); + //setParam("OutputFormats", "json"); } void SevanaAqua::close() { - std::unique_lock l(mMutex); - if (!mContext) - return; - SSA_ReleaseAudioQualityAnalyzer(mContext); - mContext = nullptr; + std::unique_lock l(mMutex); + if (!mContext) + return; + SSA_ReleaseAudioQualityAnalyzer(mContext); + mContext = nullptr; } bool SevanaAqua::isOpen() const { - return mContext != nullptr; + return mContext != nullptr; } void SevanaAqua::setTempPath(const std::string& temp_path) { - mTempPath = temp_path; + mTempPath = temp_path; } std::string SevanaAqua::getTempPath() const { - return mTempPath; + return mTempPath; } SevanaAqua::CompareResult SevanaAqua::compare(AudioBuffer& reference, AudioBuffer& test) { - // Clear previous temporary file - if (!mTempPath.empty()) - ::remove(mTempPath.c_str()); + // Clear previous temporary file + if (!mTempPath.empty()) + ::remove(mTempPath.c_str()); - // Result value - CompareResult r; + // Result value + CompareResult r; - std::unique_lock l(mMutex); + std::unique_lock l(mMutex); - if (!mContext || !reference.isInitialized() || !test.isInitialized()) - return r; + if (!mContext || !reference.isInitialized() || !test.isInitialized()) + return r; - // Make analysis - TSSA_AQuA_AudioData aad; - aad.dSrcData.dNChannels = reference.mChannels; - aad.dSrcData.dSampleRate = reference.mRate; - aad.dSrcData.pSamples = (short*)reference.mData->data(); - aad.dSrcData.dNSamples = (long)reference.mData->size() / 2 / reference.mChannels; + // Make analysis + TSSA_AQuA_AudioData aad; + aad.dSrcData.dNChannels = reference.mChannels; + aad.dSrcData.dSampleRate = reference.mRate; + aad.dSrcData.pSamples = (short*)reference.mData->data(); + aad.dSrcData.dNSamples = (long)reference.mData->size() / 2 / reference.mChannels; - aad.dTstData.dNChannels = test.mChannels; - aad.dTstData.dSampleRate = test.mRate; - aad.dTstData.pSamples = (short*)test.mData->data(); - aad.dTstData.dNSamples = (long)test.mData->size() / 2 / test.mChannels; + aad.dTstData.dNChannels = test.mChannels; + aad.dTstData.dSampleRate = test.mRate; + aad.dTstData.pSamples = (short*)test.mData->data(); + aad.dTstData.dNSamples = (long)test.mData->size() / 2 / test.mChannels; - int rescode; - rescode = SSA_OnTestAudioData(mContext, &aad); - if (rescode) - return r; + int rescode; + rescode = SSA_OnTestAudioData(mContext, &aad); + if (rescode) + return r; - // Get results + // Get results - int len = SSA_GetQualityStringSize(mContext); - char* qs = (char*)alloca(len + 10); - SSA_FillQualityString(mContext, qs); - //std::cout << qs << std::endl; - std::istringstream iss(qs); - while (!iss.eof()) - { - std::string l; - std::getline(iss, l); - - // Split by : - std::vector p; - StringHelper::split(l, p, "\t"); - if (p.size() == 3) + int len = SSA_GetQualityStringSize(mContext); + char* qs = (char*)alloca(len + 10); + SSA_FillQualityString(mContext, qs); + //std::cout << qs << std::endl; + std::istringstream iss(qs); + while (!iss.eof()) { - p[1] = StringHelper::trim(p[1]); - p[2] = StringHelper::trim(p[2]); - r.mReport[p[1]] = p[2]; + std::string l; + std::getline(iss, l); + + // Split by : + std::vector p; + StringHelper::split(l, p, "\t"); + if (p.size() == 3) + { + p[1] = StringHelper::trim(p[1]); + p[2] = StringHelper::trim(p[2]); + r.mReport[p[1]] = p[2]; + } } - } - - len = SSA_GetSrcSignalSpecSize(mContext); - float* srcSpecs = new float[len]; - SSA_FillSrcSignalSpecArray(mContext, srcSpecs); - Json::Value src_spec_signal; - for(int i=0; i<16 && i 0) { - faults_str = new char[faults_str_len + 1]; - SSA_FillFaultsAnalysisString(mContext, faults_str); - faults_str[faults_str_len] = 0; + if (mTempPath.empty()) + { + faults_str_len = SSA_GetFaultsAnalysisStringSize(mContext); + + if (faults_str_len > 0) { + faults_str = new char[faults_str_len + 1]; + SSA_FillFaultsAnalysisString(mContext, faults_str); + faults_str[faults_str_len] = 0; + } } - } - char* pairs_str = nullptr; - int pairs_str_len = SSA_GetSpecPairsStringSize(mContext); - if (pairs_str_len > 0) - { - char *pairs_str = new char[pairs_str_len + 1]; - SSA_FillSpecPairsString(mContext, pairs_str, pairs_str_len); - pairs_str[pairs_str_len] = 0; - } + char* pairs_str = nullptr; + int pairs_str_len = SSA_GetSpecPairsStringSize(mContext); + if (pairs_str_len > 0) + { + char *pairs_str = new char[pairs_str_len + 1]; + SSA_FillSpecPairsString(mContext, pairs_str, pairs_str_len); + pairs_str[pairs_str_len] = 0; + } - TSSA_AQuA_Results iResults; - SSA_FillQualityResultsStruct(mContext, &iResults); - r.mReport["dPercent"] = iResults.dPercent; - r.mReport["dMOSLike"] = iResults.dMOSLike; + TSSA_AQuA_Results iResults; + SSA_FillQualityResultsStruct(mContext, &iResults); + r.mReport["dPercent"] = iResults.dPercent; + r.mReport["dMOSLike"] = iResults.dMOSLike; - if (faults_str_len > 0) - { - std::istringstream iss(faults_str); - r.mFaults = loadFaultsReport(iss); - } - else - if (!mTempPath.empty()) - { - std::ifstream ifs(mTempPath.c_str()); - r.mFaults = loadFaultsReport(ifs); - } + if (faults_str_len > 0) + { + std::istringstream iss(faults_str); + r.mFaults = loadFaultsReport(iss); + } + else + if (!mTempPath.empty()) + { + std::ifstream ifs(mTempPath.c_str()); + r.mFaults = loadFaultsReport(ifs); + } - delete[] faults_str; faults_str = nullptr; - delete[] pairs_str; pairs_str = nullptr; + delete[] faults_str; faults_str = nullptr; + delete[] pairs_str; pairs_str = nullptr; - r.mMos = (float)iResults.dMOSLike; + r.mMos = (float)iResults.dMOSLike; - return r; + return r; } void SevanaAqua::configureWith(const Config& config) { - if (!mContext) - return; + if (!mContext) + return; - for (auto& item: config) - { - const std::string& name = item.first; - const std::string& value = item.second; - if (!SSA_SetAnyString(mContext, const_cast(name.c_str()), const_cast(value.c_str()))) - throw std::runtime_error(std::string("SSA_SetAnyString returned failed for pair ") + name + " " + value); - } + for (auto& item: config) + { + const std::string& name = item.first; + const std::string& value = item.second; + if (!SSA_SetAnyString(mContext, const_cast(name.c_str()), const_cast(value.c_str()))) + throw std::runtime_error(std::string("SSA_SetAnyString returned failed for pair ") + name + " " + value); + } } SevanaAqua::Config SevanaAqua::parseConfig(const std::string& line) { - Config result; + Config result; - // Split command line to parts - std::vector pl; - StringHelper::split(line, pl, "-"); + // Split command line to parts + std::vector pl; + StringHelper::split(line, pl, "-"); - for (const std::string& s: pl) - { - std::string::size_type p = s.find(' '); - if (p != std::string::npos) + for (const std::string& s: pl) { - std::string name = StringHelper::trim(s.substr(0, p)); - std::string value = StringHelper::trim(s.substr(p + 1)); + std::string::size_type p = s.find(' '); + if (p != std::string::npos) + { + std::string name = StringHelper::trim(s.substr(0, p)); + std::string value = StringHelper::trim(s.substr(p + 1)); - result[name] = value; + result[name] = value; + } } - } - return result; + return result; } SevanaAqua::PFaultsReport SevanaAqua::loadFaultsReport(std::istream& input) { - PFaultsReport result = std::make_shared(); - std::string line; - std::vector parts; + PFaultsReport result = std::make_shared(); + std::string line; + std::vector parts; - // Parse output - while (!input.eof()) - { - std::getline(input, line); - if (line.size() < 3) - continue; - - std::string::size_type p = line.find(":"); - if (p != std::string::npos) + // Parse output + while (!input.eof()) { - std::string name = StringHelper::trim(line.substr(0, p)); + std::getline(input, line); + if (line.size() < 3) + continue; - FaultsReport::Result r; - - // Split report line to components - parts.clear(); - StringHelper::split(line.substr(p + 1), parts, " \t"); - - // Remove empty components - parts.erase(std::remove_if(parts.begin(), parts.end(), [](const std::string& item){return item.empty();}), parts.end()); - - if (parts.size() >= 2) - { - r.mSource = parts[0]; - r.mDegrated = parts[1]; - if (parts.size()> 2) - r.mUnit = parts[2]; - result->mResultMap[name] = r; - } - } - else - { - p = line.find("ms."); - if (p != std::string::npos) - { - parts.clear(); - StringHelper::split(line, parts, " \t"); - if (parts.size() >= 3) - { - if (parts.back() == "ms.") - result->mSignalAdvancedInMilliseconds = (float)std::atof(parts[parts.size() - 2].c_str()); - } - } - else - { - p = line.find("percent."); + std::string::size_type p = line.find(":"); if (p != std::string::npos) { - parts.clear(); - StringHelper::split(line, parts, " \t"); - if (parts.size() >= 3) - { - if (parts.back() == "percent.") - result->mMistimingInPercents = (float)std::atof(parts[parts.size() - 2].c_str()); - } - } - } - } - } + std::string name = StringHelper::trim(line.substr(0, p)); - return result; + FaultsReport::Result r; + + // Split report line to components + parts.clear(); + StringHelper::split(line.substr(p + 1), parts, " \t"); + + // Remove empty components + parts.erase(std::remove_if(parts.begin(), parts.end(), [](const std::string& item){return item.empty();}), parts.end()); + + if (parts.size() >= 2) + { + r.mSource = parts[0]; + r.mDegrated = parts[1]; + if (parts.size()> 2) + r.mUnit = parts[2]; + result->mResultMap[name] = r; + } + } + else + { + p = line.find("ms."); + if (p != std::string::npos) + { + parts.clear(); + StringHelper::split(line, parts, " \t"); + if (parts.size() >= 3) + { + if (parts.back() == "ms.") + result->mSignalAdvancedInMilliseconds = (float)std::atof(parts[parts.size() - 2].c_str()); + } + } + else + { + p = line.find("percent."); + if (p != std::string::npos) + { + parts.clear(); + StringHelper::split(line, parts, " \t"); + if (parts.size() >= 3) + { + if (parts.back() == "percent.") + result->mMistimingInPercents = (float)std::atof(parts[parts.size() - 2].c_str()); + } + } + } + } + } + + return result; } } // end of namespace MT diff --git a/src/engine/media/MT_SevanaMos.h b/src/engine/media/MT_SevanaMos.h index 5deb9b2c..3a30ee0c 100644 --- a/src/engine/media/MT_SevanaMos.h +++ b/src/engine/media/MT_SevanaMos.h @@ -108,7 +108,7 @@ namespace MT OPUS }; - float getResults(std::string& report, const EchoData** echo, int samplerate, Codec codec); + float getResults(std::string& report, EchoData** echo, int samplerate, Codec codec); // Report is interval report. Names are output detector names. startIndex is column's start index in interval report of first detector. struct DetectorsList