Skip to content

Commit e6be51f

Browse files
committed
Add Playback Scene + Tone Properties
[Problem] The playback button doesn't do anything at the moment. [Solution] Added the playback scene which displays a waveform based on the properties of the tone. Properties of the tone are stored in the application context and then passed to the view model that displays the animation. The view for the animation is simply a custom view that has a callback called to draw to its canvas continuously. The waveform is drawn with lines in order to ensure it is drawn properly connected. Can currently display sine waves and square waves. [Testing] Ran on device and confirmed working as expected.
1 parent 82ea48e commit e6be51f

8 files changed

+149
-4
lines changed

README.MD

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This project depends on [uFBT](https://github.com/flipperdevices/flipperzero-ufb
1111
The application is structured such that each scene can be self contained, with the sharing of data between scenes done via the app context. All of the scenes can be found with their corresponding source and header files in the `src/scenes` directory. The scenes do as follows:
1212

1313
- **Starting Scene**: The scene where the application starts. This has the main menu options that users see when they start the app.
14+
- **Playback Scene**: The scene where the sound is played. Animates a waveform with the *approximate* shape of the sound being played.
1415
- **Settings Scene**: The scene where users can configure the tone's properties.
1516

1617
Note as well that the app context file is generic, and designed in such as way that it should not need to be updated for things specific to the application. This allows for an easier time to allow scenes to self manage, insteaed of having somewhere else that centrally manages everything.

src/app_context.c

+4
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ AppContextStatus initializeAppContext(
6060

6161
AppContextStatus addViewToAppContext(struct AppContext_t** context, struct View_t* view) {
6262
if(view->viewId > (*context)->activeViewsCount || view->viewId < 0) {
63+
FURI_LOG_I(TAG, "Not enough views!");
6364
return APP_CONTEXT_NOT_ENOUGH_VIEWS;
6465
}
6566
(*context)->activeViews[view->viewId] = view;
@@ -77,6 +78,9 @@ AppContextStatus freeAppContextViews(struct AppContext_t** context) {
7778
case MENU:
7879
menu_free(view->viewData);
7980
break;
81+
case VIEW:
82+
view_free(view->viewData);
83+
break;
8084
case POPUP:
8185
popup_free(view->viewData);
8286
break;

src/app_context.h

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
typedef enum {
1111
MENU,
12+
VIEW,
1213
POPUP,
1314
} ViewType;
1415

@@ -31,6 +32,7 @@ struct AppContext_t {
3132
ViewDispatcher* view_dispatcher;
3233
struct View_t** activeViews;
3334
int activeViewsCount;
35+
void* additionalData;
3436
};
3537

3638
/// @brief Creates an app context with the desired scene handlers.

src/scenes/playback_scene.c

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#include "playback_scene.h"
2+
#include "../app_context.h"
3+
#include "../tone_gen.h"
4+
5+
#define SINE_WAVE(x, toneModelData) \
6+
(toneDataModel->amplitude * \
7+
sin((x + toneDataModel->animationOffset) * 50 * toneDataModel->period) * 20 + \
8+
(64 / 2))
9+
10+
#define SQUARE_WAVE(x, toneModelData) \
11+
(toneDataModel->amplitude * \
12+
(sin((x + toneDataModel->animationOffset) * 50 * toneDataModel->period) > 0 ? 1 : -1) * \
13+
20 + \
14+
(64 / 2))
15+
16+
// Renders the waveform
17+
static void playback_view_draw_callback(Canvas* canvas, void* model) {
18+
UNUSED(model);
19+
20+
struct ToneData_t* toneDataModel = (struct ToneData_t*)model;
21+
for(int x = 1; x < 128; x++) {
22+
int x1 = x - 1;
23+
int x2 = x;
24+
int y1 = 0;
25+
int y2 = 0;
26+
switch(toneDataModel->waveType) {
27+
case SINE:
28+
y1 = SINE_WAVE(x1, toneDataModel);
29+
y2 = SINE_WAVE(x2, toneDataModel);
30+
break;
31+
case SQUARE:
32+
y1 = SQUARE_WAVE(x1, toneDataModel);
33+
y2 = SQUARE_WAVE(x2, toneDataModel);
34+
break;
35+
default:
36+
y1 = 64 / 2;
37+
y2 = 64 / 2;
38+
break;
39+
}
40+
// Draw lines to connect the pieces of the wave.
41+
canvas_draw_line(canvas, x1, y1, x2, y2);
42+
}
43+
if(toneDataModel->animationOffset < 128) {
44+
toneDataModel->animationOffset += 2;
45+
} else {
46+
toneDataModel->animationOffset = 0;
47+
}
48+
}
49+
50+
// Sets up the waveform to be displayed
51+
void scene_on_enter_playback_scene(void* context) {
52+
FURI_LOG_I(TAG, "scene_on_enter_playback_scene");
53+
struct AppContext_t* app = (struct AppContext_t*)context;
54+
struct View_t* playbackView = app->activeViews[ToneGenAppView_PlaybackView];
55+
56+
// Configure the custom view
57+
view_set_draw_callback(playbackView->viewData, playback_view_draw_callback);
58+
view_set_context(playbackView->viewData, context);
59+
60+
FURI_LOG_I(TAG, "setting view model");
61+
struct ToneData_t* toneDataModel = (struct ToneData_t*)view_get_model(playbackView->viewData);
62+
toneDataModel->amplitude = ((struct ToneData_t*)app->additionalData)->amplitude;
63+
toneDataModel->period = ((struct ToneData_t*)app->additionalData)->period;
64+
toneDataModel->waveType = ((struct ToneData_t*)app->additionalData)->waveType;
65+
66+
// Set the currently active view
67+
FURI_LOG_I(TAG, "setting active view");
68+
view_dispatcher_switch_to_view(app->view_dispatcher, ToneGenAppView_PlaybackView);
69+
}
70+
71+
// Not actively used in this instance.
72+
bool scene_on_event_playback_scene(void* context, SceneManagerEvent event) {
73+
FURI_LOG_I(TAG, "scene_on_event_playback_scene");
74+
UNUSED(context);
75+
UNUSED(event);
76+
return false;
77+
}
78+
79+
// Not actively used in this instance.
80+
void scene_on_exit_playback_scene(void* context) {
81+
FURI_LOG_I(TAG, "scene_on_exit_playback_scene");
82+
UNUSED(context);
83+
}

src/scenes/playback_scene.h

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef _PLAYBACK_SCENE_H_
2+
3+
#define _PLAYBACK_SCENE_H_
4+
5+
#include <gui/scene_manager.h>
6+
7+
void scene_on_enter_playback_scene(void* context);
8+
bool scene_on_event_playback_scene(void* context, SceneManagerEvent event);
9+
void scene_on_exit_playback_scene(void* context);
10+
11+
#endif

src/scenes/starting_scene.c

+3-4
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ void menu_callback_starting_scene(void* context, uint32_t index) {
1919
struct AppContext_t* app = context;
2020
switch(index) {
2121
case ToneGenAppMenuSelection_Play:
22-
FURI_LOG_I(TAG, "selection one");
23-
// scene_manager_handle_custom_event(app->scene_manager, ToneGenAppEvent_StartPlayback);
22+
scene_manager_handle_custom_event(app->scene_manager, ToneGenAppMenuSelection_Play);
2423
break;
2524
case ToneGenAppMenuSelection_Adjust:
2625
scene_manager_handle_custom_event(app->scene_manager, ToneGenAppMenuSelection_Adjust);
@@ -68,8 +67,8 @@ bool scene_on_event_starting_scene(void* context, SceneManagerEvent event) {
6867
case SceneManagerEventTypeCustom:
6968
switch(event.event) {
7069
case ToneGenAppMenuSelection_Play:
71-
// scene_manager_next_scene(app->scene_manager, ToneGenAppScene_Playback);
72-
// consumed = true;
70+
scene_manager_next_scene(app->scene_manager, ToneGenAppScene_Playback);
71+
consumed = true;
7372
break;
7473
case ToneGenAppMenuSelection_Adjust:
7574
scene_manager_next_scene(app->scene_manager, ToneGenAppScene_Settings);

src/tone_gen.c

+34
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <gui/canvas.h>
12
#include <gui/modules/menu.h>
23

34
/* generated by fbt from .png files in images folder */
@@ -7,23 +8,27 @@
78
#include "tone_gen.h"
89

910
#include "scenes/starting_scene.h"
11+
#include "scenes/playback_scene.h"
1012
#include "scenes/settings_scene.h"
1113

1214
/** collection of all scene on_enter handlers - in the same order as their enum */
1315
void (*const scene_on_enter_handlers[])(void*) = {
1416
scene_on_enter_starting_scene,
17+
scene_on_enter_playback_scene,
1518
scene_on_enter_settings_scene,
1619
};
1720

1821
/** collection of all scene on event handlers - in the same order as their enum */
1922
bool (*const scene_on_event_handlers[])(void*, SceneManagerEvent) = {
2023
scene_on_event_starting_scene,
24+
scene_on_event_playback_scene,
2125
scene_on_event_settings_scene,
2226
};
2327

2428
/** collection of all scene on exit handlers - in the same order as their enum */
2529
void (*const scene_on_exit_handlers[])(void*) = {
2630
scene_on_exit_starting_scene,
31+
scene_on_exit_playback_scene,
2732
scene_on_exit_settings_scene,
2833
};
2934

@@ -35,23 +40,46 @@ const SceneManagerHandlers scene_event_handlers = {
3540

3641
int setupViews(struct AppContext_t** appContext) {
3742
// Create views
43+
FURI_LOG_I(TAG, "Creating views");
3844
struct View_t* sharedMenuView = malloc(sizeof(struct View_t));
3945
sharedMenuView->viewData = menu_alloc();
4046
sharedMenuView->viewId = ToneGenAppView_SharedMenu;
4147
sharedMenuView->type = MENU;
4248

49+
struct View_t* playbackView = malloc(sizeof(struct View_t));
50+
playbackView->viewData = view_alloc();
51+
playbackView->viewId = ToneGenAppView_PlaybackView;
52+
playbackView->type = VIEW;
53+
4354
// Add views to the app context for management later
55+
FURI_LOG_I(TAG, "Adding views to app context");
4456
AppContextStatus result = addViewToAppContext(appContext, sharedMenuView);
4557
if(result != APP_CONTEXT_OK) {
4658
FURI_LOG_E(TAG, "There was a problem adding the view %d!", sharedMenuView->viewId);
4759
return -1;
4860
}
4961

62+
result = addViewToAppContext(appContext, playbackView);
63+
if(result != APP_CONTEXT_OK) {
64+
FURI_LOG_E(TAG, "There was a problem adding the view %d!", playbackView->viewId);
65+
return -1;
66+
}
67+
5068
// Add views to the view dispatcher for usage later
69+
FURI_LOG_I(TAG, "Adding views to view dispatcher");
5170
view_dispatcher_add_view(
5271
(*appContext)->view_dispatcher,
5372
sharedMenuView->viewId,
5473
menu_get_view(sharedMenuView->viewData));
74+
FURI_LOG_I(TAG, "Adding next view to app context");
75+
view_dispatcher_add_view(
76+
(*appContext)->view_dispatcher, playbackView->viewId, playbackView->viewData);
77+
FURI_LOG_I(TAG, "Done making views");
78+
79+
// On the playback view, ensure we only allocate for the model once
80+
FURI_LOG_I(TAG, "allocating view model for playback");
81+
view_allocate_model(playbackView->viewData, ViewModelTypeLockFree, sizeof(struct ToneData_t));
82+
5583
return 0;
5684
}
5785

@@ -65,6 +93,12 @@ int32_t tone_gen_app(void* p) {
6593
initializeAppContext(&appContext, ToneGenAppView_count, &scene_event_handlers);
6694

6795
if(result == APP_CONTEXT_OK) {
96+
appContext->additionalData = malloc(sizeof(struct ToneData_t));
97+
((struct ToneData_t*)appContext->additionalData)->animationOffset = 0;
98+
((struct ToneData_t*)appContext->additionalData)->amplitude = 1;
99+
((struct ToneData_t*)appContext->additionalData)->period = 1;
100+
((struct ToneData_t*)appContext->additionalData)->waveType = SINE;
101+
68102
result = setupViews(&appContext);
69103
if(result == 0) {
70104
// set the scene and launch the main loop

src/tone_gen.h

+11
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,26 @@
1010
// ids for all scenes used by the app
1111
typedef enum {
1212
ToneGenAppScene_Starting,
13+
ToneGenAppScene_Playback,
1314
ToneGenAppScene_Settings,
1415
ToneGenAppScene_count
1516
} ToneGenAppScene;
1617

1718
// ids for the 2 types of view used by the app
1819
typedef enum {
1920
ToneGenAppView_SharedMenu,
21+
ToneGenAppView_PlaybackView,
2022
ToneGenAppView_Popup,
2123
ToneGenAppView_count
2224
} ToneGenAppView;
2325

26+
typedef enum { SQUARE, SINE } ToneWaveType;
27+
28+
struct ToneData_t {
29+
int animationOffset;
30+
int amplitude;
31+
int period;
32+
ToneWaveType waveType;
33+
};
34+
2435
#endif

0 commit comments

Comments
 (0)