diff options
author | Roland Reichwein <mail@reichwein.it> | 2025-01-04 11:28:20 +0100 |
---|---|---|
committer | Roland Reichwein <mail@reichwein.it> | 2025-01-04 11:28:20 +0100 |
commit | b2c35cdf69a9084806ac7930cf4475980d596cf6 (patch) | |
tree | b74b8f2ee2c66c59f7385407cfc34c2a0a16961f | |
parent | aaafcea7e26791acbf5b9612e3fb396edcdfcc8f (diff) |
Separate out implementation from headers
-rw-r--r-- | MIDI.cpp | 171 | ||||
-rw-r--r-- | MIDI.h | 169 | ||||
-rw-r--r-- | PCM.cpp | 149 | ||||
-rw-r--r-- | PCM.h | 155 | ||||
-rw-r--r-- | Timer.cpp | 37 | ||||
-rw-r--r-- | Timer.h | 37 | ||||
-rw-r--r-- | debug.cpp | 20 | ||||
-rw-r--r-- | debug.h | 18 | ||||
-rw-r--r-- | log.cpp | 53 | ||||
-rw-r--r-- | log.h | 50 |
10 files changed, 469 insertions, 390 deletions
@@ -1,2 +1,173 @@ #include "MIDI.h" +MIDI::MIDI() +{ + if (0 > snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK)) + { + throw std::runtime_error("MIDI sequencer couldn't be opened"); + } + + if (0 > snd_seq_set_client_name(seq_handle, "Midi Listener")) + { + throw std::runtime_error("MIDI client name couldn't be set"); + } + + if (0 > ((in_port = snd_seq_create_simple_port(seq_handle, "24:0", + SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_APPLICATION)))) + { + throw std::runtime_error("MIDI port couldn't be opened"); + } + + debug_cout << "in_port: " << std::to_string(in_port) << std::endl; + +#if 1 + snd_seq_addr_t sender, dest; + snd_seq_port_subscribe_t *subs; + sender.client = 24; + sender.port = 0; + dest.client = snd_seq_client_id(seq_handle); + dest.port = in_port; + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_sender(subs, &sender); + snd_seq_port_subscribe_set_dest(subs, &dest); + snd_seq_port_subscribe_set_queue(subs, 1); + snd_seq_port_subscribe_set_time_update(subs, 1); + snd_seq_port_subscribe_set_time_real(subs, 1); + // TODO: fix timestamp (currently always 0) + if (0 > snd_seq_subscribe_port(seq_handle, subs)) +#endif + //if (0 > snd_seq_connect_from(seq_handle, in_port, 24, 0)) + { + throw std::runtime_error("MIDI port couldn't be connected"); + } + + npfd = snd_seq_poll_descriptors_count(seq_handle, POLLIN); + if (npfd < 0) { + throw std::runtime_error("snd_seq_poll_descriptors_count() failed"); + } + pfd = (struct pollfd *)malloc(npfd * sizeof(struct pollfd)); + if (pfd == nullptr) { + throw std::runtime_error("alloca() error for MIDI"); + } + if (0 > snd_seq_poll_descriptors(seq_handle, pfd, npfd, POLLIN)) + { + throw std::runtime_error("snd_seq_poll_descriptors() failure"); + } + + if (npfd != 1) { + std::cout << "Warning: " << std::to_string(npfd) << " poll fds for MIDI" << std::endl; + } else if (fd() <= 2) { + std::cout << "Warning: Bad MIDI fd: " << std::to_string(fd()) << std::endl; + } +} + +MIDI::~MIDI() +{ + free(pfd); +} + +int MIDI::fd() +{ + return pfd->fd; +} + +bool MIDI::event_ready() +{ + int result = snd_seq_event_input_pending(seq_handle, 1); + if (result < 0) { + throw std::runtime_error("snd_seq_event_input_pending() failed"); + } + + return result > 0; +} + +snd_seq_event_t* MIDI::read(void) +{ + snd_seq_event_t *ev = NULL; + if (0 > snd_seq_event_input(seq_handle, &ev)) + { + std::cerr << "snd_seq_event_input(): -EAGAIN" << std::endl; + } + + return ev; +} + +namespace { +uint64_t timestamp_from_event(const snd_seq_event_t *ev) +{ + return ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick; +} +} + +// returns if click starts +void MIDI::process(snd_seq_event_t *ev) +{ + uint64_t timestamp = timestamp_from_event(ev); // original timestamp + + // TODO: fix timestamp to be set automatically + struct timespec t; + if (TIME_UTC != timespec_get(&t, TIME_UTC)) + { + log_cout << "Error: timespec_get()" << std::endl; + } + else + { + ev->time.time.tv_sec = t.tv_sec; + ev->time.time.tv_nsec = t.tv_nsec; + } + + if ((ev->type == SND_SEQ_EVENT_NOTEON) ||(ev->type == SND_SEQ_EVENT_NOTEOFF)) { + const char *type = (ev->type == SND_SEQ_EVENT_NOTEON) ? "on " : "off"; + debug_cout << fmt::format("[{}] Note {}: {:2x} vel({:2x}), ch {:2x}\n", timestamp, + type, + ev->data.note.note, + ev->data.note.velocity, + ev->data.control.channel); + if (ev->type == SND_SEQ_EVENT_NOTEON) { + signal_note(ev->data.control.channel, ev->data.note.note, timestamp); + } + } + else if (ev->type == SND_SEQ_EVENT_CONTROLLER) + { + debug_cout << fmt::format("[{}] Control: {:2x} val({:2x})\n", timestamp_from_event(ev), + ev->data.control.param, + ev->data.control.value); + } + else if (ev->type == SND_SEQ_EVENT_SENSING) + { + signal_active_sensing(); + debug_cout << fmt::format("[{}] Active Sensing\n", timestamp_from_event(ev)) << std::endl; + } + else if (ev->type == SND_SEQ_EVENT_CLOCK) + { + signal_clock(); + debug_cout << fmt::format("[{}] Clock\n", timestamp_from_event(ev)) << std::endl; + } + else + { + log_cout << fmt::format("[{}] Unknown MIDI event: {}\n", timestamp_from_event(ev), ev->type) << std::endl; + } +} + +void MIDI::flush() +{ + while (event_ready()) { + (void) read(); + } +} + +void MIDI::wait_for_event() +{ + int result = poll(pfd, npfd, 10000); + if (result > 0) + { + // event + } + else if (result == 0) { + // timeout + } else { + throw std::runtime_error("Poll unsuccessful"); + } +} + @@ -13,178 +13,25 @@ class MIDI { public: - MIDI() - { - if (0 > snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK)) - { - throw std::runtime_error("MIDI sequencer couldn't be opened"); - } - - if (0 > snd_seq_set_client_name(seq_handle, "Midi Listener")) - { - throw std::runtime_error("MIDI client name couldn't be set"); - } - - if (0 > ((in_port = snd_seq_create_simple_port(seq_handle, "24:0", - SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, - SND_SEQ_PORT_TYPE_APPLICATION)))) - { - throw std::runtime_error("MIDI port couldn't be opened"); - } - - debug_cout << "in_port: " << std::to_string(in_port) << std::endl; - -#if 1 - snd_seq_addr_t sender, dest; - snd_seq_port_subscribe_t *subs; - sender.client = 24; - sender.port = 0; - dest.client = snd_seq_client_id(seq_handle); - dest.port = in_port; - snd_seq_port_subscribe_alloca(&subs); - snd_seq_port_subscribe_set_sender(subs, &sender); - snd_seq_port_subscribe_set_dest(subs, &dest); - snd_seq_port_subscribe_set_queue(subs, 1); - snd_seq_port_subscribe_set_time_update(subs, 1); - snd_seq_port_subscribe_set_time_real(subs, 1); - // TODO: fix timestamp (currently always 0) - if (0 > snd_seq_subscribe_port(seq_handle, subs)) -#endif - //if (0 > snd_seq_connect_from(seq_handle, in_port, 24, 0)) - { - throw std::runtime_error("MIDI port couldn't be connected"); - } - - npfd = snd_seq_poll_descriptors_count(seq_handle, POLLIN); - if (npfd < 0) { - throw std::runtime_error("snd_seq_poll_descriptors_count() failed"); - } - pfd = (struct pollfd *)malloc(npfd * sizeof(struct pollfd)); - if (pfd == nullptr) { - throw std::runtime_error("alloca() error for MIDI"); - } - if (0 > snd_seq_poll_descriptors(seq_handle, pfd, npfd, POLLIN)) - { - throw std::runtime_error("snd_seq_poll_descriptors() failure"); - } - - if (npfd != 1) { - std::cout << "Warning: " << std::to_string(npfd) << " poll fds for MIDI" << std::endl; - } else if (fd() <= 2) { - std::cout << "Warning: Bad MIDI fd: " << std::to_string(fd()) << std::endl; - } - } - - ~MIDI() - { - free(pfd); - } + MIDI(); + ~MIDI(); boost::signals2::signal<void(int, int, uint64_t)> signal_note; boost::signals2::signal<void()> signal_active_sensing; boost::signals2::signal<void()> signal_clock; - int fd() - { - return pfd->fd; - } - - bool event_ready() - { - int result = snd_seq_event_input_pending(seq_handle, 1); - if (result < 0) { - throw std::runtime_error("snd_seq_event_input_pending() failed"); - } - - return result > 0; - } + int fd(); - snd_seq_event_t *read(void) - { - snd_seq_event_t *ev = NULL; - if (0 > snd_seq_event_input(seq_handle, &ev)) - { - std::cerr << "snd_seq_event_input(): -EAGAIN" << std::endl; - } + bool event_ready(); - return ev; - } - - uint64_t timestamp_from_event(const snd_seq_event_t *ev) - { - return ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick; - } + snd_seq_event_t *read(void); // returns if click starts - void process(snd_seq_event_t *ev) - { - uint64_t timestamp = timestamp_from_event(ev); // original timestamp - - // TODO: fix timestamp to be set automatically - struct timespec t; - if (TIME_UTC != timespec_get(&t, TIME_UTC)) - { - log_cout << "Error: timespec_get()" << std::endl; - } - else - { - ev->time.time.tv_sec = t.tv_sec; - ev->time.time.tv_nsec = t.tv_nsec; - } - - if ((ev->type == SND_SEQ_EVENT_NOTEON) ||(ev->type == SND_SEQ_EVENT_NOTEOFF)) { - const char *type = (ev->type == SND_SEQ_EVENT_NOTEON) ? "on " : "off"; - debug_cout << fmt::format("[{}] Note {}: {:2x} vel({:2x}), ch {:2x}\n", timestamp, - type, - ev->data.note.note, - ev->data.note.velocity, - ev->data.control.channel); - if (ev->type == SND_SEQ_EVENT_NOTEON) { - signal_note(ev->data.control.channel, ev->data.note.note, timestamp); - } - } - else if (ev->type == SND_SEQ_EVENT_CONTROLLER) - { - debug_cout << fmt::format("[{}] Control: {:2x} val({:2x})\n", timestamp_from_event(ev), - ev->data.control.param, - ev->data.control.value); - } - else if (ev->type == SND_SEQ_EVENT_SENSING) - { - signal_active_sensing(); - debug_cout << fmt::format("[{}] Active Sensing\n", timestamp_from_event(ev)) << std::endl; - } - else if (ev->type == SND_SEQ_EVENT_CLOCK) - { - signal_clock(); - debug_cout << fmt::format("[{}] Clock\n", timestamp_from_event(ev)) << std::endl; - } - else - { - log_cout << fmt::format("[{}] Unknown MIDI event: {}\n", timestamp_from_event(ev), ev->type) << std::endl; - } - } + void process(snd_seq_event_t *ev); - void flush() - { - while (event_ready()) { - (void) read(); - } - } + void flush(); - void wait_for_event() - { - int result = poll(pfd, npfd, 10000); - if (result > 0) - { - // event - } - else if (result == 0) { - // timeout - } else { - throw std::runtime_error("Poll unsuccessful"); - } - } + void wait_for_event(); private: snd_seq_t *seq_handle; @@ -1 +1,150 @@ #include "PCM.h" + +PCM::PCM(): m_phase(1000000) +{ + // prepare the sample + std::string data_s = Reichwein::File::getFile("media/click.s16le"); + m_data.resize(data_s.size() / 2); // src is in bytes + memcpy(m_data.data(), data_s.data(), data_s.size()); + + // non-blocking + if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { + throw std::runtime_error(fmt::format("Playback open error: {}", snd_strerror(err))); + } + if ((err = snd_pcm_set_params(handle, + SND_PCM_FORMAT_S16_LE, + SND_PCM_ACCESS_RW_INTERLEAVED, + 1, + f_sample, + 1, + pcm_latency_us)) < 0) { // latency in us + throw std::runtime_error(fmt::format("Playback open error: {}", snd_strerror(err))); + } + + generate(); + + npfd = snd_pcm_poll_descriptors_count(handle); + if (npfd < 0) { + throw std::runtime_error("snd_pcm_poll_descriptors_count() failed"); + } + pfd = (struct pollfd *)malloc(npfd * sizeof(struct pollfd)); + if (pfd == nullptr) { + throw std::runtime_error("alloca() error for PCM"); + } + if (0 > snd_pcm_poll_descriptors(handle, pfd, npfd)) + { + throw std::runtime_error("snd_pcm_poll_descriptors() failure"); + } + + if (0 > snd_pcm_start(handle)) + { + throw std::runtime_error("PCM could not be started"); + } + + if (npfd != 1) { + std::cout << "Warning: " << std::to_string(npfd) << " poll fds for pcm" << std::endl; + } else if (fd() <= 2) { + std::cout << "Warning: Bad PCM fd: " << std::to_string(fd()) << std::endl; + } +} + +PCM::~PCM() +{ + // pass the remaining samples, otherwise they're dropped in close + err = snd_pcm_drain(handle); + if (err < 0) + std::cerr << fmt::format("snd_pcm_drain failed: {}", snd_strerror(err)) << std::endl; + snd_pcm_close(handle); + + free(pfd); +} + +void PCM::click() +{ + snd_pcm_sframes_t delay; + if (0 > snd_pcm_delay(handle, &delay)) { + } + + m_phase = - click_latency_frames + delay; +} + +// generate 1 buffer size +void PCM::generate() +{ + int i; + + for (i = 0; i < nframes; i++) { + if (m_phase < 0 || m_phase >= m_data.size()) + { + buffer[i] = 0; + } + else + { + buffer[i] = m_data[m_phase]; + } + m_phase++; + } +} + +int PCM::fd() +{ + return pfd->fd; +} + +// write from buffer to ALSA PCM +void PCM::write() +{ + snd_pcm_sframes_t written = snd_pcm_writei(handle, buffer, nframes); + if (written < 0) { + if (written == -EPIPE) { + std::cout << "Warning: PCM underrun" << std::endl; + } + std::cout << "Recovering." << std::endl; + written = snd_pcm_recover(handle, written, 0); + } + if (written < 0) { + throw std::runtime_error("snd_pcm_writei failed: "s + snd_strerror(written)); + } + + if (written != nframes) { + std::cout << "Warning: written " << std::to_string(written) << " frames instead of "<< std::to_string(nframes) << std::endl; + } + + snd_pcm_sframes_t avail; + snd_pcm_sframes_t delay; + if (0 > snd_pcm_avail_delay(handle, &avail, &delay)) { + log_cout << "Error detecting avail and delay" << std::endl; + } else { + debug_cout << fmt::format("Delay: {}, avail. buffer; {} frames", delay, avail) << std::endl; + } + + generate(); +} + +bool PCM::wait_for_event() +{ + int result = poll(pfd, npfd, 10000); + if (result > 0) + { + // event + return true; + } + else if (result == 0) { + // timeout + return false; + } else { + throw std::runtime_error("Poll unsuccessful"); + } +} + +bool PCM::write_available() +{ + snd_pcm_sframes_t result = snd_pcm_avail(handle); + if (0 > result) { + std::cerr << "Error: snd_pcm_avail()" << std::endl; + //throw std::runtime_error("snd_pcm_avail()"); + } + + return result >= nframes; +} + @@ -18,153 +18,22 @@ using namespace std::string_literals; class PCM { public: - PCM(): m_phase(1000000) - { - // prepare the sample - std::string data_s = Reichwein::File::getFile("media/click.s16le"); - m_data.resize(data_s.size() / 2); // src is in bytes - memcpy(m_data.data(), data_s.data(), data_s.size()); - - // non-blocking - if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { - throw std::runtime_error(fmt::format("Playback open error: {}", snd_strerror(err))); - } - if ((err = snd_pcm_set_params(handle, - SND_PCM_FORMAT_S16_LE, - SND_PCM_ACCESS_RW_INTERLEAVED, - 1, - f_sample, - 1, - pcm_latency_us)) < 0) { // latency in us - throw std::runtime_error(fmt::format("Playback open error: {}", snd_strerror(err))); - } - - generate(); - - npfd = snd_pcm_poll_descriptors_count(handle); - if (npfd < 0) { - throw std::runtime_error("snd_pcm_poll_descriptors_count() failed"); - } - pfd = (struct pollfd *)malloc(npfd * sizeof(struct pollfd)); - if (pfd == nullptr) { - throw std::runtime_error("alloca() error for PCM"); - } - if (0 > snd_pcm_poll_descriptors(handle, pfd, npfd)) - { - throw std::runtime_error("snd_pcm_poll_descriptors() failure"); - } - - if (0 > snd_pcm_start(handle)) - { - throw std::runtime_error("PCM could not be started"); - } - - if (npfd != 1) { - std::cout << "Warning: " << std::to_string(npfd) << " poll fds for pcm" << std::endl; - } else if (fd() <= 2) { - std::cout << "Warning: Bad PCM fd: " << std::to_string(fd()) << std::endl; - } - } - - ~PCM() - { - // pass the remaining samples, otherwise they're dropped in close - err = snd_pcm_drain(handle); - if (err < 0) - std::cerr << fmt::format("snd_pcm_drain failed: {}", snd_strerror(err)) << std::endl; - snd_pcm_close(handle); - - free(pfd); - } - - void click() - { - snd_pcm_sframes_t delay; - if (0 > snd_pcm_delay(handle, &delay)) { - } - - m_phase = - click_latency_frames + delay; - } + PCM(); + ~PCM(); + + void click(); // generate 1 buffer size - void generate() - { - int i; - - for (i = 0; i < nframes; i++) { - if (m_phase < 0 || m_phase >= m_data.size()) - { - buffer[i] = 0; - } - else - { - buffer[i] = m_data[m_phase]; - } - m_phase++; - } - } - - int fd() - { - return pfd->fd; - } + void generate(); + + int fd(); // write from buffer to ALSA PCM - void write() - { - snd_pcm_sframes_t written = snd_pcm_writei(handle, buffer, nframes); - if (written < 0) { - if (written == -EPIPE) { - std::cout << "Warning: PCM underrun" << std::endl; - } - std::cout << "Recovering." << std::endl; - written = snd_pcm_recover(handle, written, 0); - } - if (written < 0) { - throw std::runtime_error("snd_pcm_writei failed: "s + snd_strerror(written)); - } - - if (written != nframes) { - std::cout << "Warning: written " << std::to_string(written) << " frames instead of "<< std::to_string(nframes) << std::endl; - } - - snd_pcm_sframes_t avail; - snd_pcm_sframes_t delay; - if (0 > snd_pcm_avail_delay(handle, &avail, &delay)) { - log_cout << "Error detecting avail and delay" << std::endl; - } else { - debug_cout << fmt::format("Delay: {}, avail. buffer; {} frames", delay, avail) << std::endl; - } - - generate(); - } - - bool wait_for_event() - { - int result = poll(pfd, npfd, 10000); - if (result > 0) - { - // event - return true; - } - else if (result == 0) { - // timeout - return false; - } else { - throw std::runtime_error("Poll unsuccessful"); - } - } - - bool write_available() - { - snd_pcm_sframes_t result = snd_pcm_avail(handle); - if (0 > result) { - std::cerr << "Error: snd_pcm_avail()" << std::endl; - //throw std::runtime_error("snd_pcm_avail()"); - } - - return result >= nframes; - } + void write(); + + bool wait_for_event(); + + bool write_available(); private: int err; @@ -1 +1,38 @@ #include "Timer.h" + +Timer::Timer(std::chrono::milliseconds interval, bool cyclic) : + m_start_time(clock_type::now()), + m_interval(interval), + m_running(false), + m_cyclic(cyclic) +{ +} + +void Timer::start() +{ + m_running = true; + m_start_time = clock_type::now(); +} + +void Timer::stop() +{ + m_running = false; +} + +bool Timer::is_elapsed() const +{ + return m_start_time + m_interval < clock_type::now(); +} + +void Timer::update() +{ + if (m_running && is_elapsed()) { + elapsed(); + if (m_cyclic) { + start(); + } else { + stop(); + } + } +} + @@ -11,39 +11,18 @@ using clock_type = std::chrono::high_resolution_clock; class Timer { public: - Timer(std::chrono::milliseconds interval, bool cyclic) : m_start_time(clock_type::now()), m_interval(interval), m_running(false), m_cyclic(cyclic) - {} + Timer(std::chrono::milliseconds interval, bool cyclic); // connect to this signal boost::signals2::signal<void()> elapsed; - void start() - { - m_running = true; - m_start_time = clock_type::now(); - } - - void stop() - { - m_running = false; - } - - bool is_elapsed() - { - return m_start_time + m_interval < clock_type::now(); - } - - void update() - { - if (m_running && is_elapsed()) { - elapsed(); - if (m_cyclic) { - start(); - } else { - stop(); - } - } - } + void start(); + + void stop(); + + bool is_elapsed() const; + + void update(); private: std::chrono::time_point<clock_type> m_start_time; @@ -1,3 +1,23 @@ #include "debug.h" debug_ostream debug_cout; + +debug_ostream::debug_ostream(): m_active(false) {} + +void debug_ostream::activate() +{ + m_active = true; +} + +void debug_ostream::deactivate() +{ + m_active = false; +} + +debug_ostream& debug_ostream::operator<<( + std::basic_ostream<char>& (*func) + (std::basic_ostream<char>&) ) { + if (m_active) + std::cout << *func; + return *this; +} @@ -6,17 +6,11 @@ class debug_ostream { public: - debug_ostream(): m_active(false) {} + debug_ostream(); - void activate() - { - m_active = true; - } + void activate(); - void deactivate() - { - m_active = false; - } + void deactivate(); template<typename T> debug_ostream& operator<<(const T& arg) { @@ -27,11 +21,7 @@ public: debug_ostream& operator<<( std::basic_ostream<char>& (*func) - (std::basic_ostream<char>&) ) { - if (m_active) - std::cout << *func; - return *this; - } + (std::basic_ostream<char>&) ); private: bool m_active; @@ -2,3 +2,56 @@ log_stream log_cout; +log_stream::log_stream(): m_active(false), m_buffer(), m_log_lines() {} + +void log_stream::log_lines(int n) { + m_log_lines = n; +} + +std::string log_stream::get_log() { + return m_buffer.str(); +} + +// log to buffer +void log_stream::activate() +{ + m_active = true; +} + +// log to plain console +void log_stream::deactivate() +{ + m_active = false; +} + +log_stream& log_stream::operator<<( + std::basic_ostream<char>& (*func) + (std::basic_ostream<char>&) ) { + if (m_active) { + m_buffer << *func; + trim_buffer(); + } + else + { + std::cout << *func; + } + return *this; +} + +void log_stream::trim_buffer() +{ + std::string s = m_buffer.str(); + size_t pos = s.npos; + for (int i = 0; i <= m_log_lines; ++i) { + pos = s.rfind("\n", pos); + if (pos == s.npos) { + // too few lines + return; + } + if (pos > 0) { + --pos; + } + } + + m_buffer.str(s.substr((pos <= (s.size() - 2)) ? pos + 2 : pos)); +} @@ -8,27 +8,17 @@ class log_stream { public: - log_stream(): m_active(false), m_buffer(), m_log_lines() {} + log_stream(); - void log_lines(int n) { - m_log_lines = n; - } + void log_lines(int n); - std::string get_log() { - return m_buffer.str(); - } + std::string get_log(); // log to buffer - void activate() - { - m_active = true; - } + void activate(); // log to plain console - void deactivate() - { - m_active = false; - } + void deactivate(); template<typename T> log_stream& operator<<(const T& arg) { @@ -45,36 +35,10 @@ public: log_stream& operator<<( std::basic_ostream<char>& (*func) - (std::basic_ostream<char>&) ) { - if (m_active) { - m_buffer << *func; - trim_buffer(); - } - else - { - std::cout << *func; - } - return *this; - } + (std::basic_ostream<char>&) ); private: - void trim_buffer() - { - std::string s = m_buffer.str(); - size_t pos = s.npos; - for (int i = 0; i <= m_log_lines; ++i) { - pos = s.rfind("\n", pos); - if (pos == s.npos) { - // too few lines - return; - } - if (pos > 0) { - --pos; - } - } - - m_buffer.str(s.substr((pos <= (s.size() - 2)) ? pos + 2 : pos)); - } + void trim_buffer(); bool m_active; std::stringstream m_buffer; |