1
Fork 0

fixed player timing on seek/stop/play

This commit is contained in:
Leonard Ritter 2010-02-09 23:44:06 +01:00
parent 98546885fa
commit 45b54322d9
8 changed files with 291 additions and 149 deletions

View File

@ -105,8 +105,11 @@ public:
void on_stop_action() { void on_stop_action() {
if (!player) if (!player)
return; return;
player->stop(); if (player->is_playing()) {
player->set_position(0); player->stop();
} else {
player->seek(0);
}
} }
void add_dialog_filters(Gtk::FileChooserDialog &dialog) { void add_dialog_filters(Gtk::FileChooserDialog &dialog) {
@ -243,7 +246,7 @@ public:
void on_seek_request(int frame) { void on_seek_request(int frame) {
if (!player) if (!player)
return; return;
player->set_position(frame); player->seek(frame);
} }
void init_track_view() { void init_track_view() {

View File

@ -330,6 +330,9 @@ void Model::reset() {
Pattern &Model::new_pattern() { Pattern &Model::new_pattern() {
Pattern *pattern = new Pattern(); Pattern *pattern = new Pattern();
char text[64];
sprintf(text, "Pattern %i", patterns.size()+1);
pattern->name = text;
pattern->set_length(frames_per_beat * beats_per_bar); pattern->set_length(frames_per_beat * beats_per_bar);
patterns.push_back(pattern); patterns.push_back(pattern);
return *pattern; return *pattern;

View File

@ -85,7 +85,7 @@ enum {
}; };
enum { enum {
MaxBuses = 32, MaxTracks = 32,
MaxChannels = 256, MaxChannels = 256,
}; };

View File

@ -17,6 +17,90 @@ enum {
//============================================================================= //=============================================================================
Message::Message() {
type = TypeEmpty;
timestamp = 0;
frame = 0;
bus = 0;
bus_channel = 0;
}
//=============================================================================
MessageQueue::MessageQueue()
: RingBuffer<Message>(MaxMessageCount) {
write_samples = 0;
position = 0;
read_samples = 0;
}
void MessageQueue::init_message(Message &msg) {
msg.timestamp = write_samples;
msg.frame = position;
}
void MessageQueue::on_cc(int bus, int ccindex, int ccvalue) {
if (ccindex == ValueNone)
return;
if (ccvalue == ValueNone)
return;
Message msg;
init_message(msg);
msg.type = Message::TypeMIDI;
msg.bus = bus;
msg.command = MIDI::CommandControlChange;
msg.channel = 0;
msg.data1 = ccindex;
msg.data2 = ccvalue;
push(msg);
}
void MessageQueue::on_volume(int bus, int channel, int volume) {
if (volume == ValueNone)
return;
Message msg;
init_message(msg);
msg.type = Message::TypeMIDI;
msg.bus = bus;
msg.bus_channel = channel;
msg.command = MIDI::CommandControlChange;
msg.channel = 0;
msg.data1 = CCVolume;
msg.data2 = volume;
push(msg);
}
void MessageQueue::on_note(int bus, int channel, int note) {
if (note == ValueNone)
return;
Message msg;
init_message(msg);
msg.type = Message::TypeMIDI;
msg.bus = bus;
msg.bus_channel = channel;
if (note == NoteOff) {
msg.command = MIDI::CommandNoteOff;
msg.channel = 0;
msg.data1 = 0;
msg.data2 = 0;
} else {
msg.command = MIDI::CommandNoteOn;
msg.channel = 0;
msg.data1 = note;
msg.data2 = 0;
}
push(msg);
}
void MessageQueue::status_msg() {
Message msg;
init_message(msg);
msg.type = Message::TypeEmpty;
push(msg);
}
//=============================================================================
Player::Channel::Channel() { Player::Channel::Channel() {
note = ValueNone; note = ValueNone;
volume = 0x7f; volume = 0x7f;
@ -30,25 +114,25 @@ Player::Bus::Bus() {
//============================================================================= //=============================================================================
Player::Message::Message() { Player::Player() {
timestamp = 0; buses.resize(MaxTracks);
frame = 0;
bus = 0;
bus_channel = 0;
}
Player::Player()
: messages(MaxMessageCount),
rt_messages(128) {
buses.resize(MaxBuses);
model = NULL; model = NULL;
sample_rate = 44100; sample_rate = 44100;
write_samples = 0;
read_samples = 0;
read_frame_block = 0;
position = 0;
read_position = 0; read_position = 0;
playing = false; playing = false;
front_index = 0;
}
MessageQueue &Player::get_back() {
return messages[(front_index+1)%QueueCount];
}
MessageQueue &Player::get_front() {
return messages[front_index];
}
void Player::flip() {
front_index = (front_index+1)%QueueCount;
} }
void Player::set_model(class Model &model) { void Player::set_model(class Model &model) {
@ -63,22 +147,34 @@ void Player::stop() {
if (!playing) if (!playing)
return; return;
playing = false; playing = false;
seek(read_position);
} }
void Player::play() { void Player::play() {
if (playing) if (playing)
return; return;
read_frame_block = 0;
read_samples = 0;
write_samples = 0;
messages.clear();
mix_events(PreMixSize);// fill buffer
read_position = position;
playing = true; playing = true;
seek(read_position);
} }
void Player::set_position(int position) { bool Player::is_playing() const {
this->position = position; return playing;
}
void Player::premix() {
MessageQueue &queue = get_back();
queue.clear();
queue.read_samples = 0;
queue.write_samples = 0;
mix_events(queue, PreMixSize);// fill buffer
}
void Player::seek(int position) {
MessageQueue &queue = get_back();
queue.position = position;
if (playing)
premix();
flip();
} }
int Player::get_position() const { int Player::get_position() const {
@ -94,77 +190,28 @@ long long Player::get_frame_size() {
(model->frames_per_beat * model->beats_per_minute); (model->frames_per_beat * model->beats_per_minute);
} }
void Player::on_cc(int ccindex, int ccvalue) {
if (ccindex == ValueNone)
return;
if (ccvalue == ValueNone)
return;
Message msg;
init_message(msg);
msg.command = MIDI::CommandControlChange;
msg.channel = 0;
msg.data1 = ccindex;
msg.data2 = ccvalue;
messages.push(msg);
}
void Player::on_volume(int channel, int volume) {
if (volume == ValueNone)
return;
Message msg;
init_message(msg);
msg.bus_channel = channel;
msg.command = MIDI::CommandControlChange;
msg.channel = 0;
msg.data1 = CCVolume;
msg.data2 = volume;
messages.push(msg);
}
void Player::on_note(int channel, int note, bool rt/*=false*/) {
if (note == ValueNone)
return;
Message msg;
init_message(msg);
if (note == NoteOff) {
msg.bus_channel = channel;
msg.command = MIDI::CommandNoteOff;
msg.channel = 0;
msg.data1 = 0;
msg.data2 = 0;
} else {
msg.bus_channel = channel;
msg.command = MIDI::CommandNoteOn;
msg.channel = 0;
msg.data1 = note;
msg.data2 = 0;
}
if (rt)
rt_messages.push(msg);
else
messages.push(msg);
}
void Player::play_event(const class PatternEvent &event) { void Player::play_event(const class PatternEvent &event) {
if (event.param != ParamNote) if (event.param != ParamNote)
return; return;
int note = event.value; int note = event.value;
if (note == ValueNone) if (note == ValueNone)
return; return;
on_note(event.channel, note, true); rt_messages.on_note(0, event.channel, note);
} }
void Player::mix_events(int samples) { void Player::mix_events(MessageQueue &queue, int samples) {
assert(model); assert(model);
long long target = read_samples + ((long long)samples<<32); long long target = queue.read_samples + ((long long)samples<<32);
long long framesize = get_frame_size(); long long framesize = get_frame_size();
int mixed = 0; int mixed = 0;
while (write_samples < target) while (queue.write_samples < target)
{ {
mix_frame(); // send status package
position++; queue.status_msg();
write_samples += framesize; mix_frame(queue);
queue.position++;
queue.write_samples += framesize;
mixed++; mixed++;
} }
} }
@ -172,19 +219,14 @@ void Player::mix_events(int samples) {
void Player::mix() { void Player::mix() {
if (!playing) if (!playing)
return; return;
mix_events(PreMixSize); mix_events(get_front(), PreMixSize);
} }
void Player::init_message(Message &msg) { void Player::mix_frame(MessageQueue &queue) {
msg.timestamp = write_samples;
msg.frame = position;
}
void Player::mix_frame() {
assert(model); assert(model);
Song::IterList events; Song::IterList events;
model->song.find_events(position, events); model->song.find_events(queue.position, events);
if (events.empty()) if (events.empty())
return; return;
@ -194,25 +236,32 @@ void Player::mix_frame() {
Pattern &pattern = *event.pattern; Pattern &pattern = *event.pattern;
Pattern::iterator row_iter = pattern.begin(); Pattern::iterator row_iter = pattern.begin();
Pattern::Row row; Pattern::Row row;
pattern.collect_events(position - event.frame, row_iter, row); pattern.collect_events(queue.position - event.frame, row_iter, row);
// first run: process all cc events // first run: process all cc events
for (int channel = 0; channel < pattern.get_channel_count(); ++channel) { for (int channel = 0; channel < pattern.get_channel_count(); ++channel) {
on_cc(row.get_value(channel, ParamCCIndex), queue.on_cc(event.track, row.get_value(channel, ParamCCIndex),
row.get_value(channel, ParamCCValue)); row.get_value(channel, ParamCCValue));
} }
// second run: process volume and notes // second run: process volume and notes
for (int channel = 0; channel < pattern.get_channel_count(); ++channel) { for (int channel = 0; channel < pattern.get_channel_count(); ++channel) {
on_volume(channel, row.get_value(channel, ParamVolume)); queue.on_volume(event.track, channel,
on_note(channel, row.get_value(channel, ParamNote)); row.get_value(channel, ParamVolume));
queue.on_note(event.track, channel,
row.get_value(channel, ParamNote));
} }
} }
} }
void Player::handle_message(Message msg) { void Player::handle_message(Message msg) {
Bus &bus = buses[0]; if (msg.type == Message::TypeEmpty) {
// status package, discard
return;
}
Bus &bus = buses[msg.bus];
Channel &values = bus.channels[msg.bus_channel]; Channel &values = bus.channels[msg.bus_channel];
if (msg.command == MIDI::CommandControlChange) { if (msg.command == MIDI::CommandControlChange) {
@ -252,45 +301,40 @@ void Player::process_messages(int _size) {
Message next_msg; Message next_msg;
Message msg; Message msg;
while (!rt_messages.empty()) {
msg = rt_messages.pop();
msg.timestamp = 0;
handle_message(msg);
}
MessageQueue &queue = get_front();
if (!playing) {
read_position = queue.position;
return;
}
while (size) { while (size) {
bool block_set = false;
while (rt_messages.get_read_size()) {
msg = rt_messages.pop();
msg.timestamp = 0;
handle_message(msg);
}
long long delta = size; long long delta = size;
if (messages.get_read_size()) { if (!queue.empty()) {
next_msg = messages.peek(); next_msg = queue.peek();
delta = std::min( delta = std::min(
next_msg.timestamp - read_samples,size); next_msg.timestamp - queue.read_samples,size);
if (delta < 0) { if (delta < 0) {
// drop // drop
messages.pop(); queue.pop();
delta = 0; delta = 0;
} if (delta < size) { } if (delta < size) {
msg = messages.pop(); msg = queue.pop();
read_position = msg.frame; read_position = msg.frame;
read_frame_block = 0;
block_set = true;
msg.timestamp = offset; msg.timestamp = offset;
handle_message(msg); handle_message(msg);
} }
} }
if (playing) { queue.read_samples += delta;
read_samples += delta;
read_frame_block += delta;
long long framesize = get_frame_size();
while (read_frame_block >= framesize) {
read_position++;
read_frame_block -= framesize;
}
} else {
read_position = position;
}
size -= delta; size -= delta;
offset += delta; offset += delta;

View File

@ -8,8 +8,48 @@ namespace Jacker {
//============================================================================= //=============================================================================
struct Message : MIDI::Message {
enum Type {
// empty, for updating position
TypeEmpty = 0,
// midi package
TypeMIDI = 1,
};
Type type;
long long timestamp;
int frame;
int bus;
int bus_channel;
Message();
};
class MessageQueue : public RingBuffer<Message> {
public:
MessageQueue();
volatile long long write_samples; // 0-32: subsample, 32-64: sample
volatile int position; // in frames
volatile long long read_samples;
void on_note(int bus, int channel, int value);
void on_volume(int bus, int channel, int value);
void on_cc(int bus, int ccindex, int ccvalue);
void status_msg();
void init_message(Message &msg);
};
class Player { class Player {
public: public:
enum {
// how many message queues are used
// for flipping?
QueueCount = 4,
};
struct Channel { struct Channel {
int note; int note;
int volume; int volume;
@ -24,20 +64,10 @@ public:
Bus(); Bus();
}; };
struct Message : MIDI::Message {
long long timestamp;
int frame;
int bus;
int bus_channel;
Message();
};
Player(); Player();
void reset(); void reset();
void mix(); void mix();
void mix_frame();
void process_messages(int size); void process_messages(int size);
virtual void on_message(const Message &msg) {} virtual void on_message(const Message &msg) {}
@ -46,32 +76,33 @@ public:
void stop(); void stop();
void play(); void play();
void set_position(int position); void seek(int position);
int get_position() const; int get_position() const;
bool is_playing() const;
void play_event(const class PatternEvent &event); void play_event(const class PatternEvent &event);
protected: protected:
void mix_events(int samples); void premix();
void init_message(Message &msg); void mix_events(MessageQueue &queue, int samples);
void mix_frame(MessageQueue &queue);
void handle_message(Message msg); void handle_message(Message msg);
void on_note(int channel, int value, bool rt=false);
void on_volume(int channel, int value);
void on_cc(int ccindex, int ccvalue);
long long get_frame_size(); long long get_frame_size();
MessageQueue &get_back();
MessageQueue &get_front();
void flip();
int sample_rate; int sample_rate;
volatile int front_index; // index of messages front buffer
std::vector<Bus> buses; std::vector<Bus> buses;
RingBuffer<Message> messages; MessageQueue messages[QueueCount];
RingBuffer<Message> rt_messages; MessageQueue rt_messages;
class Model *model; class Model *model;
long long write_samples; // 0-32: subsample, 32-64: sample volatile int read_position; // last read position, in frames
long long read_samples; volatile bool playing;
long long read_frame_block;
int position; // in frames
int read_position; // last read position, in frames
bool playing;
}; };

View File

@ -14,10 +14,22 @@ public:
volatile size_t written; volatile size_t written;
volatile size_t read_count; volatile size_t read_count;
RingBuffer() {
buffer.resize(10);
clear();
}
RingBuffer(int size) { RingBuffer(int size) {
buffer.resize(size); buffer.resize(size);
clear(); clear();
} }
void resize(int size) {
buffer.resize(size);
clear();
}
virtual ~RingBuffer() { virtual ~RingBuffer() {
} }

View File

@ -447,10 +447,48 @@ void TrackView::invalidate_select_box() {
} }
void TrackView::select_from_box() { void TrackView::select_from_box() {
int x0,y0,w,h;
drag.get_rect(x0,y0,w,h);
if (w <= 1)
return;
if (h <= 1)
return;
int x1 = x0+w;
int y1 = y0+h;
Song::iterator iter;
for (iter = model->song.begin(); iter != model->song.end(); ++iter) {
int ex0,ey0,ew,eh;
get_event_rect(iter, ex0, ey0, ew, eh);
int ex1 = ex0+ew;
int ey1 = ey0+eh;
if (ex0 >= x1)
continue;
if (ex1 < x0)
continue;
if (ey0 >= y1)
continue;
if (ey1 < y0)
continue;
selection.push_back(iter);
}
}
void TrackView::clone_selection() {
Song::IterList new_selection;
Song::IterList::iterator iter;
for (iter = selection.begin(); iter != selection.end(); ++iter) {
Song::Event event = (*iter)->second;
new_selection.push_back(model->song.add_event(event));
}
selection = new_selection;
} }
bool TrackView::on_motion_notify_event(GdkEventMotion *event) { bool TrackView::on_motion_notify_event(GdkEventMotion *event) {
bool shift_down = event->state & Gdk::SHIFT_MASK;
if (interact_mode == InteractNone) { if (interact_mode == InteractNone) {
TrackCursor cur(*this); TrackCursor cur(*this);
cur.set_pos(event->x, event->y); cur.set_pos(event->x, event->y);
@ -474,6 +512,10 @@ bool TrackView::on_motion_notify_event(GdkEventMotion *event) {
if (find_event(cur, evt) && can_resize_event(evt,drag.start_x)) { if (find_event(cur, evt) && can_resize_event(evt,drag.start_x)) {
interact_mode = InteractResize; interact_mode = InteractResize;
} else { } else {
if (shift_down) {
invalidate_selection();
clone_selection();
}
interact_mode = InteractMove; interact_mode = InteractMove;
} }
} }
@ -549,12 +591,17 @@ void TrackView::apply_resize() {
} }
bool TrackView::on_button_release_event(GdkEventButton* event) { bool TrackView::on_button_release_event(GdkEventButton* event) {
bool ctrl_down = event->state & Gdk::CONTROL_MASK;
if (moving()) { if (moving()) {
apply_move(); apply_move();
} else if (resizing()) { } else if (resizing()) {
apply_resize(); apply_resize();
} else if (selecting()) { } else if (selecting()) {
invalidate_select_box(); invalidate_select_box();
if (!ctrl_down) {
invalidate_selection();
selection.clear();
}
select_from_box(); select_from_box();
} }
interact_mode = InteractNone; interact_mode = InteractNone;

View File

@ -162,6 +162,8 @@ protected:
void update_adjustments(); void update_adjustments();
void on_adjustment_value_changed(); void on_adjustment_value_changed();
void clone_selection();
// zoomlevel (0=1:1, 1=1:2, 2=1:4, etc.) // zoomlevel (0=1:1, 1=1:2, 2=1:4, etc.)
int zoomlevel; int zoomlevel;