diff --git a/CHANGELOG.md b/CHANGELOG.md
index cef4809..f86b599 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@ Notable changes will be documented here. This project strives for [Semantic Vers
- Bug fix for issue #11, which spews out a bunch of unnecessary warnings.
- README now links to the wiki.
- Fix for issue #15: higher notes are now at the top of the plotting area.
+- Functionality to remove notes more quickly. You can now drag across a row with grid cells set to turn them off.
+- No longer rely on installation of MIDI-Perl. It is now included in the source directory under lib/.
## 0.01 - 2016-03-25
### Added
diff --git a/SeekMIDI.pl b/SeekMIDI.pl
index c5d4fcb..ec71dd5 100644
--- a/SeekMIDI.pl
+++ b/SeekMIDI.pl
@@ -30,7 +30,7 @@ package Gtk2::MIDIPlot;
# makes a class-global array that holds true/false values for which note blocks are enabled, and the global drawing area
my @gtkObjects;
my $this;
-my ($dragRow, $dragStart) = (-1, -1);
+my ($dragRow, $dragStart, $dragMode) = (-1, -1, -1);
# sets up the class; asks for the signals we need; sets main widget size
sub new {
@@ -115,15 +115,19 @@ sub button {
# if the left mouse button then invert this gridbox's state value
if ($event->button == 1) {
my ($xind, $yind) = (($event->x - ($event->x % 12)) / 12, ($event->y - ($event->y % 8)) / 8);
- $gtkObjects[$xind][$yind] = !$gtkObjects[$xind][$yind];
if($gtkObjects[$xind][$yind] == 0) {
- expose($this);
- } else {
+ $gtkObjects[$xind][$yind] = 1;
+
# makes new Cairo context
my $thisCairo = Gtk2::Gdk::Cairo::Context->create($this->get_window());
$thisCairo->rectangle($xind * 12, $yind * 8, 12, 8);
$thisCairo->fill();
+ $dragMode = 1;
+ } else {
+ $gtkObjects[$xind][$yind] = 0;
+ $dragMode = 0;
+ expose($this);
}
# initialize drag variables
@@ -143,11 +147,11 @@ sub motion {
my ($xind, $yind) = (($event->x - ($event->x % 12)) / 12, ($event->y - ($event->y % 8)) / 8);
# check if the underlying cell is set or not and if not, check which mouse button is pressed, then draw and set $gtkObjects
- if($gtkObjects[$xind][$dragRow] == 0) {
- if(grep('button1-mask', $event->state)) {
- # makes new Cairo context
- my $thisCairo = Gtk2::Gdk::Cairo::Context->create($this->get_window());
+ if(grep('button1-mask', $event->state)) {
+ # makes new Cairo context
+ my $thisCairo = Gtk2::Gdk::Cairo::Context->create($this->get_window());
+ if($dragMode == 1) {
# checks whether our overall drag is to the left or right and draws rectangles and updates $gtkObjects accordingly
if($xind >= $dragStart) {
$thisCairo->rectangle($dragStart * 12, $dragRow * 8, ($xind - $dragStart + 1) * 12, 8);
@@ -160,14 +164,27 @@ sub motion {
$gtkObjects[$inc][$dragRow] = 1;
}
}
- $thisCairo->fill();
+ } else {
+ # checks whether our overall drag is to the left or right and updates $gtkObjects accordingly
+ if($xind >= $dragStart) {
+ for(my $inc = $dragStart; $inc <= $xind; $inc++) {
+ $gtkObjects[$inc][$dragRow] = 0;
+ }
+ expose($this);
+ } else {
+ for(my $inc = $xind; $inc <= $dragStart; $inc++) {
+ $gtkObjects[$inc][$dragRow] = 0;
+ }
+ expose($this);
+ }
}
+ $thisCairo->fill();
}
}
-# clears the current drag row and start point when the drag is ended
+# clears the current drag row, start point, and drag mode when the drag is ended
sub release {
- ($dragRow, $dragStart) = (-1, -1);
+ ($dragRow, $dragStart, $dragMode) = (-1, -1, -1);
}
sub getMIDI {
@@ -200,6 +217,7 @@ sub getMIDI {
package main;
+use lib './lib/';
use MIDI;
use Gtk2 -init;
# use Locale::gettext;
diff --git a/lib/MIDI.pm b/lib/MIDI.pm
new file mode 100644
index 0000000..deb4287
--- /dev/null
+++ b/lib/MIDI.pm
@@ -0,0 +1,425 @@
+
+# Time-stamp: "2010-02-14 21:39:10 conklin"
+require 5;
+package MIDI;
+use strict;
+use vars qw($Debug $VERSION %number2note %note2number %number2patch
+ %patch2number %notenum2percussion %percussion2notenum);
+use MIDI::Opus;
+use MIDI::Track;
+use MIDI::Event;
+use MIDI::Score;
+
+# Doesn't use MIDI::Simple -- but MIDI::Simple uses this
+
+$Debug = 0; # currently doesn't do anything
+$VERSION = '0.83';
+
+# MIDI.pm doesn't do much other than 1) 'use' all the necessary submodules
+# 2) provide some publicly useful hashes, 3) house a few private routines
+# common to the MIDI::* modules, and 4) contain POD, glorious POD.
+
+=head1 NAME
+
+MIDI - read, compose, modify, and write MIDI files
+
+=head1 SYNOPSIS
+
+ use MIDI;
+ use strict;
+ use warnings;
+ my @events = (
+ ['text_event',0, 'MORE COWBELL'],
+ ['set_tempo', 0, 450_000], # 1qn = .45 seconds
+ );
+
+ for (1 .. 20) {
+ push @events,
+ ['note_on' , 90, 9, 56, 127],
+ ['note_off', 6, 9, 56, 127],
+ ;
+ }
+ foreach my $delay (reverse(1..96)) {
+ push @events,
+ ['note_on' , 0, 9, 56, 127],
+ ['note_off', $delay, 9, 56, 127],
+ ;
+ }
+
+ my $cowbell_track = MIDI::Track->new({ 'events' => \@events });
+ my $opus = MIDI::Opus->new(
+ { 'format' => 0, 'ticks' => 96, 'tracks' => [ $cowbell_track ] } );
+ $opus->write_to_file( 'cowbell.mid' );
+
+
+=head1 DESCRIPTION
+
+This suite of modules provides routines for reading, composing, modifying,
+and writing MIDI files.
+
+From FOLDOC (C):
+
+=over
+
+B
+
+Emultimedia, file formatE (MIDI /mi'-dee/, /mee'-dee/) A
+hardware specification and protocol used to communicate note and
+effect information between synthesisers, computers, music keyboards,
+controllers and other electronic music devices. [...]
+
+The basic unit of information is a "note on/off" event which includes
+a note number (pitch) and key velocity (loudness). There are many
+other message types for events such as pitch bend, patch changes and
+synthesizer-specific events for loading new patches etc.
+
+There is a file format for expressing MIDI data which is like a dump
+of data sent over a MIDI port. [...]
+
+=back
+
+=head1 COMPONENTS
+
+The MIDI-Perl suite consists of these modules:
+
+L (which you're looking at), L, L,
+L, L, and
+L. All of these contain documentation in pod format.
+You should read all of these pods.
+
+The order you want to read them in will depend on what you want to do
+with this suite of modules: if you are focused on manipulating the
+guts of existing MIDI files, read the pods in the order given above.
+
+But if you aim to compose music with this suite, read this pod, then
+L and L, and then skim the rest.
+
+
+=head1 INTRODUCTION
+
+This suite of modules is basically object-oriented, with the exception
+of MIDI::Simple. MIDI opuses ("songs") are represented as objects
+belonging to the class MIDI::Opus. An opus contains tracks, which are
+objects belonging to the class MIDI::Track. A track will generally
+contain a list of events, where each event is a list consisting of a
+command, a delta-time, and some number of parameters. In other words,
+opuses and tracks are objects, and the events in a track comprise a
+LoL (and if you don't know what an LoL is, you must read L).
+
+Furthermore, for some purposes it's useful to analyze the totality of
+a track's events as a "score" -- where a score consists of notes where
+each event is a list consisting of a command, a time offset from the
+start of the track, and some number of parameters. This is the level
+of abstraction that MIDI::Score and MIDI::Simple deal with.
+
+While this suite does provide some functionality accessible only if
+you're comfortable with various kinds of references, and while there
+are some options that deal with the guts of MIDI encoding, you can (I
+hope) get along just fine with just a basic grasp of the MIDI
+"standard", and a command of LoLs. I have tried, at various points in
+this documentation, to point out what things are not likely to be of
+use to the casual user.
+
+=head1 GOODIES
+
+The bare module MIDI.pm doesn't I much more than C