# GNU Solfege - free ear training software # Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007 Tom Cato Amundsen # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin ST, Fifth Floor, Boston, MA 02110-1301 USA import gtk, gobject import soundcard import abstract, const, statistics, statisticsviewer, gu, random import lessonfile class Teacher(abstract.Teacher): def __init__(self, exname, app): abstract.Teacher.__init__(self, exname, app) self.lessonfileclass = lessonfile.IdByNameLessonfile # 60, 84, 100, 120 self.m_ped = [60, 120, 40, 168, 208, 80, 96, 139, 48, 69, 108, 192, 152, 88, 63, 100, 116, 176, 200, 52, 76, 126, 160, 84, 56, 44, 66, 72, 92, 104, 112, 132, 144, 184] self.m_bpms = [40, 44, 48, 52, 56, 60, 63, 66, 69, # 0 - 8 72, 76, 80, 84, 88, 92, 96, 100, 104, # 9 - 17 108, 112, 116, 120, 126, 132, 138, 144, # 18 - 25 152, 160, 168, 176, 184, 192, 200, 208] # 26 - 33 self.m_statistics = statistics.LessonStatistics(self) self.m_timeout_tag = None self.m_question = None self.m_practise_these = {} v = self.get_list('active_bpms') for bpm in self.m_bpms: if bpm in v: self.m_practise_these[bpm] = 1 else: self.m_practise_these[bpm] = 0 def toggle_active(self, bpm): self.m_practise_these[bpm] = not self.m_practise_these[bpm] v = filter(lambda key, self=self: self.m_practise_these[key], self.m_practise_these.keys()) self.set_string('active_bpms', str(v)) def get_possible_bpms(self): return [n for n in self.m_practise_these.keys() \ if self.m_practise_these[n]] def get_number_of_levels(self): return len(self.m_ped) def new_question(self): last = self.m_question if not self.get_possible_bpms(): return False self.m_question = random.choice(self.get_possible_bpms()) while self.m_question == last and len(self.get_possible_bpms()) > 1: self.m_question = random.choice(self.get_possible_bpms()) self.start_question(self.m_question) self.q_status = const.QSTATUS_NEW return True def start_question(self, bpm): self.tick() if self.m_timeout_tag: gobject.source_remove(self.m_timeout_tag) self.m_timeout_tag = gobject.timeout_add(60000/bpm, self.tick) def stop_question(self): if self.m_timeout_tag: gobject.source_remove(self.m_timeout_tag) self.m_timeout_tag = None def tick(self, _o=None): soundcard.play_perc(8, 60, 90) return 1 def guess_answer(self, bpm): assert self.q_status != const.QSTATUS_NO if self.m_question == bpm: self.stop_question() if self.q_status == const.QSTATUS_NEW: self.m_statistics.add_correct(str(bpm)) self.q_status = const.QSTATUS_SOLVED self.m_app.play_happy_sound() return 1 else: if self.q_status == const.QSTATUS_NEW: self.m_statistics.add_wrong(str(self.m_question), str(bpm)) self.q_status = const.QSTATUS_WRONG self.m_app.play_sad_sound() def end_practise(self): super(Teacher, self).end_practise() self.stop_question() self.q_status = const.QSTATUS_NO def give_up(self): self.stop_question() print "FIXME not saving statistics" self.q_status = const.QSTATUS_GIVE_UP class Gui(abstract.Gui): def __init__(self, teacher, window): abstract.Gui.__init__(self, teacher, window) ################ # practise_box # ################ self.practise_box.set_spacing(gu.PAD) self.m_buttons = [] vbox = gu.bVBox(self.practise_box, False) for s, e in ((0, 9), (9, 18), (18, 26), (26, 34)): box = gtk.HBox() vbox.pack_start(box) for i in range(s, e): bpm = self.m_t.m_bpms[i] button = gtk.Button(str(bpm)) box.pack_start(button) button.connect('clicked', self.on_click) button.connect('event', self.on_event) button.set_data('bpm', bpm) self.m_buttons.append(button) self.g_flashbar = gu.FlashBar() self.g_flashbar.show() self.practise_box.pack_start(self.g_flashbar, False) self.g_new = gu.bButton(self.action_area, _("_New tempo"), self.on_new) self.g_give_up = gu.bButton(self.action_area, _("_Give up"), self.on_give_up) self.g_give_up.set_sensitive(False) self.practise_box.show_all() ############## # statistics # ############## self.g_statview = statisticsviewer.StatisticsViewer(self.m_t.m_statistics, _('Bpm')) self.g_statview.show() self.g_notebook.append_page(self.g_statview, gtk.Label(_("Statistics"))) ######## # init # ######## self.g_notebook.get_nth_page(1).hide() self.update_buttons() def on_enable_bpm(self, _o=None): print "enable", _o def on_event(self, button, event): if event.type == gtk.gdk.BUTTON_RELEASE and event.button == 3: self.m_t.toggle_active(button.get_data('bpm')) self.update_buttons() def on_level_change(self, adjustment): self.set_int('level', adjustment.value) self.update_buttons() def on_new(self, _o=None): if self.m_t.new_question(): self.g_new.set_sensitive( not self.get_bool('config/picky_on_new_question')) self.g_give_up.set_sensitive(True) self.m_buttons[0].grab_focus() else: self.g_flashbar.flash(_("You have to select some tempos to practise.")) def on_give_up(self, _o): self.m_t.give_up() self.g_flashbar.flash(str(self.m_t.m_question)) self.g_new.set_sensitive(True) self.g_give_up.set_sensitive(False) def update_buttons(self): v = self.m_t.get_possible_bpms() for b in self.m_buttons: if b.get_data('bpm') in v: b.get_children()[0].set_name("BpmActiveLabel") else: b.get_children()[0].set_name("BpmInactiveLabel") def on_click(self, _o): if self.m_t.q_status in (const.QSTATUS_SOLVED, const.QSTATUS_GIVE_UP): return if _o.get_data('bpm') not in self.m_t.get_possible_bpms(): return if self.m_t.q_status == const.QSTATUS_NO: self.g_flashbar.flash(_("Click 'New tempo' to begin.")) return if self.m_t.guess_answer(_o.get_data('bpm')): self.g_flashbar.flash(_("Correct, it is %i") % _o.get_data('bpm')) self.g_give_up.set_sensitive(False) self.g_new.set_sensitive(True) self.g_new.grab_focus() else: self.g_flashbar.flash(_("Wrong")) self.g_statview.update() def on_start_practise(self): self.g_flashbar.flash(_("Click 'New tempo' to begin.")) self.g_new.grab_focus() self.g_statview.update() def on_end_practise(self): self.m_t.end_practise() self.g_new.set_sensitive(True) self.g_give_up.set_sensitive(False)