22 Commits

Author SHA1 Message Date
bp f7d2bcaf82 Merge branch 'dev.general_modifications' into dev 2016-08-09 19:45:20 +01:00
bp a98a77546f Updated CMakeLists and .gitignore
Modified CMakeLists to allows for easier adding of source files.
Added filetypes to .gitignore
2016-08-09 19:44:56 +01:00
bp 8912e4cad6 Merge branch 'dev.git-overcommit' into dev 2016-08-09 15:25:26 +01:00
bp e1dada8f10 Added overcommit and untracked file checker
Added overcommit for git hook management
Added a pre-commit checker to check for source files in the repo that
are neither tracked or ignored.
2016-08-09 15:23:45 +01:00
bp aa2c235e1e Merge branch 'fixup-switch' into dev 2016-08-09 10:56:40 +01:00
bp f4d151f1dd Fixed missing break statements in AudioFile switch. 2016-08-09 10:56:23 +01:00
bp 2c5d541e3c Merge branch 'dev.analyses' into dev 2016-08-09 10:52:03 +01:00
bp ed3b3a20ac Added critical missing files from previous commits. 2016-08-09 10:49:09 +01:00
bp 5fd9e91f70 Implemented initial analyses using a polymorphic base class 2016-08-09 10:49:09 +01:00
bp c24e851e4e Begun creating analysis factory object.
Created namespace for audiofile.
2016-08-09 10:48:49 +01:00
bp 3a951787af Fixed analyse member function in AnalysedAudioFile. 2016-08-07 10:07:45 +01:00
bp f8b5659d1a Merge branch 'dev.hdf5' into dev 2016-08-06 20:43:24 +01:00
bp a6a7242d22 Finished basic implementation of HDF5 in AnalysedAudioFile class. 2016-08-06 20:42:48 +01:00
bp 31ee30d9fd Fixed logging in tests and stopped HDF5 from printing exceptions automatically.
Fixed test case for ensuring group creation in AudioFile class.
2016-08-06 20:42:45 +01:00
bp e054065a49 Replaced object based logger with a global macro based logger.
Concatenator tests are now building.
2016-08-06 20:42:41 +01:00
bp 54ffb86f53 Added HDF5 to cmakelist, begun implementing HDF5 reading in AudioDatabase
Begun AnalysedAudioFile class. Added H5 groups to class for data storage

Added template function in AnalysedAudioFile for reading/creating data groups in the HDF5 file
2016-08-06 20:42:35 +01:00
bp 0ea6a3d047 Merge branch 'dev.config' into dev 2016-08-01 17:55:34 +01:00
bp abd7866ae8 Added ability to provide a custom configuration file to the program via a --config flag 2016-08-01 17:49:09 +01:00
bp 91005c27c0 Merge branch 'dev.config' into dev 2016-08-01 15:07:49 +01:00
bp 6b7363ac5e Finished adding config.ini parsing to project 2016-08-01 15:07:16 +01:00
bp eb4479bf9d Logger is no longer passed to objects by pointer. 2016-08-01 15:07:16 +01:00
bp a16a2e3a3e Begun config parser 2016-07-26 21:09:55 +01:00
23 changed files with 586 additions and 245 deletions
+1 -1
View File
@@ -1 +1 @@
-I ./include -I ./external/Catch/include/ -std=c++11
-I ./include -I ./external/Catch/include/ -std=c++14
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env python
import subprocess
import os
import pdb
import fnmatch
import sys
def main():
p = subprocess.Popen(["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
out = out.strip('\n')
track_filepath = os.path.join(out, ".gittrack")
p = subprocess.Popen(["git", "ls-files", out, "--exclude-standard", "--others"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
out = out.splitlines()
try:
with open(track_filepath) as f:
content = f.read().splitlines()
except IOError:
return 0
untracked = []
for filepath in out:
for name in content:
if fnmatch.fnmatch(filepath, name):
untracked.append(filepath)
if untracked:
print "The following files are not tracked: "
for i in untracked:
print i
print "Please either stage these files for the commit or add them to the project's .gitignore to disregard them."
return 1
else:
return 0
if __name__ == "__main__":
exit(main())
+8
View File
@@ -1,3 +1,5 @@
.DS_Store
bin/
# Compiled Object files
*.slo
*.lo
@@ -27,3 +29,9 @@
*.exe
*.out
*.app
# Log files
*.log
external/
CMakeFiles/
+2
View File
@@ -0,0 +1,2 @@
*.cpp
*.h
+41
View File
@@ -0,0 +1,41 @@
# Use this file to configure the Overcommit hooks you wish to use. This will
# extend the default configuration defined in:
# https://github.com/brigade/overcommit/blob/master/config/default.yml
#
# At the topmost level of this YAML file is a key representing type of hook
# being run (e.g. pre-commit, commit-msg, etc.). Within each type you can
# customize each hook, such as whether to only run it on certain files (via
# `include`), whether to only display output if it fails (via `quiet`), etc.
#
# For a complete list of hooks, see:
# https://github.com/brigade/overcommit/tree/master/lib/overcommit/hook
#
# For a complete list of options that you can use to customize hooks, see:
# https://github.com/brigade/overcommit#configuration
#
# Uncomment the following lines to make the configuration take effect.
#PreCommit:
# RuboCop:
# enabled: true
# on_warn: fail # Treat all warnings as failures
#
# TrailingWhitespace:
# enabled: true
# exclude:
# - '**/db/structure.sql' # Ignore trailing whitespace in generated files
#
#PostCheckout:
# ALL: # Special hook name that customizes all hooks of this type
# quiet: true # Change all post-checkout hooks to only display output on failure
#
# IndexTags:
# enabled: true # Generate a tags file with `ctags` each time HEAD changes
PreCommit:
CheckUntracked:
enabled: true
quiet: false
description: 'Check for files that should be tracked or ignored.'
required_executable: './.git-hooks/pre-commit/check_untracked.py'
+23 -8
View File
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.5.2)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)
# Set the project name
project (concatenator)
@@ -9,6 +9,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
### Boost Settings
find_package(Boost 1.60.0 COMPONENTS program_options log log_setup thread date_time filesystem system REQUIRED)
find_package(LibSndFile REQUIRED)
find_package(HDF5 COMPONENTS CXX REQUIRED)
if (NOT FO_BOOST_STATIC_LINK)
add_definitions(-DBOOST_ALL_NO_LIB -DBOOST_ALL_DYN_LINK -DBOOST_LOG_DYN_LINK)
@@ -24,34 +25,43 @@ set(CMAKE_CXX_FLAGS "-g -Wall")
# Set cmake to output executable to the bin directory
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CONCATENATOR_SOURCE_DIR ${PROJECT_SOURCE_DIR}/src)
set(CONCATENATOR_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
set(CONCATENATOR_TEST_DIR ${PROJECT_SOURCE_DIR}/test)
set(CONCATENATOR_SOURCE_FILES
${CONCATENATOR_SOURCE_DIR}/Concatenator.cpp
# Declare source files that are used in both the tests and the main program.
set(GLOBAL_SOURCE_FILES
${CONCATENATOR_SOURCE_DIR}/AudioDatabase.cpp
${CONCATENATOR_SOURCE_DIR}/AudioFile.cpp
${CONCATENATOR_SOURCE_DIR}/Logger.cpp
${CONCATENATOR_SOURCE_DIR}/ArgumentParser.cpp
)
# Declare source files that are only used in the main program
set(CONCATENATOR_SOURCE_FILES
${CONCATENATOR_SOURCE_DIR}/Concatenator.cpp
${GLOBAL_SOURCE_FILES}
)
# Declare all header files
set(CONCATENATOR_HEADER_FILES
${CONCATENATOR_INCLUDE_DIR}/ArgumentParser.h
${CONCATENATOR_INCLUDE_DIR}/AudioDatabase.h
${CONCATENATOR_INCLUDE_DIR}/AudioFile.h
${CONCATENATOR_INCLUDE_DIR}/Logger.h
${CONCATENATOR_INCLUDE_DIR}/hdf5helper.h
)
# Declare source files that are only used in the test suite
set(CONCATENATOR_TEST_SOURCES
${CONCATENATOR_TEST_DIR}/Concatenator_Test.cpp
${CONCATENATOR_TEST_DIR}/Basic_Tests.cpp
${GLOBAL_SOURCE_FILES}
)
include_directories(${CONCATENATOR_INCLUDE_DIR})
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${LIBSNDFILE_INCLUDE_DIR})
include_directories(${HDF5_INCLUDE_DIRS})
add_subdirectory(external)
add_executable(concatenator ${CONCATENATOR_SOURCE_FILES} ${CONCATENATOR_HEADER_FILES})
@@ -59,6 +69,7 @@ add_executable(concatenator ${CONCATENATOR_SOURCE_FILES} ${CONCATENATOR_HEADER_F
# Link to external libraries
target_link_libraries(concatenator ${Boost_LIBRARIES})
target_link_libraries(concatenator ${LIBSNDFILE_LIBRARIES})
target_link_libraries(concatenator ${HDF5_LIBRARIES})
# Test build options (this code adapted from: https://github.com/ComicSansMS/GhulbusBase/blob/master/CMakeLists.txt)
option(BUILD_TESTS "Determines whether to build tests." ON)
@@ -90,9 +101,13 @@ if(BUILD_TESTS)
target_include_directories(Catch INTERFACE ${CMAKE_BINARY_DIR}/external/Catch/include)
endif()
add_executable(Concatenator_Test ${CONCATENATOR_TEST_SOURCES} ${CONCATENATOR_HEADER_FILES})
add_executable(Concatenator_Test ${CONCATENATOR_TEST_SOURCES})
target_link_libraries(Concatenator_Test ${Boost_LIBRARIES})
target_link_libraries(Concatenator_Test ${LIBSNDFILE_LIBRARIES})
target_link_libraries(Concatenator_Test ${HDF5_LIBRARIES})
target_link_libraries(Concatenator_Test Catch)
add_test(NAME TestBase COMMAND Concatenator_Test)
endif()
+9
View File
@@ -0,0 +1,9 @@
source = /Volumes/Storage/AudioDatabases/Viola/
target = /Volumes/Storage/AnalysedAudioDatabases/C++_test1/
output = /Volumes/Storage/OutputAudioDatabases/C++_test/
# Set the audio descriptors to be used for analysis and matching.
active_analyses = f0
active_analyses = rms
+13 -3
View File
@@ -3,16 +3,19 @@
#include <vector>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#include "logger.h"
using std::vector;
using std::string;
namespace po = boost::program_options;
namespace fs = boost::filesystem;
namespace pt = boost::property_tree;
class ArgumentParser {
public:
ArgumentParser();
~ArgumentParser() {};
ArgumentParser(const ArgumentParser&);
ArgumentParser& operator=(const ArgumentParser&);
int parseargs(int argc, char** argv);
@@ -27,14 +30,21 @@ class ArgumentParser {
//Create a positional options object for parsing input, output etc
//positional arguments from command line.
po::positional_options_description positionalOptions;
po::options_description desc;
po::options_description generic;
po::options_description config;
string config_path;
};
class ConcatenatorArgParse : public ArgumentParser {
public:
vector<string> get_analyses() { return (*this)["analyses"].as<vector<string>>(); }
vector<string> get_analyses() {
std::cout << (*this)["active_analyses"].as<vector<string>>()[0] << std::endl;
return (*this)["active_analyses"].as<vector<string>>();
}
string get_source_db() { return (*this)["source"].as<string>(); }
string get_target_db() { return (*this)["target"].as<string>(); }
fs::path get_tar_audio_dir() { return ((*this)["tar_audio"].empty() ? fs::path("") : fs::path((*this)["tar_audio"].as<string>())); }
fs::path get_src_audio_dir() { return ((*this)["src_audio"].empty() ? fs::path("") : fs::path((*this)["src_audio"].as<string>())); }
fs::path get_config_path() { return fs::path((*this)["config"].as<string>()); }
};
+17 -6
View File
@@ -1,9 +1,12 @@
#ifndef AUDIODATABASE_H
#define AUDIODATABASE_H
#include <iostream>
#include <string>
#include <list>
#include <set>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <H5Cpp.h>
#include "logger.h"
using std::string;
@@ -12,6 +15,7 @@ using std::endl;
using std::list;
using std::vector;
/*!
* A class that encapsulates a collection of AudioFile objects in order to
* perform analysis and synthesis operations on batches of audio files.
@@ -21,24 +25,30 @@ class AudioDatabase {
public:
AudioDatabase(
const std::string database_dir,
vector<string>& analyses,
Logger* log
vector<string> analyses
);
void load_database(boost::filesystem::path source_dir, bool reanalyse=false);
void load_database(boost::filesystem::path source_dir);
void analyse_database(const bool& reanalyse=false);
private:
boost::filesystem::path database_dir;
boost::filesystem::path audio_dir;
// Define a set that stores the locations of audiofiles in the database.
std::set<boost::filesystem::path> audio_file_set;
std::set<boost::filesystem::path> audio_files;
std::vector<std::string> analyses;
std::map<string, boost::filesystem::path> database_dirs;
Logger* log;
void validate_analysis_list();
void validate_analysis_list(vector<string>& analyses);
bool validate_filetype(const boost::filesystem::path& filepath);
void create_subdirs();
void organise_audio(boost::filesystem::path source_dir, bool symlink=true);
// Used to find and store paths to all audio that is part of the current database
void register_audio();
// Declare a HDF5 file object to be used for storing analysis data.
H5::H5File data_file;
void register_data();
};
/*! A function that determines whether a string value is found in the container.
@@ -86,3 +96,4 @@ std::list<string> check_analyses_valid(Iter iterator, Iter end)
}
return invalid;
}
#endif
+78 -14
View File
@@ -1,19 +1,83 @@
#ifndef AUDIOFILE_H
#define AUDIOFILE_H
#include <string>
#include <sndfile.hh>
#include <boost/filesystem.hpp>
#include <map>
#include "analyses/analysis.h"
#include "logger.h"
#include "H5Cpp.h"
class AudioFile {
public:
AudioFile(const char * &name, const int &mode=SFM_RDWR, const int &format=0, const int &channels=0, const int &samplerate=0);
int open(const int &mode=SFM_READ, const int &format=0, const int &channels=0, const int &samplerate=0);
protected:
SndfileHandle file;
SF_INFO* file_info;
private:
std::string name;
};
namespace fs = boost::filesystem;
namespace aa = AudioAnalysis;
class AnalysedAudioFile : public AudioFile {
public:
private:
};
namespace AudioFile {
class AnalysedAudioFile {
public:
AnalysedAudioFile(fs::path filepath) : filepath(filepath) {}
template <typename T>
int open_data(T& data)
{
try {
data.createGroup(name());
LOGDEBUG << "Created data group for audio file: " << name();
}
catch(H5::Exception& e){
filegroup = data.openGroup(name());
LOGDEBUG << "Loaded data group for audio file: " << name();
}
return 0;
}
template <typename Iter>
int analyse(Iter it, Iter end, const bool reanalyse)
{
LOGDEBUG << "Creating analyses for audio file: " << name();
// Initialize a factory object for creating analysis objects.
aa::AnalysisFactory factory = aa::AnalysisFactory(reanalyse);
for(; it != end; ++it) {
unique_ptr<analysis> a = factory.create(aa::analysis_index_from_string(*it));
a->create_analysis();
}
return 0;
}
int open(const int &mode, const int &format, const int &channels, const int &samplerate)
{
switch(mode){
case SFM_READ:
file = SndfileHandle(filepath.string());
break;
case SFM_WRITE:
file = SndfileHandle(filepath.string(), SFM_WRITE, format, channels, samplerate);
break;
case SFM_RDWR:
file = SndfileHandle(filepath.string(), SFM_RDWR, format, channels, samplerate);
break;
}
return 0;
}
int open()
{
file = SndfileHandle(filepath.string());
return 0;
}
std::string name()
{
return filepath.stem().string();
}
private:
SndfileHandle file;
fs::path filepath;
H5::Group filegroup;
};
}
#endif
+42
View File
@@ -0,0 +1,42 @@
#ifndef ANALYSIS_H
#define ANALYSIS_H
#include "analysis_base.h"
#include "rms.h"
#include "f0.h"
#include <memory>
#include <map>
#include <string>
using namespace std;
namespace AudioAnalysis {
enum analyses {
RMS,
F0,
};
static inline analyses analysis_index_from_string(const string& s) {
static map<string, analyses> analysis_map = { {"RMS", RMS}, {"F0", F0} };
return analysis_map[s];
}
class AnalysisFactory
{
public:
AnalysisFactory(const bool& reanalyse) : reanalyse(reanalyse) {}
unique_ptr<analysis> create(analyses analysis_type)
{
switch(analysis_type)
{
case RMS:
return make_unique<rms>();
case F0:
return make_unique<f0>();
}
}
private:
bool reanalyse;
};
}
#endif
+11
View File
@@ -0,0 +1,11 @@
#ifndef ANALYSIS_BASE_H
#define ANALYSIS_BASE_H
class analysis
{
public:
virtual ~analysis() {};
virtual void create_analysis() = 0;
private:
};
#endif
+12
View File
@@ -0,0 +1,12 @@
#ifndef F0_H
#define F0_H
#include "analysis.h"
#include "logger.h"
class f0 : public analysis
{
public:
void create_analysis() {LOGDEBUG << "Creating F0 analysis (WARNING: currently not implemented)";};
private:
};
#endif
+12
View File
@@ -0,0 +1,12 @@
#ifndef RMS_H
#define RMS_H
#include "analysis.h"
#include "logger.h"
class rms : public analysis
{
public:
void create_analysis() {LOGDEBUG << "Creating RMS analysis (WARNING: currently not implemented)";};
private:
};
#endif
+15
View File
@@ -0,0 +1,15 @@
#include "H5Cpp.h"
namespace hdf5helper {
template <typename T>
bool groupExists(T group, std::string name)
{
try {
group.openGroup(name);
return true;
}
catch(H5::GroupIException) {
return false;
}
}
}
+16 -32
View File
@@ -1,36 +1,20 @@
#ifndef LOGGER_H
#define LOGGER_H
#include <boost/shared_ptr.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/utility/formatting_ostream.hpp>
#pragma once
#include <boost/log/expressions.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup.hpp>
class Logger {
public:
Logger();
~Logger();
void trace(std::string str);
void debug(std::string str);
void info(std::string str);
void warning(std::string str);
void error(std::string str);
void fatal(std::string str);
#define LOGTRACE BOOST_LOG_SEV(logger::get(), boost::log::trivial::trace)
#define LOGDEBUG BOOST_LOG_SEV(logger::get(), boost::log::trivial::debug)
#define LOGINFO BOOST_LOG_SEV(logger::get(), boost::log::trivial::info)
#define LOGWARN BOOST_LOG_SEV(logger::get(), boost::log::trivial::warning)
#define LOGERROR BOOST_LOG_SEV(logger::get(), boost::log::trivial::error)
#define LOGFATAL BOOST_LOG_SEV(logger::get(), boost::log::trivial::fatal)
private:
//Narrow-char thread-safe logger.
typedef boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level> logger_t;
static void log_formatter(boost::log::record_view const& rec, boost::log::formatting_ostream& strm);
// Define types for logging backends
typedef boost::log::sinks::synchronous_sink<boost::log::sinks::text_ostream_backend> console_backend;
typedef boost::log::sinks::synchronous_sink<boost::log::sinks::text_file_backend> file_backend;
// Define a sink for console output and for log file output
boost::shared_ptr<console_backend> console_sink;
boost::shared_ptr<file_backend> file_sink;
//Define a logger
boost::log::sources::severity_logger< boost::log::trivial::severity_level > lg;
};
#endif
//declares a global logger with a custom initialization
BOOST_LOG_GLOBAL_LOGGER(logger, logger_t)
+67 -11
View File
@@ -1,22 +1,48 @@
#include "ArgumentParser.h"
#include <stdlib.h>
#include <vector>
#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using namespace std;
ArgumentParser::ArgumentParser() : desc("Allowed options") {
namespace fs = boost::filesystem;
namespace pt = boost::property_tree;
ArgumentParser::ArgumentParser() : generic("Command line options"), config("Configuration") {
// Add positional arguments to specify source, target and output database locations.
positionalOptions.add("source", 1);
positionalOptions.add("target", 1);
positionalOptions.add("output", 1);
// Add optional arguments to allow control over application settings from the command line.
desc.add_options()
generic.add_options()
("help,h", "produce help message")
("source", po::value<string>(), "Source location")
("target", po::value<string>(), "Target location")
("output", po::value<string>(), "Output location")
("active_analyses,a", po::value<vector<string>>()->multitoken(), "Analysis "
"strings specifying analyses to use for database comparison.")
("src_audio", po::value<string>(), "Specifies the "
"directory to create the source database and store analyses in. If "
"not specified then the " "source directory will be used directly.")
("tar_audio", po::value<string>(), "Specifies the "
"directory to create the target database and store analyses in. If "
"not specified then the target directory will be used directly.")
("config,c", po::value<string>()->default_value("config.ini"),
"Specifies the location of a config file to be used for configuring "
"the program. If no config is specified, the default ./config.ini "
"file found in the concatenator project directory, will be used.")
;
config.add_options()
("source", po::value<string>()->required(), "Source location")
("target", po::value<string>()->required(), "Target location")
("output", po::value<string>()->required(), "Output location")
("analyses,a", po::value<vector<string>>()->multitoken(), "Analysis "
("active_analyses", po::value<vector<string>>()->multitoken(), "Analysis "
"strings specifying analyses to use for database comparison.")
("src_audio", po::value<string>(), "Specifies the "
"directory to create the source database and store analyses in. If "
@@ -25,22 +51,34 @@ ArgumentParser::ArgumentParser() : desc("Allowed options") {
"directory to create the target database and store analyses in. If "
"not specified then the target directory will be used directly.")
;
}
int ArgumentParser::parseargs(int argc, char** argv) {
po::store(po::command_line_parser(argc, argv).options(desc).positional(positionalOptions).run(), vm);
po::options_description cmdline_options;
cmdline_options.add(generic).add(config);
po::options_description config_file_options;
config_file_options.add(config);
po::store(po::command_line_parser(argc, argv).options(generic).positional(positionalOptions).run(), vm);;
po::notify(vm);
std::ifstream settings_file(vm["config"].as<string>());
if(!settings_file) {
throw runtime_error("The configuration file could not be found: " + vm["config"].as<string>());
}
po::store(po::parse_config_file(settings_file, config), vm);
po::notify(vm);
// If help option is specified then output help message
if (vm.count("help")) {
cout << desc << "\n";
return 1;
cout << generic << "\n";
return 0;
}
po::notify(vm);
if (vm["analyses"].empty()) {
throw runtime_error("No analysis strings provided as arguments.");
}
return 0;
}
@@ -51,3 +89,21 @@ const po::variable_value& ArgumentParser::operator [](char const *b) const {
po::variables_map::size_type ArgumentParser::count(char const *ref) {
return vm.count(ref);
}
/*
void split(const string &s, char delim, vector<string> &elems) {
stringstream ss(s);
string item;
while (getline(ss, item, delim)) {
elems.push_back(item);
}
}
vector<string> split(const string &s, char delim=' ') {
vector<string> elems;
split(s, delim, elems);
return elems;
}
vector<string> ConcatenatorArgParse::get_analyses() { return split((*this)["active_analyses"].as<string>()); }
*/
+71 -32
View File
@@ -1,33 +1,43 @@
#include <string>
#include <vector>
#include <list>
#include "AudioDatabase.h"
#include "AudioFile.h"
#include <stdexcept>
#include <set>
#include <algorithm>
#include <boost/algorithm/string/join.hpp>
#include <boost/filesystem.hpp>
#include <boost/regex.hpp>
#include "H5Cpp.h"
namespace fs = boost::filesystem;
using namespace std;
using namespace H5;
using namespace AudioFile;
AudioDatabase::AudioDatabase(
const string database_dir,
vector<string>& analyses,
Logger* log
)
vector<string> analyses
) : analyses(analyses)
{
this->log = log;
log->info("Database directory: " + database_dir);
LOGINFO << "Database directory: " << database_dir;
// Remove duplicate strings from vector of analyses.
std::vector<string>::iterator it;
it = std::unique (analyses.begin(), analyses.end());
analyses.resize(std::distance(analyses.begin(),it));
it = std::unique (this->analyses.begin(), this->analyses.end());
analyses.resize(std::distance(this->analyses.begin(),it));
validate_analysis_list(this->analyses);
database_dirs.insert({"root", fs::path(database_dir)});
this->audio_dir = fs::path(audio_dir);
}
void AudioDatabase::validate_analysis_list(vector<string>& analyses)
{
// Check that all analysis strings supplied refer to valid analyses.
list<string> invalid = check_analyses_valid(analyses.begin(), analyses.end());
if(!invalid.empty()) {
@@ -35,27 +45,20 @@ AudioDatabase::AudioDatabase(
string invalid_strings = boost::algorithm::join(invalid, " ");
throw std::runtime_error(err + invalid_strings);
}
database_dirs.insert({"root", fs::path(database_dir)});
this->audio_dir = fs::path(audio_dir);
}
void AudioDatabase::validate_analysis_list()
{
}
void AudioDatabase::load_database(fs::path source_dir, bool reanalyse)
void AudioDatabase::load_database(fs::path source_dir)
{
// Make sure the database root directory exists.
try
{
if(create_directory(database_dirs["root"]))
{
log->debug("Database directory created: " + database_dir.string());
LOGDEBUG << "Database directory created: " << database_dir.string();
}
else if(exists(database_dirs["root"]))
{
log->debug("Database directory already exists: " + database_dir.string());
LOGDEBUG << "Database directory already exists: " << database_dir.string();
}
}
catch(boost::filesystem::filesystem_error &e)
@@ -69,7 +72,7 @@ void AudioDatabase::load_database(fs::path source_dir, bool reanalyse)
if(source_dir.empty()) {
source_dir = database_dirs["audio"];
log->debug("Source directory not provided. Setting to:" + source_dir.string());
LOGDEBUG << "Source directory not provided. Setting to:" << source_dir.string();
}
if(!exists(source_dir)) {
@@ -85,11 +88,47 @@ void AudioDatabase::load_database(fs::path source_dir, bool reanalyse)
// Find all audio in the database and store references for use later...
register_audio();
//Find/create HDF5 file for storage of analysis data.
register_data();
}
void AudioDatabase::analyse_database(const bool& reanalyse)
{
for(auto afile_path : audio_files)
{
AnalysedAudioFile afile = AnalysedAudioFile(afile_path);
afile.open_data(data_file);
afile.analyse(analyses.begin(), analyses.end(), reanalyse);
}
}
void AudioDatabase::register_data()
{
fs::path data_path = database_dirs["data"]/fs::path("data.hdf5");
try
{
data_file = H5File(data_path.string(), H5F_ACC_RDWR);
LOGINFO << "Reading database data from: " << (database_dirs["data"]/fs::path("data.hdf5")).string();
}
catch(FileIException &e)
{
if(!fs::exists(data_path.string()))
{
data_file = H5File(data_path.string(), H5F_ACC_TRUNC);
LOGINFO << "Creating new database file at: " << (database_dirs["data"]/fs::path("data.hdf5")).string();
}
else
{
LOGINFO << "Data file exists but cannot be read: " << data_path.string();
throw;
}
}
}
void AudioDatabase::create_subdirs()
{
array<fs::path, 2> directory_names = {{
static array<fs::path, 2> directory_names = {{
fs::path("audio"),
fs::path("data")
}};
@@ -99,11 +138,11 @@ void AudioDatabase::create_subdirs()
try
{
if(create_directory(subdir)) {
log->info("Subdirectory created: " + subdir.string());
LOGINFO << "Subdirectory created: " << subdir.string();
}
else if(exists(database_dirs["root"]))
{
log->info("Subdirectory already exists: " + subdir.string());
LOGINFO << "Subdirectory already exists: " << subdir.string();
}
}
catch(boost::filesystem::filesystem_error &e)
@@ -138,7 +177,7 @@ bool AudioDatabase::validate_filetype(const fs::path& filepath)
void AudioDatabase::organise_audio(fs::path source_dir, bool symlink)
{
log->info("Organising audio directory at: " + database_dirs["audio"].string());
LOGINFO << "Organising audio directory at: " << database_dirs["audio"].string();
// Define the destination for copying/linking all valid audio files found.
for(fs::recursive_directory_iterator iter(source_dir), end; iter != end; ++iter)
{
@@ -149,7 +188,7 @@ void AudioDatabase::organise_audio(fs::path source_dir, bool symlink)
}
if(!validate_filetype(iter->path())) {
log->debug("File: " + iter->path().string() + " isn't a supported audiofile. Skipping...");
LOGINFO << "File: " << iter->path().string() << " isn't a supported audiofile. Skipping...";
continue;
}
@@ -160,17 +199,17 @@ void AudioDatabase::organise_audio(fs::path source_dir, bool symlink)
// Try to symlink the file to the audio directory of the database.
try {
fs::create_symlink(iter->path(), destination_file);
log->debug("Linked: " + iter->path().string() + " to: " + destination_file.string());
LOGINFO << "Linked: " << iter->path().string() << " to: " << destination_file.string();
}
catch(boost::filesystem::filesystem_error &e){
// If symbolic linking fails then the file probably already exists at the location.
log->debug("Failed to link: " + iter->path().string() + " to " + destination_file.string() + " File may already exists.");
LOGINFO << "Failed to link: " << iter->path().string() << " to " << destination_file.string() << " File may already exists.";
}
}
else {
// If it is in the database as a symlink, but a full copy is required
if(fs::exists(destination_file) && !fs::is_symlink(destination_file)) {
log->debug("File already exists: " + iter->path().string());
LOGINFO << "File already exists: " << iter->path().string();
continue;
}
@@ -178,11 +217,11 @@ void AudioDatabase::organise_audio(fs::path source_dir, bool symlink)
try {
fs::remove(destination_file);
fs::copy_file(iter->path(), destination_file, fs::copy_option::overwrite_if_exists);
log->debug("Copied: " + iter->path().string() + " to: " + destination_file.string());
LOGDEBUG << "Copied: " << iter->path().string() << " to: " << destination_file.string();
}
catch(boost::filesystem::filesystem_error &e){
// If symbolic linking fails then the file probably already exists at the location.
log->debug("Failed to copy source file to: " + destination_file.string() + " File may already exists.");
LOGDEBUG << "Failed to copy source file to: " << destination_file.string() << " File may already exists.";
}
}
}
@@ -191,12 +230,12 @@ void AudioDatabase::organise_audio(fs::path source_dir, bool symlink)
void AudioDatabase::register_audio()
{
// Clear any previous entries from set.
audio_file_set.clear();
audio_files.clear();
for(auto& entry : boost::make_iterator_range(fs::directory_iterator(database_dirs["audio"]), {}))
{
if(validate_filetype(entry.path())) {
log->info("Registered audio file: " + entry.path().string());
audio_file_set.insert(entry.path());
LOGINFO << "Registered audio file: " << entry.path().string();
audio_files.insert(entry.path());
}
}
}
-23
View File
@@ -1,23 +0,0 @@
#include "AudioFile.h"
#include <string>
#include <vector>
#include <iostream>
using namespace std;
AudioFile::AudioFile(const char * &name, const int &mode, const int &format, const int &channels, const int &samplerate)
{
this->name = name;
open(mode, format, channels, samplerate);
}
int AudioFile::open(const int &mode, const int &format, const int &channels, const int &samplerate)
{
switch(mode){
case SFM_READ: file = SndfileHandle(name);
case SFM_WRITE: file = SndfileHandle(name, SFM_WRITE, format, channels, samplerate);
case SFM_RDWR: file = SndfileHandle(name, SFM_RDWR, format, channels, samplerate);
}
return 0;
}
+16 -22
View File
@@ -1,12 +1,16 @@
#include <iostream>
#include "logger.h"
#include "Logger.h"
#include "ArgumentParser.h"
#include "AudioDatabase.h"
#include <list>
#include <string>
#include <boost/property_tree/ptree.hpp>
#include "H5Cpp.h"
using namespace std;
namespace pt = boost::property_tree;
namespace
{
const size_t ERROR_IN_COMMAND_LINE = 1;
@@ -16,14 +20,9 @@ namespace
}
int main(int argc, char** argv) {
// Initialize a logger object to be used for message handeling throughout
// the program
Logger log = Logger();
/*
try
{
*/
// Prevent HDF5 library from printing errors that are handeled in try/catch blocks.
H5::Exception::dontPrint();
//
// Initialize object to parse arguments supplied by user from command
// line
ConcatenatorArgParse argparse = ConcatenatorArgParse();
@@ -32,16 +31,20 @@ int main(int argc, char** argv) {
if(argparse.parseargs(argc, argv)) {
return SUCCESS;
}
fs::path config_filepath = argparse.get_config_path();
if(config_filepath.empty()) {
config_filepath = fs::path("./config.json");
}
vector<string> analyses = argparse.get_analyses();
// Initialize the source audio database object with arguments provided from the command line.
AudioDatabase source_db = AudioDatabase(
argparse.get_source_db(),
analyses,
&log
analyses
);
source_db.load_database(argparse.get_src_audio_dir());
source_db.analyse_database();
/*
// Initialize the target audio database object with arguments provided from the command line.
@@ -51,18 +54,9 @@ int main(int argc, char** argv) {
&log
);
target_db.load_database(argparse.get_tar_audio_dir());
*/
/*
}
catch(std::exception& e)
{
string error("Unhandled Exception reached the top of main:\n");
error.append(e.what());
log.error(error);
throw;
}
*/
return SUCCESS;
}
+30 -91
View File
@@ -1,102 +1,41 @@
#include <boost/log/common.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/core/null_deleter.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/log/trivial.hpp>
#include <ostream>
#include <fstream>
#include "Logger.h"
namespace attrs = boost::log::attributes;
namespace expr = boost::log::expressions;
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace keywords = boost::log::keywords;
//BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int)
Logger::Logger() {
boost::log::register_simple_formatter_factory< boost::log::trivial::severity_level, char >("Severity");
Logger::console_sink = boost::make_shared<console_backend>();
Logger::file_sink =
boost::make_shared<file_backend>(
keywords::file_name = "concatenator_log_%N.log",
keywords::rotation_size = 5 * 1024 * 1024
);
Logger::file_sink->set_formatter(
expr::stream <<
"[" <<
expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S") <<
"] [" <<
logging::trivial::severity <<
"] --- " <<
expr::smessage
);
Logger::console_sink->set_formatter(
expr::stream <<
"[" <<
expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S") <<
"] [" <<
logging::trivial::severity <<
"] --- " <<
expr::smessage
);
boost::shared_ptr<std::ostream> stdout_stream{&std::clog, boost::null_deleter{}};
Logger::console_sink->locked_backend()->add_stream(stdout_stream);
Logger::console_sink->locked_backend()->auto_flush(true);
Logger::file_sink->locked_backend()->auto_flush(true);
//Logger::sink->set_filter(severity > 0);
logging::core::get()->add_sink(Logger::file_sink);
logging::core::get()->add_sink(Logger::console_sink);
//Defines a global logger initialization routine
//Defines a global logger initialization routine
BOOST_LOG_GLOBAL_LOGGER_INIT(logger, logger_t)
{
logger_t lg;
logging::add_common_attributes();
};
logging::add_file_log(
boost::log::keywords::file_name = "concatenator_log_%N.log",
boost::log::keywords::format = (
expr::stream << "[" << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
<< "] [" << expr::attr< boost::log::trivial::severity_level >("Severity") << "] --- "
<< expr::smessage
)
);
Logger::~Logger() {
Logger::file_sink->flush();
Logger::console_sink->flush();
}
void Logger::trace(std::string str) {
BOOST_LOG_SEV(lg, logging::trivial::trace) << str;
}
logging::add_console_log(
std::cout,
boost::log::keywords::format = (
expr::stream << "[" << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
<< "] [" << expr::attr< boost::log::trivial::severity_level >("Severity") << "] --- "
<< expr::smessage
)
);
void Logger::debug(std::string str) {
BOOST_LOG_SEV(lg, logging::trivial::debug) << str;
}
/*
logging::core::get()->set_filter
(
logging::trivial::severity >= logging::trivial::info
);
*/
void Logger::info(std::string str) {
BOOST_LOG_SEV(lg, logging::trivial::info) << str;
}
void Logger::warning(std::string str) {
BOOST_LOG_SEV(lg, logging::trivial::warning) << str;
}
void Logger::error(std::string str) {
BOOST_LOG_SEV(lg, logging::trivial::error) << str;
Logger::file_sink->flush();
Logger::console_sink->flush();
}
void Logger::fatal(std::string str) {
BOOST_LOG_SEV(lg, logging::trivial::fatal) << str;
Logger::file_sink->flush();
Logger::console_sink->flush();
return lg;
}
+38
View File
@@ -1 +1,39 @@
#include "catch.hpp"
#include <boost/filesystem.hpp>
#include "H5Cpp.h"
#include "hdf5helper.h"
#include "logger.h"
#include "AudioFile.h"
namespace fs = boost::filesystem;
using namespace H5;
using namespace AudioFile;
SCENARIO("HDF5 groups are handled correctly in the AnalysedAudioFile class", "[HDF5][AnalysedAudioFile]") {
GIVEN("An AnalysedAudioFile and a HDF5 file") {
H5File data_file = H5File("./.test_data.hdf5", H5F_ACC_TRUNC);
AnalysedAudioFile test_audio = AnalysedAudioFile(fs::path(".test_audio.wav"));
WHEN("open_data is called using a H5File object") {
test_audio.open_data(data_file);
THEN("a group is created using the name of the audio file") {
REQUIRE(hdf5helper::groupExists(data_file, test_audio.name()));
AND_WHEN("open_data is called again to read a group that now already exists") {
test_audio.open_data(data_file);
THEN("The group should be loaded without error")
{
//TODO: Add check that group still contains data created in previous test.
}
}
}
}
}
if(fs::exists("./.test_data.hdf5")) {
fs::remove("./.test_data.hdf5");
}
if(fs::exists(".test_audio.wav")) {
fs::remove(".test_audio.wav");
}
}
+17 -2
View File
@@ -1,4 +1,19 @@
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include <catch.hpp>
#define CATCH_CONFIG_RUNNER
#include "catch.hpp"
#include "H5Cpp.h"
using namespace H5;
int main( int argc, char* const argv[] )
{
// Global setup goes here...
// Prevent HDF5 library from printing errors that are handeled in try/catch blocks.
Exception::dontPrint();
int result = Catch::Session().run( argc, argv );
// global clean-up goes here...
return result;
}