Skip to content

Commit

Permalink
Initial commit of Ripple-To-FLAC conversion code.
Browse files Browse the repository at this point in the history
  • Loading branch information
mrkrause committed Apr 20, 2015
0 parents commit e5bc131
Show file tree
Hide file tree
Showing 18 changed files with 1,337 additions and 0 deletions.
149 changes: 149 additions & 0 deletions Config.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#include "Config.h"


Config::Config(void) : valid(false), desc("Convert a Ripple NSx file to FLAC") {
desc.add_options()
("help", "Show this help message")
("input-file,i",
opts::value<std::string>(),
"NSx file to convert")
("output-dir,o",
opts::value<std::string>()->default_value("./"),
"Place the converted FLAC files in this directory")
("output-prefix",
opts::value<std::string>()->default_value("ch"),
"The converted FLAC filenames start with this, followed by the channel number")
("threads",
opts::value<unsigned>()->default_value(1),
"Number of threads to use for compression")
("read-size",
opts::value<unsigned>()->default_value(60000),
"Maximum number of samples to read at once")
("flac-compression",
opts::value<unsigned>()->default_value(8),
"FLAC compression level")
;


pos.add("input-file", 1);
pos.add("output-dir", 2);
pos.add("output-prefix", 3);
}

unsigned int Config::nThreads(void) const {
if(valid)
return _nThreads;
else
throw(std::runtime_error("Options not initalized"));
}

unsigned int Config::readSize(void) const {
if(valid)
return _readSize;
else
throw(std::runtime_error("Options not initalized"));
}

unsigned int Config::flacCompression(void) const {
if(valid)
return _flacCompression;
else
throw(std::runtime_error("Options not initalized"));
}

std::string Config::inputFile(void) const {
if(valid)
return _inputFile;
else
throw(std::runtime_error("Options not initalized"));
}

std::string Config::outputDir(void) const {
if(valid)
return _outputDir;
else
throw(std::runtime_error("Options not initalized"));
}

std::string Config::outputPrefix(void) const {
if(valid)
return _outputPrefix;
else
throw(std::runtime_error("Options not initalized"));
}

void Config::parse(int argc, char* argv[]) {

opts::variables_map vm;
opts::store(opts::command_line_parser(argc, argv).
options(desc).positional(pos).run(), vm);

if(vm.count("help") || argc==1) {
std::cout << desc << std::endl;
exit(0);
}

opts::notify(vm);

// Check input file and return it if valid
_inputFile = getInputFilename(vm);

// Check output directory and return it if valid
_outputDir = getOutputDir(vm);

_outputPrefix = vm["output-prefix"].as<std::string>();
_nThreads = vm["threads"].as<unsigned>();
_readSize = vm["read-size"].as<unsigned>();
_flacCompression = vm["flac-compression"].as<unsigned>();
valid = true;
}


std::string Config::getInputFilename(const opts::variables_map& vm) {

if(!vm.count("input-file")) {
throw(std::runtime_error("No input file found!"));
} else {

std::string filename = vm["input-file"].as<std::string>();
fs::path inputPath(filename);
if(fs::exists(inputPath)) {
if(fs::is_regular_file(inputPath) || fs::is_symlink(inputPath)) {
return filename;
} else {
throw(std::runtime_error("The input file is not a regular file"));
}
} else {
throw(std::runtime_error("The input file does not exist"));
}
}
}

std::string Config::getOutputDir(const opts::variables_map& vm) {
std::string filename = vm["output-dir"].as<std::string>();
outputPath = fs::path(filename);

if(fs::exists(outputPath) && fs::is_regular_file(outputPath))
throw(std::runtime_error("Cannot create a directory with the same name as a file!"));

fs::create_directories(outputPath); //Throws on failure, does nothing if exists
return filename;
}

std::string Config::outputFilename(std::uint16_t electrode) const {
std::ostringstream str;

str << outputPrefix() << std::setfill('0') << std::setw(3) << electrode;
str << ".flac";
std::string outfile = (outputPath / str.str()).string();

return(outfile);
}








52 changes: 52 additions & 0 deletions Config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#ifndef __CONVERT_CONFIG__
#define __CONVERT_CONFIG__

#include <exception>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>

#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>

namespace opts = boost::program_options;
namespace fs = boost::filesystem;

class Config {

public:
Config();
void parse(int argc, char* argv[]);

std::string inputFile(void) const;
std::string outputDir(void) const;
std::string outputPrefix(void) const;

unsigned int nThreads(void) const;
unsigned int readSize(void) const;
unsigned int flacCompression(void) const;

std::string outputFilename(std::uint16_t electrode) const;

private:
bool valid;

std::string _inputFile;
std::string _outputDir;
std::string _outputPrefix;

fs::path outputPath;
unsigned _nThreads;
unsigned _readSize;
unsigned _flacCompression;

opts::positional_options_description pos;
opts::options_description desc;

std::string getInputFilename(const opts::variables_map& vm);
std::string getOutputDir(const opts::variables_map& vm);
};


#endif /* __CONVERT_CONFIG__*/
17 changes: 17 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CC=g++
CFLAGS= -O3 -Wall -Werror -std=gnu++11
LIBS=-lFLAC++ -lboost_program_options -lboost_filesystem -lboost_system
TARGET = rippleToFlac
DEPS = NSxFile.h Config.h
OBJ = Config.o NSxFile.o NSxChannel.o NSxHeader.o rippleToFlac.o


%.o: %.cpp $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)

rippleToFlac: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)

.PHONY: clean
clean:
rm -f *.o *~ core
17 changes: 17 additions & 0 deletions Makefile.win
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CC=i686-pc-mingw32-g++
CFLAGS= -O3 -Wall -Werror -std=gnu++11
LIBS=-logg -lFLAC -logg -lFLAC++ -logg -lFLAC -logg -lboost_program_options-mt -lboost_filesystem-mt -lboost_system-mt
TARGET = rippleToFlac
DEPS = NSxFile.h Config.h
OBJ = Config.o NSxFile.o NSxChannel.o NSxHeader.o rippleToFlac.o


%.o: %.cpp $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS) -DWINDOWS

rippleToFlac.exe: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS) $(LIBS) -static

.PHONY: clean
clean:
rm -f *.o *~ core
13 changes: 13 additions & 0 deletions NEVFile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
NEVFile::NEVFile(std::string filename) {

file.open(filename, std::ios_base::binary | std::ios_base::binary);
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
if(!file) {
throw(std::runtime_error("Cannot open file for reading"));
}

/* Read the magic word to ensure this is the right kind of file*/
std::string typeString = "NEURALEV";
file.read


17 changes: 17 additions & 0 deletions NEVFile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef __NEVFile.h__
#define __NEVFile.h__

#include <cstdint>
#include <ifstream>
#include <stdexcept>
#include <string>


class NEVFile {
public:
NEVFile(std::string filename);


protected:

}
57 changes: 57 additions & 0 deletions NSxChannel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// NSxChannel.cpp
// dumpNEV
//
// Created by Matthew Krause on 2/23/15.
// Copyright (c) 2015 Matthew Krause. All rights reserved.
//

#include "NSxChannel.h"

NSxChannel::NSxChannel(std::ifstream &file) {
std::streampos start= file.tellg();

try {
char buffer[2];
file.read(buffer, 2);
if((buffer[0] != 'C') || (buffer[1] != 'C')) {
std::cerr << "Failed on filetype " << int(buffer[0]) << int(buffer[1]) << "@" << file.tellg() << std::endl;
throw(std::runtime_error("Incorrect file type; expected a Ripple .ns5 (or other .nsX) file"));
}

file.read(reinterpret_cast<char *>(&electrodeID), sizeof(electrodeID));
file.read(reinterpret_cast<char *>(&electrodeLabel), sizeof(electrodeLabel));
file.read(reinterpret_cast<char *>(&frontEndID), sizeof(frontEndID));
file.read(reinterpret_cast<char *>(&pin), sizeof(pin));
file.read(reinterpret_cast<char *>(&maxDigital), sizeof(maxDigital));
file.read(reinterpret_cast<char *>(&minDigital), sizeof(minDigital));
file.read(reinterpret_cast<char *>(&minAnalog), sizeof(minAnalog));
file.read(reinterpret_cast<char *>(&maxAnalog), sizeof(maxAnalog));
file.read(reinterpret_cast<char *>(&unitLabel), sizeof(unitLabel));
file.read(reinterpret_cast<char *>(&highpass), sizeof(highpass));
file.read(reinterpret_cast<char *>(&lowpass), sizeof(lowpass));
}

catch (...) {
std::cerr << "Error at " << file.tellg() << std::endl;
file.seekg(start);
throw;
}
}


std::string NSxChannel::rippleSpec() const {
std::ostringstream s;

char port = (frontEndID/4) + 'A';
unsigned frontEnd = unsigned(frontEndID % 4) + 1;

s << char(port) << '-' << frontEnd << '-' << int(pin);
return(s.str());
}


std::ostream& operator<<(std::ostream& out, const NSxChannel& c) {
out << "Electrode #" << c.electrodeID << " (" << c.electrodeLabel << "): " << c.rippleSpec() << std::endl;
return out;
}
50 changes: 50 additions & 0 deletions NSxChannel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#ifndef ___NSxChannel_h__
#define ___NSxChannel_h__

#include <cstdint>
#include <fstream>
#include <stdexcept>
#include <iostream>
#include <sstream>


enum FilterType : std::uint16_t {NONE = 0, BUTTERWORTH = 1, CHEBYSHEV = 2};

#pragma pack(push,1)
struct Filter {
std::uint32_t cornerFreq;
std::uint32_t order;
FilterType type;
};
#pragma pack(pop)


class NSxChannel {

public:
NSxChannel();
NSxChannel(std::ifstream &file);

std::string rippleSpec() const;
std::uint16_t getNumericID() const {return electrodeID;}

friend std::ostream& operator<<(std::ostream& out, const NSxChannel& c);
protected:
std::uint16_t electrodeID;
char electrodeLabel[16];
char unitLabel[16];

std::uint8_t frontEndID;
std::uint8_t pin;

std::int16_t minDigital;
std::int16_t maxDigital;

std::int16_t minAnalog;
std::int16_t maxAnalog;

Filter lowpass;
Filter highpass;

};
#endif /* ___NSxChannel_h__ */
Loading

0 comments on commit e5bc131

Please sign in to comment.