#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
+#include <asoundlib.h>
+
#include "score.h"
#define unused(foo) foo __attribute__((unused))
+#define MIDI_BUF_SIZE 4096
+
typedef struct scherzo
{
+ GtkWidget *window;
score_t *score;
int staff_height;
+ score_staff_t *treble;
+ score_staff_t *bass;
+ int midi_fd;
+ snd_midi_event_t *snd_midi_event;
} scherzo_t;
static int
return FALSE;
}
+static void
+_midi_to_score_pitch_and_octave (unsigned char midi_note,
+ score_pitch_t *pitch,
+ int *octave)
+{
+ *octave = midi_note / 12 - 1;
+
+ switch (midi_note % 12)
+ {
+ case 0:
+ *pitch = SCORE_PITCH_C;
+ break;
+ case 1:
+ *pitch = SCORE_PITCH_Cs;
+ break;
+ case 2:
+ *pitch = SCORE_PITCH_D;
+ break;
+ case 3:
+ *pitch = SCORE_PITCH_Ds;
+ break;
+ case 4:
+ *pitch = SCORE_PITCH_E;
+ break;
+ case 5:
+ *pitch = SCORE_PITCH_F;
+ break;
+ case 6:
+ *pitch = SCORE_PITCH_Fs;
+ break;
+ case 7:
+ *pitch = SCORE_PITCH_G;
+ break;
+ case 8:
+ *pitch = SCORE_PITCH_Gs;
+ break;
+ case 9:
+ *pitch = SCORE_PITCH_A;
+ break;
+ case 10:
+ *pitch = SCORE_PITCH_As;
+ break;
+ case 11:
+ *pitch = SCORE_PITCH_B;
+ break;
+ }
+}
+
+static void
+scherzo_add_note_midi (scherzo_t *scherzo, unsigned char midi_note)
+{
+ score_staff_t *staff;
+ score_pitch_t pitch;
+ int octave;
+
+ /* Anything at Middle C and above goes on the treble staff by default. */
+ if (midi_note >= 60)
+ staff = scherzo->treble;
+ else
+ staff = scherzo->bass;
+
+ _midi_to_score_pitch_and_octave (midi_note, &pitch, &octave);
+
+ score_staff_add_note (staff, pitch, octave, SCORE_DURATION_WHOLE);
+}
+
+static void
+scherzo_remove_note_midi (scherzo_t *scherzo, unsigned char midi_note)
+{
+ score_staff_t *staff;
+ score_pitch_t pitch;
+ int octave;
+ score_note_t *note;
+
+ /* Anything at Middle C and above goes on the treble staff by default. */
+ if (midi_note >= 60)
+ staff = scherzo->treble;
+ else
+ staff = scherzo->bass;
+
+ _midi_to_score_pitch_and_octave (midi_note, &pitch, &octave);
+
+ note = score_staff_find_note (staff, pitch, octave, SCORE_DURATION_WHOLE);
+ score_staff_remove_note (staff, note);
+}
+
+static int
+on_midi_input (unused (GIOChannel *channel),
+ unused (GIOCondition condition),
+ void *user_data)
+{
+ unsigned char buf[MIDI_BUF_SIZE], *next;
+ scherzo_t *scherzo = user_data;
+ ssize_t remaining;
+ snd_seq_event_t event;
+
+ remaining = read (scherzo->midi_fd, buf, MIDI_BUF_SIZE);
+
+ next = buf;
+ while (remaining) {
+ long consumed;
+
+ consumed = snd_midi_event_encode (scherzo->snd_midi_event,
+ next, remaining, &event);
+
+ remaining -= consumed;
+
+ switch (event.type) {
+ case SND_SEQ_EVENT_NONE:
+ /* Incomplete event. Nothing to do. */
+ break;
+ case SND_SEQ_EVENT_NOTEON:
+ scherzo_add_note_midi (scherzo, event.data.note.note);
+ gtk_widget_queue_draw (scherzo->window);
+ break;
+ case SND_SEQ_EVENT_NOTEOFF:
+ scherzo_remove_note_midi (scherzo, event.data.note.note);
+ gtk_widget_queue_draw (scherzo->window);
+ break;
+ case SND_SEQ_EVENT_CLOCK:
+ /* Ignore for now as my piano sends a constant stream of these. */
+ break;
+ case SND_SEQ_EVENT_SENSING:
+ /* Ignore for now as my piano sends a constant stream of these. */
+ break;
+ default:
+ fprintf (stderr, "Fixme: Do not yet know how to handle MIDI event %d\n",
+ event.type);
+ break;
+ }
+ }
+
+ /* Return TRUE to continue to get called in the future. */
+ return TRUE;
+}
+
int
main (int argc, char *argv[])
{
- GtkWidget *window;
GtkWidget *drawing_area;
scherzo_t scherzo;
- score_staff_t *treble;
- score_staff_t *bass;
+ int err;
gtk_init (&argc, &argv);
score_set_staff_height (scherzo.score, scherzo.staff_height);
score_add_brace (scherzo.score, 2);
- treble = score_add_staff (scherzo.score, SCORE_CLEF_G);
- bass = score_add_staff (scherzo.score, SCORE_CLEF_F);
-
- score_staff_add_note (treble, SCORE_BUILD_NOTE (D, 4, WHOLE));
- score_staff_add_note (treble, SCORE_BUILD_NOTE (E, 4, WHOLE));
- score_staff_add_note (treble, SCORE_BUILD_NOTE (F, 4, WHOLE));
- score_staff_add_note (treble, SCORE_BUILD_NOTE (G, 4, WHOLE));
- score_staff_add_note (treble, SCORE_BUILD_NOTE (A, 4, WHOLE));
- score_staff_add_note (treble, SCORE_BUILD_NOTE (B, 4, WHOLE));
- score_staff_add_note (treble, SCORE_BUILD_NOTE (C, 5, WHOLE));
- score_staff_add_note (treble, SCORE_BUILD_NOTE (D, 5, WHOLE));
-
- score_staff_add_note (bass, SCORE_BUILD_NOTE (B, 2, WHOLE));
- score_staff_add_note (bass, SCORE_BUILD_NOTE (C, 3, WHOLE));
- score_staff_add_note (bass, SCORE_BUILD_NOTE (D, 3, WHOLE));
- score_staff_add_note (bass, SCORE_BUILD_NOTE (E, 3, WHOLE));
- score_staff_add_note (bass, SCORE_BUILD_NOTE (F, 3, WHOLE));
- score_staff_add_note (bass, SCORE_BUILD_NOTE (G, 3, WHOLE));
- score_staff_add_note (bass, SCORE_BUILD_NOTE (A, 3, WHOLE));
- score_staff_add_note (bass, SCORE_BUILD_NOTE (B, 3, WHOLE));
-
- window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-
- gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
-
- g_signal_connect (window, "delete-event",
+ scherzo.treble = score_add_staff (scherzo.score, SCORE_CLEF_G);
+ scherzo.bass = score_add_staff (scherzo.score, SCORE_CLEF_F);
+
+ err = snd_midi_event_new (MIDI_BUF_SIZE, &scherzo.snd_midi_event);
+ if (err) {
+ fprintf (stderr, "Out of memory.\n");
+ return 1;
+ }
+
+#define MIDI_DEVICE "/dev/midi1"
+ scherzo.midi_fd = open (MIDI_DEVICE, O_RDONLY);
+ if (scherzo.midi_fd < 0) {
+ printf ("failed to open " MIDI_DEVICE ". Midi input will not be available.\n");
+ } else {
+ GIOChannel *channel;
+
+ channel = g_io_channel_unix_new (scherzo.midi_fd);
+ g_io_channel_set_encoding (channel, NULL, NULL);
+ g_io_add_watch (channel, G_IO_IN, on_midi_input, &scherzo);
+ }
+
+ scherzo.window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_default_size (GTK_WINDOW (scherzo.window), 600, 400);
+
+ g_signal_connect (scherzo.window, "delete-event",
G_CALLBACK (on_delete_event_quit), NULL);
drawing_area = gtk_drawing_area_new ();
- gtk_container_add (GTK_CONTAINER (window), drawing_area);
+ gtk_container_add (GTK_CONTAINER (scherzo.window), drawing_area);
g_signal_connect (drawing_area, "expose-event",
G_CALLBACK (on_expose_event_draw),
&scherzo);
- g_signal_connect (window, "key-press-event",
+ g_signal_connect (scherzo.window, "key-press-event",
G_CALLBACK (on_key_press_event),
&scherzo);
- gtk_widget_show_all (window);
+ gtk_widget_show_all (scherzo.window);
gtk_main ();
+ snd_midi_event_free (scherzo.snd_midi_event);
+ talloc_free (scherzo.score);
+
return 0;
}
* along with this program. If not, see http://www.gnu.org/licenses/ .
*/
+#include <string.h>
+
#include "score.h"
struct score_staff
{
int i;
cairo_glyph_t clef_glyph;
- cairo_text_extents_t clef_extents;
cairo_save (cr);
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
cairo_show_glyphs (cr, &clef_glyph, 1);
- cairo_glyph_extents (cr, &clef_glyph, 1, &clef_extents);
-
/* Draw staff lines */
for (i = 0; i < 5; i++) {
cairo_move_to (cr, 0, i * score->space_height + score->line_width / 2.0);
cairo_stroke (cr);
/* Make space for clef before drawing notes */
- cairo_translate (cr, (int) (1.5 * clef_extents.width), 0);
+ cairo_translate (cr, (int) (4 * score->space_height), 0);
/* Draw notes */
for (i = 0; i < staff->num_notes; i++) {
_draw_note (score, cr, staff, staff->notes[i]);
+ /* Draw all notes concurrent for now (as a chord)
cairo_translate (cr, score->space_height * 2.0, 0);
+ */
}
cairo_restore (cr);
return note;
}
+void
+score_staff_remove_note (score_staff_t *staff, score_note_t *note)
+{
+ int i;
+
+ for (i = 0; i < staff->num_notes; i++)
+ if (staff->notes[i] == note)
+ break;
+
+ if (i == staff->num_notes)
+ return;
+
+ if (i < staff->num_notes - 1)
+ {
+ memmove (staff->notes + i,
+ staff->notes + i + 1,
+ (staff->num_notes - 1 - i) * sizeof (score_note_t *));
+ }
+
+ staff->num_notes -= 1;
+}
+
+score_note_t *
+score_staff_find_note (score_staff_t *staff,
+ score_pitch_t pitch,
+ int octave,
+ score_duration_t duration)
+{
+ int i;
+ score_note_t *note;
+
+ for (i = 0; i < staff->num_notes; i++) {
+ note = staff->notes[i];
+ if (note->pitch == pitch &&
+ note->octave == octave &&
+ note->duration == duration)
+ {
+ return note;
+ }
+ }
+
+ return NULL;
+}
+