fixed player timing on seek/stop/play
This commit is contained in:
parent
98546885fa
commit
45b54322d9
9
main.cpp
9
main.cpp
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -85,7 +85,7 @@ enum {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MaxBuses = 32,
|
MaxTracks = 32,
|
||||||
MaxChannels = 256,
|
MaxChannels = 256,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
286
player.cpp
286
player.cpp
|
@ -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;
|
||||||
|
|
79
player.hpp
79
player.hpp
|
@ -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;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue