Ok, I wrote something, but I'm pressed for time (busy weekend - indeed busy 
week coming too) and it is late here, so it is not as nice as I'd like...

In particular it is not as well commented as I wanted, and is probably opaque 
in places.

I hope that a little time looking at the code and checking the fltk docs will 
lead to clarity however!

Attached below.

Though - note that my mail has taken to making things "quoted printable" a lot 
of the time, so that may well mess things up horribly in transit.
Here's hoping, anyway...

I tried to style it to look as much like your demo as possible, well, "inspired 
by", anyway!

On 3 Mar 2013, at 16:15, edgar wrote:
>> 
>> Rather, I'd offer a list of answers and allow the user to pick one - =
>> that removes the parsing aspect and makes things a lot simpler, with no =
>> loss of generality...
>> 
> Good idea. I could use buttons labelled with note names...

OK - I've shown that in the demo.
> 


> Eventually, I'd like this to be a portion of a larger program.  (eg. this 
> would be one choice from a drop down menu... )In that case, is it more 
> important to destroy widgets that go out of scope?

Probably not: How many will you have? A hidden fltk widget consumes a few tens 
of bytes, so even if you have thousands of them, most modern PC's would not 
even notice...

The PNG's you load are probably bigger!



> Thanks very much!  I never got this kind of response from my C++ prof!

You haven't seen my code yet...  ;-)



>> - If you are going to render a lot of musical symbols, loading a musical =
>> font (such as Musica or etc.) will be easier than rendering lots of PNG =
>> glyphs, and will also anti-alias and scale automatically.
> 
> I would really like to learn how to do this.  I was using .png files, which 
> worked and didn't look bad, but It doesn't allow resizing and is pretty 
> inflexible.

I haven't shown loading fonts, because actually making the fonts available to 
the system is a pain, in a not all that portable sort of a way.

But for more complex scores, it is certainly the way to go. We can deal with 
that when the time comes!



> I remember you mentioned Musescore. Do you know of tutorials on the web for 
> loading and implementing font libraries?

I'm not sure how Musescore does it.
For my code with fltk, it is just like loading any other font, so *if* the font 
is installed on your system, it Just Works...

If the font is not installed (i.e. is private to your application) then you 
need platform specific handling, since OSX, WinXX and *nix all do it very 
differently.

Here's the sample code; it turned out longer than I initially thought...

---------------
//
// Test demo - draw a staff
//
// fltk-config --compile staff-demo.cxx
//

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Output.H>
#include <FL/fl_draw.H>

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

// some widgets we will use...
static Fl_Double_Window *main_win = 0;
static Fl_Button *quit_bt = 0;
static Fl_Button *repeat_bt = 0;
static Fl_Button *note_bt[8]; // 8 note names is enough for now...
static Fl_Output *out_txt = 0;

static int score = 0;

// class to display the staff
class staff_view : public Fl_Box {
protected:
        void draw();

public:
        // index used to hold the current note. Only 11 values used here,
        // 0..10, for notes D to G, since I can't be bothered drawing any
        // ledger lines...
        // For ease, I have made D == 0 here, but we really want A == 0 later 
so... yuck..
        int note;

        // constructor
        staff_view(int X, int Y, int W, int H) : Fl_Box(X, Y, W, H), note(0) { 
};
};

void staff_view::draw() {
        static char clef[2] = {'G', 0}; // a G clef... sort of...
        Fl_Box::draw(); // draw the base-class box first to give us an outline 
to fill...

        // determine some limits for us to daw of staff lines into
        int ww = w();
        int hh = h();
        int xo = x() + 20; // X origin
        int yo = y() + 20; // Y origin
        int len = ww - 40; // staff length
        int xe = xo + len; // X end

        int gap = 15; // space between the lines...
        int ht = gap * 4; // 5 lines, 4 gaps...

        fl_color(FL_BLACK); // set the colour first
        fl_line_style(FL_SOLID, 2); // then set the line style

        // draw the endcaps... this is not pretty code...
        // get rid of all these dodgy hard-coded offsets...
        fl_line(xo, yo, xo, yo + ht);
        fl_line(xe-8, yo, xe-8, yo + ht);
        fl_line_style(FL_SOLID, 4);
        fl_line(xe-2, yo, xe-2, yo + ht);
        fl_line_style(FL_SOLID, 2);

        // draw 5 lines for stave
        int yl = yo;
        for (int idx = 0; idx < 5; ++idx) {
                fl_line(xo, yl, xe, yl);
                yl += gap;
        }

        // draw the clef; A big letter G here...
        fl_font(FL_TIMES, ht);
        // measure the clef symbol, so we can figure out where to draw it...
        int dx, dy, cw, ch;
        fl_text_extents(clef, dx, dy, cw, ch);
        int y_nudge = (ht - ch) / 2; // roughly centre the G in the staff - 
wrong, but looks OK...
        fl_draw(clef, xo + dx, yo + ht + dy + ch - y_nudge);

        // draw the note... I really ought to do this better, but for now, just 
draw
        // a filled ellipse and pretend it is a breve...!
        int note_width = (int)(gap * 1.4);
        int xn = xo + (len / 2); // set note x origin
        int yn = yo + ht; // set note y origin
        yn -= (gap * note) / 2; // offset note vertically by note value
        fl_pie(xn, yn , note_width, gap, 0.0, 360.0); // a breve, sort of...

        // now draw a semibreve too, just for fun...
        fl_line_style(FL_SOLID, 3);
        fl_arc(xn + 30, yn , note_width, gap, 0.0, 360.0);

        fl_line_style(0); // restore linestyle defaults before we leave...
}

// an instance of the staff class...
staff_view *staff = 0;

// to exit
static void cb_quit_bt(Fl_Button*, void*) {
  main_win->hide();
}

// pick a random note
static void throw_dice() {
        int old = staff->note;
        int r = old;
        while (old == r) {
                r = rand();
                r = r % 11; // only 11 valid values in our test here
        }
        staff->note = r;
}

// turn the answer buttons on and off...
static void set_ans_bt_state(int s) {
        if (s != 0) {
                for(int i = 0; i < 7; ++i) {
                        note_bt[i]->activate();
                }
        }
        else {
                for(int i = 0; i < 7; ++i) {
                        note_bt[i]->deactivate();
                }
        }
}

// to play again
static void cb_repeat_bt(Fl_Button*, void*) {
  throw_dice();
  staff->redraw();
  set_ans_bt_state(1);
}

// process the answer
static void cb_note_bt(Fl_Button* o, void*) {
  static char result[128];
  const char *lb = o->label();
  int ans = (staff->note + 3) % 7; // remap fnote form D == 0 to A == 0 range...
  int guess = lb[0] - 'A';
  if(guess == ans) {
        score++;
        snprintf(result, 128, "Correct: You selected %s [%d]", lb, score);
  }
  else {
        snprintf(result, 128, "WRONG: You selected %s, but we expected %c 
[%d]", lb, (ans + 'A'), score);
  }
  out_txt->value(result);
  set_ans_bt_state(0);
}

int main(int argc, char *argv[])
{
        // we'll use the basic random sequence generator,
        // seed it here
        srand(time(0));

        // create the GUI
        main_win = new Fl_Double_Window(400, 400, "Treble Clef Note 
Identification");
        main_win->begin();

        // pick a new note
        repeat_bt = new Fl_Button(10, 355, 65, 35, "Repeat");
        repeat_bt->box(FL_THIN_UP_BOX);
        repeat_bt->callback((Fl_Callback*)cb_repeat_bt);

        // shows the staff
        staff = new staff_view(10, 104, 380, 100);
        staff->box(FL_THIN_DOWN_BOX);
        staff->color(FL_BACKGROUND2_COLOR);

        // show the "instructions" text
        Fl_Box* instructions = new Fl_Box(20, 14, 365, 67, "Click on the note 
name below");
        instructions->box(FL_FLAT_BOX);

        // make a set of note-name buttons here...
        int bt_x = 10;
        for (int n = 0; n < 7; ++n) {
                char label[2] = {'A', 0};
                label[0] += n;
                note_bt[n] = new Fl_Button(bt_x, 220, 25, 25);
                note_bt[n]->box(FL_THIN_UP_BOX);
                note_bt[n]->callback((Fl_Callback*)cb_note_bt);
                note_bt[n]->copy_label(label);
                bt_x += 30;
        }

        // something to display results in
        out_txt = new Fl_Output(10, 270, 380, 30);
        out_txt->box(FL_THIN_DOWN_BOX);

        // time to leave...
        quit_bt = new Fl_Button(320, 355, 65, 35, "Quit");
        quit_bt->box(FL_THIN_UP_BOX);
        quit_bt->callback((Fl_Callback*)cb_quit_bt);

        main_win->end();

        // start with a random note...
        throw_dice();

        // display the GUI
        main_win->show(argc, argv);
        return Fl::run();

} // main

// end of file

---------------





_______________________________________________
fltk mailing list
[email protected]
http://lists.easysw.com/mailman/listinfo/fltk

Reply via email to