> But, I'm still thinking that loading a musical font is the "best" way
> to go, and on that basis I was keen to show how to measure a font
> symbol so that you can render it in the "right place".
> 
> And I was rushing anyway...!
> 
> Really, I meant for the code to support resizing, but the version I
> posted doesn't (yet) - though the bits for that are more or less in
> place. Probably today...


OK - lunch break over; here's the "revised" version.

It should now support resizing and do something reasonable.

Also;
IF you are on a Windows platform,
AND you create a folder called "fonts" under the build folder,
AND you place a copy of "Musica306.otf" in the fonts folder,
THEN there's a fair chance that the code will use a real treble clef glyph...

Musica306.otf is the font file for version 3.06 of George Douros' Musica font, 
which looks OK on the screen and is fairly free.

I wonder where to download it? Hmm, maybe here?
http://users.teilar.gr/~g1951d/Musica306.zip  

I did this on the wild assumption that Edgar probably has access to a Windows 
machine...

This code (Windows build) tested on Win7 with Msys/mingw, and works nicely.

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

#ifdef WIN32 // need this dance to make font handling load on Win32
#  define _WIN32_WINNT 0x0500
#  include <windows.h>
#endif

#include <FL/Fl.H>
#include <FL/Fl_Group.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;

// Special platfom code to handle app specific fonts under Windows
static int have_music_font = 0;
/*****************************************************************************/
static void load_extra_font(void)
{
#ifdef WIN32
    /* Load the font using the Windows API */
    have_music_font = AddFontResourceEx("./fonts/Musica306.otf", FR_PRIVATE, 0);
#endif
    /* setup the extra font */
    Fl::set_font(FL_SYMBOL,
#ifdef __APPLE__
                 "Musica");
#else
                 " Musica");
#endif
} // load_extra_font

/******************************************************************************/
static void free_extra_font(void)
{
#ifdef WIN32
    if (have_music_font) {
        DWORD fl = FR_PRIVATE;
        PVOID pdv = 0;
        RemoveFontResourceEx("./media/Musica306/Musica306.otf", fl, pdv);
    }
#endif
} // free_extra_font

/******************************************************************************/

// 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_G[2] = {'G', 0}; // a G clef... sort of...
        // U_treble_clef = 0x1d11e;  Unicode code-point for a treble clef
        static char treble_clef[5] = {0xF0, 0x9D, 0x84, 0x9E, 0}; // a G 
clef... as UTF8

        Fl_Box::draw(); // draw the base-class box first to give us an outline 
to fill...

        // determine some limits for us to draw staff lines into
        int ww = w();
        int hh = h();

        int gap = hh / 8; // space between the lines...
        int ht = gap * 4; // 5 lines, 4 gaps...
        int yo = y() + (2 * gap); // Y origin

        int xo = x() + 20; // X origin
        int len = ww - 40; // staff length
        int xe = xo + len; // X end

        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 - measure the clef symbol, so we can figure out where 
to draw it...
        int dx1, dy1, cw1, ch1; // for "real" clef
        int dx2, dy2, cw2, ch2; // for "fallback" clef

        if(have_music_font)
        {
                // get the music font, and ask for it a bit bigger
                fl_font(FL_SYMBOL, (ht + (gap * 3)));
                //fl_utf8encode(U_treble_clef, clef); // Unicode treble clef
                fl_text_extents(treble_clef, dx1, dy1, cw1, ch1);
                int y_nudge = gap; // align the clef on the G line...
                fl_draw(treble_clef, xo - dx1 + 6, yo + dy1 + ch1 + ht - 
y_nudge);
        }
        else
        {
                // draw the clef; A big letter G in this case...
                fl_font(FL_TIMES, ht);
                fl_text_extents(clef_G, dx2, dy2, cw2, ch2);
                int y_nudge = (ht - ch2) / 2; // roughly centre the G in the 
staff - wrong, but looks OK...
                fl_draw(clef_G, xo - dx2 + 6, yo + ht + dy2 + ch2 - 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) - gap; // 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 + (2 * gap), 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 note from 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));

        load_extra_font(); // load the app-specific music font, if we support it

        // create the GUI
        main_win = new Fl_Double_Window(400, 400, "Treble Clef Note 
Identification");
        main_win->begin();
        // Let us set a minimum size for the window, just for
        // cosmetic reasons...
        main_win->size_range(280, 380);

        // shows the staff
        staff = new staff_view(10, 80, 380, 120);
        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);

        // To allow easier control of re-size behaviour, put the control
        // buttons into a dummy group to manage where the stretching ends up...
        Fl_Group *button_group = new Fl_Group(0, 210, 400, 190);
        button_group->begin();
        button_group->box(FL_NO_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;
        }

        // add an invisible dummy box that can "eat" all the resize stretching 
for us...
        Fl_Box *eat_resizes = new Fl_Box(bt_x, 250, 2, 2);
        eat_resizes->box(FL_NO_BOX);

        // something to display results in
        out_txt = new Fl_Output(10, 270, 380, 30);
        out_txt->box(FL_THIN_DOWN_BOX);
        out_txt->clear_visible_focus(); // hide the caret in the text output

        // 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);

        // 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);

        button_group->end();
        button_group->resizable(eat_resizes);

        main_win->end();
        main_win->resizable(staff);

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

        // display the GUI
        main_win->show(argc, argv);

        int exit_result = Fl::run();

        free_extra_font(); // unload any extra fonts - if we loaded them!

        return exit_result;

} // main

// end of file
----------------------

Selex ES Ltd
Registered Office: Sigma House, Christopher Martin Road, Basildon, Essex SS14 
3EL
A company registered in England & Wales.  Company no. 02426132
********************************************************************
This email and any attachments are confidential to the intended
recipient and may also be privileged. If you are not the intended
recipient please delete it from your system and notify the sender.
You should not copy it or use it for any purpose nor disclose or
distribute its contents to any other person.
********************************************************************

_______________________________________________
fltk mailing list
fltk@easysw.com
http://lists.easysw.com/mailman/listinfo/fltk

Reply via email to