From a0a16d9cdb9d0522053c2e830b7440e9e96231a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gl=C3=B6ckner?= Date: Wed, 20 Jan 2016 17:46:50 +0100 Subject: [incomplete] added MidifileInputEngine and WavfileOutputEngine --- drumgizmo/drumgizmoc.cc | 20 +--- drumgizmo/enginefactory.cc | 50 ++++++++++ drumgizmo/enginefactory.h | 37 ++++++++ drumgizmo/input/midifile.cc | 160 +++++++++++++++++++++++++++++++ drumgizmo/input/midifile.h | 66 +++++++++++++ drumgizmo/output/wavfile.cc | 98 +++++++++++++++++++ drumgizmo/output/wavfile.h | 223 ++++++++++++++++++++++++++++++++++++++++++++ src/audiooutputengine.h | 2 + 8 files changed, 640 insertions(+), 16 deletions(-) create mode 100644 drumgizmo/enginefactory.cc create mode 100644 drumgizmo/enginefactory.h create mode 100644 drumgizmo/input/midifile.cc create mode 100644 drumgizmo/input/midifile.h create mode 100644 drumgizmo/output/wavfile.cc create mode 100644 drumgizmo/output/wavfile.h diff --git a/drumgizmo/drumgizmoc.cc b/drumgizmo/drumgizmoc.cc index 10dcda5..978b0cc 100644 --- a/drumgizmo/drumgizmoc.cc +++ b/drumgizmo/drumgizmoc.cc @@ -36,9 +36,7 @@ #include "drumgizmo.h" #include "drumgizmoc.h" - -#include "audiooutputenginedl.h" -#include "audioinputenginedl.h" +#include "enginefactory.h" #include "event.h" @@ -228,7 +226,7 @@ int CliMain::run(int argc, char *argv[]) return 1; } - AudioInputEngine *ie = new AudioInputEngineDL(inputengine); + auto ie = createInputEngine(inputengine); if(ie == NULL) { printf("Invalid input engine: %s\n", inputengine.c_str()); @@ -264,11 +262,10 @@ int CliMain::run(int argc, char *argv[]) if(outputengine == "") { printf("Missing output engine\n"); - delete ie; return 1; } - AudioOutputEngineDL *oe = new AudioOutputEngineDL(outputengine); + auto oe = createOutputEngine(outputengine); if(oe == NULL) { printf("Invalid output engine: %s\n", outputengine.c_str()); @@ -309,8 +306,6 @@ int CliMain::run(int argc, char *argv[]) if(kitfile != "") { printf("Can only handle a single kitfile.\n"); printf(usage_str, argv[0]); - delete ie; - delete oe; return 1; } kitfile = argv[optind++]; @@ -319,14 +314,12 @@ int CliMain::run(int argc, char *argv[]) } else { printf("Missing kitfile.\n"); printf(usage_str, argv[0]); - delete ie; - delete oe; return 1; } printf("Using kitfile: %s\n", kitfile.c_str()); - DrumGizmo gizmo(oe, ie); + DrumGizmo gizmo(oe.get(), ie.get()); gizmo.setFrameSize(oe->getBufferSize()); @@ -354,8 +347,6 @@ int CliMain::run(int argc, char *argv[]) if(!gizmo.init()) { printf("Failed init engine.\n"); - delete ie; - delete oe; return 1; } @@ -363,9 +354,6 @@ int CliMain::run(int argc, char *argv[]) printf("Quit.\n"); fflush(stdout); - delete oe; - delete ie; - hug_close(); return 0; diff --git a/drumgizmo/enginefactory.cc b/drumgizmo/enginefactory.cc new file mode 100644 index 0000000..b6569e0 --- /dev/null +++ b/drumgizmo/enginefactory.cc @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * enginefactory.cc + * + * Mi 20. Jan 10:46:07 CET 2016 + * Copyright 2016 Christian Glöckner + * cgloeckner@freenet.de + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 "enginefactory.h" +#include "jackclient.h" +#include "input/midifile.h" +#include "output/wavfile.h" + +InputEnginePtr createInputEngine(std::string const & name) { + if (name == "midifile") { + return std::make_unique(); + } + // todo: add more engines + + printf("Unsupported input engine: %s\n", name.c_str()); + return nullptr; +} + +OutputEnginePtr createOutputEngine(std::string const & name) { + if (name == "wavfile") { + return std::make_unique(); + } + // todo: add more engines + + printf("Unsupported output engine: %s\n", name.c_str()); + return nullptr; +} diff --git a/drumgizmo/enginefactory.h b/drumgizmo/enginefactory.h new file mode 100644 index 0000000..93624b9 --- /dev/null +++ b/drumgizmo/enginefactory.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * enginefactory.h + * + * Mi 20. Jan 10:46:07 CET 2016 + * Copyright 2016 Christian Glöckner + * cgloeckner@freenet.de + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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. + */ +#pragma once +#include +#include "cpp11fix.h" // required for c++11 + +#include "audioinputengine.h" + +using InputEnginePtr = std::unique_ptr; +using OutputEnginePtr = std::unique_ptr; + +InputEnginePtr createInputEngine(std::string const & name); +OutputEnginePtr createOutputEngine(std::string const & name); diff --git a/drumgizmo/input/midifile.cc b/drumgizmo/input/midifile.cc new file mode 100644 index 0000000..b0685ec --- /dev/null +++ b/drumgizmo/input/midifile.cc @@ -0,0 +1,160 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * midifile.cc + * + * Mi 20. Jan 16:07:57 CET 2016 + * Copyright 2016 Christian Glöckner + * cgloeckner@freenet.de + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 "midifile.h" + +int const NOTE_ON = 0x90; + +MidifileInputEngine::MidifileInputEngine() + : smf{nullptr} + , current_event{nullptr} + , file{} + , midimap{} + , speed{1.f} + , track{-1} // all tracks + , loop{false} + , offset{0.0} { +} + +MidifileInputEngine::~MidifileInputEngine() { + if (smf != nullptr) { + smf_delete(smf); + } +} + +bool MidifileInputEngine::isMidiEngine() { +} + +bool MidifileInputEngine::init(Instruments& instruments) { + if (file == "") { + fprintf(stderr, "Missing midifile argument 'file'\n"); + return false; + } + if (midimap == "") { + fprintf(stderr, "Missing midimapfile argument 'midimap'.\n"); + return false; + } + smf = smf_load(file.c_str()); + if (sfml == nullptr) { + fprintf(stderr, "Could not open midifile '%s'.\n", filename.c_str()); + return false; + } + MidiMapParser p{midimap}; + if (p.parse()) { + fprintf(stderr, "Could not parse midimapfile '%s'.\n", midimapfile.c_str()); + return false; + } + midiMapper.midimap = std::move(p.midimap); + for (auto i = 0u; i < instruments.size(); ++i) { + auto name = instruments[0]->name; + midiMapper.instrmap[name] = i; + } + return true; +} + +void MidifileInputEngine::setParm(std::string parm, std::string value) { + if(parm == "file") { + filen = value; + } else if(parm == "speed") { + speed = std::stof(value); + } else if (parm == "midimap") { + midimap = value; + } else if (parm == "loop") { + loop = true; + } else { + printf("Unsupported midifile parameter '%s'\n", parm); + } +} + +bool MidifileInputEngine::start() { + return true; +} + +void MidifileInputEngine::stop() { +} + +void MidifileInputEngine::pre() { +} + +event_t* MidifileInputEngine::run(size_t pos, size_t len, size_t *nevents) { + event_t* evs{nullptr}; + std::size_t nevs{0u}; + double current_max_time = 1.0 * (pos + len) / (44100.0 / speed); + current_max_time -= offset; + if (current_event == nullptr) { + current_event = smf_get_next_event(smf); + } + while(current_event && current_event->time_seconds < cur_max_time) { + if(!smf_event_is_metadata(current_event)) { + if((current_event->midi_buffer_length == 3) && + ((current_event->midi_buffer[0] & NOTE_ON) == NOTE_ON) && + (track == -1 || current_event->track_number == track) && + current_event->midi_buffer[2] > 0) { + if(evs == NULL) { + evs = (event_t *)malloc(sizeof(event_t) * 1000); + } + int key = current_event->midi_buffer[1]; + int velocity = current_event->midi_buffer[2]; + + evs[nevs].type = TYPE_ONSET; + size_t evpos = current_event->time_seconds * (44100.0 / speed); + evs[nevs].offset = evpos - pos; + + int i = midiMap.lookup(key); + if(i != -1) { + evs[nevs].instrument = i; + evs[nevs].velocity = velocity / 127.0; + nevs++; + if(nevs > 999) { + fprintf(stderr, "PANIC!\n"); + break; + } + } + } + } + current_event = smf_get_next_event(smf); + } + + if(!current_event) { + if(loop) { + smf_rewind(smf); + offset += cur_max_time; + } else { + if(evs == NULL) { + evs = (event_t *)malloc(sizeof(event_t) * 1000); + } + evs[nevs].type = TYPE_STOP; + evs[nevs].offset = len - 1; + nevs++; + } + } + + *nevents = nevs; + return evs; +} + +void MidifileInputEngine::post() { +} diff --git a/drumgizmo/input/midifile.h b/drumgizmo/input/midifile.h new file mode 100644 index 0000000..c2fcdb6 --- /dev/null +++ b/drumgizmo/input/midifile.h @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * minifile.h + * + * Mi 20. Jan 16:07:57 CET 2016 + * Copyright 2016 Christian Glöckner + * cgloeckner@freenet.de + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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. + */ +#pragma once +#include + +#include +#include + +#include +#include +#include + +#define NOTE_ON 0x90 + +class MidifileInputEngine + : public AudioInputEngine { + public: + MidifileInputEngine(); + ~MidifileInputEngine(); + + // based on AudioInputEngine + bool isMidiEngine() override; + bool init(Instruments &instruments) override; + void setParm(std::string parm, std::string value) override; + bool start() override; + void stop() override; + void pre() override; + event_t* run(size_t pos, size_t len, size_t* nevents) override; + void post() override; + + private: + smf_t* smf; + smf_event_t* current_event; + + MidiMapper midiMapper; + + std::string file, midimap; + float speed; + int track; + bool loop; + double offset; +}; diff --git a/drumgizmo/output/wavfile.cc b/drumgizmo/output/wavfile.cc new file mode 100644 index 0000000..e0c18cb --- /dev/null +++ b/drumgizmo/output/wavfile.cc @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * wavfile.cc + * + * Mi 20. Jan 16:57:16 CET 2016 + * Copyright 2016 Christian Glöckner + * cgloeckner@freenet.de + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 "wavfile.h" + +WavfileOutputEngine::WavfileOutputEngine() + : info{} + , channels{} + , file{"output"} { + info.frames = 0; + info.samplerrate = 44100; + info.channels = 1; + info.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; + info.sections = 0; + info.seekable = 0; +} + +WavfileOutputEngine::~WavfileOutputEngine() { + for (auto& ptr: channels) { + if (ptr != nullptr) { + sf_close(ptr); + } + } +} + +bool WavfileOutputEngine::init(Channels channels) { + channels.clear(), + channels.resize(channels.size(), nullptr); + for (auto i = 0u; i < channels.size(); ++i) { + // write channel to file + auto fname = file + channels[i]->name + "-" + std::to_string(i); + channels[i] = sf_open(fname, SFM_WRITE, &info); + if (channels[i] == nullptr) { + printf("Write error...\n"); + return false; + } + } + return true; +} + +void WavfileOutputEngine::setParm(std::string parm, std::string value) { + if (parm == "file") { + file = value; + } else if (parm == "srate") {[ + info.samplerate = std::stoi(value); + } else { + printf("Unsupported wavfile parameter '%s'\n", parm); + } +} + +bool WavfileOutputEngine::start() { + return true; +} + +void WavfileOutputEngine::stop() { +} + +void WavfileOutputEngine::pre(size_t nsamples) { +} + +void WavfileOutputEngine::run(int ch, sample_t* samples, size_t nsamples) { + if (ch >= channels.size()) { + printf("Invalid channel %d (%d channels available)", ch, channels.size()); + return; + } + + sf_writef_float(channels[ch], samples, nsampels); +} + +void WavfileOutputEngine::post(size_t nsamples) { +} + +size_t WavfileOutputEngine::samplerate() { + return info.samplerate; +} diff --git a/drumgizmo/output/wavfile.h b/drumgizmo/output/wavfile.h new file mode 100644 index 0000000..0f93049 --- /dev/null +++ b/drumgizmo/output/wavfile.h @@ -0,0 +1,223 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * wavfile.h + * + * Mi 20. Jan 16:57:16 CET 2016 + * Copyright 2016 Christian Glöckner + * cgloeckner@freenet.de + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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. + */ +#pragma once +#include +#include + +#include + +#include "audiooutputengine.h" + +class WavfileOutputEngine + : public AudioOutputEngine { + public: + WavfileOutputEngine(); + ~WavfileOutputEngine(); + + // based on AudioOutputEngine + bool init(Channels channels) override; + void setParm(std::string parm, std::string value) override; + bool start() override; + void stop() override; + void pre(size_t nsamples) override; + void run(int ch, sample_t* samples, size_t nsamples) override; + void post(size_t nsamples) override; + size_t samplerate() override; + + private: + SF_INFO info; + std::vector channels; + size_t num_channels; + + std::string file; +}; + + + +#include + +#include +#include +#include + + +class WavFile { +public: + WavFile(); + ~WavFile(); + bool init(int channels, char *cnames[]); + void setParm(std::string parm, std::string value); + bool start(); + void stop(); + void pre(size_t size); + void run(int channel, sample_t* data, size_t size); + void post(size_t size); + size_t samplerate(); + +private: + +}; + +WavFile::WavFile() +{ + fh = NULL; + filename = "output"; + + memset(&sf_info, 0, sizeof(sf_info)); + sf_info.channels = 1;//channels; + sf_info.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; + sf_info.samplerate = 44100; +} + +WavFile::~WavFile() +{ + if(fh == NULL) return; + + for(size_t i override; i < channels; i++) { + if(fh[i]) sf_close(fh[i]); + } + + if(fh) free(fh); +} + +bool WavFile::init(int channels, char *cnames[]) +{ + this->channels = channels; + + fh = (SNDFILE **)malloc(sizeof(SNDFILE *)*channels); + + for(size_t i override; i < this->channels; i++) fh[i] = NULL; + + for(size_t i override; i < this->channels; i++) { + char fname[512]; + + sprintf(fname, "%s%s-%d.wav", filename.c_str(), cnames[i], (int)i); + // printf("[%s]\n", fname); + + fh[i] = sf_open(fname, SFM_WRITE, &sf_info); + if(!fh[i]) { + printf("Write error...\n"); + return false; + } + } + + return true; +} + +void WavFile::setParm(std::string parm, std::string value) +{ + if(parm == "file") filename = value; + if(parm == "srate") sf_info.samplerate = atoi(value.c_str()); +} + +bool WavFile::start() +{ + return true; +} + +void WavFile::stop() +{ +} + +void WavFile::pre(size_t size) +{ +} + +void WavFile::run(int channel, sample_t* cdata, size_t csize) +{ + if(channel < (int)channels) sf_writef_float(fh[channel], cdata, csize); +} + +void WavFile::post(size_t size) +{ +} + +size_t WavFile::samplerate() +{ + return sf_info.samplerate; +} + +extern "C" { + void *create() + { + return new WavFile(); + } + + void destroy(void *h) + { + WavFile *sndfile = (WavFile*)h; + delete sndfile; + } + + bool init(void *h, int cs, char *cnames[]) + { + WavFile *sndfile = (WavFile*)h; + return sndfile->init(cs, cnames); + } + + void setparm(void *h, const char *parm, const char *value) + { + WavFile *sndfile = (WavFile*)h; + sndfile->setParm(parm, value); + } + + bool start(void *h) + { + WavFile *sndfile = (WavFile*)h; + return sndfile->start(); + } + + void stop(void *h) + { + WavFile *sndfile = (WavFile*)h; + sndfile->stop(); + } + + void pre(void *h, size_t s) + { + WavFile *sndfile = (WavFile*)h; + sndfile->pre(s); + } + + void run(void *h, int ch, sample_t *data, size_t size) + { + WavFile *sndfile = (WavFile*)h; + sndfile->run(ch, data, size); + } + + void post(void *h, size_t s) + { + WavFile *sndfile = (WavFile*)h; + sndfile->post(s); + } + + size_t samplerate(void *h) + { + WavFile *wavfile = (WavFile*)h; + return wavfile->samplerate(); + } +} diff --git a/src/audiooutputengine.h b/src/audiooutputengine.h index 7f15e49..562a46b 100644 --- a/src/audiooutputengine.h +++ b/src/audiooutputengine.h @@ -55,6 +55,8 @@ public: * Overload this method to force engine to use different buffer size. */ virtual size_t getBufferSize() { return 1024; } + + virtual size_t samplerate() { return 44100; } }; #endif/*__DRUMGIZMO_AUDIOOUTPUTENGINE_H__*/ -- cgit v1.2.3