Piano Sample Overview

Mobile native

The Piano sample demonstrates how to implement a complex view using EFL UI components with different EDC styles.

The purpose of this sample application is to demonstrate how to make a complex view by recursive composition of standard EFL UI components and provide possibility to support gestures and multitouch functionality. Additionally, this application provides source code to play sounds using Tizen built-in player.

The sample uses UI components, such as elm_conformant and elm_layout for the view management and elm_button components for the content inside view.

The following figure illustrates the main view of the Piano application and the UI component tree.

Figure: Piano main view

Piano main view

Implementation

The piano_createview() function creates the window which consists of a layout with a series of elm_button components that represent real piano notes.

void piano_createview(void *data)
{
   _applyTheme(data);
   _createlayout(data);
   _createnotes(data);
   _registerKeyDown(data);
   _createNoteTimer(data);
}

static void _createnotes(void *data)
{
   _createWhiteNotes(data);
   _createBlackNotes(data);

   _noteButton_fill_rect(data);
}

static void _createBlackNotes(void *data)
{
   RETM_IF(!data, "data is NULL");

   app_data *ad = (app_data *)data;

   note_struct *prev_note = NULL;

   const int black_buttons_count = 6;

   for (int i = 0; i < black_buttons_count; i++)
   {
      note_struct *note = (note_struct*) calloc(1, sizeof(note_struct));
      RETM_IF(!note, "note is NULL");

      note->button = elm_button_add(ad->win->win);
      note->type = PIANO_NOTE_BLACK;
      note->ad = ad;
      note->prev = prev_note;

      if (prev_note)
      {
         prev_note->next = note;
      }

      _noteButton_fill(note, i);
      prev_note = note;
   }
}

For each newly created button, the application registers different callbacks to provide multitouch and gesture support:

  • EVAS_CALLBACK_MULTI_DOWN
  • EVAS_CALLBACK_MULTI_UP
  • EVAS_CALLBACK_MOUSE_DOWN
  • EVAS_CALLBACK_MOUSE_UP
  • EVAS_CALLBACK_MOUSE_OUT

To optimize the application performance, each player must be initialized and prepared before playing a sound corresponding to the linked note.

static void _noteButton_fill(void* data, int index)
{
   RETM_IF(!data, "data is NULL");

   note_struct *note = (note_struct *)data;

   if (note->type == PIANO_NOTE_BLACK)
   {
      snprintf(note->sound_name, MAXNAMLEN, "black_%d.wav", index + 1);

      char part_name[MAXNAMLEN] = {0};
      snprintf(part_name, MAXNAMLEN, "note_black_%d", index + 1);
      elm_object_part_content_set(note->ad->win->layout, part_name, note->button);

      elm_object_theme_set(note->button, note->ad->win->theme);
      elm_object_style_set(note->button, "circle/button_black");
   }
   if (note->type == PIANO_NOTE_WHITE)
   {
      snprintf(note->sound_name, MAXNAMLEN, "white_%d.wav", index + 1);

      char part_name[MAXNAMLEN] = {0};
      snprintf(part_name, MAXNAMLEN, "note_white_%d", index + 1);
      elm_object_part_content_set(note->ad->win->layout, part_name, note->button);

      elm_object_theme_set(note->button, note->ad->win->theme);
      elm_object_style_set(note->button, "circle/button_white");
   }

   evas_object_show(note->button);
   evas_object_event_callback_add(note->button, EVAS_CALLBACK_MULTI_DOWN, piano_note_touch_down_cb, note);
   evas_object_event_callback_add(note->button, EVAS_CALLBACK_MULTI_UP, piano_note_touch_up_cb, note);

   evas_object_event_callback_add(note->button, EVAS_CALLBACK_MOUSE_DOWN, piano_note_mouse_down_cb, note);
   evas_object_event_callback_add(note->button, EVAS_CALLBACK_MOUSE_UP, piano_note_mouse_up_cb, note);

   evas_object_event_callback_add(note->button, EVAS_CALLBACK_MOUSE_OUT, piano_note_mouse_out_cb, note);

   evas_object_event_callback_add(note->button, EVAS_CALLBACK_MOUSE_MOVE, piano_note_mouse_move_cb, note);

   piano_note_player_init(note);

   piano_note_player_prepare(note);

   evas_object_data_set(note->button, NOTE_BUTTON_DATA_KEY_NAME, note);

   note->ad->buttonList = eina_list_append(note->ad->buttonList, note);
}

Responding to different types of mouse events, the application extracts the target note, changes its style to indicate the pressed or unpressed note view, and plays the associated sound file.

void piano_note_touch_down(void *data, Evas_Point coord_on_main_win, Eina_Bool check_collision)
{
   RETM_IF(!data, "data is NULL");
   note_struct *note = (note_struct*) data;

   Evas_Coord button_left = 0, button_top = 0, button_width = 0, button_height = 0;
   evas_object_geometry_get(note->button, &button_left, &button_top, &button_width, &button_height);

   if (PIANO_NOTE_WHITE == note->type)
   {
      if (check_collision && (coord_on_main_win.y - button_top) < PIANO_NOTE_BORDER_OFFSET)
      {
         piano_note_touch_down(note->prev, coord_on_main_win, EINA_FALSE);
      }
      else if (check_collision && button_height - (coord_on_main_win.y - button_top) < PIANO_NOTE_BORDER_OFFSET)
      {
         piano_note_touch_down(note->next, coord_on_main_win, EINA_FALSE);
      }
   }

   switch (note->type)
   {
      case PIANO_NOTE_WHITE:
         elm_object_style_set((Evas_Object*)note->button, "circle/button_white_pressed");
         break;
      case PIANO_NOTE_BLACK:
         elm_object_style_set((Evas_Object*)note->button, "circle/button_black_pressed");
         break;
   }

   piano_player_play(note);
} 

Finally, to play sounds, the application checks the current status of the selected note and starts playing the associated sound file:

void piano_player_play(void *data)
{
   RETM_IF(!data, "data is NULL");
   note_struct *note = (note_struct*) data;

   piano_player_stop(note);

   int ret = player_start(note->player);
   _print_player_state(note->player);
   RETM_IF(ret != PLAYER_ERROR_NONE, "ret=%d, note->player=%p", ret, note->player);
}