Marsyas  0.2
/home/gperciva/src/marsyas/src/marsyas/ArffFileSink.cpp
00001 /*
00002 ** Copyright (C) 2009 Stefaan Lippens <soxofaan@gmail.com>
00003 **
00004 ** This program is free software; you can redistribute it and/or modify
00005 ** it under the terms of the GNU General Public License as published by
00006 ** the Free Software Foundation; either version 2 of the License, or
00007 ** (at your option) any later version.
00008 **
00009 ** This program is distributed in the hope that it will be useful,
00010 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 ** GNU General Public License for more details.
00013 **
00014 ** You should have received a copy of the GNU General Public License
00015 ** along with this program; if not, write to the Free Software
00016 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017 */
00018 
00019 #include "ArffFileSink.h"
00020 
00021 #include <fstream>
00022 #include <iomanip>
00023 
00024  
00025 using std::ostringstream;
00026 using std::ofstream;
00027 using std::ios_base;
00028 using std::endl;
00029 using std::vector;
00030 using std::fixed;
00031 using std::setprecision;
00032 
00033 
00034 using namespace Marsyas;
00035 
00036 ArffFileSink::ArffFileSink(mrs_string name) : MarSystem("ArffFileSink", name)
00037 {
00038     os_ = NULL;
00039     filename_ = "";
00040     decimationCounter = 0;
00041     addControls();
00042 }
00043 
00044 ArffFileSink::ArffFileSink(const ArffFileSink& a) : MarSystem(a)
00045 {
00046     os_ = NULL;
00047     filename_ = "";
00048     decimationCounter = 0;
00049 
00052     ctrl_floatPrecision_ = getControl("mrs_natural/floatPrecision");
00053     ctrl_decimationFactor_ = getControl("mrs_natural/decimationFactor");
00054     ctrl_filename_ = getControl("mrs_string/filename");
00055 }
00056 
00057 
00058 ArffFileSink::~ArffFileSink()
00059 {
00060     closeOutput();
00061 }
00062 
00063 MarSystem*
00064 ArffFileSink::clone() const
00065 {
00066     return new ArffFileSink(*this);
00067 }
00068 
00069 void
00070 ArffFileSink::addControls()
00071 {
00073     addControl("mrs_natural/floatPrecision", 6, ctrl_floatPrecision_);
00074     addControl("mrs_natural/decimationFactor", 1, ctrl_decimationFactor_);
00075     addControl("mrs_string/filename", "data.arff", ctrl_filename_);
00076 }
00077 
00078 void
00079 ArffFileSink::myUpdate(MarControlPtr sender)
00080 {
00082     MarSystem::myUpdate(sender);
00083 
00084     // Cache float precision and decimation for usage in myProcess().
00085     floatPrecision_ = ctrl_floatPrecision_->to<mrs_natural>();
00086     decimationFactor_ = ctrl_decimationFactor_->to<mrs_natural>();
00087     // Sanitize decimation factor.
00088     decimationFactor_ = decimationFactor_ == 0 ? 1 : decimationFactor_;
00089     // Reset decimation counter variable.
00090     decimationCounter = 0;
00091 
00092 }
00093 
00095 void
00096 ArffFileSink::closeOutput()
00097 {
00098     if (os_ != NULL)
00099     {
00100         os_->flush();
00101         os_->close();
00102         delete os_;
00103     }
00104     filename_ = "";
00105 }
00106 
00108 void
00109 ArffFileSink::prepareOutput()
00110 {
00111     // If internal file name differs from the file name we should be writing to:
00112     // close previous file, open a new file and write header.
00113     if (filename_ != ctrl_filename_->to<mrs_string>())
00114     {
00115         // Close current file.
00116         closeOutput();
00117         // Open a new output stream for the new file.
00118         filename_ = ctrl_filename_->to<mrs_string>();
00119         os_ = new ofstream;
00120         os_->open(filename_.c_str());
00121         if (os_->fail()) {
00122             // \todo make this cleaner, e.g. by creating and using a dedicated
00123             // exception for IO/file errors.
00124             ostringstream oss;
00125             oss << "[Error in " << __FILE__ << ":" << __LINE__ << "] "
00126                 << "could not open file '" << filename_ << "' for writing.";
00127             throw ios_base::failure(oss.str());
00128         }
00129         // Write ARFF header.
00130         writeArffHeader();
00131     }
00132 }
00133 
00134 
00135 void
00136 ArffFileSink::writeArffHeader()
00137 {
00138     // General header stuff.
00139     (*os_) << "% ARFF file Created by Marsyas (ArffFileSink)" << endl;
00140     (*os_) << "@relation " << filename_ << endl;
00141 
00142     // Print the attributes.
00143     // Get and output the observation names.
00144     mrs_string onObsNames = ctrl_onObsNames_->to<mrs_string>();
00145     vector<mrs_string> attributeNames = stringSplit(onObsNames, ",");
00146     const mrs_natural onObservations = ctrl_onObservations_->to<mrs_natural>();
00147     // Print the observation names, if available.
00148     for (mrs_natural i = 0; i < onObservations; ++i)
00149     {
00150         if (i < (mrs_natural)attributeNames.size() && !attributeNames[i].empty())
00151         {
00152             (*os_ ) << "@attribute " << attributeNames[i] << " real" << endl;
00153         }
00154         else
00155         {
00156             (*os_ ) << "@attribute " << "untitled" << i << " real" << endl;
00157         }
00158     }
00159 
00160     // End of header, we are ready now for outputting the data.
00161     (*os_) << "\n@data" << endl;
00162 }
00163 
00164 void
00165 ArffFileSink::myProcess(realvec& in, realvec& out)
00166 {
00167     mrs_natural o,t;
00168     // Make sure we can write to the output stream.
00169     prepareOutput();
00170 
00171     // Copy input to output.
00172     for (t = 0; t < inSamples_; t++)
00173     {
00174         for (o = 0; o < inObservations_; o++)
00175         {
00176             out(o, t) = in(o, t);
00177         }
00178     }
00179 
00180     if (!ctrl_mute_->isTrue()) {
00181         // Write data to file.
00182         for (t = 0; t < inSamples_; t++)
00183         {
00184             if (decimationCounter % decimationFactor_ == 0)
00185             {
00186                 for (o = 0; o < inObservations_; o++)
00187                 {
00188                     (*os_) << fixed << setprecision(floatPrecision_) << out(o, t);
00189                     if (o < inObservations_ - 1)
00190                     {
00191                         (*os_) << ",";
00192                     }
00193                 }
00194                 (*os_) << endl;
00195             }
00196             decimationCounter ++;
00197         }
00198     }
00199 }