From 4ba9b7cc26694b0250df9b36cb523d36792f75a4 Mon Sep 17 00:00:00 2001 From: petern Date: Wed, 22 Jul 2009 07:24:44 +0000 Subject: [PATCH] -Add: Curses UI. Beware of coding style... git-svn-id: http://svn.fuzzle.org/mloop/mloop/trunk@8 ba049829-c6ef-42ef-81ac-908dd8d2e907 --- src/jack.cpp | 6 ++-- src/jack.h | 15 ++++++++ src/loop.cpp | 8 ++--- src/loop.h | 21 +++++++++++ src/ui.cpp | 99 +++++++++++++++++++++++++++++++++++++++++++++++----- src/wscript | 3 +- 6 files changed, 135 insertions(+), 17 deletions(-) diff --git a/src/jack.cpp b/src/jack.cpp index 07adf96..9693ff1 100644 --- a/src/jack.cpp +++ b/src/jack.cpp @@ -1,6 +1,5 @@ /* $Id$ */ -#include #include #include #include @@ -108,7 +107,7 @@ int Jack::ProcessCallback(jack_nframes_t nframes) m_buffer->Write((uint8_t *)&ev.size, sizeof ev.size); m_buffer->Write((uint8_t *)ev.buffer, ev.size); } else { - printf("Buffer full, dropping input!\n"); + fprintf(stderr, "Buffer full, dropping input!\n"); } } @@ -126,6 +125,7 @@ int Jack::ProcessCallback(jack_nframes_t nframes) if (m_recording) { m_recording_time += nframes; + m_loops[m_recording_loop].SetLength(m_recording_time); } return 0; @@ -136,9 +136,11 @@ void Jack::ToggleRecording(int loop) if (m_recording) { m_recording = false; m_loops[m_recording_loop].SetLength(m_recording_time); + m_loops[m_recording_loop].SetState(LS_IDLE); m_loops[m_recording_loop].EndFromNoteCache(m_notecache); } else { m_recording_loop = loop; + m_loops[m_recording_loop].SetState(LS_RECORDING); m_loops[m_recording_loop].StartFromNoteCache(m_notecache); m_recording_time = 0; m_recording = true; diff --git a/src/jack.h b/src/jack.h index e5bed0c..376941a 100644 --- a/src/jack.h +++ b/src/jack.h @@ -58,6 +58,21 @@ public: { return m_recording; } + + float LoopLength(int loop) + { + return (float)m_loops[loop].Length() / m_sample_rate; + } + + float LoopPosition(int loop) + { + return (float)m_loops[loop].Position() / m_sample_rate; + } + + LoopState GetLoopState(int loop) + { + return m_loops[loop].State(); + } }; #endif /* JACK_H */ diff --git a/src/loop.cpp b/src/loop.cpp index 7f00a40..9951aea 100644 --- a/src/loop.cpp +++ b/src/loop.cpp @@ -1,6 +1,5 @@ /* $Id$ */ -#include #include "loop.h" Loop::Loop() @@ -16,11 +15,9 @@ Loop::~Loop() void Loop::PlayFrame(void *port_buffer, jack_nframes_t frame) { - if (m_state == LS_IDLE) return; + if (m_state == LS_IDLE || m_state == LS_RECORDING) return; if (m_state == LS_STOPPING) { - printf("Stopping, so send all notes off!\n"); - uint8_t buffer[3]; buffer[1] = 0x78; buffer[2] = 0; @@ -49,7 +46,6 @@ void Loop::PlayFrame(void *port_buffer, jack_nframes_t frame) if (m_state == LS_PLAY_ONCE) { m_state = LS_IDLE; } - printf("Completed %u frames\n", m_position); m_position = 0; m_iterator = m_events.begin(); } @@ -65,7 +61,7 @@ void Loop::AddEvent(jack_nframes_t position, jack_midi_event_t *event) void Loop::SetLength(jack_nframes_t length) { - if (m_state != LS_IDLE) return; + if (m_state != LS_RECORDING) return; m_length = length; } diff --git a/src/loop.h b/src/loop.h index b9a8022..84dcfb6 100644 --- a/src/loop.h +++ b/src/loop.h @@ -14,6 +14,7 @@ enum LoopState { LS_PLAY_LOOP, LS_PLAY_ONCE, LS_STOPPING, + LS_RECORDING, }; typedef std::pair Event; @@ -35,6 +36,11 @@ public: void PlayFrame(void *port_buffer, jack_nframes_t frame); void AddEvent(jack_nframes_t position, jack_midi_event_t *event); + void SetState(LoopState state) + { + m_state = state; + } + void SetLength(jack_nframes_t length); void Start(bool loop); void Stop(); @@ -42,6 +48,21 @@ public: void StartFromNoteCache(NoteCache &cache); void EndFromNoteCache(NoteCache &cache); + + LoopState State() const + { + return m_state; + } + + jack_nframes_t Length() const + { + return m_length; + } + + jack_nframes_t Position() const + { + return m_position; + } }; #endif /* LOOP_H */ diff --git a/src/ui.cpp b/src/ui.cpp index 54b6366..d4b90c1 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -3,9 +3,11 @@ #include #include #include +#include #include #include #include "jack.h" +#include "loop.h" #include "ui.h" struct termios orig_termios; @@ -21,12 +23,14 @@ void set_conio_terminal_mode() /* take two copies - one for now, one for later */ tcgetattr(0, &orig_termios); +#if 0 memcpy(&new_termios, &orig_termios, sizeof(new_termios)); /* register cleanup handler, and set the new terminal mode */ atexit(reset_terminal_mode); cfmakeraw(&new_termios); tcsetattr(0, TCSANOW, &new_termios); +#endif } int kbhit() @@ -37,6 +41,7 @@ int kbhit() return select(1, &fds, NULL, NULL, &tv); } +/* int getch() { int r; @@ -47,27 +52,95 @@ int getch() return c; } } +*/ + +int bpm = 120; +int color_map[4]; +char status[1024]; bool UI::Run(Jack &j) { + char buf[1024]; static bool first = true; if (first) { set_conio_terminal_mode(); first = false; m_loop = 0; + + initscr(); + noecho(); + nonl(); + intrflush(stdscr, false); + keypad(stdscr, true); + nodelay(stdscr, true); + raw(); + + start_color(); + init_pair(1, COLOR_WHITE, COLOR_BLUE); + init_pair(2, COLOR_BLACK, COLOR_CYAN); + init_pair(3, COLOR_YELLOW, COLOR_RED); + + for (int i = 0; i < 4; i++) { + color_map[i] = COLOR_PAIR(i); + } + + snprintf(status, sizeof status, "..."); } - if (!kbhit()) return false; + snprintf(buf, sizeof buf, " mloop -- %d bpm", bpm); + bkgdset(color_map[1]); + attrset(color_map[1]); + mvaddstr(0, 0, buf); + clrtoeol(); - char c = getch(); + mvaddstr(11, 0, status); + clrtoeol(); + + for (int i = 0; i < NUM_LOOPS; i++) { + bkgdset(color_map[0]); + attrset(color_map[0]); + + snprintf(buf, sizeof buf, " [ ] %2d: Position: %0.2f beats (%0.2fs) Length: %0.2f beats (%0.2fs)", i, bpm * j.LoopPosition(i) / 60.0, j.LoopPosition(i), bpm * j.LoopLength(i) / 60.0, j.LoopLength(i)); + mvaddstr(i + 1, 0, buf); + clrtoeol(); + + const char *c; int k = 3; + switch (j.GetLoopState(i)) { + case LS_IDLE: c = " "; k = 0; break; + case LS_PLAY_ONCE: c = ">"; break; + case LS_PLAY_LOOP: c = "»"; break; + case LS_STOPPING: c = "·"; break; + case LS_RECORDING: c = "R"; break; + } + + bkgdset(color_map[m_loop == i ? 2 : k]); + attrset(color_map[m_loop == i ? 2 : k]); + + mvaddstr(i + 1, 2, c); + } + + if (!kbhit()) { + refresh(); + return false; + } + + int c = getch(); switch (c) { case 3: - case 'q': return true; + case 'q': { + endwin(); + reset_terminal_mode(); + return true; + } case 'r': j.ToggleRecording(m_loop); - printf("%s recording loop\n", j.Recording() ? "Started" : "Finished"); + if (j.Recording()) { + snprintf(status, sizeof status, "Start recording loop %d", m_loop); + } else { + snprintf(status, sizeof status, "Finished recording loop"); + } break; case '0': @@ -81,24 +154,34 @@ bool UI::Run(Jack &j) case '8': case '9': m_loop = (c - '0'); - printf("Selected loop %d\n", m_loop); + snprintf(status, sizeof status, "Selected loop %d", m_loop); break; case 'z': case 'x': - printf("Starting loop %d (%s)\n", m_loop, c == 'x' ? "loop" : "once"); + snprintf(status, sizeof status, "Starting loop %d (%s)", m_loop, c == 'x' ? "loop" : "once"); j.StartLoop(m_loop, c == 'x'); break; case 'c': - printf("Stopping loop %d\n", m_loop); + snprintf(status, sizeof status, "Stopping loop %d", m_loop); j.StopLoop(m_loop); break; case 'e': - printf("Erasing loop %d\n", m_loop); + snprintf(status, sizeof status, "Erasing loop %d", m_loop); j.EraseLoop(m_loop); break; + + case KEY_UP: + m_loop--; + if (m_loop == -1) m_loop = NUM_LOOPS - 1; + break; + + case KEY_DOWN: + m_loop++; + if (m_loop == NUM_LOOPS) m_loop = 0; + break; } return false; diff --git a/src/wscript b/src/wscript index 09601f5..fc7d9fe 100644 --- a/src/wscript +++ b/src/wscript @@ -3,10 +3,11 @@ def configure(conf): conf.check_cfg(package='jack', uselib_store='JACK', args='--cflags --libs', atleast_version='1.9.2') + conf.env['LIB_NCURSES'] = ['ncurses'] def build(bld): bld.new_task_gen( features = 'cxx cprogram', source = 'jack.cpp loop.cpp mloop.cpp ui.cpp', target = 'mloop', - uselib = 'JACK') + uselib = 'JACK NCURSES')