/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * drumkit_creator.cc * * Thu Jan 12 18:51:34 CET 2017 * Copyright 2017 André Nusser * andre.nusser@googlemail.com ****************************************************************************/ /* * This file is part of DrumGizmo. * * DrumGizmo is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * DrumGizmo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with DrumGizmo; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "drumkit_creator.h" #include #include "../src/random.h" #include #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #endif DrumkitCreator::~DrumkitCreator() { for (const auto& file: created_files) { auto error = unlink(file.c_str()); if (error) { std::cerr << "File could not be deleted in DrumkitCreator destructor" << std::endl; } } for (const auto& dir: created_directories) { #ifndef _WIN32 auto error = rmdir(dir.c_str()); if (error) { std::cerr << "Directory could not be deleted in DrumkitCreator destructor" << std::endl; } #else RemoveDirectory(dir.c_str()); #endif } } std::string DrumkitCreator::create(const DrumkitData& data) { std::string drumkit_filename; if (is_valid(data)) { auto dir = createTemporaryDirectory("drumkit"); for (const auto& wav_info: data.wav_infos) { createWav(wav_info, data.number_of_channels, dir); } for (const auto& instrument: data.instruments) { createInstrument(instrument, data.number_of_channels, dir); } drumkit_filename = createDrumkitFile(data, dir); } else { throw "DrumkitData not valid"; } return drumkit_filename; } void DrumkitCreator::createWav(const WavInfo& wav_info, std::size_t number_of_channels, const std::string& dir) { SF_INFO sfinfo; sfinfo.samplerate = 44100; sfinfo.channels = number_of_channels; sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; std::string filename = dir + "/" + wav_info.filename; auto sndfile = sf_open(filename.c_str(), SFM_WRITE, &sfinfo); if (!sndfile) { throw "The wav file could not be created"; } created_files.push_back(filename); auto data_vec = createData(wav_info, number_of_channels); sf_write_raw(sndfile, data_vec.data(), 2*data_vec.size()); sf_write_sync(sndfile); sf_close(sndfile); } std::string DrumkitCreator::createStdKit(const std::string& name) { std::vector wav_infos = { WavInfo("1011.wav", 1, 0x1110), WavInfo("2122.wav", 1, 0x2221) }; std::vector audiofiles1(4, Audiofile{&wav_infos.front(), 1}); std::vector audiofiles2(4, Audiofile{&wav_infos.back(), 1}); std::vector sample_data1{{"stroke1", std::move(audiofiles1)}}; std::vector sample_data2{{"stroke1", std::move(audiofiles2)}}; std::vector instruments = { InstrumentData{"instr1", "instr1.xml", std::move(sample_data1)}, InstrumentData{"instr2", "instr2.xml", std::move(sample_data2)} }; auto kit_data = DrumkitData{name, 4, instruments, wav_infos}; return create(kit_data); } std::string DrumkitCreator::createSmallKit(const std::string& name) { std::vector wav_infos = { WavInfo("small_instr.wav", 549833) }; std::vector audiofiles; for (std::size_t i = 0; i < 13; ++i) { audiofiles.push_back({&wav_infos.front(), i+1}); } std::vector sample_data{{"stroke1", std::move(audiofiles)}}; std::vector instruments = { InstrumentData{"small_instr", "small_instr.xml", std::move(sample_data)} }; auto kit_data = DrumkitData{name, 13, instruments, wav_infos}; return create(kit_data); } std::string DrumkitCreator::createHugeKit(const std::string& name) { std::vector wav_infos = { WavInfo("huge_instr.wav", 549833) }; std::vector audiofiles; for (std::size_t i = 0; i < 13; ++i) { audiofiles.push_back({&wav_infos.front(), i+1}); } std::vector sample_data; for (std::size_t i = 0; i < 50; ++i) { sample_data.push_back({"stroke" + std::to_string(i), audiofiles}); } std::vector instruments; for (std::size_t i = 0; i < 50; ++i) { instruments.push_back({"huge_instr" + std::to_string(i), "huge_instr.xml", sample_data}); } auto kit_data = DrumkitData{name, 13, instruments, wav_infos}; return create(kit_data); } std::string DrumkitCreator::createSingleChannelWav(const std::string& name) { auto dir = createTemporaryDirectory("wavfiles"); auto wav_info = WavInfo(name, 173516); createWav(wav_info, 1, dir); return dir + "/" + name; } std::string DrumkitCreator::createMultiChannelWav(const std::string& name) { auto dir = createTemporaryDirectory("wavfiles"); auto wav_info = WavInfo(name, 549833); createWav(wav_info, 13, dir); return dir + "/" + name; } std::string DrumkitCreator::create0000Wav(const std::string& name) { auto dir = createTemporaryDirectory("wavfiles"); auto wav_info = WavInfo(name, 1, 0x0000); createWav(wav_info, 1, dir); return dir + "/" + name; } std::string DrumkitCreator::createStdMidimap(const std::string& name) { auto dir = createTemporaryDirectory("midimap"); std::string content = "\n" "\n" "\n" "\n" "\n"; std::string filename = dir + "/" + name + ".xml"; std::ofstream file; file.open(filename); if (file.is_open()) { created_files.push_back(filename); file << content; } else { throw "File could not be opened"; } file.close(); return filename; } // // private member functions // bool DrumkitCreator::is_valid(const DrumkitData& data) { // TODO Check the consistency of the data. return true; } std::string DrumkitCreator::createTemporaryDirectory(const std::string& name) { #ifndef _WIN32 std::string dir_template = "/tmp/drumgizmo_" + name + "XXXXXX"; const auto dir_name = mkdtemp(&dir_template[0]); #else char temp_dir[MAX_PATH]; char dir_name[MAX_PATH]; GetTempPath(sizeof(temp_dir), temp_dir); GetTempFileName(temp_dir, name.c_str(), 0, dir_name); CreateDirectory(dir_name, 0); #endif if (dir_name) { created_directories.push_back(dir_name); return std::string(dir_name); } return ""; } auto DrumkitCreator::createData(const WavInfo& wav_info, std::size_t number_of_channels) -> std::vector { std::vector data_vec(number_of_channels * wav_info.length, wav_info.sample); if (wav_info.is_random) { Random rand(42); // Fix the seed to make it reproducable. int lower_bound = std::numeric_limits::min(); int upper_bound = std::numeric_limits::max(); std::generate(data_vec.begin(), data_vec.end(), [&](){ return Sample(rand.intInRange(lower_bound, upper_bound)); }); } return data_vec; } void DrumkitCreator::createInstrument(const InstrumentData& data, std::size_t number_of_channels, const std::string& dir) { std::string prefix = "\n" "\n" " \n"; // FIXME sampleref std::string postfix = " \n\n"; std::string samples; float power = 1.0f; for (const auto& sample: data.sample_data) { samples += "\n"; power += 0.1f; for (std::size_t i = 0; i < sample.audiofiles.size(); ++i) { const auto& audiofile = sample.audiofiles[i]; samples += "filename + "\" filechannel=\"" + std::to_string(audiofile.filechannel) + "\"/>\n"; } samples += "\n"; } // Write to file std::string filename = dir + "/" + data.filename; std::ofstream file; file.open(filename); if (file.is_open()) { created_files.push_back(filename); file << prefix + samples + postfix; } else { throw "File could not be opened"; } file.close(); } std::string DrumkitCreator::createDrumkitFile(const DrumkitData& data, const std::string& dir) { // Pre- and postfix string std::string prefix = "\n" "\n"; std::string postfix = "\n"; // Channel string std::string channels; channels += "\n"; for (std::size_t i = 0; i < data.number_of_channels; ++i) { channels += "\n"; } channels += "\n"; // Instrument string std::string instruments; instruments += "\n"; for (const auto& instrument: data.instruments) { instruments += "\n"; for (std::size_t i = 0; i < data.number_of_channels; ++i) { std::string i_str = std::to_string(i); instruments += "\n"; } instruments += "\n"; } instruments += "\n"; // Write everything to a drumkit file std::string filename = dir + "/" + data.name + ".xml"; std::ofstream file; file.open(filename); if (file.is_open()) { created_files.push_back(filename); file << prefix + channels + instruments + postfix; } else { throw "File could not be opened"; } file.close(); return filename; }