GUI: support time signatures with denominator != 4 (like 6/8)
authorStefan Westerfeld <stefan@space.twc.de>
Mon, 16 May 2011 12:31:41 +0000 (14:31 +0200)
committerStefan Westerfeld <stefan@space.twc.de>
Mon, 16 May 2011 12:31:41 +0000 (14:31 +0200)
beast-gtk/bstpianoroll.c
beast-gtk/bstpianoroll.h
beast-gtk/bstpianorollctrl.c

index 3f2ddef..11b7d2d 100644 (file)
@@ -82,8 +82,9 @@ enum {
 
 /* --- prototypes --- */
 static void    bst_piano_roll_hsetup                   (BstPianoRoll           *self,
-                                                        guint                   ppqn,
-                                                        guint                   qnpt,
+                                                        guint                   ppb,
+                                                        guint                   bpt,
+                                                         guint                   tpqn,
                                                         guint                   max_ticks,
                                                         gfloat                  hzoom);
 
@@ -154,8 +155,9 @@ bst_piano_roll_init (BstPianoRoll *self)
   
   self->proxy = 0;
   self->vzoom = KEY_DEFAULT_VPIXELS;
-  self->ppqn = 384;    /* default Parts (clock ticks) Per Quarter Note */
-  self->qnpt = 1;
+  self->tpqn = 384;     /* default Ticks per Quarter Note */
+  self->ppb = 384;     /* default Parts (clock ticks) Per Beat */
+  self->bpt = 1;
   self->max_ticks = 1;
   self->hzoom = 1;
   self->draw_qn_grid = TRUE;
@@ -170,7 +172,7 @@ bst_piano_roll_init (BstPianoRoll *self)
   self->selection_duration = 0;
   self->selection_min_note = 0;
   self->selection_max_note = 0;
-  bst_piano_roll_hsetup (self, 384, 4, 800 * 384, 1);
+  bst_piano_roll_hsetup (self, 384, 4, 384, 800 * 384, 1);
   
   bst_ascii_pixbuf_ref ();
 }
@@ -256,12 +258,12 @@ static gdouble
 ticks_to_pixels (BstPianoRoll *self,
                 gdouble       ticks)
 {
-  gdouble ppqn = self->ppqn;
+  gdouble ppb = self->ppb;
   gdouble tpixels = QNOTE_HPIXELS;
   
   /* compute pixel span of a tick range */
   
-  tpixels *= self->hzoom / ppqn * ticks;
+  tpixels *= self->hzoom / ppb * ticks;
   if (ticks)
     tpixels = MAX (tpixels, 1);
   return MIN (G_MAXINT, tpixels);
@@ -271,12 +273,12 @@ static gdouble
 pixels_to_ticks (BstPianoRoll *self,
                 gdouble       pixels)
 {
-  gdouble ppqn = self->ppqn;
+  gdouble ppb = self->ppb;
   gdouble ticks = 1.0 / (gdouble) QNOTE_HPIXELS;
 
   /* compute tick span of a pixel range */
 
-  ticks = ticks * ppqn / self->hzoom * pixels;
+  ticks = ticks * ppb / self->hzoom * pixels;
   if (pixels > 0)
     ticks = MAX (ticks, 1);
   else
@@ -311,7 +313,7 @@ coord_to_tick (BstPianoRoll *self,
 }
 
 #define        CROSSING_TACT           (1)
-#define        CROSSING_QNOTE          (2)
+#define        CROSSING_BEAT           (2)
 #define        CROSSING_QNOTE_Q        (3)
 
 static guint
@@ -329,16 +331,16 @@ coord_check_crossing (BstPianoRoll *self,
   switch (crossing)
     {
     case CROSSING_TACT:
-      lq = ltick / (self->ppqn * self->qnpt);
-      rq = rtick / (self->ppqn * self->qnpt);
+      lq = ltick / (self->ppb * self->bpt);
+      rq = rtick / (self->ppb * self->bpt);
       break;
-    case CROSSING_QNOTE:
-      lq = ltick / self->ppqn;
-      rq = rtick / self->ppqn;
+    case CROSSING_BEAT:
+      lq = ltick / self->ppb;
+      rq = rtick / self->ppb;
       break;
     case CROSSING_QNOTE_Q:
-      lq = ltick * 4 / self->ppqn;
-      rq = rtick * 4 / self->ppqn;
+      lq = ltick * 4 / self->ppb;
+      rq = rtick * 4 / self->ppb;
       break;
     }
   
@@ -740,7 +742,7 @@ bst_piano_roll_draw_canvas (GxkScrollCanvas *scc,
               gdk_gc_set_line_attributes (draw_gc, line_width, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
               gdk_draw_line (drawable, draw_gc, i, area->y, i, area->y + area->height - 1);
             }
-          else if (pass == 1 && self->draw_qn_grid && coord_check_crossing (self, i, CROSSING_QNOTE))
+          else if (pass == 1 && self->draw_qn_grid && coord_check_crossing (self, i, CROSSING_BEAT))
             {
               GdkGC *draw_gc = COLOR_GC_VGRID (self);
               guint8 dash[3] = { 2, 2, 0 };
@@ -857,15 +859,15 @@ bst_piano_roll_overlap_grow_hpanel_area (BstPianoRoll *self,
   
   /* grow hpanel exposes by surrounding tacts */
   i = coord_to_tick (self, x, FALSE);
-  i /= self->ppqn * self->qnpt;
+  i /= self->ppb * self->bpt;
   if (i > 0)
     i -= 1;            /* fudge 1 tact to the left */
-  i *= self->ppqn * self->qnpt;
+  i *= self->ppb * self->bpt;
   x = tick_to_coord (self, i);
   i = coord_to_tick (self, xbound + 1, TRUE);
-  i /= self->ppqn * self->qnpt;
+  i /= self->ppb * self->bpt;
   i += 2;              /* fudge 1 tact to the right (+1 for round-off) */
-  i *= self->ppqn * self->qnpt;
+  i *= self->ppb * self->bpt;
   xbound = tick_to_coord (self, i);
   
   area->x = x;
@@ -896,8 +898,8 @@ bst_piano_roll_draw_hpanel (GxkScrollCanvas *scc,
        {
          guint next_pixel, tact = coord_to_tick (self, i, TRUE) + 1;
           
-         tact /= (self->ppqn * self->qnpt);
-         next_pixel = tick_to_coord (self, (tact + 1) * (self->ppqn * self->qnpt));
+         tact /= (self->ppb * self->bpt);
+         next_pixel = tick_to_coord (self, (tact + 1) * (self->ppb * self->bpt));
           
          g_snprintf (buffer, 64, "%u", tact + 1);
           pango_layout_set_text (PLAYOUT_HPANEL (self), buffer, -1);
@@ -909,18 +911,18 @@ bst_piano_roll_draw_hpanel (GxkScrollCanvas *scc,
                             i - rect.width / 2, (height - rect.height) / 2,
                             PLAYOUT_HPANEL (self));
        }
-      else if (self->draw_qqn_grid && coord_check_crossing (self, i, CROSSING_QNOTE))
+      else if (self->draw_qqn_grid && coord_check_crossing (self, i, CROSSING_BEAT))
        {
-          guint next_pixel, tact = coord_to_tick (self, i, TRUE) + 1, qn = tact;
+          guint next_pixel, tact = coord_to_tick (self, i, TRUE) + 1, beat = tact;
 
-         tact /= (self->ppqn * self->qnpt);
-         qn /= self->ppqn;
-         next_pixel = tick_to_coord (self, (qn + 1) * self->ppqn);
-          qn = qn % self->qnpt + 1;
-          if (qn == 1)
+         tact /= (self->ppb * self->bpt);
+         beat /= self->ppb;
+         next_pixel = tick_to_coord (self, (beat + 1) * self->ppb);
+          beat = beat % self->bpt + 1;
+          if (beat == 1)
             continue;   /* would draw on top of tact number */
 
-         g_snprintf (buffer, 64, ":%u", qn);
+         g_snprintf (buffer, 64, ":%u", beat);
           pango_layout_set_text (PLAYOUT_HPANEL (self), buffer, -1);
           pango_layout_get_pixel_extents (PLAYOUT_HPANEL (self), NULL, &rect);
           
@@ -1013,8 +1015,8 @@ piano_roll_update_adjustments (GxkScrollCanvas *scc,
       umin = MIN (umin, umax * 1.5), umax = MAX (umin, umax);                   /* properly confine boundaries */
       scc->hadjustment->lower = 0;
       scc->hadjustment->upper = CLAMP (scc->hadjustment->upper, umin, umax);
-      scc->hadjustment->step_increment = ticks_to_pixels (self, self->ppqn);
-      scc->hadjustment->page_increment = ticks_to_pixels (self, self->ppqn * self->qnpt);
+      scc->hadjustment->step_increment = ticks_to_pixels (self, self->ppb);
+      scc->hadjustment->page_increment = ticks_to_pixels (self, self->ppb * self->bpt);
     }
   if (vadj)
     {
@@ -1026,14 +1028,16 @@ piano_roll_update_adjustments (GxkScrollCanvas *scc,
 
 static void
 bst_piano_roll_hsetup (BstPianoRoll *self,
-                      guint         ppqn,
-                      guint         qnpt,
+                      guint         ppb,
+                      guint         bpt,
+                       guint         tpqn,
                       guint         max_ticks,
                       gfloat        hzoom)
 {
   GxkScrollCanvas *scc = GXK_SCROLL_CANVAS (self);
-  guint old_ppqn = self->ppqn;
-  guint old_qnpt = self->qnpt;
+  uint old_ppb = self->ppb;
+  guint old_bpt = self->bpt;
+  uint old_tpqn = self->tpqn;
   guint old_max_ticks = self->max_ticks;
   gfloat old_hzoom = self->hzoom;
   gdouble old_hpos = pixels_to_ticks (self, scc->hadjustment->value);
@@ -1044,17 +1048,19 @@ bst_piano_roll_hsetup (BstPianoRoll *self,
    * frequently.
    */
 
-  self->ppqn = MAX (ppqn, 1);
-  self->qnpt = MAX (qnpt, 1);
+  self->ppb = MAX (ppb, 1);
+  self->bpt = MAX (bpt, 1);
+  self->tpqn = MAX (tpqn, 1);
   self->max_ticks = MAX (max_ticks, 1);
   self->hzoom = CLAMP (hzoom, 0.01, 100);
   
-  if (old_ppqn != self->ppqn ||
-      old_qnpt != self->qnpt ||
+  if (old_ppb != self->ppb ||
+      old_bpt != self->bpt ||
+      old_tpqn != self->tpqn ||
       old_hzoom != self->hzoom)
     {
-      self->draw_qn_grid = ticks_to_pixels (self, self->ppqn) >= 3;
-      self->draw_qqn_grid = ticks_to_pixels (self, self->ppqn / 4) >= 5;
+      self->draw_qn_grid = ticks_to_pixels (self, self->ppb) >= 3;
+      self->draw_qqn_grid = ticks_to_pixels (self, self->ppb / 4) >= 5;
       gtk_widget_queue_draw (GTK_WIDGET (self));
       scc->hadjustment->value = ticks_to_pixels (self, old_hpos); // fix start position when hzoom changes
       X_OFFSET (self) = GXK_SCROLL_CANVAS (self)->hadjustment->value;
@@ -1074,7 +1080,7 @@ bst_piano_roll_set_hzoom (BstPianoRoll *self,
   GxkScrollCanvas *scc = GXK_SCROLL_CANVAS (self);
   g_return_val_if_fail (BST_IS_PIANO_ROLL (self), 0);
   
-  bst_piano_roll_hsetup (self, self->ppqn, self->qnpt, self->max_ticks, hzoom);
+  bst_piano_roll_hsetup (self, self->ppb, self->bpt, self->tpqn, self->max_ticks, hzoom);
   guint i;
   /* readjust markers */
   for (i = 0; i < scc->n_markers; i++)
@@ -1175,9 +1181,9 @@ piano_roll_time_signature_changed (BstPianoRoll *self)
 {
   if (self->song)
     {
-      SfiInt numerator, denominator;
-      bse_proxy_get (self->song, "numerator", &numerator, "denominator", &denominator, NULL);
-      bst_piano_roll_hsetup (self, self->ppqn, numerator, self->max_ticks, self->hzoom);
+      SfiInt numerator, denominator, tpqn;
+      bse_proxy_get (self->song, "numerator", &numerator, "denominator", &denominator, "tpqn", &tpqn, NULL);
+      bst_piano_roll_hsetup (self, tpqn * 4 / denominator, numerator, tpqn, self->max_ticks, self->hzoom);
     }
 }
 
@@ -1200,7 +1206,7 @@ piano_roll_range_changed (BstPianoRoll *self)
 {
   guint max_ticks;
   bse_proxy_get (self->proxy, "last-tick", &max_ticks, NULL);
-  bst_piano_roll_hsetup (self, self->ppqn, self->qnpt, MAX (max_ticks, 1), self->hzoom);
+  bst_piano_roll_hsetup (self, self->ppb, self->bpt, self->tpqn, MAX (max_ticks, 1), self->hzoom);
 }
 
 static void
index c1875ee..67b5696 100644 (file)
@@ -65,8 +65,9 @@ struct _BstPianoRoll
   guint                 vzoom;
 
   /* horizontal layout */
-  guint                 ppqn;          /* parts per quarter note */
-  guint                 qnpt;          /* quarter notes per tact */
+  guint          tpqn;          /* ticks per quarter note */
+  guint                 ppb;           /* parts per beat */
+  guint                 bpt;           /* beats per tact */
   guint                 max_ticks;     /* in ticks */
   gfloat        hzoom;
 
index 3fcc1ef..51778e1 100644 (file)
@@ -762,7 +762,7 @@ insert_start (BstPianoRollController *self,
   if (drag->start_valid)
     {
       guint qtick = bst_piano_roll_controller_quantize (self, drag->start_tick);
-      guint duration = drag->proll->ppqn * 4 / NOTE_LENGTH (self);
+      guint duration = drag->proll->tpqn * 4 / NOTE_LENGTH (self);
       if (check_hoverlap (part, qtick, duration, drag->start_note, 0, 0))
        error = BSE_ERROR_INVALID_OVERLAP;
       else