Mercurial > hg > m4-sa
view runner/BinaryFeatureWriter.cpp @ 64:82248965fc74
fextractor is now a class
author | gyorgyf |
---|---|
date | Tue, 14 Feb 2012 18:27:50 +0000 |
parents | da84d2efd7a3 |
children | b2f70a775ddc |
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Sonic Annotator A utility for batch feature extraction from audio files. Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London. Copyright 2007-2008 QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #include <fstream> #include <QFileInfo> #include <ctime> #include <stdint.h> #include "BinaryFeatureWriter.h" #include "base/RealTime.h" #include "../version.h" #ifdef _WIN32 #define platform "Windows" #elif __APPLE__ #define platform "MacOS" #elif __linux__ #define platform "Linux" #else #define platform "Unix" #endif #ifdef __LP64__ //__x86_64__ #define arch "64" #elif _WIN64 #define arch "64" #else #define arch "32" #endif # define MAJOR_VERSION_PY 0 using namespace std; using namespace Vamp; // Parameter names string BinaryFeatureWriter::outputFileParam = "output"; struct BinaryFeatureWriter::OutputStream { ofstream* stream; bool newtransform; const Transform *transform; OutputStream() : newtransform(true),stream(NULL),transform(NULL) { } ~OutputStream() { if (stream != NULL) {stream->close(); delete stream;} } struct header_t { int16_t BOM16; // 16-bit BOM (FEFF as in UTF-16) int32_t BOM32; // 32-bit BOM (human readable: e.g. ABCD) char major_version; // check for binary compatibility char minor_version; // changes in txt parts only char compression; // use of stream compression (e.g. gzip) char reserved1; // reserved byte char reserved2; // reserved byte char float_size; // size of float char int_size; // size of int char info[160]; // 160 byte text field char null; }; // NULL bool open(string filename, bool append = true) { if (stream) return true; header_t header = {(int16_t) 0xFEFF,0x41424344,MAJOR_VERSION_PY,1,0,NULL,NULL,(char)sizeof(float),(char)sizeof(int),{NULL},NULL}; char* p_header = reinterpret_cast<char*>(&header); if (append) stream = new ofstream(filename.c_str(), fstream::binary | ios_base::out | ios_base::in | ofstream::ate); else stream = new ofstream(filename.c_str(), fstream::binary); if (!stream) { cerr << "ERROR: BinaryFeatureWriter::OutputStream::open(): can't open file " << filename << endl; return false; } if (append && !stream->is_open()) { cerr << endl << "NOTE: Writing new binary output file: " << filename << endl; delete stream; stream = NULL; return open(filename,false); } // verify input file format if (append) { ifstream istream; istream.open(filename.c_str(), fstream::binary | ios::in); if (!istream.is_open()) { cerr << "ERROR: BinaryFeatureWriter::OutputStream::open(): Can not verify supplied output stream." << endl; return false; } header_t iheader; istream.read(reinterpret_cast<char*>(&iheader),sizeof(header_t)); istream.close(); int16_t FEFF = 0xFEFF; if (iheader.BOM16 != FEFF) { if (iheader.BOM16 == (int16_t) 0xFFFE) { cerr << "ERROR: BinaryFeatureWriter::OutputStream::open(): This file apperas to have created on a different platform. Can not be appended. " << "Byte order mark: " << iheader.BOM32 << endl; return false; } else { cerr << "ERROR: BinaryFeatureWriter::OutputStream::open(): Invalid target file for this writer." << endl; return false; } } if (iheader.major_version != (char) MAJOR_VERSION_PY || iheader.BOM32 != (int32_t) 0x41424344) { cerr << "ERROR: BinaryFeatureWriter::OutputStream::open(): This file is not binary compatible with this version of the writer." << "file version: " << iheader.major_version << " required: " << MAJOR_VERSION_PY << endl; return false; } } if (append) stream->seekp(0,ios_base::end); if (!append) { time_t now = time(0); tm* gmtm = gmtime(&now); QString timestamp; if (gmtm != NULL) timestamp = QString("%1 %2").arg(", Created: ").arg(asctime(gmtm)).trimmed(); string info = QString(" SONIC ANNOTATOR v%1 PYTHON BINARY V0.1, Platform: %2-%3bit%4") .arg(RUNNER_VERSION) .arg(platform) .arg(arch) .arg(timestamp).toStdString(); strncpy(reinterpret_cast<char*>(&header.info),info.c_str(), info.length() <= 160 ? info.length() : 160); stream->write(p_header,sizeof(header_t)); } return true; } }; BinaryFeatureWriter::BinaryFeatureWriter() : outputFile("features") { binary = new OutputStream(); } BinaryFeatureWriter::~BinaryFeatureWriter() { if (binary) delete binary; } BinaryFeatureWriter::ParameterList BinaryFeatureWriter::getSupportedParameters() const { ParameterList pl; Parameter p; p.name = outputFileParam; p.description = "Binary output file path"; p.hasArg = true; pl.push_back(p); return pl; } void BinaryFeatureWriter::setParameters(map<string, string> ¶ms) { if (params.find(outputFileParam) != params.end()) { setOutputFile(params[outputFileParam]); params.erase(outputFileParam); } } void BinaryFeatureWriter::setOutputFile(const string &file) { outputFile = file; } void BinaryFeatureWriter::write(QString trackid, const Transform &transform, const Vamp::Plugin::OutputDescriptor& output, const Vamp::Plugin::FeatureList& featureList, std::string summaryType) { //!!! use summaryType if (summaryType != "") { //!!! IMPLEMENT cerr << "ERROR: BinaryFeatureWriter::write: Writing summaries is not yet implemented!" << endl; exit(1); } // TODO: Consider writing out NumPy arrays directly following this documentation: // https://github.com/numpy/numpy/blob/master/doc/neps/npy-format.txt // and using the .npy format // return if file could not be opened if(!openBinaryFile()) { cerr << "ERROR: BinaryFeatureWriter::write: Error opening binary output file!" << endl; exit(1); } ofstream &ofs = *(binary->stream); // The manager does not call finish() after writing different outputs from the same plugin, but we need this behaviour here: if (!binary->newtransform && binary->transform != NULL && binary->transform != &transform) finish(); // write a python dictionary string containing (some) metadata needed to interpret the results // this can be evaluated in python using the expression : d = eval(f.readline()) // given f is an open file, which should yield a valid dictionary. /* enum SampleType { /// Results from each process() align with that call's block start 0: OneSamplePerStep, /// Results are evenly spaced in time 1: FixedSampleRate, /// Results are unevenly spaced and have individual timestamps 2: VariableSampleRate }; */ if (binary->newtransform) { binary->newtransform = false; output_binCount = output.binCount; feature_count = 0; binary->transform = &transform; ofs << endl << "{" << "\"track_id\":\"" << trackid << "\"," << "\"transform_id\":\"" << transform.getIdentifier() << "\"," << "\"sample_rate\":" << transform.getSampleRate() << "," << "\"step_size\":" << transform.getStepSize() << "," << "\"block_size\":" << transform.getBlockSize() << "," << "\"window_type\":" << transform.getWindowType() << "," << "\"features_list\":" << featureList.size() << "," << "\"bin_count\":" << output.binCount << "," // << "\"output_description\":\"" << output.description << "\"," << "\"output_sample_type\":" << output.sampleType << "," << "\"output_sample_rate\":" << output.sampleRate << ","; // Write start time and duration if the transform is not for the whole file if (transform.getDuration().toString() != "0.000000000") { ofs << "\"start_time\":\"" << transform.getStartTime().toString() << "\","; ofs << "\"duration\":\"" << transform.getDuration().toString() << "\","; } // Write plugin version if known. (NOTE: using RDF transforms, it remains empty for some reason) if (!transform.getPluginVersion().isEmpty()) ofs << "\"plugin_version\":\"" << transform.getPluginVersion() << "\","; // write transform parameters into a dict: parameters:{"parameter_name":value,...} where value is float ofs << "\"parameters\":{" ; ParameterMap m = transform.getParameters(); for (ParameterMap::const_iterator i = m.begin(); i != m.end(); ++i) // note last comma is ignored by python if (i == m.begin()) ofs << QString("\"%1\":%2").arg(i->first).arg(i->second); else ofs << QString(",\"%1\":%2").arg(i->first).arg(i->second); ofs << "}"; // write the data size last, and close the line. data_size_pos = ofs.tellp(); ofs << " }" << endl; } // write the feature data feature_count += featureList.size(); for (size_t i = 0; i < featureList.size(); ++i) { for (size_t j = 0; j < featureList[i].values.size(); ++j) ofs.write( (const char*) &featureList[i].values[j], sizeof(featureList[i].values[j]) ); } // ofs << endl; // // write time stamp data // for (int i = 0; i < featureList.size(); ++i) { // for (int j = 0; j < featureList[i].values.size(); ++j) { // // float sec = (int) featureList[i].timestamp.sec; // // float nsec = (int) featureList[i].timestamp.nsec; // // (*dbfiles[id].ofs).write( (const char*) &sec, sizeof(int)); // // (*dbfiles[id].ofs).write( (const char*) &nsec, sizeof(int)); // ofs.write( (const char*) &featureList[i].timestamp.sec, sizeof(int)); // ofs.write( (const char*) &featureList[i].timestamp.nsec, sizeof(int)); // // } // // } // -- UNCOMMENT - TO - HERE -- } bool BinaryFeatureWriter::openBinaryFile() { if (outputFile.rfind(".") == string::npos) // if (!QString(outputFile.c_str()).contains(".")) return binary->open(outputFile + ".bin"); else return binary->open(outputFile); } void BinaryFeatureWriter::finish() { ofstream &ofs = *(binary->stream); binary->newtransform = true; // ofs << endl; long t = ofs.tellp(); ofs.seekp(data_size_pos); // fill in the missing information in the transform python dict that is required to read the output into an array ofs << ",\"feature_count\":" << feature_count << ",\"data_size\":" << feature_count * output_binCount * sizeof(float); ofs.seekp(t); // FileFeatureWriter::finish(); }