klick-0.12.2/0000755000175000017500000000000011253043155010770 5ustar dasdasklick-0.12.2/src/0000755000175000017500000000000011253043155011557 5ustar dasdasklick-0.12.2/src/osc_handler.cc0000644000175000017500000004027611224756531014367 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2008-2009 Dominic Sacré * * This program 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. */ #include "osc_handler.hh" #include "klick.hh" #include "metronome.hh" #include "metronome_simple.hh" #include "metronome_map.hh" #include "metronome_jack.hh" #include "audio_interface_jack.hh" #include "tempomap.hh" #include #include #include "util/debug.hh" #include "util/logstream.hh" OSCHandler::OSCHandler(std::string const & port, std::string const & return_port, Klick & klick, AudioInterfaceJack & audio) : _osc(new OSCInterface(port)) , _klick(klick) , _audio(audio) , _current_tempo(0.0f) { add_method("/klick/ping", "", &OSCHandler::on_ping); add_method("/klick/ping", "s", &OSCHandler::on_ping); add_method("/klick/check","", &OSCHandler::on_check); add_method("/klick/check","s", &OSCHandler::on_check); add_method("/klick/register_client", "", &OSCHandler::on_register_client); add_method("/klick/register_client", "s", &OSCHandler::on_register_client); add_method("/klick/unregister_client", "", &OSCHandler::on_unregister_client); add_method("/klick/unregister_client", "s", &OSCHandler::on_unregister_client); add_method("/klick/query", "", &OSCHandler::on_query); add_method("/klick/query", "s", &OSCHandler::on_query); add_method("/klick/quit", "", &OSCHandler::on_quit); add_method("/klick/config/set_sound", "i", &OSCHandler::on_config_set_sound); add_method("/klick/config/set_sound", "ss", &OSCHandler::on_config_set_sound_custom); add_method("/klick/config/set_sound_volume", "ff", &OSCHandler::on_config_set_sound_volume); add_method("/klick/config/set_sound_pitch", "ff", &OSCHandler::on_config_set_sound_pitch); add_method("/klick/config/set_volume", "f", &OSCHandler::on_config_set_volume); add_method("/klick/config/connect", NULL, &OSCHandler::on_config_connect); add_method("/klick/config/autoconnect", "", &OSCHandler::on_config_autoconnect); add_method("/klick/config/disconnect_all", "", &OSCHandler::on_config_disconnect_all); add_method("/klick/config/get_available_ports", "", &OSCHandler::on_config_get_available_ports); add_method("/klick/config/get_available_ports", "s", &OSCHandler::on_config_get_available_ports); add_method("/klick/config/query", "", &OSCHandler::on_config_query); add_method("/klick/config/query", "s", &OSCHandler::on_config_query); add_method("/klick/metro/set_type", "s", &OSCHandler::on_metro_set_type); add_method("/klick/metro/start", "", &OSCHandler::on_metro_start); add_method("/klick/metro/stop", "", &OSCHandler::on_metro_stop); add_method("/klick/metro/query", "", &OSCHandler::on_metro_query); add_method("/klick/metro/query", "s", &OSCHandler::on_metro_query); add_method("/klick/simple/set_tempo", "f", &OSCHandler::on_simple_set_tempo); add_method("/klick/simple/set_tempo_increment", "f", &OSCHandler::on_simple_set_tempo_increment); add_method("/klick/simple/set_tempo_start", "f", &OSCHandler::on_simple_set_tempo_start); add_method("/klick/simple/set_tempo_limit", "f", &OSCHandler::on_simple_set_tempo_limit); add_method("/klick/simple/set_meter", "ii", &OSCHandler::on_simple_set_meter); add_method("/klick/simple/tap", "", &OSCHandler::on_simple_tap); add_method("/klick/simple/tap", "d", &OSCHandler::on_simple_tap); add_method("/klick/simple/set_pattern", "s", &OSCHandler::on_simple_set_pattern); add_method("/klick/simple/query", "", &OSCHandler::on_simple_query); add_method("/klick/simple/query", "s", &OSCHandler::on_simple_query); add_method("/klick/map/load_file", "s", &OSCHandler::on_map_load_file); add_method("/klick/map/set_preroll", "i", &OSCHandler::on_map_set_preroll); add_method("/klick/map/set_tempo_multiplier", "f", &OSCHandler::on_map_set_tempo_multiplier); add_method("/klick/map/query", "", &OSCHandler::on_map_query); add_method("/klick/map/query", "s", &OSCHandler::on_map_query); add_method("/klick/jack/query", "", &OSCHandler::on_jack_query); add_method("/klick/jack/query", "s", &OSCHandler::on_jack_query); add_method(NULL, NULL, &OSCHandler::fallback); if (!return_port.empty()) { _osc->send(return_port, "/klick/ready"); } } OSCHandler::~OSCHandler() { _osc->stop(); } void OSCHandler::start() { _osc->start(); } void OSCHandler::update() { if (metro_simple()) { if (_current_tempo != metro_simple()->current_tempo()) { _current_tempo = metro_simple()->current_tempo(); _osc->send(_clients, "/klick/simple/current_tempo", _current_tempo); } } } void OSCHandler::add_method(char const *path, char const *types, MessageHandler func) { _osc->add_method(path, types, boost::bind(&OSCHandler::generic_callback, this, func, _1)); } template void OSCHandler::add_method(char const *path, char const *types, MessageHandler func) { _osc->add_method(path, types, boost::bind(&OSCHandler::type_specific_callback, this, func, _1)); } void OSCHandler::generic_callback(MessageHandler func, Message const & msg) { try { (this->*func)(msg); } catch (OSCInterface::OSCError const & e) { std::cerr << msg.path << ": " << e.what() << std::endl; } } template void OSCHandler::type_specific_callback(MessageHandler func, Message const & msg) { try { if (boost::dynamic_pointer_cast(metro())) { (this->*func)(msg); } else { std::cerr << msg.path << ": " << "function not available for current metronome type" << std::endl; } } catch (OSCInterface::OSCError const & e) { std::cerr << msg.path << ": " << e.what() << std::endl; } } OSCInterface::Address OSCHandler::optional_address(Message const & msg, std::size_t i) { if (msg.args.size() > i) { return OSCInterface::Address(boost::get(msg.args[i])); } else { return msg.src; } } void OSCHandler::on_ping(Message const & msg) { OSCInterface::Address addr(optional_address(msg)); std::cout << "ping from " << msg.src.url() << std::endl; _osc->send(addr, "/klick/pong"); } void OSCHandler::on_check(Message const & msg) { _osc->send(optional_address(msg), "/klick/ready"); } void OSCHandler::on_register_client(Message const & msg) { OSCInterface::Address addr(optional_address(msg)); ClientList::iterator i = find(_clients.begin(), _clients.end(), addr); if (i == _clients.end()) { _clients.push_back(addr); das::logv << "client " << addr.url() << " registered" << std::endl; } } void OSCHandler::on_unregister_client(Message const & msg) { OSCInterface::Address addr(optional_address(msg)); ClientList::iterator i = find(_clients.begin(), _clients.end(), addr); if (i != _clients.end()) { _clients.erase(i); das::logv << "client " << addr.url() << " unregistered" << std::endl; } } void OSCHandler::on_query(Message const & msg) { on_config_query(msg); on_metro_query(msg); if (metro_simple()) { on_simple_query(msg); } else if (metro_map()) { on_map_query(msg); } else if (metro_jack()) { on_jack_query(msg); } else { FAIL(); } } void OSCHandler::on_quit(Message const & /*msg*/) { _klick.signal_quit(); } void OSCHandler::on_config_set_sound(Message const & msg) { _klick.set_sound(boost::get(msg.args[0])); _osc->send(_clients, "/klick/config/sound", _klick.sound()); } void OSCHandler::on_config_set_sound_custom(Message const & msg) { std::string emphasis = boost::get(msg.args[0]); std::string normal = boost::get(msg.args[1]); _klick.set_sound_custom(emphasis, normal); std::string res_emphasis, res_normal; boost::tie(res_emphasis, res_normal) = _klick.sound_custom(); _osc->send(_clients, "/klick/config/sound", res_emphasis, res_normal); if (res_emphasis != emphasis) { _osc->send(_clients, "/klick/config/sound_loading_failed", emphasis); } if (res_normal != normal) { _osc->send(_clients, "/klick/config/sound_loading_failed", normal); } } void OSCHandler::on_config_set_sound_volume(Message const & msg) { _klick.set_sound_volume(boost::get(msg.args[0]), boost::get(msg.args[1])); _osc->send(_clients, "/klick/config/sound_volume", _klick.sound_volume().get<0>(), _klick.sound_volume().get<1>()); } void OSCHandler::on_config_set_sound_pitch(Message const & msg) { _klick.set_sound_pitch(boost::get(msg.args[0]), boost::get(msg.args[1])); _osc->send(_clients, "/klick/config/sound_pitch", _klick.sound_pitch().get<0>(), _klick.sound_pitch().get<1>()); } void OSCHandler::on_config_set_volume(Message const & msg) { _audio.set_volume(boost::get(msg.args[0])); _osc->send(_clients, "/klick/config/volume", _audio.volume()); } void OSCHandler::on_config_connect(Message const & msg) { if (msg.types.find_first_not_of("s") != std::string::npos) { std::cerr << msg.path << ": invalid argument type" << std::endl; return; } for (OSCInterface::ArgumentVector::const_iterator i = msg.args.begin(); i != msg.args.end(); ++i) { try { _audio.connect(boost::get(*i)); } catch (AudioInterfaceJack::AudioError const & e) { std::cerr << msg.path << ": " << e.what() << std::endl; } } } void OSCHandler::on_config_autoconnect(Message const & /*msg*/) { _audio.autoconnect(); } void OSCHandler::on_config_disconnect_all(Message const &) { _audio.disconnect_all(); } void OSCHandler::on_config_get_available_ports(Message const & msg) { std::vector v = _audio.available_ports(); _osc->send(optional_address(msg), "/klick/config/available_ports", OSCInterface::ArgumentVector(v.begin(), v.end())); } void OSCHandler::on_config_query(Message const & msg) { OSCInterface::Address addr(optional_address(msg)); if (_klick.sound() != -1) { _osc->send(addr, "/klick/config/sound", _klick.sound()); } else { _osc->send(addr, "/klick/config/sound", _klick.sound_custom().get<0>(), _klick.sound_custom().get<1>()); } _osc->send(addr, "/klick/config/sound_volume", _klick.sound_volume().get<0>(), _klick.sound_volume().get<1>()); _osc->send(addr, "/klick/config/sound_pitch", _klick.sound_pitch().get<0>(), _klick.sound_pitch().get<1>()); _osc->send(addr, "/klick/config/volume", _audio.volume()); } void OSCHandler::on_metro_set_type(Message const & msg) { std::string type = boost::get(msg.args[0]); if (type == "simple") { _klick.set_metronome(Options::METRONOME_TYPE_SIMPLE); } else if (type == "map") { _klick.set_metronome(Options::METRONOME_TYPE_MAP); } else if (type == "jack") { _klick.set_metronome(Options::METRONOME_TYPE_JACK); } else { std::cerr << msg.path << ": invalid metronome type '" << type << "'" << std::endl; return; } _osc->send(_clients, "/klick/metro/type", type); } void OSCHandler::on_metro_start(Message const & /*msg*/) { metro()->start(); _osc->send(_clients, "/klick/metro/active", metro()->active()); } void OSCHandler::on_metro_stop(Message const & /*msg*/) { metro()->stop(); _osc->send(_clients, "/klick/metro/active", metro()->active()); } void OSCHandler::on_metro_query(Message const & msg) { OSCInterface::Address addr(optional_address(msg)); if (metro_simple()) { _osc->send(addr, "/klick/metro/type", "simple"); } else if (metro_map()) { _osc->send(addr, "/klick/metro/type", "map"); } else if (metro_jack()) { _osc->send(addr, "/klick/metro/type", "jack"); } else { FAIL(); } _osc->send(addr, "/klick/metro/active", metro()->active()); } void OSCHandler::on_simple_set_tempo(Message const & msg) { metro_simple()->set_tempo(boost::get(msg.args[0])); _osc->send(_clients, "/klick/simple/tempo", metro_simple()->tempo()); } void OSCHandler::on_simple_set_tempo_increment(Message const & msg) { metro_simple()->set_tempo_increment(boost::get(msg.args[0])); _osc->send(_clients, "/klick/simple/tempo_increment", metro_simple()->tempo_increment()); } void OSCHandler::on_simple_set_tempo_start(Message const & msg) { metro_simple()->set_tempo_start(boost::get(msg.args[0])); _osc->send(_clients, "/klick/simple/tempo_start", metro_simple()->tempo_start()); } void OSCHandler::on_simple_set_tempo_limit(Message const & msg) { metro_simple()->set_tempo_limit(boost::get(msg.args[0])); _osc->send(_clients, "/klick/simple/tempo_limit", metro_simple()->tempo_limit()); } void OSCHandler::on_simple_set_meter(Message const & msg) { metro_simple()->set_meter(boost::get(msg.args[0]), boost::get(msg.args[1])); _osc->send(_clients, "/klick/simple/meter", metro_simple()->beats(), metro_simple()->denom()); } void OSCHandler::on_simple_tap(Message const & msg) { if (!msg.args.empty()) { metro_simple()->tap(boost::get(msg.args[0])); } else { metro_simple()->tap(); } _osc->send(_clients, "/klick/simple/tempo", metro_simple()->tempo()); } void OSCHandler::on_simple_set_pattern(Message const & msg) { try { TempoMap::Pattern p = TempoMap::parse_pattern(boost::get(msg.args[0]), std::max(1, metro_simple()->beats())); metro_simple()->set_pattern(p); } catch (TempoMap::ParseError const & e) { std::cerr << msg.path << ": " << e.what() << std::endl; return; } _osc->send(_clients, "/klick/simple/pattern", TempoMap::pattern_to_string(metro_simple()->pattern())); } void OSCHandler::on_simple_query(Message const & msg) { OSCInterface::Address addr(optional_address(msg)); boost::shared_ptr m(metro_simple()); _osc->send(addr, "/klick/simple/tempo", m->tempo()); _osc->send(addr, "/klick/simple/tempo_increment", m->tempo_increment()); _osc->send(addr, "/klick/simple/tempo_start", m->tempo_start()); _osc->send(addr, "/klick/simple/tempo_limit", m->tempo_limit()); _osc->send(addr, "/klick/simple/current_tempo", m->current_tempo()); _osc->send(addr, "/klick/simple/meter", m->beats(), m->denom()); _osc->send(addr, "/klick/simple/pattern", TempoMap::pattern_to_string(m->pattern())); } void OSCHandler::on_map_load_file(Message const & msg) { try { _klick.set_tempomap_filename(boost::get(msg.args[0])); } catch (std::runtime_error const & e) { std::cerr << msg.path << ": " << e.what() << std::endl; return; } _osc->send(_clients, "/klick/map/filename", _klick.tempomap_filename()); } void OSCHandler::on_map_set_preroll(Message const & msg) { _klick.set_tempomap_preroll(boost::get(msg.args[0])); _osc->send(_clients, "/klick/map/preroll", _klick.tempomap_preroll()); } void OSCHandler::on_map_set_tempo_multiplier(Message const & msg) { _klick.set_tempomap_multiplier(boost::get(msg.args[0])); _osc->send(_clients, "/klick/map/tempo_multiplier", _klick.tempomap_multiplier()); } void OSCHandler::on_map_query(Message const & msg) { OSCInterface::Address addr(optional_address(msg)); _osc->send(addr, "/klick/map/filename", _klick.tempomap_filename()); _osc->send(addr, "/klick/map/preroll", _klick.tempomap_preroll()); _osc->send(addr, "/klick/map/tempo_multiplier", _klick.tempomap_multiplier()); } void OSCHandler::on_jack_query(Message const & /*msg*/) { // nothing } void OSCHandler::fallback(Message const & msg) { std::cerr << "unknown message: " << msg.path << " ," << msg.types << std::endl; } klick-0.12.2/src/tempomap.hh0000644000175000017500000000601611133467215013731 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _TEMPOMAP_HH #define _TEMPOMAP_HH #include #include #include #include typedef boost::shared_ptr TempoMapPtr; typedef boost::shared_ptr TempoMapConstPtr; class TempoMap { public: struct ParseError : public std::runtime_error { ParseError(std::string const & w) : std::runtime_error(w) { } }; enum BeatType { BEAT_EMPHASIS = 1, BEAT_NORMAL, BEAT_SILENT }; typedef std::vector Pattern; struct Entry { std::string label; int bars; // -1 means play ad infinitum float tempo; // zero if tempo is given for each beat float tempo2; // zero if tempo is constant std::vector tempi; // empty unless tempo == 0.0 int beats; int denom; Pattern pattern; // empty if default float volume; }; typedef std::vector Entries; // get all entries Entries const & entries() const { return _entries; } // get n'th entry Entry const & entry(std::size_t n) const { return _entries[n]; } Entry const & operator[](std::size_t n) const { return _entries[n]; } // get number of entries std::size_t size() const { return _entries.size(); } // get entry with label l, NULL if no such entry exists Entry const * entry(std::string const & l) const { if (l.empty()) return NULL; for (Entries::const_iterator i = _entries.begin(); i != _entries.end(); ++i) { if (i->label == l) return &*i; } return NULL; } void add(Entry const & e) { _entries.push_back(e); } std::string dump() const; static TempoMapPtr join(TempoMapConstPtr const, TempoMapConstPtr const); static TempoMapPtr new_from_file(std::string const & filename); static TempoMapPtr new_from_cmdline(std::string const & line); static TempoMapPtr new_simple(int bars, float tempo, int beats, int denom, Pattern const & pattern = Pattern(), float volume = 1.0f); public: // builds a vector of beat types, based on the string description static Pattern parse_pattern(std::string const &s, int nbeats); static std::string pattern_to_string(Pattern const & p); private: // parses a comma-separated tempo string static std::vector parse_tempi(std::string const &s, float tempo1, int nbeats_total); static void check_entry(Entry const & e); Entries _entries; }; #endif // _TEMPOMAP_HH klick-0.12.2/src/metronome_map.hh0000644000175000017500000000257611253040563014754 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _METRONOME_MAP_HH #define _METRONOME_MAP_HH #include "metronome.hh" #include "tempomap.hh" #include "position.hh" #include /* * plays a click track using a predefined tempomap */ class MetronomeMap : public Metronome { public: MetronomeMap( AudioInterface & audio, TempoMapConstPtr tempomap, float tempo_multiplier, bool transport, bool master, int preroll, std::string const & start_label ); virtual ~MetronomeMap(); virtual void do_start(); virtual void do_stop(); bool running() const; nframes_t current_frame() const; nframes_t total_frames() const; virtual void process_callback(sample_t *, nframes_t); virtual void timebase_callback(position_t *); private: static double const TICKS_PER_BEAT = 1920.0; // transport position nframes_t _current; // position in tempomap Position _pos; bool _transport_enabled; bool _transport_master; }; #endif // _METRONOME_MAP_HH klick-0.12.2/src/metronome_jack.hh0000644000175000017500000000174211253040563015101 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _METRONOME_JACK_HH #define _METRONOME_JACK_HH #include "audio.hh" #include "metronome.hh" #include "audio_interface_jack.hh" /* * plays a click track following jack transport; no tempomap! */ class MetronomeJack : public Metronome { public: MetronomeJack(AudioInterfaceJack & audio); virtual ~MetronomeJack(); virtual bool running() const { return true; } virtual void process_callback(sample_t *, nframes_t); private: static nframes_t const MIN_FRAMES_DIFF = 64; AudioInterfaceJack & _audio; nframes_t _last_click_frame; }; #endif // _METRONOME_JACK_HH klick-0.12.2/src/position.hh0000644000175000017500000000457711224756531013770 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _POSITION_HH #define _POSITION_HH #include "audio.hh" #include "tempomap.hh" #include #include /* * keeps track of the position in the tempomap */ class Position { public: typedef double float_frames_t; struct Tick { nframes_t frame; TempoMap::BeatType type; float volume; }; Position(TempoMapConstPtr tempomap, float_frames_t samplerate, float multiplier); void set_start_label(std::string const & start_label); void add_preroll(int nbars); // move to frame void locate(nframes_t f); // move position one tick forward void advance(); // distance from previous (current) tick to the next float_frames_t dist_to_next() const; // frame of next tick float_frames_t next_frame() const { return frame() + dist_to_next(); } // get current tick const Tick tick() const; // end of tempomap reached? bool end() const { return _end; } float_frames_t frame() const { return _frame; } int entry() const { return _entry; } int bar() const { return _bar; } int beat() const { return _beat; } int bar_total() const { return _bar_total; } // current tempomap entry TempoMap::Entry const & map_entry() const { return (*_tempomap)[_entry]; } // total length of tempomap float_frames_t total_frames() const { return _start_frames.back(); } private: // reset, locate at start of tempomap void reset(); // calculate length of entry or beat(s) float_frames_t frame_dist(TempoMap::Entry const & e, int start, int end) const; float_frames_t _frame; // frame position of current tick int _entry, _bar, _beat; // current position in tempomap int _bar_total; // current bar number (including previous entries) bool _init, _end; TempoMapConstPtr _tempomap; float_frames_t _samplerate; float _multiplier; std::vector _start_frames; std::vector _start_bars; }; #endif // _POSITION_HH klick-0.12.2/src/metronome_simple.cc0000644000175000017500000001140111253041647015445 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #include "metronome_simple.hh" #include #include #include #include #include "util/debug.hh" MetronomeSimple::MetronomeSimple(AudioInterface & audio, TempoMap::Entry const * params) : Metronome(audio) , _tempo(120.0) , _tempo_increment(0.0) , _tempo_start(0.0) , _tempo_limit(0.0) , _current_tempo(0.0) , _beats(4) , _denom(4) , _frame(0) , _next(0) , _beat(0) , _tapped(false) { if (params) { set_all(*params); } } MetronomeSimple::~MetronomeSimple() { } void MetronomeSimple::set_tempo(float tempo) { _tempo = tempo; if (active()) { _current_tempo = _tempo; } } void MetronomeSimple::set_tempo_increment(float tempo_increment) { _tempo_increment = tempo_increment; } void MetronomeSimple::set_tempo_start(float tempo_start) { _tempo_start = tempo_start; } void MetronomeSimple::set_tempo_limit(float tempo_limit) { _tempo_limit = tempo_limit; } void MetronomeSimple::set_meter(int beats, int denom) { if (beats != _beats && _pattern.size()) { // TODO: handle this properly _pattern.clear(); } _beats = beats; _denom = denom; } void MetronomeSimple::set_pattern(TempoMap::Pattern const & pattern) { _pattern = pattern; } void MetronomeSimple::set_all(TempoMap::Entry const & params) { set_tempo(params.tempo); set_meter(params.beats, params.denom); set_pattern(params.pattern); } void MetronomeSimple::do_start() { _beat = 0; _next = 0; _frame = 0; if (_tempo_increment && _tempo_start) { _current_tempo = _tempo_start; } else { _current_tempo = _tempo; } } void MetronomeSimple::do_stop() { _current_tempo = 0.0f; } void MetronomeSimple::tap(double now) { if (_taps.size() && now < _taps.back()) { // distortion in space-time continuum _taps.clear(); } _taps.push_back(now); if ((int)_taps.size() > MAX_TAPS) { _taps.pop_front(); } // forget taps which happened too long ago _taps.erase( std::remove_if(_taps.begin(), _taps.end(), boost::lambda::_1 < now - MAX_TAP_AGE), _taps.end() ); if (_taps.size() > 1) { _tempo = 60.0f * (_taps.size() - 1) / (_taps.back() - _taps.front()); if (active()) { _current_tempo = _tempo; _tapped = true; } } } void MetronomeSimple::tap() { ::timeval tv; ::gettimeofday(&tv, NULL); double now = tv.tv_sec + 1.e-6 * tv.tv_usec; tap(now); } void MetronomeSimple::process_callback(sample_t * /*buffer*/, nframes_t nframes) { if (_tapped) { // TODO: this is crap. read user's mind instead nframes_t delta = (nframes_t)(TAP_DIFF * _audio.samplerate()); if (_frame - _prev < delta) { // delay next beat _next += _frame - _prev; } else { // play beat now _next = _frame; } _tapped = false; } if (!active()) { return; } if (_frame + nframes > _next) { // offset in current period nframes_t offset = _next - _frame; if (_pattern.size()) { // play click, user-defined pattern ASSERT((int)_pattern.size() == std::max(1, _beats)); if (_pattern[_beat] != TempoMap::BEAT_SILENT) { bool emphasis = (_pattern[_beat] == TempoMap::BEAT_EMPHASIS); play_click(emphasis, offset); } } else { // play click, default pattern play_click(_beat == 0 && _beats > 0, offset); } // speed trainer if (_frame && _tempo_increment) { _current_tempo += _tempo_increment / std::max(_beats, 1); if (_tempo_limit) { _current_tempo = _tempo_increment > 0.0f ? std::min(_current_tempo, _tempo_limit) : std::max(_current_tempo, _tempo_limit); } else if (_tempo_start) { _current_tempo = _tempo_increment > 0.0f ? std::min(_current_tempo, _tempo) : std::max(_current_tempo, _tempo); } } _prev = _next; _next += (nframes_t)(_audio.samplerate() * 240.0 / (_current_tempo * _denom)); if (++_beat >= _beats) { _beat = 0; } } _frame += nframes; } klick-0.12.2/src/metronome.cc0000644000175000017500000000206611253040563014077 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #include "metronome.hh" #include "audio_interface_jack.hh" #include "util/debug.hh" Metronome::Metronome(AudioInterface & audio) : _audio(audio) , _active(false) { } void Metronome::set_active(bool b) { if (b) { do_start(); } else { do_stop(); } _active = b; } void Metronome::set_sound(AudioChunkConstPtr emphasis, AudioChunkConstPtr normal) { _click_emphasis = emphasis; _click_normal = normal; } void Metronome::play_click(bool emphasis, nframes_t offset, float volume) { ASSERT(_click_emphasis); ASSERT(_click_normal); AudioChunkConstPtr click = emphasis ? _click_emphasis : _click_normal; _audio.play(click, offset, volume); } klick-0.12.2/src/terminal_handler.hh0000644000175000017500000000175611133467215015425 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2008-2009 Dominic Sacré * * This program 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. */ #include #include class Klick; class AudioInterface; class TerminalHandler : boost::noncopyable { public: TerminalHandler(Klick & klick, AudioInterface & audio); ~TerminalHandler(); void handle_input(); private: bool key_pressed(); int get_key(); int peek_key(); bool munch_key(int); void set_beats(int); void set_denom(int); void change_tempo(float); void multiply_tempo(float); void change_volume(float); void toggle_running(); void update(); Klick & _klick; AudioInterface & _audio; ::termios _old_mode; }; klick-0.12.2/src/osc_interface.hh0000644000175000017500000000714311133467215014715 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2008-2009 Dominic Sacré * * This program 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. */ #ifndef _OSC_INTERFACE_HH #define _OSC_INTERFACE_HH #include #include #include #include #include #include #include #include #include #include #include #include #include class OSCInterface : boost::noncopyable { public: struct OSCError : public std::runtime_error { OSCError(std::string const & w) : std::runtime_error(w) { } }; class Address { public: Address(Address const &); Address(std::string const & url); ~Address(); Address & operator=(Address const &); bool operator==(Address const &); lo_address addr() const { return _addr; } std::string url() const; private: lo_address _addr; }; typedef boost::variant ArgumentVariant; typedef std::vector ArgumentVector; struct Message { Message(std::string const & path_, std::string const & types_, Address const & src_) : path(path_) , types(types_) , src(src_) { } std::string path; std::string types; ArgumentVector args; Address src; }; typedef boost::function Callback; OSCInterface(std::string const & port); virtual ~OSCInterface(); void add_method(char const *path, char const *types, Callback const & cb); void start(); void stop(); // basic send function void send(Address const & target, std::string const & path, ArgumentVector const & args = ArgumentVector()); // address as string void send(std::string const & target, std::string const & path, ArgumentVector const & args = ArgumentVector()) { send(Address(target), path, args); } // allow multiple recipients template void send(T const & targets, std::string const & path, ArgumentVector const & args = ArgumentVector()) { for (typename T::const_iterator i = targets.begin(); i != targets.end(); ++i) { send(*i, path, args); } } // allow arguments to be passed directly to send(), without manually filling a vector #define PP_PUSH_BACK(z, n, arg) args.push_back(arg ## n); #define PP_SEND(z, n, data) \ template \ void send(A target, std::string const & path, BOOST_PP_ENUM_BINARY_PARAMS(n, const T, & t)) { \ ArgumentVector args; \ BOOST_PP_REPEAT_ ## z(n, PP_PUSH_BACK, t) \ send(target, path, args); \ } BOOST_PP_REPEAT_FROM_TO(1, 10, PP_SEND, ~) #undef PP_PUSH_BACK #undef PP_SEND std::string const & url() const { return _url; } private: static int callback_(char const *path, char const *types, lo_arg **argv, int argc, lo_message msg, void *data); lo_server_thread _thread; std::string _url; std::list _callbacks; }; #endif // _OSC_INTERFACE_HH klick-0.12.2/src/audio_interface_jack.cc0000644000175000017500000001143511224756531016212 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #include "audio_interface_jack.hh" #include #include #include #include #include #include "util/string.hh" #include "util/debug.hh" AudioInterfaceJack::AudioInterfaceJack(std::string const & name) : _shutdown(false) { if ((_client = jack_client_open(name.c_str(), JackNullOption, NULL)) == 0) { throw AudioError("can't connect to jack server"); } jack_set_process_callback(_client, &process_callback_, static_cast(this)); jack_on_shutdown(_client, &shutdown_callback_, static_cast(this)); if ((_output_port = jack_port_register(_client, "out", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) == NULL) { throw AudioError("can't register output port"); } if (jack_activate(_client)) { throw AudioError("can't activate client"); } } AudioInterfaceJack::~AudioInterfaceJack() { jack_deactivate(_client); jack_client_close(_client); } std::string AudioInterfaceJack::client_name() const { return std::string(jack_get_client_name(_client)); } pthread_t AudioInterfaceJack::client_thread() const { return jack_client_thread_id(_client); } nframes_t AudioInterfaceJack::samplerate() const { return jack_get_sample_rate(_client); } bool AudioInterfaceJack::is_shutdown() const { return _shutdown; } void AudioInterfaceJack::set_timebase_callback(TimebaseCallback cb) { if (cb) { if (jack_set_timebase_callback(_client, 0, &timebase_callback_, static_cast(this)) != 0) { throw AudioError("failed to become jack transport master"); } } else { if (_timebase_cb) { jack_release_timebase(_client); } } _timebase_cb = cb; } void AudioInterfaceJack::connect(std::string const & port) { int error = jack_connect(_client, jack_port_name(_output_port), port.c_str()); if (error && error != EEXIST) { throw AudioError(das::make_string() << "can't connect " << jack_port_name(_output_port) << " to " << port.c_str()); } } void AudioInterfaceJack::autoconnect() { // find first two hardware outs char const **hw_ports = jack_get_ports(_client, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsInput); if (hw_ports) { for (int n = 0; n < 2 && hw_ports[n] != NULL; ++n) { jack_connect(_client, jack_port_name(_output_port), hw_ports[n]); } std::free(hw_ports); } } void AudioInterfaceJack::disconnect_all() { jack_port_disconnect(_client, _output_port); } std::vector AudioInterfaceJack::available_ports() { std::vector v; char const **ports = jack_get_ports(_client, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput); if (ports) { char const **p = ports; while (*p) { v.push_back(*p++); } std::free(ports); } return v; } bool AudioInterfaceJack::transport_rolling() const { return (jack_transport_query(_client, NULL) == JackTransportRolling); } position_t AudioInterfaceJack::position() const { position_t pos; jack_transport_query(_client, &pos); return pos; } nframes_t AudioInterfaceJack::frame() const { return position().frame; } bool AudioInterfaceJack::set_position(position_t const & pos) { // jack doesn't modify pos, should have been const anyway, i guess... return (jack_transport_reposition(_client, const_cast(&pos)) == 0); } bool AudioInterfaceJack::set_frame(nframes_t frame) { return (jack_transport_locate(_client, frame) == 0); } int AudioInterfaceJack::process_callback_(nframes_t nframes, void *arg) { AudioInterfaceJack *this_ = static_cast(arg); sample_t *buffer = (sample_t *)jack_port_get_buffer(this_->_output_port, nframes); std::memset(buffer, 0, nframes * sizeof(sample_t)); if (this_->_process_cb) { this_->_process_cb(buffer, nframes); } this_->process_mix(buffer, nframes); return 0; } void AudioInterfaceJack::timebase_callback_(jack_transport_state_t /*state*/, nframes_t /*nframes*/, position_t *pos, int /*new_pos*/, void *arg) { AudioInterfaceJack *this_ = static_cast(arg); if (this_->_timebase_cb) { this_->_timebase_cb(pos); } } void AudioInterfaceJack::shutdown_callback_(void *arg) { static_cast(arg)->_shutdown = true; } klick-0.12.2/src/klick.hh0000644000175000017500000000552611224756531013214 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _KLICK_HH #define _KLICK_HH #include #include #include #include #include #include "audio.hh" #include "options.hh" class AudioInterface; class TempoMap; class Metronome; class OSCHandler; class TerminalHandler; namespace das { class garbage_collector; } class Klick : boost::noncopyable { public: Klick(int argc, char *argv[]); ~Klick(); void run(); void signal_quit(); boost::shared_ptr metronome() const { return _metro; } void set_metronome(Options::MetronomeType type); void set_sound(int n); void set_sound_custom(std::string const &, std::string const &); void set_sound_volume(float, float); void set_sound_pitch(float, float); int sound() const { return _options->click_sample; } boost::tuple sound_custom() const { return boost::make_tuple(_options->click_filename_emphasis, _options->click_filename_normal); } boost::tuple sound_volume() const { return boost::make_tuple(_options->volume_emphasis, _options->volume_normal); } boost::tuple sound_pitch() const { return boost::make_tuple(_options->pitch_emphasis, _options->pitch_normal); } void set_tempomap_filename(std::string const & filename); void set_tempomap_preroll(int bars); void set_tempomap_multiplier(float mult); std::string const & tempomap_filename() const { return _options->filename; } int tempomap_preroll() const { return _options->preroll; } float tempomap_multiplier() const { return _options->tempo_multiplier; } private: void setup_jack(); void setup_sndfile(); void load_tempomap(); void load_samples(); void load_metronome(); boost::tuple sample_filenames(int n, Options::EmphasisMode emphasis_mode); AudioChunkPtr load_sample(std::string const & filename, float volume, float pitch); void run_jack(); void run_sndfile(); boost::scoped_ptr _options; boost::scoped_ptr _gc; boost::shared_ptr _audio; AudioChunkPtr _click_emphasis; AudioChunkPtr _click_normal; boost::shared_ptr _map; boost::shared_ptr _osc; boost::shared_ptr _term; boost::shared_ptr _metro; volatile bool _quit; }; #endif // _KLICK_HH klick-0.12.2/src/terminal_handler.cc0000644000175000017500000001317611133467215015412 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2008-2009 Dominic Sacré * * This program 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. */ #include "klick.hh" #include "audio_interface.hh" #include "terminal_handler.hh" #include "metronome_simple.hh" #include #include #include #include #include #include #include "util/debug.hh" typedef boost::shared_ptr MetronomeSimplePtr; TerminalHandler::TerminalHandler(Klick & klick, AudioInterface & audio) : _klick(klick) , _audio(audio) { // save current terminal mode ::tcgetattr(STDIN_FILENO, &_old_mode); // turn off line buffering ::termios term; ::tcgetattr(STDIN_FILENO, &term); term.c_lflag &= ~(ICANON | ECHO); ::tcsetattr(STDIN_FILENO, TCSANOW, &term); ::setbuf(stdin, NULL); // hide cursor std::cout << "\033[?25l"; update(); } TerminalHandler::~TerminalHandler() { // restore previous terminal mode ::tcsetattr(STDIN_FILENO, TCSANOW, &_old_mode); // show cursor again std::cout << "\033[?25h"; std::cout << std::endl; } bool TerminalHandler::key_pressed() { if (!::isatty(STDIN_FILENO)) { return false; } ::timeval timeout = { 0, 0 }; ::fd_set rdset; FD_ZERO(&rdset); FD_SET(STDIN_FILENO, &rdset); return ::select(STDIN_FILENO + 1, &rdset, NULL, NULL, &timeout); } int TerminalHandler::get_key() { return std::fgetc(stdin); } int TerminalHandler::peek_key() { if (key_pressed()) { int k = std::fgetc(stdin); std::ungetc(k, stdin); return k; } else { return 0; } } bool TerminalHandler::munch_key(int k) { if (peek_key() == k) { get_key(); return true; } else { return false; } } void TerminalHandler::handle_input() { while (key_pressed()) { switch (get_key()) { case 033: if (key_pressed() && get_key() == '[' && key_pressed()) { // escape sequence switch (get_key()) { // arrow keys: tempo case 'A': change_tempo(+10); break; case 'B': change_tempo(-10); break; case 'C': change_tempo(+1); break; case 'D': change_tempo(-1); break; // pgup/pgdn: double/halve tempo case '5': if (munch_key('~')) multiply_tempo(2.0f); break; case '6': if (munch_key('~')) multiply_tempo(0.5f); break; default: // unknown escape sequence while (key_pressed()) get_key(); break; } } else { // escape key _klick.signal_quit(); } break; // beats case '0': set_beats(0); break; case '1': set_beats(1); break; case '2': set_beats(2); break; case '3': set_beats(3); break; case '4': set_beats(4); break; case '5': set_beats(5); break; case '6': set_beats(6); break; case '7': set_beats(7); break; case '8': set_beats(8); break; case '9': set_beats(9); break; // denominator case 'q': set_denom(1); break; case 'w': set_denom(2); break; case 'e': set_denom(4); break; case 'r': set_denom(8); break; // volume case '+': case '=': change_volume(+0.1); break; case '-': change_volume(-0.1); break; case ' ': toggle_running(); break; default: break; } update(); } } void TerminalHandler::set_beats(int n) { MetronomeSimplePtr m = boost::dynamic_pointer_cast(_klick.metronome()); m->set_meter(n, m->denom()); } void TerminalHandler::set_denom(int n) { MetronomeSimplePtr m = boost::dynamic_pointer_cast(_klick.metronome()); m->set_meter(m->beats(), n); } void TerminalHandler::change_tempo(float f) { MetronomeSimplePtr m = boost::dynamic_pointer_cast(_klick.metronome()); float t = m->tempo() + f; t = std::min(std::max(t, 10.0f), 1000.0f); m->set_tempo(t); } void TerminalHandler::multiply_tempo(float f) { MetronomeSimplePtr m = boost::dynamic_pointer_cast(_klick.metronome()); float t = m->tempo() * f; t = std::min(std::max(t, 10.0f), 1000.0f); m->set_tempo(t); } void TerminalHandler::change_volume(float f) { float v = _audio.volume() + f; v = std::min(std::max(v, 0.0f), 2.0f); _audio.set_volume(v); } void TerminalHandler::toggle_running() { bool active = _klick.metronome()->active(); _klick.metronome()->set_active(!active); } void TerminalHandler::update() { MetronomeSimplePtr m = boost::dynamic_pointer_cast(_klick.metronome()); ASSERT(m); // clear current line std::cout << "\r\033[K\r"; std::cout << "meter: " << m->beats() << "/" << m->denom() << ", "; std::cout << std::fixed << std::setprecision(0) << "tempo: " << m->tempo() << ", "; std::cout << std::setprecision(1) << "volume: " << _audio.volume(); std::cout << std::flush; } klick-0.12.2/src/options.hh0000644000175000017500000000432711224756531013610 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _OPTIONS_HH #define _OPTIONS_HH #include "audio.hh" #include #include #include #include "util/string.hh" class Options { public: Options(); void parse(int argc, char *argv[]); std::string client_name; std::vector connect_ports; bool auto_connect; bool use_osc; std::string osc_port; std::string osc_return_port; bool interactive; bool follow_transport; std::string filename; std::string cmdline; std::string output_filename; nframes_t output_samplerate; enum MetronomeType { METRONOME_TYPE_SIMPLE, METRONOME_TYPE_MAP, METRONOME_TYPE_JACK }; MetronomeType type; static int const CLICK_SAMPLE_FROM_FILE = -2; static int const CLICK_SAMPLE_SILENT = -1; int click_sample; std::string click_filename_emphasis; std::string click_filename_normal; enum EmphasisMode { EMPHASIS_MODE_NORMAL, EMPHASIS_MODE_NONE, EMPHASIS_MODE_ALL }; EmphasisMode emphasis_mode; float volume_emphasis, volume_normal; float pitch_emphasis, pitch_normal; bool transport_enabled; bool transport_master; float delay; static int const PREROLL_NONE = -1; static int const PREROLL_2_BEATS = 0; int preroll; std::string start_label; float tempo_multiplier; bool verbose; private: struct CmdlineError : public std::runtime_error { CmdlineError(std::string const & w) : std::runtime_error(w) { } }; struct InvalidArgument : public std::runtime_error { InvalidArgument(char a, std::string const & w) : std::runtime_error(das::make_string() << "invalid argument to -" << a << " (" << w << ")") { } }; void print_version(); void print_usage(); }; #endif // _OPTIONS_HH klick-0.12.2/src/metronome.hh0000644000175000017500000000263611253040563014114 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _METRONOME_HH #define _METRONOME_HH #include "audio_interface.hh" #include "audio_chunk.hh" #include #include "util/disposable.hh" /* * abstract metronome base class */ class Metronome : public das::disposable , boost::noncopyable { public: Metronome(AudioInterface & audio); virtual ~Metronome() { } void set_sound(AudioChunkConstPtr emphasis, AudioChunkConstPtr normal); void set_active(bool b); void start() { set_active(true); } void stop() { set_active(false); } bool active() const { return _active; } virtual void do_start() { } virtual void do_stop() { } virtual void process_callback(sample_t *, nframes_t) = 0; virtual void timebase_callback(position_t *) { } virtual bool running() const = 0; protected: void play_click(bool emphasis, nframes_t offset, float volume = 1.0f); AudioInterface & _audio; AudioChunkConstPtr _click_emphasis; AudioChunkConstPtr _click_normal; private: bool _active; }; #endif // _METRONOME_HH klick-0.12.2/src/main.hh0000644000175000017500000000133411133467215013031 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _MAIN_HH #define _MAIN_HH #include #include class Exit : public std::exception { public: Exit(int status) : _status(status) { } virtual ~Exit() throw () { } int status() const throw() { return _status; } protected: int _status; }; std::string data_file(std::string const & path); #endif // _MAIN_HH klick-0.12.2/src/metronome_map.cc0000644000175000017500000001205711224756531014744 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #include "metronome_map.hh" #include "options.hh" #include "audio_interface_jack.hh" #include "audio_chunk.hh" #include "tempomap.hh" #include #include #include "util/debug.hh" MetronomeMap::MetronomeMap( AudioInterface & audio, TempoMapConstPtr tempomap, float tempo_multiplier, bool transport, bool master, int preroll, std::string const & start_label ) : Metronome(audio) , _current(0) , _pos(tempomap, audio.samplerate(), tempo_multiplier) , _transport_enabled(transport) , _transport_master(master) { ASSERT(tempomap); ASSERT(tempomap->size() > 0); ASSERT(tempo_multiplier > 0.0f); // set start label if (!start_label.empty()) { _pos.set_start_label(start_label); } // add preroll if (preroll != Options::PREROLL_NONE) { _pos.add_preroll(preroll); } } MetronomeMap::~MetronomeMap() { } void MetronomeMap::do_start() { _pos.locate(0); _current = 0; } void MetronomeMap::do_stop() { } bool MetronomeMap::running() const { // if transport is enabled, we never quit, even at the end of the tempomap return _transport_enabled ? true : !_pos.end(); } nframes_t MetronomeMap::current_frame() const { return _current; } nframes_t MetronomeMap::total_frames() const { return static_cast(_pos.total_frames()); } void MetronomeMap::process_callback(sample_t * /*buffer*/, nframes_t nframes) { if (!active()) { return; } AudioInterfaceJack *a = dynamic_cast(&_audio); if (_transport_enabled && a) { if (!a->transport_rolling()) return; nframes_t p = a->frame(); if (p != _current) { // position changed since last period, need to relocate _current = p; _pos.locate(p); } } else { if (_pos.end()) return; } // check if a new tick starts in this period if (_current + nframes > _pos.next_frame()) { // move position to next tick. // loop just in case two beats are less than one period apart (which we don't really handle) do { _pos.advance(); } while (_pos.frame() < _current); Position::Tick tick = _pos.tick(); //cout << tick.frame << ": " << (tick.type == TempoMap::BEAT_EMPHASIS) << endl; if (tick.type != TempoMap::BEAT_SILENT) { // start playing the click sample play_click(tick.type == TempoMap::BEAT_EMPHASIS, tick.frame - _current, tick.volume); } } _current += nframes; } void MetronomeMap::timebase_callback(position_t *p) { if (p->frame != _current) { // current position doesn't match jack transport frame. // assume we're wrong and jack is right ;) _current = p->frame; _pos.locate(p->frame); } if (_pos.end()) { // end of tempomap, no valid position p->valid = (jack_position_bits_t)0; return; } p->valid = JackPositionBBT; p->bar = _pos.bar_total() + 1; // jack counts from 1 p->beat = _pos.beat() + 1; p->beats_per_bar = _pos.map_entry().beats; p->beat_type = _pos.map_entry().denom; double d = _pos.dist_to_next(); if (d) { p->tick = (nframes_t)(((double)_current - _pos.frame()) * TICKS_PER_BEAT / d); } else { p->tick = 0; } if (p->tick >= TICKS_PER_BEAT) { // already at the next beat, but _pos.advance() won't be called until the next process cycle p->tick -= (int32_t)TICKS_PER_BEAT; p->beat++; if (p->beat > _pos.map_entry().beats) { p->bar++; } } //cout << p->frame << ": " << p->bar << "|" << p->beat << "|" << p->tick << endl; p->ticks_per_beat = TICKS_PER_BEAT; // NOTE: jack's notion of bpm is different from ours. // all tempo values are converted from "quarters per minute" // to the actual beats per minute used by jack if (_pos.map_entry().tempo && (!_pos.map_entry().tempo2 || d == 0.0)) { // constant tempo, and/or start of tempomap p->beats_per_minute = _pos.map_entry().tempo * _pos.map_entry().denom / 4.0; } else if (_pos.map_entry().tempo2 && _pos.end()) { // end of tempomap, last entry had tempo change, so use tempo2 p->beats_per_minute = _pos.map_entry().tempo2 * _pos.map_entry().denom / 4.0; } else if (_pos.map_entry().tempo2) { // tempo change, use average tempo for this beat p->beats_per_minute = (double)_audio.samplerate() * 60.0 / d; } else if (!_pos.map_entry().tempo) { // tempo per beat std::size_t n = _pos.bar() * _pos.map_entry().beats + _pos.beat(); p->beats_per_minute = _pos.map_entry().tempi[n]; } } klick-0.12.2/src/util/0000755000175000017500000000000011253043155012534 5ustar dasdasklick-0.12.2/src/util/ringbuffer.hh0000644000175000017500000000251211047162132015204 0ustar dasdas/* * Copyright (C) 2008 Dominic Sacré * * This program 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. */ #ifndef _DAS_RINGBUFFER_HH #define _DAS_RINGBUFFER_HH #include #include namespace das { template class ringbuffer : boost::noncopyable { public: ringbuffer(std::size_t size) { _rb = jack_ringbuffer_create(size * sizeof(T)); } ~ringbuffer() { jack_ringbuffer_free(_rb); } bool write(T const & item) { if (!write_space()) return false; jack_ringbuffer_write(_rb, reinterpret_cast(&item), sizeof(T)); return true; } bool read(T & item) { if (!read_space()) return false; jack_ringbuffer_read(_rb, reinterpret_cast(&item), sizeof(T)); return true; } std::size_t write_space() { return jack_ringbuffer_write_space(_rb) / sizeof(T); } std::size_t read_space() { return jack_ringbuffer_read_space(_rb) / sizeof(T); } private: jack_ringbuffer_t * _rb; }; } // namespace das #endif // _DAS_RINGBUFFER_HH klick-0.12.2/src/util/garbage_collector.hh0000644000175000017500000000402111047162132016506 0ustar dasdas/* * Copyright (C) 2008 Dominic Sacré * * This program 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. */ #ifndef _DAS_GARBAGE_COLLECTOR_HH #define _DAS_GARBAGE_COLLECTOR_HH #include "disposable.hh" #include "ringbuffer.hh" #include "debug.hh" #include #include #include #include namespace das { /* * explicitly driven garbage collector, allowing objects to be deleted from a realtime thread. * since jack_ringbuffer is single-writer, only one thread is allowed to queue deletions. */ class garbage_collector : boost::noncopyable { public: garbage_collector(std::size_t size = 255, pthread_t tid = 0) : disposer(boost::bind(&garbage_collector::dispose, this, _1)) , _rb(size) , _tid(tid) { } ~garbage_collector() { } // set the thread whose deletions are to be handled by the garbage collector void set_thread(pthread_t tid) { _tid = tid; } // if called from the realtime thread, queue object for deletion by the garbage collector, // otherwise delete immediately void dispose(disposable * p) { if (pthread_self() != _tid) { delete p; } else { queue_dispose(p); } } // queue object for deletion void queue_dispose(disposable * p) { VERIFY(_rb.write(p)); } // collect the garbage and delete it void collect() { while (_rb.read_space()) { disposable * p; _rb.read(p); delete p; } } // functor that calls this->dispose(), useful as deleter for boost::shared_ptr boost::function disposer; private: ringbuffer _rb; pthread_t _tid; }; } // namespace das #endif // _DAS_GARBAGE_COLLECTOR_HH klick-0.12.2/src/util/debug.hh0000644000175000017500000000121411047162237014145 0ustar dasdas/* * Copyright (C) 2007-2008 Dominic Sacré * * This program 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. */ #ifndef _DAS_DEBUG_HH #define _DAS_DEBUG_HH #ifndef NDEBUG #include #define ASSERT(f) assert(f) #define VERIFY(f) assert(f) #define FAIL() assert(false) #else #define ASSERT(f) ((void)0) #define VERIFY(f) ((void)(f)) #define FAIL() ((void)0) #endif #endif // _DAS_DEBUG_HH klick-0.12.2/src/util/logstream.hh0000644000175000017500000000174611047162237015066 0ustar dasdas/* * Copyright (C) 2007-2008 Dominic Sacré * * This program 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. */ #ifndef _DAS_LOGSTREAM_HH #define _DAS_LOGSTREAM_HH #include namespace das { class logstream { public: logstream(std::ostream & s, bool b = true) : _stream(s), _enabled(b) { } void enable(bool b) { _enabled = b; } template logstream & operator<< (T const & p) { if (_enabled) _stream << p; return *this; } logstream & operator<< (std::ostream & (*pf)(std::ostream &)) { if (_enabled) pf(_stream); return *this; } private: std::ostream & _stream; bool _enabled; }; extern logstream logv; } // namespace das #endif // _DAS_LOGSTREAM_HH klick-0.12.2/src/util/lexical_cast.hh0000644000175000017500000000237611101210461015504 0ustar dasdas/* * Copyright (C) 2008 Dominic Sacré * * This program 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. */ #ifndef _DAS_LEXICAL_CAST_HH #define _DAS_LEXICAL_CAST_HH #include #include #include #include namespace das { typedef boost::bad_lexical_cast bad_lexical_cast; template T lexical_cast(S const & arg) { return boost::lexical_cast(arg); } template T lexical_cast(S const & arg, T const & fallback) { try { return boost::lexical_cast(arg); } catch (boost::bad_lexical_cast const &) { return fallback; } } template typename boost::enable_if, T>::type lexical_cast(S const & arg, E const & exc) { try { return boost::lexical_cast(arg); } catch (boost::bad_lexical_cast const &) { throw exc; } } } // namespace das #endif // _DAS_LEXICAL_CAST_HH klick-0.12.2/src/util/disposable.hh0000644000175000017500000000111611047162132015177 0ustar dasdas/* * Copyright (C) 2008 Dominic Sacré * * This program 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. */ #ifndef _DAS_DISPOSABLE_HH #define _DAS_DISPOSABLE_HH namespace das { // base class for objects that can be garbage collected class disposable { public: disposable() { } virtual ~disposable() { } }; } // namespace das #endif // _DAS_DISPOSABLE_HH klick-0.12.2/src/util/string.hh0000644000175000017500000000162311047162237014371 0ustar dasdas/* * Copyright (C) 2007-2008 Dominic Sacré * * This program 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. */ #ifndef _DAS_STRING_HH #define _DAS_STRING_HH #include #include namespace das { class make_string { public: template make_string & operator<< (T const& t) { _stream << t; return *this; } make_string & operator<< (std::ostream & (*pf)(std::ostream &)) { pf(_stream); return *this; } operator std::string() { return _stream.str(); } private: std::ostringstream _stream; }; std::string indent(std::string const & s, int n); } // namespace das #endif // _DAS_STRING_HH klick-0.12.2/src/util/util.cc0000644000175000017500000000142611047162237014027 0ustar dasdas/* * Copyright (C) 2007-2008 Dominic Sacré * * This program 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. */ #include "logstream.hh" #include "string.hh" #include #include namespace das { logstream logv(std::cout); std::string indent(std::string const & s, int n) { std::string ws(n, ' '); std::istringstream is(s); std::ostringstream os; while (true) { std::string tmp; std::getline(is, tmp); if (is.eof()) break; os << ws << tmp << std::endl; } return os.str(); } } // namespace das klick-0.12.2/src/audio_interface_sndfile.cc0000644000175000017500000000471711224756531016733 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #include "audio_interface_sndfile.hh" #include #include #include #include #include "util/string.hh" AudioInterfaceSndfile::AudioInterfaceSndfile(std::string const & filename, nframes_t samplerate) : _samplerate(samplerate) { SF_INFO sfinfo; std::memset(&sfinfo, 0, sizeof(sfinfo)); sfinfo.samplerate = samplerate; sfinfo.channels = 1; // detect desired file format based on filename extension std::string ext = get_filename_extension(filename); if (ext == "wav") { sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; } else if (ext == "aiff" || ext == "aif") { sfinfo.format = SF_FORMAT_AIFF | SF_FORMAT_PCM_16; } else if (ext == "flac") { sfinfo.format = SF_FORMAT_FLAC | SF_FORMAT_PCM_16; #ifdef HAVE_SNDFILE_OGG } else if (ext == "ogg" || ext == "oga") { sfinfo.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS; #endif } else if (ext == "raw") { sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; } else { throw AudioError(das::make_string() << "failed to recognize file extension '" << ext << "'"); } // open output file for writing SNDFILE *f = sf_open(filename.c_str(), SFM_WRITE, &sfinfo); if (!f) { throw AudioError(das::make_string() << "couldn't open '" << filename << "' for output"); } _sndfile.reset(f, sf_close); } void AudioInterfaceSndfile::process(std::size_t buffer_size) { sample_t buffer[buffer_size]; std::fill(buffer, buffer+buffer_size, 0.0f); // run process callback (metronome) _process_cb(buffer, buffer_size); // mix audio data to buffer process_mix(buffer, buffer_size); // write to output file sf_writef_float(_sndfile.get(), buffer, buffer_size); } std::string AudioInterfaceSndfile::get_filename_extension(std::string const & filename) { std::string::size_type period = filename.find_last_of('.'); if (period == std::string::npos) { return ""; } std::string ext(filename, period + 1); std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); return ext; } klick-0.12.2/src/klick.cc0000644000175000017500000002731311253040563013171 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #include "klick.hh" #include "main.hh" #include "audio_interface_jack.hh" #include "audio_interface_sndfile.hh" #include "audio_chunk.hh" #ifdef ENABLE_OSC #include "osc_handler.hh" #endif #ifdef ENABLE_TERMINAL #include "terminal_handler.hh" #endif #include "tempomap.hh" #include "metronome_map.hh" #include "metronome_jack.hh" #include "metronome_simple.hh" #include #include #include #include #include #include "util/debug.hh" #include "util/string.hh" #include "util/logstream.hh" #include "util/garbage_collector.hh" Klick::Klick(int argc, char *argv[]) : _options(new Options) , _gc(new das::garbage_collector(15)) , _quit(false) { _options->parse(argc, argv); das::logv.enable(_options->verbose); // determine client name if (_options->client_name.empty()) { _options->client_name = "klick"; } if (!_options->follow_transport) { load_tempomap(); } if (_options->output_filename.empty()) { setup_jack(); } else { setup_sndfile(); } load_samples(); load_metronome(); #ifdef ENABLE_OSC if (_options->use_osc) { // yuck! AudioInterfaceJack & a = dynamic_cast(*_audio); _osc.reset(new OSCHandler(_options->osc_port, _options->osc_return_port, *this, a)); } #endif #ifdef ENABLE_TERMINAL if (_options->interactive) { _term.reset(new TerminalHandler(*this, *_audio)); } #endif } Klick::~Klick() { } void Klick::setup_jack() { boost::shared_ptr audio(new AudioInterfaceJack(_options->client_name)); das::logv << "jack client name: " << audio->client_name() << std::endl; if (_options->connect_ports.size()) { for (std::vector::const_iterator i = _options->connect_ports.begin(); i != _options->connect_ports.end(); ++i) { try { audio->connect(*i); das::logv << "connected to " << i->c_str() << std::endl; } catch (AudioInterface::AudioError const & e) { std::cerr << e.what() << std::endl; } } } if (_options->auto_connect) { audio->autoconnect(); } _gc->set_thread(audio->client_thread()); _audio = audio; } void Klick::setup_sndfile() { _audio.reset(new AudioInterfaceSndfile(_options->output_filename, _options->output_samplerate)); das::logv << "output filename: " << _options->output_filename << std::endl; } void Klick::load_tempomap() { if (_options->filename.length()) { das::logv << "loading tempo map from file" << std::endl; _map = TempoMap::new_from_file(_options->filename); } else if (!_options->cmdline.empty()) { das::logv << "loading tempo map from command line" << std::endl; _map = TempoMap::new_from_cmdline(_options->cmdline); } else { _map = TempoMap::new_simple(-1, 120, 4, 4); } das::logv << "tempo map:" << std::endl << das::indent(_map->dump(), 2); // make sure the start label exists if (_options->start_label.length()) { if (_map->entry(_options->start_label)) { das::logv << "starting at label: " << _options->start_label << std::endl; } else { throw std::runtime_error(das::make_string() << "label '" << _options->start_label << "' not found in tempo map"); } } if (!_options->output_filename.empty() && _map->entries().back().bars == -1) { throw std::runtime_error("can't export tempo map of infinite length"); } } boost::tuple Klick::sample_filenames(int n, Options::EmphasisMode emphasis_mode) { std::string emphasis, normal; switch (n) { case 0: emphasis = data_file("samples/square_emphasis.wav"); normal = data_file("samples/square_normal.wav"); break; case 1: emphasis = data_file("samples/sine_emphasis.wav"); normal = data_file("samples/sine_normal.wav"); break; case 2: emphasis = data_file("samples/noise_emphasis.wav"); normal = data_file("samples/noise_normal.wav"); break; case 3: emphasis = data_file("samples/click_emphasis.wav"); normal = data_file("samples/click_normal.wav"); break; case Options::CLICK_SAMPLE_FROM_FILE: emphasis = _options->click_filename_emphasis; normal = _options->click_filename_normal; break; case Options::CLICK_SAMPLE_SILENT: emphasis = ""; normal = ""; break; default: FAIL(); } switch (emphasis_mode) { case Options::EMPHASIS_MODE_NONE: emphasis = normal; break; case Options::EMPHASIS_MODE_ALL: normal = emphasis; break; default: break; } return boost::make_tuple(emphasis, normal); } AudioChunkPtr Klick::load_sample(std::string const & filename, float volume, float pitch) { AudioChunkPtr p; if (!filename.empty()) { p.reset(new AudioChunk(filename, _audio->samplerate()), _gc->disposer); } else { p.reset(new AudioChunk(_audio->samplerate()), _gc->disposer); } if (volume != 1.0f) { p->adjust_volume(volume); } if (pitch != 1.0f) { p->adjust_pitch(pitch); } return p; } void Klick::load_samples() { std::string emphasis, normal; boost::tie(emphasis, normal) = sample_filenames(_options->click_sample, _options->emphasis_mode); das::logv << "loading samples:\n" << " emphasis: " << emphasis << "\n" << " normal: " << normal << std::endl; _click_emphasis = load_sample(emphasis, _options->volume_emphasis, _options->pitch_emphasis); _click_normal = load_sample(normal, _options->volume_normal, _options->pitch_normal); } void Klick::set_sound(int n) { if ((n < 0 || n > 3) && !(n == Options::CLICK_SAMPLE_SILENT)) return; if (n == _options->click_sample) return; _options->click_sample = n; load_samples(); _metro->set_sound(_click_emphasis, _click_normal); } void Klick::set_sound_custom(std::string const & emphasis, std::string const & normal) { _options->click_sample = Options::CLICK_SAMPLE_FROM_FILE; _options->click_filename_emphasis = emphasis; _options->click_filename_normal = normal; das::logv << "loading samples:\n" << " emphasis: " << emphasis << "\n" << " normal: " << normal << std::endl; try { _click_emphasis = load_sample(emphasis, _options->volume_emphasis, _options->pitch_emphasis); } catch (std::runtime_error const & e) { std::cerr << e.what() << std::endl; _click_emphasis.reset(new AudioChunk(_audio->samplerate()), _gc->disposer); _options->click_filename_emphasis = ""; } try { _click_normal = load_sample(normal, _options->volume_normal, _options->pitch_normal); } catch (std::runtime_error const & e) { std::cerr << e.what() << std::endl; _click_normal.reset(new AudioChunk(_audio->samplerate()), _gc->disposer); _options->click_filename_normal = ""; } _metro->set_sound(_click_emphasis, _click_normal); } void Klick::set_sound_volume(float emphasis, float normal) { if (emphasis == _options->volume_emphasis && normal == _options->volume_normal) { return; } _options->volume_emphasis = emphasis; _options->volume_normal = normal; load_samples(); _metro->set_sound(_click_emphasis, _click_normal); } void Klick::set_sound_pitch(float emphasis, float normal) { if (emphasis == _options->pitch_emphasis && normal == _options->pitch_normal) { return; } _options->pitch_emphasis = emphasis; _options->pitch_normal = normal; load_samples(); _metro->set_sound(_click_emphasis, _click_normal); } void Klick::set_metronome(Options::MetronomeType type) { _options->type = type; load_metronome(); } void Klick::load_metronome() { Metronome * m = NULL; switch (_options->type) { case Options::METRONOME_TYPE_SIMPLE: m = new MetronomeSimple(*_audio, &(*_map)[0]); break; case Options::METRONOME_TYPE_JACK: // let's hope this cast is safe... m = new MetronomeJack(dynamic_cast(*_audio)); break; case Options::METRONOME_TYPE_MAP: m = new MetronomeMap(*_audio, _map, _options->tempo_multiplier, _options->transport_enabled, _options->transport_master, _options->preroll, _options->start_label); break; } _metro.reset(m, _gc->disposer); _metro->set_sound(_click_emphasis, _click_normal); _audio->set_process_callback(boost::bind(&Metronome::process_callback, _metro, _1, _2)); if (_options->transport_master) { boost::shared_ptr m = boost::dynamic_pointer_cast(_metro); boost::shared_ptr a = boost::dynamic_pointer_cast(_audio); if (m && a) { try { a->set_timebase_callback(boost::bind(&Metronome::timebase_callback, m, _1)); } catch (AudioInterface::AudioError const & e) { std::cerr << e.what() << std::endl; } } } } void Klick::set_tempomap_filename(std::string const & filename) { _options->filename = filename; load_tempomap(); load_metronome(); } void Klick::set_tempomap_preroll(int bars) { _options->preroll = bars; load_metronome(); } void Klick::set_tempomap_multiplier(float mult) { _options->tempo_multiplier = mult; load_metronome(); } void Klick::run() { if (_options->output_filename.empty()) { run_jack(); } else { run_sndfile(); } } void Klick::run_jack() { if (!_options->transport_enabled && _options->delay) { das::logv << "waiting for " << _options->delay << " seconds..." << std::endl; ::usleep(static_cast(_options->delay * 1000000)); } if (!_osc) { das::logv << "starting metronome..." << std::endl; _metro->start(); } #ifdef ENABLE_OSC if (_osc) { _osc->start(); } #endif for (;;) { ::usleep(10000); _gc->collect(); #ifdef ENABLE_TERMINAL if (_term) { _term->handle_input(); } #endif #ifdef ENABLE_OSC if (_osc) { _osc->update(); } #endif if (_quit) { das::logv << "terminating" << std::endl; break; } else if (!_metro->running() && !_osc) { das::logv << "end of tempo map reached" << std::endl; break; } else if (_audio->is_shutdown()) { throw std::runtime_error("shut down by the jack server"); } } } void Klick::run_sndfile() { AudioInterfaceSndfile *a = dynamic_cast(&*_audio); ASSERT(a); MetronomeMap *m = dynamic_cast(&*_metro); ASSERT(m); static nframes_t const BUFFER_SIZE = 1024; m->start(); while (m->current_frame() < m->total_frames() && !_quit) { a->process(std::min(BUFFER_SIZE, m->total_frames() - m->current_frame())); } } void Klick::signal_quit() { _quit = true; } klick-0.12.2/src/audio_interface_sndfile.hh0000644000175000017500000000202311224756531016731 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _AUDIO_INTERFACE_SNDFILE_HH #define _AUDIO_INTERFACE_SNDFILE_HH #include "audio_interface.hh" #include #include #include class AudioInterfaceSndfile : public AudioInterface { public: AudioInterfaceSndfile(std::string const & filename, nframes_t samplerate); void process(std::size_t buffer_size); nframes_t samplerate() const { return _samplerate; } bool is_shutdown() const { return false; } private: std::string get_filename_extension(std::string const & filename); nframes_t _samplerate; boost::shared_ptr _sndfile; }; #endif // _AUDIO_INTERFACE_SNDFILE_HH klick-0.12.2/src/options.cc0000644000175000017500000002374211225446724013601 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #include "options.hh" #include "main.hh" #include #include #include #include #include #include "util/lexical_cast.hh" typedef boost::char_separator char_sep; typedef boost::tokenizer tokenizer; Options::Options() : auto_connect(false), use_osc(false), interactive(false), follow_transport(false), output_samplerate(48000), click_sample(0), emphasis_mode(EMPHASIS_MODE_NORMAL), volume_emphasis(1.0), volume_normal(1.0), pitch_emphasis(1.0), pitch_normal(1.0), transport_enabled(false), transport_master(false), delay(0.0f), preroll(PREROLL_NONE), tempo_multiplier(1.0), verbose(false) { } void Options::print_version() { std::cout << "klick " VERSION << std::endl; } void Options::print_usage() { std::cout << "Usage:\n" << " klick [options] [bars] [meter] tempo[-tempo2/accel] [pattern]\n" << " klick [options] -f filename\n" #ifdef ENABLE_TERMINAL << " klick [options] -i\n" #endif << " klick [options] -j\n" << "\n" << "Options:\n" << " -f filename load tempo map from file\n" << " -j no tempo map, just follow jack transport\n" << " -n name set jack client name\n" << " -p port,.. jack port(s) to connect to\n" << " -P automatically connect to hardware ports\n" #ifdef ENABLE_OSC << " -o port OSC port to listen on\n" #endif #ifdef ENABLE_TERMINAL << " -i interactive mode\n" #endif << " -W filename export click track to audio file\n" << " -r samplerate sample rate of export (default: 48000)\n" << " -s number use built-in sounds:\n" << " 0: square wave (default)\n" << " 1: sine wave\n" << " 2: noise\n" << " 3: acoustic bell/click\n" << " -S file[,file] load sounds from file(s)\n" << " -e no emphasized beats\n" << " -E emphasized beats only\n" << " -v mult[,mult] adjust playback volume (default: 1.0)\n" << " -w mult[,mult] adjust playback pitch (default: 1.0)\n" << " -t enable jack transport\n" << " -T become transport master (implies -t)\n" << " -d seconds delay before starting playback\n" << " -c bars pre-roll. use -c 0 for 2 beats\n" << " -l label start playback at the given label\n" << " -x multiplier multiply tempo by the given factor\n" << " -h show this help\n" << "\n" << "Tempo Map File Format:\n" << " [label:] bars [meter] tempo [pattern] [volume]\n" << " ..." << std::endl; } void Options::parse(int argc, char *argv[]) { int c; char optstring[] = "+f:jn:p:Po:R:iW:r:s:S:eEv:w:tTd:c:l:x:LVh"; if (argc < 2 || (argc == 2 && std::string(argv[1]) == "--help")) { // run with no arguments, print usage print_version(); std::cout << std::endl; print_usage(); throw Exit(EXIT_SUCCESS); } while ((c = ::getopt(argc, argv, optstring)) != -1) { switch (c) { case 'f': filename = std::string(::optarg); break; case 'j': follow_transport = true; break; case 'n': client_name = std::string(::optarg); break; case 'p': { std::string str(::optarg); char_sep sep(","); tokenizer tok(str, sep); for (tokenizer::iterator i = tok.begin(); i != tok.end(); ++i) { connect_ports.push_back(*i); } } break; case 'P': auto_connect = true; break; #ifdef ENABLE_OSC case 'o': use_osc = true; osc_port = ::optarg; break; case 'R': use_osc = true; osc_return_port = ::optarg; break; #endif #ifdef ENABLE_TERMINAL case 'i': interactive = true; break; #endif case 'W': output_filename = ::optarg; break; case 'r': output_samplerate = das::lexical_cast(::optarg, InvalidArgument(c, "samplerate")); break; case 's': click_sample = das::lexical_cast(::optarg, -1); if (click_sample < 0 || click_sample > 3) { throw InvalidArgument(c, "click sample"); } break; case 'S': { std::string str(::optarg); char_sep sep(","); tokenizer tok(str, sep); tokenizer::iterator i = tok.begin(); click_filename_emphasis = *i; if (++i == tok.end()) { click_filename_normal = click_filename_emphasis; } else { click_filename_normal = *i; if (++i != tok.end()) throw InvalidArgument(c, "sample file names"); } click_sample = CLICK_SAMPLE_FROM_FILE; } break; case 'e': emphasis_mode = EMPHASIS_MODE_NONE; break; case 'E': emphasis_mode = EMPHASIS_MODE_ALL; break; case 'v': { std::string str(::optarg); char_sep sep(","); tokenizer tok(str, sep); tokenizer::iterator i = tok.begin(); volume_emphasis = das::lexical_cast(*i, InvalidArgument(c, "volume")); i++; if (i == tok.end()) { volume_normal = volume_emphasis; } else { volume_normal = das::lexical_cast(*i, InvalidArgument(c, "volume")); } } break; case 'w': { std::string str(::optarg); char_sep sep(","); tokenizer tok(str, sep); tokenizer::iterator i = tok.begin(); pitch_emphasis = das::lexical_cast(*i, InvalidArgument(c, "pitch")); i++; if (i == tok.end()) { pitch_normal = pitch_emphasis; } else { pitch_normal = das::lexical_cast(*i, InvalidArgument(c, "pitch")); } } break; case 't': transport_enabled = true; break; case 'T': transport_master = true; transport_enabled = true; break; case 'd': delay = das::lexical_cast(::optarg, InvalidArgument(c, "delay")); if (delay < 0.0f) throw InvalidArgument(c, "delay"); break; case 'c': preroll = das::lexical_cast(::optarg, InvalidArgument(c, "pre-roll")); if (preroll < 0) throw InvalidArgument(c, "pre-roll"); break; case 'l': start_label = std::string(::optarg); break; case 'x': tempo_multiplier = das::lexical_cast(::optarg, InvalidArgument(c, "tempo multiplier")); if (tempo_multiplier <= 0) throw InvalidArgument(c, "tempo multiplier"); break; case 'L': verbose = true; break; case 'V': print_version(); throw Exit(EXIT_SUCCESS); case 'h': print_version(); std::cout << std::endl; print_usage(); throw Exit(EXIT_SUCCESS); break; default: throw Exit(EXIT_FAILURE); break; } } // all remaining arguments make up the "tempomap" for (int n = ::optind; n < argc; n++) { cmdline += std::string(argv[n]); if (n < argc - 1) cmdline += " "; } // catch some common command line errors... if (!output_filename.empty() && filename.empty() && cmdline.empty()) { throw CmdlineError("need a tempo map to export to audio file"); } if (!use_osc) { if (follow_transport && (filename.length() || cmdline.length())) { throw CmdlineError("can't use explicit tempo or tempo map together with -j option"); } if (filename.length() && cmdline.length()) { throw CmdlineError("can't use tempo map from file and command line at the same time"); } if (!follow_transport && !interactive && filename.empty() && cmdline.empty()) { throw CmdlineError("no tempo specified"); } } if (use_osc && interactive) { throw CmdlineError("can't enable OSC and terminal control at the same time, sorry"); } if ((use_osc || interactive) && !output_filename.empty()) { throw CmdlineError("can't export to audio file when using OSC or interactive mode"); } // determine metronome type type = output_filename.length() ? METRONOME_TYPE_MAP : interactive ? METRONOME_TYPE_SIMPLE : use_osc ? METRONOME_TYPE_SIMPLE : follow_transport ? METRONOME_TYPE_JACK : METRONOME_TYPE_MAP; } klick-0.12.2/src/main.cc0000644000175000017500000000263211225162611013013 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #include "main.hh" #include "klick.hh" #include #include #include #include #include static boost::shared_ptr app; static int ret = EXIT_SUCCESS; std::string data_file(std::string const & path) { std::fstream f; f.open(path.c_str(), std::ios::in); if (f.is_open()) { f.close(); return path; } return std::string(DATA_DIR"/") + path; } static void signal_handler(int sig) { if (sig != SIGINT) { ret = EXIT_FAILURE; } app->signal_quit(); } int main(int argc, char *argv[]) { try { app.reset(new Klick(argc, argv)); // exit cleanly when terminated signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); app->run(); return ret; } catch (Exit const & e) { return e.status(); } catch (std::runtime_error const & e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } } klick-0.12.2/src/metronome_jack.cc0000644000175000017500000000512011224756531015070 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #include "metronome_jack.hh" #include "audio_interface_jack.hh" #include "audio_chunk.hh" #include #include #include "util/debug.hh" MetronomeJack::MetronomeJack(AudioInterfaceJack & audio) : Metronome(audio) , _audio(audio) , _last_click_frame(0) { } MetronomeJack::~MetronomeJack() { } void MetronomeJack::process_callback(sample_t * /*buffer*/, nframes_t nframes) { if (!active() || !_audio.transport_rolling()) { return; } jack_position_t pos = _audio.position(); if (!(pos.valid & JackPositionBBT)) { // not much we can do return; } // make sure the transport master provided us with sane position info ASSERT(pos.beats_per_minute > 0.0); ASSERT(pos.beats_per_bar > 0.0); ASSERT(pos.ticks_per_beat > 0.0); ASSERT(pos.beat > 0 && pos.beat <= pos.beats_per_bar); ASSERT(pos.tick >= 0 && pos.tick < pos.ticks_per_beat); // convert BBT position to a frame number in this period double frames_per_beat = _audio.samplerate() * 60.0 / pos.beats_per_minute; nframes_t offset = (nframes_t)(frames_per_beat * (1.0 - (pos.tick / pos.ticks_per_beat))); bool emphasis; // avoid playing the same click twice due to rounding errors if (_last_click_frame && (pos.frame >= _last_click_frame) && (pos.frame < _last_click_frame + MIN_FRAMES_DIFF)) { return; } if (offset % (nframes_t)frames_per_beat == 0) { // click starts at first frame. pos already refers to this beat offset = 0; emphasis = (pos.beat == 1); // deal with faulty timebase masters if ((pos.beat == pos.beats_per_bar + 1) || (pos.beat == pos.beats_per_bar && pos.tick == pos.ticks_per_beat)) { emphasis = true; } _last_click_frame = pos.frame; } else if (offset < nframes) { // click starts somewhere during this period. since pos is the position at the start // of the period, the click played is actually at "pos + 1" emphasis = (pos.beat == (int)pos.beats_per_bar); _last_click_frame = pos.frame + offset; } else { // no click in this period return; } play_click(emphasis, offset); } klick-0.12.2/src/metronome_simple.hh0000644000175000017500000000401311253040563015454 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _METRONOME_SIMPLE_HH #define _METRONOME_SIMPLE_HH #include "audio.hh" #include "metronome.hh" #include "tempomap.hh" #include #include class MetronomeSimple : public Metronome { public: MetronomeSimple(AudioInterface & audio, TempoMap::Entry const * params = NULL); virtual ~MetronomeSimple(); virtual bool running() const { return true; } void set_tempo(float); void set_tempo_increment(float); void set_tempo_start(float); void set_tempo_limit(float); void set_meter(int, int); void set_pattern(TempoMap::Pattern const &); void set_all(TempoMap::Entry const & params); void tap(double now); void tap(); float tempo() const { return _tempo; } float tempo_increment() const { return _tempo_increment; } float tempo_start() const { return _tempo_start; } float tempo_limit() const { return _tempo_limit; } float current_tempo() const { return _current_tempo; } int beats() const { return _beats; } int denom() const { return _denom; } TempoMap::Pattern const & pattern() const { return _pattern; } virtual void do_start(); virtual void do_stop(); virtual void process_callback(sample_t *, nframes_t); private: static int const MAX_TAPS = 5; static float const MAX_TAP_AGE = 3.0; static float const TAP_DIFF = 0.2; float _tempo; float _tempo_increment, _tempo_start, _tempo_limit; float _current_tempo; int _beats, _denom; TempoMap::Pattern _pattern; nframes_t _frame; nframes_t _next; int _beat; std::deque _taps; nframes_t _prev; bool _tapped; }; #endif // _METRONOME_SIMPLE_HH klick-0.12.2/src/audio.hh0000644000175000017500000000137511224756531013216 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _AUDIO_HH #define _AUDIO_HH #include #include #include typedef jack_default_audio_sample_t sample_t; typedef jack_nframes_t nframes_t; typedef jack_position_t position_t; typedef boost::shared_ptr AudioChunkPtr; typedef boost::shared_ptr AudioChunkConstPtr; #endif // _AUDIO_HH klick-0.12.2/src/audio_interface_jack.hh0000644000175000017500000000330311224756531016217 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _AUDIO_INTERFACE_JACK_HH #define _AUDIO_INTERFACE_JACK_HH #include "audio_interface.hh" #include #include #include class AudioInterfaceJack : public AudioInterfaceTransport { public: AudioInterfaceJack(std::string const & name); virtual ~AudioInterfaceJack(); virtual void set_timebase_callback(TimebaseCallback cb); // get JACK client name std::string client_name() const; // get id of JACK audio processing thread pthread_t client_thread() const; virtual nframes_t samplerate() const; virtual bool is_shutdown() const; // JACK connections void connect(std::string const & port); void autoconnect(); void disconnect_all(); std::vector available_ports(); // JACK transport virtual bool transport_rolling() const; virtual position_t position() const; virtual nframes_t frame() const; virtual bool set_position(position_t const &); virtual bool set_frame(nframes_t); private: static int process_callback_(nframes_t, void *); static void timebase_callback_(jack_transport_state_t, nframes_t, position_t *, int, void *); static void shutdown_callback_(void *); jack_client_t *_client; jack_port_t *_output_port; volatile bool _shutdown; }; #endif // _AUDIO_INTERFACE_JACK_HH klick-0.12.2/src/position.cc0000644000175000017500000001720011133467215013736 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #include "position.hh" #include "options.hh" #include #include #include #include #include #include #include "util/debug.hh" Position::Position(TempoMapConstPtr tempomap, float_frames_t samplerate, float multiplier) : _tempomap(tempomap), _samplerate(samplerate), _multiplier(multiplier) { float_frames_t f = 0.0; int b = 0; // calculate first frame of each tempomap entry for (TempoMap::Entries::const_iterator i = tempomap->entries().begin(); i != tempomap->entries().end(); ++i) { _start_frames.push_back(f); _start_bars.push_back(b); if (i->bars != -1) { f += frame_dist(*i, 0, i->bars * i->beats); b += i->bars; } else { // play entry ad infinitum f = std::numeric_limits::max(); b = std::numeric_limits::max(); } } // add end of tempomap _start_frames.push_back(f); _start_bars.push_back(b); reset(); } void Position::reset() { _frame = 0.0; _entry = _bar = _beat = 0; _bar_total = 0; _init = true; _end = false; } void Position::set_start_label(std::string const & start_label) { TempoMapPtr t(new TempoMap()); // remove everything before the start label TempoMap::Entries::const_iterator i = _tempomap->entries().begin(); while (i->label != start_label) ++i; for ( ; i != _tempomap->entries().end(); ++i) t->add(*i); _tempomap = t; } void Position::add_preroll(int nbars) { TempoMap::Entry const & e = (*_tempomap)[0]; TempoMapPtr preroll; // create a new tempomap for preroll if (nbars == Options::PREROLL_2_BEATS) { std::vector pattern(2, TempoMap::BEAT_NORMAL); preroll = TempoMap::new_simple(1, e.tempo, 2, e.denom, pattern, 0.66f); } else { preroll = TempoMap::new_simple(nbars, e.tempo, e.beats, e.denom, e.pattern, 0.66f); } // join preroll and our actual tempomap _tempomap = TempoMap::join(preroll, _tempomap); } void Position::locate(nframes_t f) { reset(); if (f == 0) { // nothing else to do return; } // find the tempomap entry f is in _entry = std::distance(_start_frames.begin(), std::upper_bound(_start_frames.begin(), _start_frames.end(), f) - 1); if (_entry == (int)_tempomap->size()) { // end of tempomap _end = true; return; } TempoMap::Entry const & e = (*_tempomap)[_entry]; // difference between start of entry and desired position float_frames_t diff = f - _start_frames[_entry]; double secs = diff / (float_frames_t)_samplerate * _multiplier; // constant tempo if (e.tempo && !e.tempo2) { int nbeats = (int)(secs / 240.0 * e.tempo * e.denom); _bar = nbeats / e.beats; _beat = nbeats % e.beats; _frame = _start_frames[_entry] + frame_dist(e, 0, _bar * e.beats + _beat); _bar_total = _start_bars[_entry] + _bar; } // gradual tempo change else if (e.tempo && e.tempo2) { int nbeats = 0; // do a binary search for the beat at (or before) f. // this is not the most efficient implementation possible, // but good enough for now... int low = 0; int high = e.bars * e.beats; float_frames_t v = f - _start_frames[_entry]; while (low <= high) { int mid = (low + high) / 2; float_frames_t d = frame_dist(e, 0, mid); if (d > v) { high = mid - 1; } else if (d <= v) { // unless there's a beat exactly at f, what we're really looking for // is the last beat before f. // check if mid+1 would be after f, in which case we're already done float_frames_t dd = frame_dist(e, 0, mid + 1); if (dd < v) { low = mid + 1; } else { // found it nbeats = mid; break; } } } _bar = nbeats / e.beats; _beat = nbeats % e.beats; _frame = _start_frames[_entry] + frame_dist(e, 0, _bar * e.beats + _beat); _bar_total = _start_bars[_entry] + _bar; } // tempo per beat else if (!e.tempo) { _bar = _beat = 0; _frame = _start_frames[_entry]; _bar_total = _start_bars[_entry]; // terribly inefficient, but who uses tempo per beat anyway...? while (_frame + dist_to_next() <= f && !_end) { advance(); } } // make sure we don't miss the first beat if it starts at f _init = (_frame == f); } void Position::advance() { if (_init) { _init = false; return; } _frame += dist_to_next(); TempoMap::Entry const & e = (*_tempomap)[_entry]; // move to next beat if (++_beat >= e.beats) { _beat = 0; // move to next bar if (++_bar >= e.bars && e.bars != -1) { _bar = 0; // move to next entry if (++_entry >= (int)_tempomap->size()) { _entry--; // no such entry _end = true; } } _bar_total++; } } Position::float_frames_t Position::dist_to_next() const { // no valid next tick if (_init) return 0.0; if (_end) return std::numeric_limits::max(); TempoMap::Entry const & e = (*_tempomap)[_entry]; return frame_dist(e, _bar * e.beats + _beat, _bar * e.beats + _beat + 1); } Position::float_frames_t Position::frame_dist(TempoMap::Entry const & e, int start, int end) const { if (start == end) { return 0.0; } ASSERT(start < end); int nbeats = end - start; double secs = 0.0; // constant tempo if ((e.tempo && !e.tempo2) || (e.tempo && e.tempo == e.tempo2)) { secs = nbeats * 240.0 / (e.tempo * e.denom); } // gradual tempo change else if (e.tempo && e.tempo2) { double tdiff = e.tempo2 - e.tempo; double t1 = (double)e.tempo + tdiff * ((double)start / (e.bars * e.beats)); double t2 = (double)e.tempo + tdiff * ((double)end / (e.bars * e.beats)); double avg_tempo = (t1 - t2) / (log(t1) - log(t2)); secs = (nbeats * 240.0) / (avg_tempo * e.denom); } // different tempo for each beat else if (!e.tempo) { secs = std::accumulate(e.tempi.begin() + start, e.tempi.begin() + end, 0.0, boost::lambda::_1 + 240.0 / (boost::lambda::_2 * e.denom)); } return secs * (float_frames_t)_samplerate / _multiplier; } Position::Tick const Position::tick() const { if (_end) { // end of tempomap, return "nothing" return (Tick) { (nframes_t)_frame, TempoMap::BEAT_SILENT, 0 }; } TempoMap::Entry const & e = (*_tempomap)[_entry]; TempoMap::BeatType t; if (e.pattern.empty()) { // use default pattern t = (_beat == 0) ? TempoMap::BEAT_EMPHASIS : TempoMap::BEAT_NORMAL; } else { // use pattern as specified in the tempomap t = e.pattern[_beat]; } return (Tick) { (nframes_t)_frame, t, e.volume }; } klick-0.12.2/src/osc_handler.hh0000644000175000017500000000723411224756531014376 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2008-2009 Dominic Sacré * * This program 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. */ #ifndef _OSC_HANDLER_HH #define _OSC_HANDLER_HH #include #include #include #include #include "klick.hh" #include "osc_interface.hh" #include "audio_interface_jack.hh" class OSCHandler : boost::noncopyable { public: OSCHandler(std::string const & port, std::string const & return_port, Klick & klick, AudioInterfaceJack & audio); ~OSCHandler(); void start(); void update(); private: typedef OSCInterface::Message Message; typedef void (OSCHandler::*MessageHandler)(Message const &); void add_method(char const *path, char const *types, MessageHandler func); template void add_method(char const *path, char const *types, MessageHandler func); void generic_callback(MessageHandler func, Message const & msg); template void type_specific_callback(MessageHandler func, Message const & msg); OSCInterface::Address optional_address(OSCInterface::Message const & msg, std::size_t i = 0); boost::shared_ptr metro() { return _klick.metronome(); } boost::shared_ptr metro_simple() { return boost::dynamic_pointer_cast(_klick.metronome()); } boost::shared_ptr metro_map() { return boost::dynamic_pointer_cast(_klick.metronome()); } boost::shared_ptr metro_jack() { return boost::dynamic_pointer_cast(_klick.metronome()); } void on_ping(Message const &); void on_check(Message const &); void on_register_client(Message const &); void on_unregister_client(Message const &); void on_query(Message const &); void on_quit(Message const &); void on_config_set_sound(Message const &); void on_config_set_sound_custom(Message const &); void on_config_set_sound_volume(Message const &); void on_config_set_sound_pitch(Message const &); void on_config_set_volume(Message const &); void on_config_connect(Message const &); void on_config_autoconnect(Message const &); void on_config_disconnect_all(Message const &); void on_config_get_available_ports(Message const &); void on_config_query(Message const &); void on_metro_set_type(Message const &); void on_metro_start(Message const &); void on_metro_stop(Message const &); void on_metro_query(Message const &); void on_simple_set_tempo(Message const &); void on_simple_set_tempo_increment(Message const &); void on_simple_set_tempo_start(Message const &); void on_simple_set_tempo_limit(Message const &); void on_simple_set_meter(Message const &); void on_simple_tap(Message const &); void on_simple_set_pattern(Message const &); void on_simple_query(Message const &); void on_map_load_file(Message const &); void on_map_set_preroll(Message const &); void on_map_set_tempo_multiplier(Message const &); void on_map_query(Message const &); void on_jack_query(Message const &); void fallback(Message const &); boost::shared_ptr _osc; Klick & _klick; AudioInterfaceJack & _audio; typedef std::list ClientList; ClientList _clients; float _current_tempo; }; #endif // _OSC_HANDLER_HH klick-0.12.2/src/audio_chunk.cc0000644000175000017500000000742311224756531014374 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #include "audio_chunk.hh" #include #include #include #include #include #include #include #ifdef ENABLE_RUBBERBAND #include #endif #include "util/string.hh" #include "util/debug.hh" AudioChunk::AudioChunk(std::string const & filename, nframes_t samplerate) { SF_INFO sfinfo; std::memset(&sfinfo, 0, sizeof(sfinfo)); SNDFILE *f; if ((f = sf_open(filename.c_str(), SFM_READ, &sfinfo)) == NULL) { throw std::runtime_error(das::make_string() << "failed to open audio file '" << filename << "'"); } _samples.reset(new sample_t[sfinfo.frames * sfinfo.channels]); _length = sfinfo.frames; _samplerate = sfinfo.samplerate; sf_readf_float(f, _samples.get(), sfinfo.frames); // convert stereo to mono if (sfinfo.channels == 2) { SamplePtr mono_samples(new sample_t[sfinfo.frames]); for (int i = 0; i < sfinfo.frames; i++) { mono_samples[i] = (_samples[i*2] + _samples[i*2 + 1]) / 2; } _samples = mono_samples; } // convert samplerate if (_samplerate != samplerate) { resample(samplerate); } sf_close(f); } void AudioChunk::adjust_volume(float volume) { if (volume == 1.0f) return; for (nframes_t i = 0; i < _length; i++) { _samples[i] *= volume; } } void AudioChunk::adjust_pitch(float factor) { if (factor == 1.0f || !_length) return; #ifdef ENABLE_RUBBERBAND pitch_shift(factor); #else nframes_t s = _samplerate; resample(static_cast(_samplerate / factor)); _samplerate = s; #endif } void AudioChunk::resample(nframes_t samplerate) { SRC_DATA src_data; int error; src_data.input_frames = _length; src_data.data_in = _samples.get(); src_data.src_ratio = static_cast(samplerate) / static_cast(_samplerate); src_data.output_frames = std::max(static_cast(_length * src_data.src_ratio), 1L); SamplePtr samples_new(new sample_t[src_data.output_frames]()); src_data.data_out = samples_new.get(); if ((error = src_simple(&src_data, SRC_SINC_BEST_QUALITY, 1)) != 0) { throw std::runtime_error(das::make_string() << "error converting samplerate: " << src_strerror(error)); } _samples = samples_new; _length = src_data.output_frames; _samplerate = samplerate; } #ifdef ENABLE_RUBBERBAND void AudioChunk::pitch_shift(float factor) { nframes_t const BLOCK_SIZE = 1024; RubberBand::RubberBandStretcher rb(_samplerate, 1, RubberBand::RubberBandStretcher::PercussiveOptions); rb.setPitchScale(factor); rb.setExpectedInputDuration(_length); SamplePtr samples_new(new sample_t[_length]()); sample_t *buf; nframes_t k = 0; // study all samples buf = _samples.get(); rb.study(&buf, _length, true); for (nframes_t i = 0; i < _length; i += BLOCK_SIZE) { // process one block of samples buf = _samples.get() + i; nframes_t s = std::min(BLOCK_SIZE, _length - i); rb.process(&buf, s, i + BLOCK_SIZE >= _length); // retrieve available samples buf = samples_new.get() + k; s = std::min(static_cast(rb.available()), _length - k); if (s > 0) { k += rb.retrieve(&buf, s); } } ASSERT(rb.available() == -1); _length = k; _samples = samples_new; } #endif klick-0.12.2/src/audio_interface.cc0000644000175000017500000000344611224756531015225 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #include "audio_interface.hh" #include "audio_chunk.hh" #include "util/debug.hh" AudioInterface::AudioInterface() : _next_chunk(0) , _volume(1.0f) { } void AudioInterface::set_process_callback(ProcessCallback cb) { _process_cb = cb; } void AudioInterface::play(AudioChunkConstPtr chunk, nframes_t offset, float volume) { ASSERT(chunk->samplerate() == samplerate()); _chunks[_next_chunk].chunk = chunk; _chunks[_next_chunk].offset = offset; _chunks[_next_chunk].pos = 0; _chunks[_next_chunk].volume = volume; _next_chunk = (_next_chunk + 1) % _chunks.size(); } void AudioInterface::process_mix(sample_t *buffer, nframes_t nframes) { for (ChunkArray::iterator i = _chunks.begin(); i != _chunks.end(); ++i) { if (i->chunk) { process_mix_samples(buffer + i->offset, i->chunk->samples() + i->pos, std::min(nframes - i->offset, i->chunk->length() - i->pos), i->volume * _volume); i->pos += nframes - i->offset; i->offset = 0; if (i->pos >= i->chunk->length()) { i->chunk.reset(); } } } } void AudioInterface::process_mix_samples(sample_t *dest, sample_t const * src, nframes_t length, float volume) { for (sample_t *end = dest + length; dest < end; dest++, src++) { *dest += *src * volume; } } klick-0.12.2/src/tempomap.cc0000644000175000017500000002614311225166425013723 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #include "tempomap.hh" #include #include #include #include #include #include #include #include #include #include "util/string.hh" #include "util/lexical_cast.hh" typedef boost::char_separator char_sep; typedef boost::tokenizer tokenizer; #define REGEX_LABEL "([[:alnum:]_-]+)" #define REGEX_INT "([[:digit:]]+)" #define REGEX_FLOAT "([[:digit:]]+(\\.[[:digit:]]*)?|\\.[[:digit:]]+)" #define REGEX_PATTERN "([Xx.]+)" static inline bool is_specified(::regmatch_t const &m) { return ((m.rm_eo - m.rm_so) != 0); } static inline std::string extract_string(std::string const &s, ::regmatch_t const &m) { int len = m.rm_eo - m.rm_so; return len ? std::string(s.c_str() + m.rm_so, len) : ""; } static inline int extract_int(std::string const &s, ::regmatch_t const &m) { int len = m.rm_eo - m.rm_so; return len ? atoi(std::string(s.c_str() + m.rm_so, len).c_str()) : 0; } static inline float extract_float(std::string const &s, ::regmatch_t const &m) { int len = m.rm_eo - m.rm_so; return len ? atof(std::string(s.c_str() + m.rm_so, len).c_str()) : 0.0f; } // matches a line that contains nothing but whitespace or comments static char const regex_blank[] = "^[[:blank:]]*(#.*)?$"; // matches any valid line in a tempomap file static char const regex_valid[] = // label "^[[:blank:]]*("REGEX_LABEL":)?" \ // bars "[[:blank:]]*"REGEX_INT"" \ // meter "([[:blank:]]+"REGEX_INT"/"REGEX_INT")?" \ // tempo "[[:blank:]]+"REGEX_FLOAT"(-"REGEX_FLOAT"|((,"REGEX_FLOAT")*))?" \ // pattern "([[:blank:]]+"REGEX_PATTERN")?" \ // volume "([[:blank:]]+"REGEX_FLOAT")?" \ // comment "[[:blank:]]*(#.*)?$"; static int const RE_NMATCHES = 22, IDX_LABEL = 2, IDX_BARS = 3, IDX_BEATS = 5, IDX_DENOM = 6, IDX_TEMPO = 7, IDX_TEMPO2 = 10, IDX_TEMPI = 12, IDX_PATTERN = 17, IDX_VOLUME = 19; // matches valid tempo parameters on the command line static char const regex_cmdline[] = // bars "^[[:blank:]]*("REGEX_INT"[[:blank:]]+)?" \ // meter "("REGEX_INT"/"REGEX_INT"[[:blank:]]+)?" \ // tempo REGEX_FLOAT"(-"REGEX_FLOAT"/"REGEX_FLOAT")?" \ // pattern "([[:blank:]]+"REGEX_PATTERN")?[[:blank:]]*$"; static int const RE_NMATCHES_CMD = 15, IDX_BARS_CMD = 2, IDX_BEATS_CMD = 4, IDX_DENOM_CMD = 5, IDX_TEMPO_CMD = 6, IDX_TEMPO2_CMD = 9, IDX_ACCEL_CMD = 11, IDX_PATTERN_CMD = 14; TempoMap::Pattern TempoMap::parse_pattern(std::string const &s, int nbeats) { Pattern pattern; if (!s.empty()) { if ((int)s.length() != nbeats) { throw ParseError("pattern length doesn't match number of beats"); } pattern.resize(nbeats); for (int n = 0; n < nbeats; n++) { pattern[n] = (s[n] == 'X') ? BEAT_EMPHASIS : (s[n] == 'x') ? BEAT_NORMAL : BEAT_SILENT; } } return pattern; } std::string TempoMap::pattern_to_string(Pattern const & p) { std::ostringstream os; for (Pattern::const_iterator i = p.begin(); i != p.end(); ++i) { os << (*i == BEAT_EMPHASIS ? "X" : *i == BEAT_NORMAL ? "x" : "."); } return os.str(); } std::vector TempoMap::parse_tempi(std::string const &s, float tempo1, int nbeats_total) { std::vector tempi; char_sep sep(","); tokenizer tok(s, sep); if (std::distance(tok.begin(), tok.end()) != nbeats_total - 1) { throw ParseError("number of tempo values doesn't match number of beats"); } tempi.push_back(tempo1); for (tokenizer::iterator i = tok.begin(); i != tok.end(); ++i) { tempi.push_back(das::lexical_cast(*i)); } return tempi; } void TempoMap::check_entry(Entry const & e) { if ((e.tempo <= 0 && e.tempi.empty()) || std::find_if(e.tempi.begin(), e.tempi.end(), std::bind2nd(std::less_equal(), 0.0f)) != e.tempi.end()) { throw ParseError("tempo must be greater than zero"); } if (e.bars <= 0 && e.bars != -1) { throw ParseError("number of bars must be greater than zero"); } if (e.beats <= 0 || e.denom <= 0) { throw ParseError("invalid time signature"); } } std::string TempoMap::dump() const { std::ostringstream os; os << std::fixed << std::setprecision(2); for (Entries::const_iterator i = _entries.begin(); i != _entries.end(); ++i) { // label os << (i->label.length() ? i->label : "-") << ": "; // bars if (i->bars != -1) os << i->bars; else os << "*"; os << " "; // meter os << i->beats << "/" << i->denom << " "; // tempo if (i->tempo) { os << i->tempo; if (i->tempo2) os << "-" << i->tempo2; } else { os << i->tempi[0]; for (std::vector::const_iterator k = i->tempi.begin() + 1; k != i->tempi.end(); ++k) { os << "," << *k; } } os << " "; // pattern if (i->pattern.empty()) { os << "-"; } else { os << pattern_to_string(i->pattern); } os << " "; // volume os << i->volume; os << std::endl; } return os.str(); } TempoMapPtr TempoMap::join(TempoMapConstPtr const m1, TempoMapConstPtr const m2) { TempoMapPtr m(new TempoMap()); std::back_insert_iterator p(m->_entries); std::copy(m1->entries().begin(), m1->entries().end(), p); std::copy(m2->entries().begin(), m2->entries().end(), p); return m; } /* * loads tempomap from a file */ TempoMapPtr TempoMap::new_from_file(std::string const & filename) { TempoMapPtr map(new TempoMap()); std::ifstream file(filename.c_str()); if (!file.is_open()) { throw std::runtime_error(das::make_string() << "can't open tempo map file: '" << filename << "'"); } ::regex_t re_blank, re; ::regmatch_t match[RE_NMATCHES]; // compile the regexes ::regcomp(&re_blank, regex_blank, REG_EXTENDED | REG_NOSUB); ::regcomp(&re, regex_valid, REG_EXTENDED); boost::shared_ptr foo(&re_blank, ::regfree); boost::shared_ptr bar(&re, ::regfree); std::string line; int lineno = 0; while (!file.eof()) { std::getline(file, line); lineno++; // discard blank lines right away if (::regexec(&re_blank, line.c_str(), 0, NULL, 0) == 0) { continue; } try { // check if this line matches the regex if (::regexec(&re, line.c_str(), RE_NMATCHES, match, 0) != 0) { throw ParseError("malformed tempo map entry"); } Entry e; e.label = extract_string(line, match[IDX_LABEL]); e.bars = extract_int(line, match[IDX_BARS]); e.tempo = extract_float(line, match[IDX_TEMPO]); e.tempo2 = extract_float(line, match[IDX_TEMPO2]); // 0 if empty e.beats = is_specified(match[IDX_BEATS]) ? extract_int(line, match[IDX_BEATS]) : 4; e.denom = is_specified(match[IDX_DENOM]) ? extract_int(line, match[IDX_DENOM]) : 4; e.pattern = parse_pattern(extract_string(line, match[IDX_PATTERN]), e.beats); e.volume = is_specified(match[IDX_VOLUME]) ? extract_float(line, match[IDX_VOLUME]) : 1.0f; if (is_specified(match[IDX_TEMPI])) { e.tempi = parse_tempi(extract_string(line, match[IDX_TEMPI]), e.tempo, e.beats * e.bars); e.tempo = 0.0f; } check_entry(e); map->_entries.push_back(e); } catch (ParseError const & e) { throw ParseError(das::make_string() << e.what() << ":\n" << "line " << lineno << ": " << line); } } return map; } /* * loads single-line tempomap from a string */ TempoMapPtr TempoMap::new_from_cmdline(std::string const & line) { TempoMapPtr map(new TempoMap()); ::regex_t re; ::regmatch_t match[RE_NMATCHES_CMD]; ::regcomp(&re, regex_cmdline, REG_EXTENDED); boost::shared_ptr foo(&re, ::regfree); try { if (::regexec(&re, line.c_str(), RE_NMATCHES_CMD, match, 0) != 0) { throw ParseError("malformed tempo map string"); } Entry e; e.label = ""; e.bars = is_specified(match[IDX_BARS_CMD]) ? extract_int(line, match[IDX_BARS_CMD]) : -1; e.beats = is_specified(match[IDX_BEATS_CMD]) ? extract_int(line, match[IDX_BEATS_CMD]) : 4; e.denom = is_specified(match[IDX_DENOM_CMD]) ? extract_int(line, match[IDX_DENOM_CMD]) : 4; e.tempo = extract_float(line, match[IDX_TEMPO_CMD]); e.tempo2 = 0.0f; e.pattern = parse_pattern(extract_string(line, match[IDX_PATTERN_CMD]), e.beats); e.volume = 1.0f; check_entry(e); if (!is_specified(match[IDX_TEMPO2_CMD])) { // no tempo change, just add this single entry map->_entries.push_back(e); } else { // tempo change... e.tempo2 = extract_float(line, match[IDX_TEMPO2_CMD]); float accel = extract_float(line, match[IDX_ACCEL_CMD]); if (accel <= 0.0f) { throw ParseError("accel must be greater than zero"); } int bars_total = e.bars; int bars_accel = (int)ceilf(accel * fabs(e.tempo2 - e.tempo)); if (bars_total == -1 || bars_total > bars_accel) { e.bars = bars_accel; map->_entries.push_back(e); // add a second entry, to be played once the "target" tempo is reached e.bars = bars_total == -1 ? -1 : bars_total - bars_accel; e.tempo = e.tempo2; e.tempo2 = 0.0f; map->_entries.push_back(e); } else { // total number of bars is shorter than acceleration e.bars = bars_total; e.tempo2 = e.tempo + (e.tempo2 - e.tempo) * bars_total / bars_accel; map->_entries.push_back(e); } } } catch (ParseError const & e) { throw ParseError(das::make_string() << e.what() << ":\n" << line); } return map; } /* * creates tempomap with one single entry */ TempoMapPtr TempoMap::new_simple(int bars, float tempo, int beats, int denom, Pattern const & pattern, float volume) { TempoMapPtr map(new TempoMap()); Entry e; e.bars = bars; e.tempo = tempo; e.tempo2 = 0.0f; e.beats = beats; e.denom = denom; e.volume = volume; e.pattern = pattern; map->_entries.push_back(e); return map; } klick-0.12.2/src/osc_interface.cc0000644000175000017500000001111211133467215014672 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2008-2009 Dominic Sacré * * This program 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. */ #include "osc_interface.hh" #include #include #include #include #include "util/logstream.hh" #include "util/string.hh" #include "util/debug.hh" OSCInterface::OSCInterface(std::string const & port) { struct error_handler { static void func(int num, char const *msg, char const * /*where*/) { std::cerr << "liblo server error " << num << ": " << msg << std::endl; } }; _thread = lo_server_thread_new(!port.empty() ? port.c_str() : NULL, error_handler::func); if (!_thread) { throw OSCError("can't create OSC server thread"); } char *tmp = lo_server_thread_get_url(_thread); _url = tmp; std::free(tmp); das::logv << "OSC server listening on: '" << _url << "'" << std::endl; } OSCInterface::~OSCInterface() { lo_server_thread_free(_thread); } void OSCInterface::start() { lo_server_thread_start(_thread); } void OSCInterface::stop() { lo_server_thread_stop(_thread); } void OSCInterface::add_method(char const *path, char const *types, Callback const & cb) { _callbacks.push_back(cb); lo_server_thread_add_method(_thread, path, types, &callback_, static_cast(&_callbacks.back())); } int OSCInterface::callback_(char const *path, char const *types, lo_arg **argv, int argc, lo_message msg, void *data) { Callback & cb = *static_cast(data); das::logv << "got message: " << path << " ," << types; char *tmp = lo_address_get_url(lo_message_get_source(msg)); Message m(path, types, Address(tmp)); std::free(tmp); for (int i = 0; i < argc; ++i) { das::logv << " "; switch (types[i]) { case 'i': m.args.push_back(argv[i]->i); das::logv << argv[i]->i; break; case 'f': m.args.push_back(argv[i]->f); das::logv << argv[i]->f; break; case 'd': m.args.push_back(argv[i]->d); das::logv << argv[i]->d; break; case 's': m.args.push_back(std::string(&argv[i]->s)); das::logv << "'" << &argv[i]->s << "'"; break; default: m.args.push_back(0); das::logv << ""; break; } } das::logv << std::endl; cb(m); return 0; } class AddArgumentVisitor : public boost::static_visitor<> { public: AddArgumentVisitor(lo_message & msg) : _msg(msg) { } void operator()(int i) const { lo_message_add_int32(_msg, i); } void operator()(float f) const { lo_message_add_float(_msg, f); } void operator()(double d) const { lo_message_add_double(_msg, d); } void operator()(std::string const & s) const { lo_message_add_string(_msg, s.c_str()); } private: lo_message & _msg; }; void OSCInterface::send(Address const & target, std::string const & path, ArgumentVector const & args) { lo_message msg = lo_message_new(); for (ArgumentVector::const_iterator i = args.begin(); i != args.end(); ++i) { boost::apply_visitor(AddArgumentVisitor(msg), *i); } lo_send_message_from(target.addr(), lo_server_thread_get_server(_thread), path.c_str(), msg); lo_message_free(msg); } OSCInterface::Address::Address(std::string const & url) { std::istringstream ss(url); unsigned int i; if (ss >> i && ss.eof()) { _addr = lo_address_new(NULL, url.c_str()); } else { _addr = lo_address_new_from_url(url.c_str()); } if (!_addr) { throw OSCError(das::make_string() << "invalid OSC port/url: " << url); } } OSCInterface::Address::Address(Address const & a) { _addr = lo_address_new_from_url(a.url().c_str()); } OSCInterface::Address::~Address() { lo_address_free(_addr); } OSCInterface::Address & OSCInterface::Address::operator=(Address const & a) { if (this == &a) { return *this; } lo_address_free(_addr); _addr = lo_address_new_from_url(a.url().c_str()); return *this; } bool OSCInterface::Address::operator==(Address const & a) { return url() == a.url(); } std::string OSCInterface::Address::url() const { char *tmp = lo_address_get_url(_addr); std::string r = tmp; std::free(tmp); return r; } klick-0.12.2/src/audio_chunk.hh0000644000175000017500000000264411133467215014403 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _AUDIO_CHUNK_HH #define _AUDIO_CHUNK_HH #include "audio.hh" #include #include "util/disposable.hh" /* * mono 32-bit float audio sample */ class AudioChunk : public das::disposable { public: // loads sample from file, converting to the given samplerate if samplerate is non-zero AudioChunk(std::string const & filename, nframes_t samplerate); // create empty audio AudioChunk(nframes_t samplerate) : _samples() , _length(0) , _samplerate(samplerate) { } void adjust_volume(float volume); void adjust_pitch(float factor); sample_t const * samples() const { return _samples.get(); } nframes_t length() const { return _length; } nframes_t samplerate() const { return _samplerate; } private: typedef boost::shared_array SamplePtr; void resample(nframes_t samplerate); #ifdef ENABLE_RUBBERBAND void pitch_shift(float factor); #endif SamplePtr _samples; nframes_t _length; nframes_t _samplerate; }; #endif // _AUDIO_CHUNK_HH klick-0.12.2/src/audio_interface.hh0000644000175000017500000000467111224756531015240 0ustar dasdas/* * klick - an advanced metronome for jack * * Copyright (C) 2007-2009 Dominic Sacré * * This program 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. */ #ifndef _AUDIO_INTERFACE_HH #define _AUDIO_INTERFACE_HH #include "audio.hh" #include #include #include #include #include class AudioInterface : boost::noncopyable { public: struct AudioError : public std::runtime_error { AudioError(std::string const & w) : std::runtime_error(w) { } }; AudioInterface(); virtual ~AudioInterface() { } typedef boost::function ProcessCallback; virtual void set_process_callback(ProcessCallback cb); // get sample rate virtual nframes_t samplerate() const = 0; // check if backend is still running virtual bool is_shutdown() const = 0; // start playing audio chunk at offset into the current period void play(AudioChunkConstPtr chunk, nframes_t offset, float volume = 1.0); void set_volume(float v) { _volume = v; } float volume() const { return _volume; } protected: ProcessCallback _process_cb; void process_mix(sample_t *, nframes_t); private: void process_mix_samples(sample_t *dest, sample_t const * src, nframes_t length, float volume = 1.0); // maximum number of audio chunks that can be played simultaneously static int const MAX_PLAYING_CHUNKS = 4; struct PlayingChunk { AudioChunkConstPtr chunk; nframes_t offset; nframes_t pos; float volume; }; typedef boost::array ChunkArray; ChunkArray _chunks; int _next_chunk; float _volume; }; class AudioInterfaceTransport : public AudioInterface { public: typedef boost::function TimebaseCallback; virtual void set_timebase_callback(TimebaseCallback cb) = 0; virtual bool transport_rolling() const = 0; virtual position_t position() const = 0; virtual nframes_t frame() const = 0; virtual bool set_position(position_t const &) = 0; virtual bool set_frame(nframes_t) = 0; protected: TimebaseCallback _timebase_cb; }; #endif // _AUDIO_INTERFACE_HH klick-0.12.2/samples/0000755000175000017500000000000011253043154012433 5ustar dasdasklick-0.12.2/samples/noise_emphasis.wav0000644000175000017500000000205611015423223016156 0ustar dasdasRIFF&WAVEfmt wdatam8yx4ں"fc13'@(=g7/W-#؋@ xQv.Yc=~*Q8\m+fu $~8I*EH(.NhW-7jXPƴýT3d,b UI*IE\ ˸=_a3M%c0E1*Ϥ蚱Q5Bp.綘!'''ɭR  GÇ㿙U?z cI& t)yF@o|Յ/q)  ӞFBxDp|à2mq-@#˦3ߟ7<k)E؛<ۤ#;4}޿}հ  Ȭ4XI_yC Ve 1_\-ԧw11(wZ/A qR9<9' ! M *4h H% _ `@_dЈ 71 /&f PX m2aA{_$#>#",^ *z<C9S$J^2c}pDQ %I@E++vǨ_)& GU!xY '1>a  ` )2" n$gfK"SG q6}B| p+ ' 0#%/*  04  Jk /] 5 ) [%c !OXLn9 U=9I klick-0.12.2/samples/click_normal.wav0000644000175000017500000001550611004215333015610 0ustar dasdasRIFF>WAVEfmt wdataNn 7 i  8a ]%-,V+ºϸI8Ύ!]pX=t.NDʗ,O&Uupc6XDDaѕלğزҍC)J Qv0{,,ACN;j||}Z/%z3" \·v)&Z7FQcG-h=ǣE%zQd[C%؃cȫe߬FJ `ܢz3ղ2) 0C0N [;;'}/13Z4@,#  1dݍc3T JU q*Way$~6/AA8* \ .]50Y# D@*%1bE=^ɟˤќN (=?-t>Jg6 {P5{$#!dQ%ؽ!Qq%)K =2[B3,!'$' O0NG9#TY|Z*L(`@Y%DC+ c&Xg1Be^ Qf/5a W. nY\|?n*0u=EE hg2 '(8?0a D7Qߵ۸<<8@ qyq \+6s652)r+#-=)>F-q G  6 =O - UT` 6)+g'O>L@0%.Y"օV;M}KoP:ؕخB>E %A1%,6-{ +  TF z E$ =2k } O "&06 0%qA #&"N1 q/sޓ!WYK[ ->vq{߭0 X -I1 e9}'  I=j_JZ A6+H!t  1 / W |:5 ^ E hkW~߼ۗڻ1ntv}\m$ :  F  ur b \ Rb93  wc+!`7dNWJru N]:uj| \? D g 8tCw {) R9 cK |-i a ~{l+ U C2TCu>@Xo 4g]I>Ll ; VYa' k Y=%&I\9Z =x~"x*!;H%03HnYdzs;?Y= g : mDn* QikH"5b7[=qt:kfWN5k3RlttO>Za7 >Lw()HU &C ii_''o=( $tKVI>=ob_qpSb(SPoC5>uXl%[xJ+#lY8qi!_,{~9QR\ X G !W QyApA}tm`HeW"zJW:3ZG G8+(IkE/}- ysW MWl&<?N\CWRQY_fH ue~qX:. x9NeZdEVvK|c@bl:JcBQw uX~hoTQ)6Hq = ) +\*J`M $#J3 2n U. CX~Cvbfl.rn:f9=.X!a R-DI }uIQu/Q[#RfQ9Z?Z+&\lyV\Q6'|J=gDBRXo+p xG |Wp@ lC1N*oJ"Vv$,,^ ~H+Xfh S;A42O* Pv&0{Z]^Y:7jb 4U(_6RmAK} #YP#U`{dQ7oh8hu74<21Ef m'A?&jF>e,d /obK43xW.fE"NM~<@3&R0/<E=}O|[(_KMMZ\mCze8~]uJOSO0eWz}jJgl69;t;<BnjHy`E{Z'! o(0oo@S ~"U%k0*ofIc%s>PN""eVL 3Y/%o=A~bK20WTP9"?>8tG,U{O:7<\xJf+GBPQbC9N9PYb]AX(G=dq\ 8 0lJ <l~\k#@|*T}/_ft%gc]'zB'4k/Hi2$)DJz?2- 0N:*Squo3)91XW@8Y/%~7o/ %W33 ;`_2-G#0AQcidM>U:qs[Yexzre-R']V^c g4# -GOez}~ )4C3$mPy 3]]C-!7IijF2@0klick-0.12.2/samples/square_normal.wav0000644000175000017500000001405411015423223016021 0ustar dasdasRIFF$WAVEfmt wdata999999999999999999999999999999999999999999999999ȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶȶ555555555555555555555555555555555555555555555555444444444444444444444444444444444444444444444444͜000000000000000000000000000000000000000000000000цццццццццццццццццццццццццццццццццццццццццццццццa,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,թթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթթX(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(X(ٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖٖو$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnffffffffffffffffffffffffffffffffffffffffffffffffiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii------------------------------------------------''''''''''''''''''''''''''''''''''''''''''''''''V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? llllllllllllllllllllllllllllllllllllllllllllllllvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQssssssssssssssssssssssssssssssssssssssssssssssss                                                &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb""""""""""""""""""""""""""""""""""""""""""""""""++++++++++++++++++++++++++++++++++++++++++++++++SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuklick-0.12.2/samples/noise_normal.wav0000644000175000017500000000405611015423223015637 0ustar dasdasRIFF&WAVEfmt wdata]j?>>+k >Ĩq޺`ܧް@09 1$*0 jz=0.LL+E+ `Գ$DK99'\!/G"3ޔT-G'LC( v` M7/o#̵³ۆ$G9+zъ̨ٚ^+2*GD 0D;| #-(khkc-4X * x)c ֎\- [1QeT/浸#ť5Ք+& E0X}2%"'E" [[5aK}ٴ@"6 H A x.|!!eaMB !quͼWQغ&.0%}ڞ=̈ګ+A)E'U,i7x'!{))RVl EX=)2z &mY   [r! 5!9# "&1% 4a g t% KG g3"Q H~ReIt<$t$h Vvn6G'I >  E_$-q~v !8-*brr3=^ tB$u-D+v)Bhn9e(gB[Ykr' @qT m <F T-w(- x 7 7  cH]{ tw M@wU%B Z bJ4 xdOKKMr| Y V6fj ` R) eJ YJs[ GhG~&ecOE(},U R;k C 5o.b$s#C>& >i TkQi9r   :L4<"-`dH[k{R y.klick-0.12.2/samples/sine_normal.wav0000644000175000017500000001106611015423223015457 0ustar dasdasRIFF.WAVEfmt wdata yk hX/+PIZge'o7uyzy#udndkYKXОJ]ĖӬ34$3eAfMW_eWijuie`XNmB35&q{/ѝi.M6-W (C63BLCIG7IyIHE@:J3*u!T "dž?v6wOϖ!;/ 4'/i6;?aBpCBA=82|+X#b-YױɌ`)B¼sЧn] '.'48;?==t<:O6\1R+W$9 :ߧт>-Ǘю޻/  Q',x14787"6Y3m/t*$t]zWo72cȼ1δQ8[J- jZ 1&*v.12220- )$c GG]0߻]Ѥ`sѥbV# $(f+E-.-,|*Q'D#ts f".YcTD4h | iE*#=&m() *l)'w%/"-=LQ'܉01Շu _ ~p!$%&n&%#!<6 .y޼ݏ eV[ { pgCkO  `DTU|K=] @5<'^ Cf]dl)0 0[{G<1A.'<x ^ ]Yrh {eH*-a/]}X   +O TNj Ni wb2n8|OH\; O5_XZ y \&:JGp.uh}P k ;Xt)a6 j0hY g1xA7V=#2  jw0 TV%\  m:g $$ n QEE q9SJ(Xd^UO5t 6f-j s $Get4<\uH3Cl }Jk!o\  >'t^gd*W-T$Z  ;KWGM q +3#VJWgygRM$\d - 6WQ W CRsS OT $ n e \T<4 J }{VS//b}bV8#Xjy6OI   UzH .S}Ia}}:G H = 2 "]Z.D@/n8VX 5 1 [ f#Ak|}DuRl)`P T  % _ ` /B^dG-40Gx/  1   L H5]/NE4TM[ ) l p :  D8?0(SVoRDdwiSC [ 'E9 `c*UZ-5nQGdH e\  8RJ!@Z)"T8MuG?SH zHiXRplOvA G#\.!-Nv&(Q9xqM/.}I7L:,DOS>V ( k XPTXt>Vr1J3DM`n)ueH42E{;p,^Vy72oG=vHx6^kR!cCMN.!(L~.`G|&;XDNFV?:S-(^U7;*KSBHv^6%o=Em#.SCyi#kRRb e=<84p*CK?"`/=Urj- VMa73me&/iD=o^^j$iqz'q3DLD.i0Dv@4fe=<me4EUnaaiz + FMD lB#$s  @IƦ!"pU8nY3fb`+կBXct[k3񑵖_B򹉤ţ?+iKYk[P8 AJa&1=`rlP8#Nm37/%sA ~)#I Q%8k_@p!ml@liwSYgC (% FxS%LݺP,%T]@31dS`)@!:c" k)~8?:&%6 :"A m "y/z_фBLBݠ:OC .m>!;D?G)IV*>*1<%+ھwDٯ(",# TލC0>8%X : 4 .d5 ,Mz-#!cB94 0gU֐@  R&7?%4-gZ[Ǒl 5pSJO!D:.]2eOu"}6 h+61 bd9`%a! !3>%+&"vne#C A%(Pnn%/ )l!Qu oSVY"Ԗګp ~g\  (@#*5"|V,G#( \\n[ ,T- ) G'%M0(EN &#n|Y9վ2xhlݨݏ ׺׻z]& "t7Nhs @g$ !TND$m_I ,(a ar|| 7&,p(Eإ^ :Ny  [Pn.n` ;O L c!^Zg+ 9VJ?2O `6 QۢJ? *B!y R-KY#%$Mq_> b!,yv+:#+I" sO"h X!bm S #At, cMP 4\pp &H ^U9` 1 k9ږ x <O u  P!j J jZ"S*J w ST7L ( o[yiOFd)ub+?J!-(\ YM|-2 =4fQ4! Y "]e[ޫ*  H~W*y* !I` sX$7%![ $~P@=T9hA# 7I|-" oy  V6l e q 3U6p yl:V *Vp;#kR&^&*[f|?rq !%  5 ~& /{Jxir(m 6 ;9 #.@ \?0 JXsp8 H_ ING:KGLn< \mq5'Pw+uNF s Fe xy/HOpf\w;& | 5^uJ [# !F_aO#I & ^f^{fy PB1u3~A=K.G-g? .2m` Imz<"H)+)w:q&# :~nJ< r 5Ws Lx f$q EPu'Vr_ ND`"w B h|M@^MlG O.)3d53 !9f XhM?  i(lE >} } .qP +w'leZp@ Z]T _:xdQP"]S5 .L.5 ITgO, Iz6 80hTY .p  HP9m$hNbla f $k6-0%/ h"4 ?'(>c$ 6D  v(kP qJYp= D 2T N f^] vg?.PM}W zK9` + :,YS & }$2[k ,35# ^GlNp 9j@SByt@+ S 8\ - hCL  l Vp'EQ& ;9=   +rn-0szAX 5 <Yf uZ *b%~IQ6> #TWQl 29>Y/ u 'a4]fX ! K xh.vmIK;> gXJ 5' B| G % x*&@ Y] 2=Gt'dIF](.~ ;=<|V Nz = % N`@(B_  AWzq }t 4 {obWnh 1 S+A  N7xM 2p6qH LP __ LxMNi1N_ ( ?"M@-Gk e^ mQ f 5cZd:8` qs: 2o DYSnFj!< 6 t?JJ7  X? ItRQ8Q"V  ZZ0:#, ){ b S'0~^>$mR _HX-RU +}l 'XF  'IQ ;8/= = Qz80. MV~j -IY8  q+I b# gl%]ZegzKK/@BQ8| `# . /8>  w>\y^XPVl" jTc7+D (YXZ u~ 7  yz|,  )wBXs#Y@  0$KGi T  s J g 3shhRP*~F DyPIG+_pz  AW  @S M }a rEF GU5$ bA@q_[ !=J > Yh>vW P9 7\*& G X )~%'4xyd4Z /I " r . gdk L\ `$nu =.T;!M 5~ yDV ! V ,?5 ,x 016{ji F  @<q= P  Ryc IAD][ 9@ F8 Txe/ Go [> D)YoF`>%DQ *  >_Hn7HA ` G 0 : 2> -@ZI  oj';+ f9 UpQ t\L2 v ;C\ Ic@3h{HP ] H JB! 35 *Wi  l/B43 M_mEV )BQ? Ty ` N 0 2OF` H 0VZ ?x p D+ F(UQ!   I ~ CT )  WCc ; qI _vm  b#  $ ]v  {$ -G\ ^ ,'A S2v]  KL\> E6B Q+$v0e :x83 076N+ !e  h! c1m~! C=1E5/ 4=_U  h   +  a} )Rn V }B;Fd JH,0$ 4T 1'x lp l^BCk 9 i ;x=Y D+ 6,~5  FA   >}A UnTO ^k2 /Dd FCh [& ( V '0A^ f XC)C _ mX X v !+K  bcG  S # J7 &{ &  7~ N#x x *b  j /|u . B  8MdHM 7 s$u &G mvdp " qWcH 9]2 e2 3u@ \ P )3 q 0 h / * , $~}YJK\ 'Z 8 yC  n } x|wY# qSC_  3>  eF < /5_ ^  uBR k es!uG 4dV f +@ An j V `9]YY5 !> n(  q 3AsF c j#; F UK a|_ mG{wM AG})$:  3UID  h #-qiB ._xc iz]o h 9 3]  a&eU nf \ "Eh" W =c A ` JtI?0v D 8vt  eoQS zi ^Ua[ <;#} dU ?   "- , - Q ] .  b! D R_`k.  7WK &pw e ~-  qC'g} ? 8 j qipL < 7 n'gy I0i1 u k `  k|*+r -y Y p/3a` m= " "m C' ]X j k qhW1+  ;Sw #\DS|d(1g= . WzgU * %/  yd/ & W5[* ] OHA U `*4< @ b [*o! 4Nf 7 [p sA16 ^ {/%{  Ixc5~fo ^T  S"  +n+>E%  "8E  ]7 T ;/_ZN) E -1 Y aYJ &.( ed ~  E_=B ~ J@ 9 F ;ik R vDs3 M\  X $E83 9 $[NJ6 * e(XO ^ 5 )  j|'; $ xusO KmP |f E )Na v $} ( %^w[]  *. WL[~-~ 6 7 rk&_aaUc 0y " \6| g 0/e @ 8lc: |riNa \^,E r ) $[K $ 9 y GMu 6 hE > .tgs 2TU 3hqYy 4 '}x[ y?:D  F? m 6;so } W_ tE 9 J a Aw|  wuK9*5 n,$_  <+{pi Gti { }M  @b'Ch 1[/O  p D c k  l~ EV8= 5FBa m 8 GeE`Kj -y8 H ? ;mkFD oF& L BJ@ '?wRv  ;  {6 Z3  | r]U $x kc a O  CZ{ a^i M ;V+ .( z# 6(   Sr[ mF , ;8 KfK L9E )h U W N'K o22 qH  ]v c Awd 3{  @ o1e %L } CF_u p_i jU~ S;ih ' VWR] E ~Ppl %1Q Y Uj2 9 g|'_wK M9rW <bB8} ef j_u 8]0 !9/ Ri   91Oa r*e] I ]jsi~+ 4 y i j `  JD SU J ^1* 5XKM U}c   tpq TM@M O Z>3X u }%: h_ v{W: 1 wz  {$ h V ]{)= q .9 e  WW hE  ls^ q 8U |e 1 U4# o q#f, :>" OF . % l pW$ki @V kcSk  wU~ -? 2vbK P- So v, S2 f )F oBL{F5v;Jz \ hV2 3@tEh 6oZp - xpB u#^ .Dg k'wzua2 o"f_ i *ci byP y Q,"& }?R @y.E_ GI1-+w'8 " ^ kj8 * yk ! d ?D :zgF }d(Xj ->v@-' @ ;'0= ^VeA( rZ +|Q 8a  )F6 9y70pLI zJ fZ& 9s< ncL  XN tfx] &Q{ =0sKWl n \IE#n  bWb@ o kzQ c  { 6 ,y8 |> > xx2zQ'  h u G yz)<o!sZ4f P(m8O' b #k~8w] U DAm  t$F\ _[j  LR * /%cO 2 hU%j/Gi  I4 +b1D1  _<I !kp +gZ  t58y[ qd)H ~/@p &md* lf  E`j3fw| j6 >bt>% : a)b* rE .  0Jp$YM /nG/s :v!014 +' bT>` [J+kS Yqe^k  s_ Hj,TnHf o uq1X( 6#9#' ;y QGK#>I a'R` !0 YW  W|KY3 S) xd8$Ro#Z ) d LjH]l  n+8n >: bg Z||!dO ? _Q^l \t,k L{yNmWop I'l|  AB$ u{T Vf& Qt.+8Hiw V' ! vd &V#tJ .V%LR6- v5 X G~u5   1YT%7,8k * m X8GW{%aR` l'K4fE: |y~`^B rk|Jr8o/mk hNK5 6H8 '43u K kS5L#bIQ:  m %g1Wle3 :o@T V,H 7L#[d D*z<V8qc: B, +1zift K)m" AI{r 3$~v KplB=fen)- 4>g\M b oY'Ns@Uj;G7 t=x673e%7h ^8+QitU[ 1C {g'N9k };Z;#,|A, '^<Rjq~uR O^?MH  ^[fRaT_   A D$]C K 2D+fv9W$Xq),s |b{M$O+" )2@-& ! B AnS ]s{^>8 6 1GS z q OlL 2bDynO9 =1q!b:3E9-; uzjp!aUFo xo1dgk  xJ}l1wFy Z{}6ddYmTq *=L9" DR.>I~!Uxrbce 4'pwaMj`p?s0E na1'jY2&{* )5sA7p<K~evl H?K9 e=CuLkq|W5jaj dl vuXp)!#lxX]I Vun\: w pFuBd@ aFHb'\] r4cx 4yF5n"pdi8B7wYlT+ e>!p+3VEY @v~\E~JA zBDo i$i:^RFri{ hu"-b1iij:3]"&xw3&QEp Ei b;W-abK)Wp>1dDQ;2%iIv\}>6{0sBb_M>/9K[!eZ&{"2|B#|>U>$b?dQ]v|'|h 1uIzoWi9jU<Ha riDwpq:A} M9o)9+ZT,Y$s&<|3 BVEiT35f^|z1]Fk48 >#4s#?FG :8&bF9+?6SE_@vu~"OC;H(0`Tw*y ;gH: ^KmxP#>!K`a.6Xh~ GhUCNnnYp<#d*]uqZj\ 8=b zX==7FODjrWD6 Z M|5n;*pz"oV_tAwArcx762 DlPp";if3/[]~o0bf4dOy:lP)Pzo5 \oTG<V$)qtArzneYK6!Q=y>>MUga  9w,Myo7jt$KO%Icqu\p!x~n4 ^ EV(1 } 2(zI=56C2z uV%-ARN~P39Y M):OMG^;LEOi~Ix0YXjAfQ=c{3*<.*A`Ly1QyBfD(sQGz4_||9=,3'tte QfYhZ,# qHW R# H* qf;YFf79]j/R~&1Q?coL2saY5ozB:>rFacLyy[C.3%dF|J_ KAL.4n#l&&x=UFq;b gtZ!6 bSLDf[(H$dwN'(Iq }(r! {]#JIEYKq~N>]/;#O aN&CYM\ M'jvX C_$~ej(hx5PL@V](D,ZBxPmka Hx<.( 4}-%fgbPOZTHTA[srw+wOsH5r1 {@EbtB }J^wrS%CEEgQmNQ{$,$u7mP'Eq )/2OR6xa*} mxP m=\v[-< 0,uuV^UspGzyDSkJk'wF bit[nOCz^<'=M(!"UF:@RrE 5v 5p!vBH8(wbmOf%('jS&B@2:p*d+ "(6U&% 2jIKXMfCZ\$N$^9tTRf$t+4iom+#gsK#]iv ,_.5(6'v:d#o2@kPTASTsCm }j)DM"DY.O$-  S ,t]pi=b105lrpKWw=/ckP9ScG\3>6tXpDrdO}[$l,~N .e]8q"JY]m/u`Q2xjT%m<u~ef*<ly7^y.r$7Zc?K[q=?ZCBr!()A,_X2h8w=UT'o '?[. n%YD)>1JI$&.,#Ttt4'11h96J{3F,v7fhlg  n}4l7GLfSaawYFshH4qQ; 5G.Rqa1 ZO[|o)$pN*pl}c M%d, TREy4OFieM$2rNt30Za* rO T1Z=- :'t` -]#g3n0 30Asust o 4\Sd hf[9m,u1T*B"?GpP9+P4n#&`._+(D_27/:\Nr=|tWc'JDL/eY]Lye+B#{HA(NSY;aC*}@[}3of/6nA,?01psXv1o:OiQ:aEa7vZ6c4S&dn '[d:.:aC-]w|=EMmzp~;<| g#/Y'uKen!CdLsA#,-.6R>3kb|5S]_b5 t;"*2LAN^ =j1gEv!D5"V*,S 6yB+K}Hs|z];,7=jVL8@G;lp>z}3f{jCER}Nt6rh6cB bV;H0r?' IX>Eo":0uMxNmlz"DcX 3I/i LOl _g7J};sS CM/(4+NveH k+wh6t)y,OUadN"X' Kb B^zQ!yi,]')$[sN?xdPq6wEw4! ";s=jK-STwhlWPXP0-8j6n6^7 G J(B!1H@#bBm[B[8X;8K'<}2|!)4wz@jymI.Hdh^|i7E-$A G\xc!tmD{~%T vB8=}o32s= 2T+W'yXm`=|aJ3"Qg6k70?Q+cO4m#&VBpdfv i.Hq=6zdt %`;1+n ^YF4jk2D7m{tZD+J[G??],l+x2XEu|[]q #uWYc w}b6,[0ig;}%Pzs$K%>O;[nxi?n{;mj$% "MjhNRfP>5L.q@ lzw Cm[gI5@d!*1E:A}E JZAoaW?D[ MV~KFI+^Vq*tD>"Thj"h_HDvV>Hi,g@Qo<g :EtNJj'Va=$>dPV ]9mp7p< c\E|>[ 0]]|c cb)An<6`4;aS \C~c$g4 oQkG3<*C$S!=PTN^$nBOvj$.G Y+l>K3n/X@*KPCpBfTM 9Y;'@ltZJcwEDw1<]O'_:*43XK1Jf<v)WV(-)I i$!DG-IU^  3 jxZR;"*m G QvV>sul}YH b2$F\'t#W%a@= mB`4g+ZsVIR*-[)NpcaOf m: r^=f\i,7)'&Wt#q|RvoSF"G"p(\wrT>]O,X7H@eJbGY/#~3?drz]% WD S |zk7P9hpI,Dbkm}93^=P?Gn g,/sq( Z\r^~dbn[@DJmVkDaovOCC7/ZWbMCu#WYI*z&^@Y JfT {jX=T?~(,~`^p~S<d.}``g4_X~aWIc)o&F - ~ #ZBX">v ElGW}M6r&ZCVAt[ #tYm 6&9@baZtGAhe! u <j"vl nQ{;.jhR3aow0/(G{4P9-K=Sn\ du.F[);s>LN BB'1z*+)oaUVjNz.u:$%lsUR6sv"ShF%G$=c[B1kI 5Dq\H%s.e;S8.wuC) 2 FWw8Y]z~e*@8(WW8}w1yVVvb/.H+ &Hu=pVaJ ~uF%HXfmOZ9%C0~M%8S&V|VT>rl/?;1)?{D){ws 2%z z ,fhAafQX[wG~_lc{LO*`T?l5G9jd ^n"z7tfqpz?n=nSX!fqLDwhl["59P:, Xu9[$_CaK-+* ^=yT|G' }c}I_ DA;u$r102p 8jf!2:Yc\15p~/*6eP =7 %FYz ^gS:9{XR3TY/0  Hvd?MJ)N L-9k#{eG')v3B" XH{%bzo)K dOqyZj'xMh$;}< <5*H=7l:toV|5(aC"-ek@/-Y![9]/ YkN8d zg IAXtYUtSG#R-0Rz4M~ >dek)$?g9T#hfE*MzFx31'f @|>g6J|6WzlFZ}h9>'[ \3 lU& Ex$+^ SVrezHD.wd"6N7/>?h%/FY1 7SAAp5ng>YODbUYzG &xub0hPMI[c*p_`Dy%~(g}&J{)p1]p[x8vvf JGDi=j'#J_Rl $W~g IrNh~/a8NDL^[/r8`-N'R'n3%J KpQi>9 _Ql`]o V;yi |k~dT vBMK'^:v<\  0`9 Pj` VE\#1=>L>XA KO{FSsBT ^b!2"ioi<2;@VT!P-B:C-v~bx>j>8B`UPM qFu#dsWat-IAJt QIt} G{ T@,-[0_J|jzrHsZ`N/>Lk(YEzPo}c ) |:T*1>l,|x/+2@Hmyh~Lm="]Rc 6]*K>z !n :>h)$-_ -=u*r&\v0kj%Ov~PxfL+.*%v+&65e`T>Ct]%H<5# *4.A%oS :@:+VHI`f Af8;Rf<t{ pC 4J. NJ K{a$xE>8 Bs&CLTGq>TjpCNuL/,*tMDS!d]z 87Fi0NQ~V(z2+) xsvgGFX{x<0|s{-(]VD1hft*D}_i F,Vxd#OkA7c_*c<_?lQ>Qt1 rTr0*,/eYA M;WtlM[]Tl}sfU6~$m k+[.KQHC.^h|.5JOO05YtpLt%.t HAE}*//.h]G"sJsZpSt|;wBc.X>4s(M\ h st!vy2)Cy Ck7%U*Q%$$/=9'c'5bGXip$mvb0Sr{\/(l6aJSG&Q V VTG e!T~( ]:\hRBZQfRQ(9J3.{Ul+!Vr"~GlV4$ xkB>hZe$Lz7|U&Y!a;y +DMp[~?kr1`025MY6&^x24* 6LN3#7F-,Je Z,Ba!at K" 3mQV6 uYGB&q'm!8Pz\- ? f7{C(Ys&MJhI >UrVBm:h,tF>!(=8Yg] AVQjn\ QVwf2w'Tt*<>1'&7R9b}& zJpp^JnYVgbI.(qEjg&b*KpKYN%\dy*=k4'/Pj^V 1^rgO+&wo:P<-UH%^ ;;SS-'bZXSIAVZb@1LBXhB2?_=&]i>"HrNt 8Y[*j2B)GoP%<4an ;Pj2 W]J>  9j(a LMsz.(C5IO7}[?"J4x a6!' BO_5pu\`/RT\t]!Xf x8@V*bT8R-Nz]2sq4"t=uo@0H$YOc"%/maip)1_sjI PqauY_9w 2K'rz.L5k4_yQYd2mo7tL~KX}C%%> a; ?r[Ld| I;CwpFB+v^Zxf*1M*1Z2lf y1Kxyc>1 7'_Myw2q  ^7vf?0u"o%4|>(mpG)l:>I 4w:<6ETY"~J_u\C`}Dy6P-8EntT+aQ^c@cT>}so:l>r`5:/3xp }cD*ec4yrfl15S6F%WPFYFAe %$3qY OO#\Yk7)_'&0_e+TOkB{BUoq;E!>DJ"cwI6'- Afj(I29hv"=3~soHp -BeM8)2)TFvN];Lot4/>1tGT56&iX0+*BGJA~]o'-=IM4_K(dlM]$QhQc? 0EA4jyqq/@B iW}= bS6d==|}RzeJck^ACO<xUP&S MoI;NudQAxUQcvvqw&<C[Z2- JJ{[w556/"c`5ihW?uQ`5Qqg\^ThV$[pHc,K'V8j`'&jo]_R`yDE:x7Qg.}+6A'8kDk fo64 8GCCG. ^[21}ef +>{i*:Q"Q)XL65WD3CCQQUwmS`,^' \)O.!| K-0*fJ "`{dmj:s?> /wC%. h*Ski WDn;zS3Q2n4&iQi"]=Nf`*8)z+%7RwxAlUu>\)5u}d;.$*)}y3YFBA4MYFo&G|pY"|Ii>%DBS^DAUOU^'8=hx=U- [eX(iKh[,6TO4Pd(ABQnmXD;?_i+Pbz0 }=F&})=D?ISu|j7&hK2*VzGQK/w 4/2{.^uCYsmc& gB lu.;"pDnwi@V =s|l4\\D6r%A(sx Q5Ll||Ylzv\dFV ec'.:sN<=e`iMk}~VD>(H Pp?[hCOvd+tS1tzUUFO<;p*Kj 1c|s#woT` @P/Ea$/9 TemOsbA-Na yZ P2n&[j, zq*Y < <r{1_nOr"P[wLzwuo!I\8q|s^)iT;8s{PyP7o%^|Z6yjY40H&'C6g *V~#n~eb9l/tIV=0&V?-M=F Mh )hIl*aF@)7''[#L^ g=j& Nu }N M S.M=%:1&(^$?RF} S+ux4  'U",<=2++K#9z87y=T$aL'>,M<'{H{ ]UHKn+w;R/]!Ln 6P mSix>E8]O#2NCWl$beF1pNdnze&9Pz;!Y&FFb)wt:O4o 8u^/P+S5/zBc&jb8k**Su&sIFY"s%xKY'!3$s FSR/ nX'b+K5 t jQ+W /S%Bk/@='\:Uq5.Z4vO^+"F>t)yCM5U^;~$?m"GEA4eB(9 Sc5$ L:"k9>)` I;P<nYAXwQ7Y5 K"A`m^'oD [,n.d %YY)QA vR=+(0whk/fh+b;5t%y%G+~/AW$fiM0!Q]qRKr0\sK0Z(~/3*)'l,HrSnN IzzU2z-Jc` M4+wH'"l11w^*ac'B\mq+7?Pl,4C.jV2z sEu k0^tu38R~Z59>6v1%P9 Y_?gVk o"NmO -L\HAE-\s[S Z|N(ms ncQV6-2]D~DQCj\j#`Us`UW*\?n+$*d_2]f#:!|B\@sMjCybcz,/ywYvLgc,Q5~vE3t;ib"po"6pNrBec[}TWp ,*wfVgCp}8P"'V3kcOr^|s*.}hTOqNlTzIw;#k`yjm4wHOfu5U4jTI~bqt A7{ZZJkBrOl:yoI,aU$pn  BL bu8s?^LSkf$D6zH~_N\2yVf4)J+^86hb|{H@`y<j8X4jaO{$B&eD|c Y[$cko{9P"Q-9|` v'[JIwN}?O"}pj|M;a:d9}u nj)anlGvB]-Myj>l[GtXSS&AFvQw7zt+yx~=!rQXn&ejNbJv`op#(A[Yx3x{:{ }Ogc _m!ti_VvJy&utqD7dkw)Hia ]enqvoRnv/nl){jf=regobuH|RU~JybkePo9^`x#^_"}hU@ea `aPkCyIV9qdjXJuc)p_kuRe+e=zz;UtV h\KcD|1|Zx*`XnN7k_dcqtAT,\{3os@Fq^j\2ZDuhWyVVvW|(nd Uyceh=S-hs ewG8k`p[&PBvx `}Z NY"^zU]*Ivczn=S0xYpJ,]Z"d%TH Y}c BR+`xRY,Bpbw&H6ov SjI WY"pMLIo];O/q| NZ04fg,y#B2~FcMJX)x 9=9`W!/L/~=R2,WW+:35ZK :I./8+\Q :+*>+GL% -*)F<1<#'7=A#/#!8'4>&"" 30$3"'03'!,& &. (($ #)!          klick-0.12.2/samples/sine_emphasis.wav0000644000175000017500000001067211015423223016002 0ustar dasdasRIFFWAVEfmt wdata6J:^akuyGyrhBYF0a*׷UvɈ !?(҆&`;2F+XenisrmcUC6/ѨȚɈzȐz ӗ.)BR_h@mm:h _Q,A-cSu2uߠ pE^>WAէ+=M4ZbIgMgcZ(N>M,!ؐŜ@Ǖ}ĥaÙF'9HT=]aa]VJ <*۔h[2|%oq#(4ɘJĭka# (4s>EIJ^H#C;0#~Rڱ ȺPβUOHƃ|ޣr? {%1C:AEF"D\?7(.r"@2aoRǀ=K*:R"g-36` 8aZ3 L 7v8Q SW'd; AvG' ~ b N, $<hDfd^ m y~H=R D~`iy27_ O6 dVCSR avAS *z ?e'l | tO\gsT3K\X I 09 o ? M$f>H*s ,TpQ{ ({ Z u Eq G2 G T5dTNK_mt  " 8{KU8L)Ya j t d N X% <=sU \ \ b ^v~56PG@rA? = R d  F:/+jB]QO - = d& 4~FT~7')  P Lpt6[5^  '  }!D.lc& U ;-7)}/D;[  $ <Aq=6x [: hH8{`%/=Eek32] +owY& Wz  NT@2vu !<<--}na>mzl`of+s4@YKv-)k^.<4X;Bmg@yA&5tOY^,;(~x\ CV&!2YyaVp!F} cHKYaoiJ LG5jv`YMF0/eb*U bQwBy^<"W#HA !z0[h"!a,msWHNAh3";}bj+_Wwj,*r[d!!YSXs(,{[z*mi2"^M^"oHddLw 8ld%i2B^l;7dEU>t|K q5ypy%j<%BSSF*Xf5(Y,am@rV=0-0=Qn<]vv]E' '4<EEE@8/$ klick-0.12.2/samples/square_emphasis.wav0000644000175000017500000001545411015423223016347 0ustar dasdasRIFF$WAVEfmt wdatao=o=o=o=o=o=o=o=o=o=o=o=o=o=o=o=o=o=o=o=o=o=o=o=::::::::::::::::::::::::999999999999999999999999Ɣ888888888888888888888888ȝȝȝȝȝȝȝȝȝȝȝȝȝȝȝȝȝȝȝȝȝȝȝ666666666666666666666666666666666666666666666666333333333333333333333333@@@@@@@@@@@@@@@@@@@@@@@@͟111111111111111111111111f/f/f/f/f/f/f/f/f/f/f/f/f/f/f/f/f/f/f/f/f/f/f/f/ѱѱѱѱѱѱѱѱѱѱѱѱѱѱѱѱѱѱѱѱѱѱѱ<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-++++++++++++++++++++++++))))))))))))))))))))))))'''''''''''''''''''''''' % % % % % % % % % % % % % % % % % % % % % % % %@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#ݫݫݫݫݫݫݫݫݫݫݫݫݫݫݫݫݫݫݫݫݫݫݫn!n!n!n!n!n!n!n!n!n!n!n!n!n!n!n!n!n!n!n!n!n!n!n!tttttttttttttttttttttttt߭........................YYYYYYYYYYYYYYYYYYYYYYYYrrrrrrrrrrrrrrrrrrrrrrrrCCCCCCCCCCCCCCCCCCCCCCCCxxxxxxxxxxxxxxxxxxxxxxxxllllllllllllllllllllllllAAAAAAAAAAAAAAAAAAAAAAAArrrrrrrrrrrrrrrrrrrrrrrrXXXXXXXXXXXXXXXXXXXXXXXX111111111111111111111111LLLLLLLLLLLLLLLLLLLLLLLL666666666666666666666666LLLLLLLLLLLLLLLLLLLLLLLL////////////////////////Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y t t t t t t t t t t t t t t t t t t t t t t t t PPPPPPPPPPPPPPPPPPPPPPPPW W W W W W W W W W W W W W W W W W W W W W W W EEEEEEEEEEEEEEEEEEEEEEEEqqqqqqqqqqqqqqqqqqqqqqqqaaaaaaaaaaaaaaaaaaaaaaaa[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\kkkkkkkkkkkkkkkkkkkkkkkk777777777777777777777777888888888888888888888888HHHHHHHHHHHHHHHHHHHHHHHHBBBBBBBBBBBBBBBBBBBBBBBB&&&&&&&&&&&&&&&&&&&&&&&&eeeeeeeeeeeeeeeeeeeeeeeeGGGGGGGGGGGGGGGGGGGGGGGG333333333333333333333333\\\\\\\\\\\\\\\\\\\\\\\\jjjjjjjjjjjjjjjjjjjjjjjjHHHHHHHHHHHHHHHHHHHHHHHH))))))))))))))))))))))))                        000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEWWWWWWWWWWWWWWWWWWWWWWWWiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxssssssssssssssssssssssssgggggggggggggggggggggggg\\\\\\\\\\\\\\\\\\\\\\\\SSSSSSSSSSSSSSSSSSSSSSSSJJJJJJJJJJJJJJJJJJJJJJJJBBBBBBBBBBBBBBBBBBBBBBBBklick-0.12.2/README0000644000175000017500000000160411204353225011647 0ustar dasdasklick - an advanced metronome for jack Copyright (C) 2007-2009 Dominic Sacré Some of the audio samples used by klick have been borrowed (under the terms of the GPL) from: * Ardour [http://ardour.org/], sounds by Nick Mainsbridge * GTick [http://www.antcom.de/gtick/], sounds by Roland Stigge * Freepats [http://freepats.zenvoid.org/] Requirements: ============= * JACK Audio Connection Kit [http://jackaudio.org/] * Boost [http://www.boost.org/] (headers only) * libsamplerate [http://www.mega-nerd.com/SRC/] * libsndfile [http://www.mega-nerd.com/libsndfile/] Optional: ========= * liblo [http://liblo.sourceforge.net/] * Rubber Band [http://www.breakfastquay.com/rubberband/] Installation: ============= In the klick source directory, run: scons and then, as root: scons install Documentation: ============== See doc/manual.html for the klick user manual. klick-0.12.2/NEWS0000644000175000017500000000566711253042671011507 0ustar dasdas2009-09-13: klick 0.12.2 * Fixed a segfault (introduced in 0.12.0) that sometimes occured when terminating klick. 2009-07-10: klick 0.12.1 * Fixed a build error on amd64. 2009-07-09: klick 0.12.0 * Added click track export functionality. * SConstruct now supports DESTDIR option to make packagers' lifes easier. 2009-01-14: klick 0.11.0 * Many OSC related fixes and improvements. * Optionally use Rubber Band for pitch shifting (build with RUBBERBAND=True). 2008-12-03: klick 0.10.0 * Several new OSC methods, most notably for managing JACK connections. * Various minor fixes, some code cleanup. * Included a proper user manual in the tarball. 2008-10-23: klick 0.9.1 * Fixed a bug that restricted the tempo to tempo_limit even when tempo_increment was disabled. 2008-10-17: klick 0.9.0 * New OSC methods: set_tempo_increment, set_tempo_limit, set_pattern. * Added optional return address parameter to all query methods. 2008-08-11: klick 0.8.0 * Added basic OSC support. * Added interactive mode (klick -i). 2008-05-24: klick 0.7.0 * New default sound (square wave). * Allow volume and pitch of emphasized and normal beats to be adjusted individually. * Provide samples as regular wave files, rather than compiling them into the executable. * Some source code restructuring. 2008-04-08: klick 0.6.3 * Fixed a bug that caused klick to play at a slightly wrong tempo when using silent beats (with the pattern feature). * Made klick compile cleanly with -W -Wall. 2008-03-19: klick 0.6.2 * Fixed a bug when using -E or -e and either -v or -w parameters at the same time. * Fixed (and actually documented) tempo-per-beat feature. * Use jack_client_open() instead of deprecated jack_client_new(), default client name is now simply "klick". 2008-02-02: klick 0.6.1 * Fixed parsing of patterns on the command line. * Don't choke on tempo map entries where tempo == tempo2. 2008-01-27: klick 0.6 * Fixed a segfault when changing the position via JACK transport. * Vastly more efficient seeking in the tempo map. * Fixed erroneous behaviour of JACK transport BBT slave mode (-j), and be a little more tolerant of faulty BBT information from the timebase master. * Try to send more sensible BBT information in timebase master mode. 2007-12-02: klick 0.5 * Additional sound (bell/click). * Added option to auto-connect to hardware output ports. * Various minor fixes and enhancements, several changes under the hood. * Improved documentation. 2007-07-08: klick 0.4 * JACK transport master support. * JACK transport slave support (running without tempomap). * Added ability to change the pitch of the click sounds. * Volume and tempo multipliers are now given as floats rather than percentage. * Hopefully more bugs fixed than newly introduced. 2007-03-26: klick 0.2 * Some bugfixes. * Improved documentation. 2007-02-10: klick 0.1 * Initial release. klick-0.12.2/COPYING0000644000175000017500000004310310645245330012027 0ustar dasdas GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. klick-0.12.2/SConstruct0000644000175000017500000000512211253016327013023 0ustar dasdas# -*- python -*- import os version = '0.12.2' env = Environment( CPPDEFINES = [ ('VERSION', '\\"%s\\"' % version), ], ENV = os.environ, ) # build options opts = Options('scache.conf') opts.AddOptions( PathOption('PREFIX', 'install prefix', '/usr/local'), PathOption('DESTDIR', 'intermediate install prefix', '', PathOption.PathAccept), BoolOption('DEBUG', 'debug mode', False), BoolOption('OSC', 'OSC support', True), BoolOption('TERMINAL', 'terminal control support', True), BoolOption('RUBBERBAND', 'use Rubber Band for pitch shifting', False), ) opts.Update(env) opts.Save('scache.conf', env) Help(opts.GenerateHelpText(env)) if env['DEBUG']: env.Append(CCFLAGS = ['-g', '-W', '-Wall']) else: env.Append(CCFLAGS = ['-O2', '-W', '-Wall']) env.Prepend(CPPDEFINES = 'NDEBUG') # install paths prefix_bin = os.path.join(env['PREFIX'], 'bin') prefix_share = os.path.join(env['PREFIX'], 'share/klick') env.Append(CPPDEFINES = ('DATA_DIR', '\\"%s\\"' % prefix_share)) # required libraries env.ParseConfig( 'pkg-config --cflags --libs jack samplerate sndfile' ) if os.system('pkg-config --atleast-version=1.0.18 sndfile') == 0: env.Append(CPPDEFINES = ['HAVE_SNDFILE_OGG']) # source files sources = [ 'src/main.cc', 'src/klick.cc', 'src/options.cc', 'src/audio_interface.cc', 'src/audio_interface_jack.cc', 'src/audio_interface_sndfile.cc', 'src/audio_chunk.cc', 'src/tempomap.cc', 'src/metronome.cc', 'src/metronome_simple.cc', 'src/metronome_map.cc', 'src/metronome_jack.cc', 'src/position.cc', 'src/util/util.cc' ] # audio samples samples = [ 'samples/square_emphasis.wav', 'samples/square_normal.wav', 'samples/sine_emphasis.wav', 'samples/sine_normal.wav', 'samples/noise_emphasis.wav', 'samples/noise_normal.wav', 'samples/click_emphasis.wav', 'samples/click_normal.wav', ] # build options if env['OSC']: env.ParseConfig('pkg-config --cflags --libs liblo') env.Append(CPPDEFINES = ['ENABLE_OSC']) sources += [ 'src/osc_interface.cc', 'src/osc_handler.cc', ] if env['TERMINAL']: env.Append(CPPDEFINES = ['ENABLE_TERMINAL']) sources += [ 'src/terminal_handler.cc', ] if env['RUBBERBAND']: env.ParseConfig('pkg-config --cflags --libs rubberband') env.Append(CPPDEFINES = ['ENABLE_RUBBERBAND']) env.Program('klick', sources) Default('klick') # installation env.Alias('install', [ env.Install(env['DESTDIR'] + prefix_bin, 'klick'), env.Install(env['DESTDIR'] + os.path.join(prefix_share, 'samples'), samples) ]) klick-0.12.2/doc/0000755000175000017500000000000011253043154011534 5ustar dasdasklick-0.12.2/doc/manual.html0000644000175000017500000003176211225166425013716 0ustar dasdas klick - User Manual

klick - User Manual

Table of Contents

Basics

klick [options] [bars] [meter] tempo[-tempo2/accel] [pattern]

Plays a simple metronome with the given meter and tempo. To play a 3/4 beat at 120 bpm:

klick 3/4 120

You can also specify a "target" tempo and acceleration rate, to use klick as a speed trainer. To play a 4/4 beat starting at 80 bpm, gradually increasing the tempo by 1 bpm every 10 measures until reaching 120 bpm:

klick 80-120/10

Once the final tempo is reached, klick will continue playing at a constant tempo.

klick [options] -f filename

Loads a tempo map from a file. This allows for more complex setups including meter and tempo changes etc.
See Tempo Map File Format.

klick [options] -i

Runs klick in interactive mode, allowing you to change tempo and meter while klick is running.
The keyboard commands available in this mode are described in Interactive Mode.

klick [options] -j

In this mode, klick gets all tempo information from JACK. Just run a sequencer as JACK transport master, and klick will play a simple metronome pattern in sync with JACK transport.

Command Line Options

-f filename       load tempo map from file
-j                no tempo map, just follow jack transport
-n name           set jack client name
-p port,..        jack port(s) to connect to
-P                automatically connect to hardware ports
-o port           OSC port to listen on
-i                interactive mode
-W filename       export click track to audio file
-r samplerate     sample rate of export (default: 48000)
-s number         use built-in sounds:
                    0: square wave (default)
                    1: sine wave
                    2: noise
                    3: acoustic bell/click
-S file[,file]    load sounds from file(s)
-e                no emphasized beats
-E                emphasized beats only
-v mult[,mult]    adjust playback volume (default: 1.0)
-w mult[,mult]    adjust playback pitch (default: 1.0)
-t                enable jack transport
-T                become transport master (implies -t)
-d seconds        delay before starting playback
-c bars           pre-roll. use -c 0 for 2 beats
-l label          start playback at the given label
-x multiplier     multiply tempo by the given factor
-h                show this help

Interactive Mode

The following keyboard commands are available in interactive mode:

Arrow up/downincrease/reduce tempo by 10 bpm
Arrow right/leftincrease/reduce tempo by 1 bpm
0-9set beats per bar (0/1: all beats are even)
q/w/e/rset beat type (whole, half, quarter, eighth note)
+/=, -increase/reduce volume
Spacestart/stop metronome
Escapeexit klick

Tempo Map File Format

A tempo map is a plain text file, consisting of an arbitrary number of lines which will be played sequentially. Each line must have the following format:

[label:] bars [meter] tempo [pattern] [volume]

Everything in brackets is optional, and can be omitted to use the default. '#' indicates the start of a comment, empty lines are ignored.

label An optional name that can be used to refer to this line, and start playback at this position.
bars The number of bars for this tempo map entry.
meter The meter, written as beats/denominator. This is optional, the default is 4/4.
tempo The tempo in beats per minute, with a "beat" always referring to quarter notes, no matter what the meter actually is.
tempo Sets the tempo for the duration of the whole tempo map entry.
tempo1-tempo2 Specifies the tempo for the start and the end of this entry, with a gradual tempo change in between.
tempo1,tempo2,... Sets a different tempo for each beat. The number of tempo values must be the same as the total number of beats for this entry (bars * beats).
pattern This can be used to alter the pattern of accented/normal beats. Use 'X' (upper case) for accented beats, 'x' (lower case) for normal beats, and '.' for silence. So for example, given a 6/8 measure, and you only want the 1st and 4th beat to be played, you could use the pattern 'X..x..'; the default is 'Xxxxxx'.
volume Allows you to set a different volume for each part, default is 1.0.

Example Tempo Map

intro:    8 120           # play 8 measures at 120 bpm (4/4)
verse1:   12 120 X.x.     # 12 measures at 120 bpm, playing only the 1st and 3rd beat
          4 120-140 X.x.  # gradually increase tempo to 140 bpm
chorus1:  16 140
bridge:   8 3/4 140 0.5   # change to 3/4 time, reduce volume
          8 3/4 140       # normal volume again
verse2:   12 120          # back to 4/4 (implied)
chorus2:  16 140          # jump to 140 bpm
outro:    6 140
          2 140-80        # ritardando over the last 2 bars

Click Track Export

By default, klick connects to JACK for audio output. To export a click track to an audio file instead, use the -W parameter to specify an output filename. The file type is determined by extension, supported formats are WAV, AIFF, FLAC, Ogg Vorbis (with libsndfile >= 1.0.18).
The -r parameter can be used to set the sample rate of the exported audio, default is 48000 Hz.

OSC Commands

klick understands the following OSC messages:

General
/klick/ping
/klick/ping ,s <return_address>
responds: /klick/pong
/klick/register_client
/klick/register_client ,s <address>
registers a client to receive notifications when any parameter changes
/klick/unregister_client
/klick/unregister_client ,s <address>
unregisters a client
/klick/query
/klick/query ,s <return_address>
reports current state (same as /klick/*/query)
/klick/quit terminates klick
Configuration
/klick/config/set_sound ,i <number> sets the sound to one of the built-in ones
/klick/config/set_sound ,ss <filename> <filename> loads the sound from two audio files
/klick/config/set_sound_volume ,ff <emphasis> <normal> changes the volume individually for both samples
/klick/config/set_sound_pitch ,ff <emphasis> <normal> changes the pitch individually for both samples
/klick/config/set_volume ,f <volume> sets the overall output volume
/klick/config/autoconnect connects klick's output port to the first two hardware ports
/klick/config/connect ,s... <port> ... connects klick's output port to the specified JACK ports
/klick/config/disconnect_all disconnects all connections from klick's output port
/klick/config/get_available_ports
/klick/config/get_available_ports ,s <return_address>
returns a list of all JACK input ports:
/klick/config/available_ports ,s... <port> ...
/klick/config/query
/klick/config/query ,s <return_address>
reports current state:
/klick/config/sound ,i
/klick/config/sound ,ss
/klick/config/sound_volume ,ff
/klick/config/sound_pitch ,ff
/klick/config/volume ,f
Generic Metronome Functions
/klick/metro/set_type ,s <type> sets the metronome type, one of 'simple', 'map', 'jack'
/klick/metro/start starts the metronome
/klick/metro/stop stops the metronome
/klick/metro/query
/klick/metro/query ,s <return_address>
reports current state:
/klick/metro/type ,s
/klick/metro/active ,i
Simple (Dynamic) Metronome
/klick/simple/set_tempo ,f <tempo> sets the metronome's tempo
/klick/simple/set_tempo_increment ,f <increment> sets the tempo increment per bar
/klick/simple/set_tempo_start ,f <start> sets the start tempo
/klick/simple/set_tempo_limit ,f <limit> sets the maximum tempo
/klick/simple/set_meter ,ii <beats> <denom> sets the metronome's meter
/klick/simple/set_pattern ,s <pattern> sets the beat pattern
/klick/simple/tap tap tempo
/klick/simple/query
/klick/simple/query ,s <return_address>
reports current state:
/klick/simple/tempo ,f
/klick/simple/tempo_increment ,f
/klick/simple/tempo_limit ,f
/klick/simple/current_tempo ,f
/klick/simple/meter ,ii
/klick/simple/pattern ,s
Tempo Map Metronome
/klick/map/load_file ,s <filename> loads a tempo map from a file
/klick/map/set_preroll ,i <bars> sets the preroll before the start of the tempo map
/klick/map/set_tempo_multiplier ,f <mult> sets the tempo multiplier
/klick/map/query
/klick/map/query ,s <return_address>
reports current state:
/klick/map/filename ,s
/klick/map/preroll ,i
/klick/map/tempo_multiplier ,f
JACK Transport Metronome
/klick/jack/query
/klick/jack/query ,s <return_address>
currently does nothing