Skip to content

Commit cf19fbb

Browse files
committed
add synt_piano
1 parent f24b1dd commit cf19fbb

File tree

8 files changed

+215
-2
lines changed

8 files changed

+215
-2
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ popd
2121
pushd apps/calculator
2222
esptool.py --chip esp32s3 --baud 921600 --before default_reset --after hard_reset write_flash 0x7A0000 build/calculator.bin
2323
popd
24+
pushd apps/synth_piano
25+
esptool.py --chip esp32s3 --baud 921600 --before default_reset --after hard_reset write_flash 0xA60000 build/synth_piano.bin
26+
popd
2427
```
2528

2629
## Build

apps/synth_piano/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# The following five lines of boilerplate have to be in your project's
2+
# CMakeLists in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.16)
4+
5+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
6+
project(synth_piano)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
idf_component_register(SRCS "synth_piano.c"
2+
INCLUDE_DIRS "."
3+
REQUIRES app_update)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
## IDF Component Manager Manifest File
2+
dependencies:
3+
espressif/esp-box: "^3.1.0"
4+
#espressif/esp-box-3: "^1.2.0"
5+
# Workaround for i2c: CONFLICT! driver_ng is not allowed to be used with this old driver
6+
esp_codec_dev:
7+
public: true
8+
version: "==1.1.0"
9+
## Required IDF version
10+
idf:
11+
version: ">=5.0.0"
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
#include <stdio.h>
2+
#include <math.h>
3+
#include "freertos/FreeRTOS.h"
4+
#include "freertos/task.h"
5+
#include "esp_log.h"
6+
#include "esp_system.h"
7+
#include "lvgl.h"
8+
#include "bsp/esp-bsp.h"
9+
#include "esp_timer.h"
10+
#include "driver/i2s.h"
11+
#include "esp_ota_ops.h"
12+
13+
#define TAG "SynthPiano"
14+
#define SAMPLE_RATE 44100
15+
#define DEFAULT_VOLUME 90
16+
17+
static lv_obj_t *octave_label;
18+
static int current_octave = 4;
19+
static int last_note_index = -1;
20+
static int last_octave_event = -1;
21+
static esp_codec_dev_handle_t spk_codec_dev = NULL;
22+
static QueueHandle_t tone_queue;
23+
24+
typedef struct {
25+
float frequency;
26+
int duration_ms;
27+
} tone_t;
28+
29+
static void update_display() {
30+
char buffer[16];
31+
snprintf(buffer, sizeof(buffer), "Octave: %d", current_octave);
32+
bsp_display_lock(0);
33+
lv_label_set_text(octave_label, buffer);
34+
bsp_display_unlock();
35+
}
36+
37+
static void play_tone(float frequency, int duration_ms) {
38+
const int sample_rate = SAMPLE_RATE;
39+
const int num_samples = (sample_rate * duration_ms) / 1000;
40+
const float amplitude = 1.0;
41+
42+
int16_t *samples = malloc(num_samples * sizeof(int16_t));
43+
if (!samples) {
44+
ESP_LOGE(TAG, "Failed to allocate memory for samples");
45+
return;
46+
}
47+
48+
for (int i = 0; i < num_samples; ++i) {
49+
samples[i] = (int16_t)(amplitude * INT16_MAX * sinf(2.0f * M_PI * frequency * i / sample_rate));
50+
}
51+
52+
esp_codec_dev_write(spk_codec_dev, samples, num_samples * sizeof(int16_t));
53+
vTaskDelay(pdMS_TO_TICKS(duration_ms)); // Ensure the tone plays for the specified duration
54+
55+
free(samples);
56+
}
57+
58+
static void tone_task(void *param) {
59+
tone_t tone;
60+
while (1) {
61+
if (xQueueReceive(tone_queue, &tone, portMAX_DELAY)) {
62+
play_tone(tone.frequency, tone.duration_ms);
63+
}
64+
}
65+
}
66+
67+
static void note_event_cb(lv_event_t *e) {
68+
lv_obj_t *btn = lv_event_get_target(e);
69+
const char *txt = lv_btnmatrix_get_btn_text(btn, lv_btnmatrix_get_selected_btn(btn));
70+
71+
float frequencies[12] = {
72+
261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88
73+
};
74+
int note_index = atoi(txt);
75+
if (note_index == last_note_index) {
76+
return; // Ignore repeated events for the same note
77+
}
78+
last_note_index = note_index;
79+
80+
if (note_index >= 0 && note_index < 12) {
81+
float frequency = frequencies[note_index] * powf(2.0f, current_octave - 4);
82+
tone_t tone = { .frequency = frequency, .duration_ms = 250 };
83+
xQueueSend(tone_queue, &tone, portMAX_DELAY); // Send tone to queue
84+
}
85+
}
86+
87+
static void octave_event_cb(lv_event_t *e) {
88+
lv_obj_t *btn = lv_event_get_target(e);
89+
const char *txt = lv_btnmatrix_get_btn_text(btn, lv_btnmatrix_get_selected_btn(btn));
90+
91+
if ((last_octave_event == 0 && strcmp(txt, "Up") == 0) || (last_octave_event == 1 && strcmp(txt, "Down") == 0)) {
92+
return; // Ignore repeated events for the same octave button
93+
}
94+
95+
if (strcmp(txt, "Up") == 0) {
96+
last_octave_event = 0;
97+
current_octave = (current_octave < 8) ? current_octave + 1 : current_octave;
98+
} else if (strcmp(txt, "Down") == 0) {
99+
last_octave_event = 1;
100+
current_octave = (current_octave > 0) ? current_octave - 1 : current_octave;
101+
}
102+
update_display();
103+
}
104+
105+
void app_audio_init(void)
106+
{
107+
/* Initialize speaker */
108+
spk_codec_dev = bsp_audio_codec_speaker_init();
109+
assert(spk_codec_dev);
110+
/* Speaker output volume */
111+
esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME);
112+
113+
esp_codec_dev_open(spk_codec_dev, &(esp_codec_dev_sample_info_t){
114+
.sample_rate = SAMPLE_RATE,
115+
.channel = 1,
116+
.bits_per_sample = 16,
117+
});
118+
}
119+
120+
void reset_to_factory_app() {
121+
// Get the partition structure for the factory partition
122+
const esp_partition_t *factory_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
123+
if (factory_partition != NULL) {
124+
if (esp_ota_set_boot_partition(factory_partition) == ESP_OK) {
125+
printf("Set boot partition to factory, restarting now.\n");
126+
} else {
127+
printf("Failed to set boot partition to factory.\n");
128+
}
129+
} else {
130+
printf("Factory partition not found.\n");
131+
}
132+
133+
fflush(stdout);
134+
}
135+
136+
void app_main(void) {
137+
// Reset to factory app for the next boot.
138+
// It should return to graphical bootloader.
139+
reset_to_factory_app();
140+
141+
// Initialize the BSP
142+
bsp_i2c_init();
143+
bsp_display_start();
144+
lv_init();
145+
app_audio_init();
146+
147+
// Create a label for the octave
148+
octave_label = lv_label_create(lv_scr_act());
149+
lv_label_set_text(octave_label, "Octave: 4");
150+
lv_obj_align(octave_label, LV_ALIGN_TOP_LEFT, 0, 10);
151+
152+
// Create a button matrix for the octave control
153+
static const char *octave_map[] = {
154+
"Up", "\n",
155+
"Down", ""
156+
};
157+
158+
lv_obj_t *octave_btnm = lv_btnmatrix_create(lv_scr_act());
159+
lv_btnmatrix_set_map(octave_btnm, octave_map);
160+
lv_obj_set_size(octave_btnm, 150, 60);
161+
lv_obj_align(octave_btnm, LV_ALIGN_TOP_RIGHT, 0, 0);
162+
lv_obj_add_event_cb(octave_btnm, octave_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
163+
164+
// Create a button matrix for the notes
165+
static const char *note_map[] = {
166+
"0", "1", "2", "3", "\n",
167+
"4", "5", "6", "7", "\n",
168+
"8", "9", "10", "11", ""
169+
};
170+
171+
lv_obj_t *note_btnm = lv_btnmatrix_create(lv_scr_act());
172+
lv_btnmatrix_set_map(note_btnm, note_map);
173+
lv_obj_set_size(note_btnm, 320, 150);
174+
lv_obj_align(note_btnm, LV_ALIGN_CENTER, 0, 30);
175+
lv_obj_add_event_cb(note_btnm, note_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
176+
177+
// Create the tone queue
178+
tone_queue = xQueueCreate(10, sizeof(tone_t));
179+
assert(tone_queue != NULL);
180+
181+
// Create the tone task
182+
xTaskCreate(tone_task, "tone_task", 2048, NULL, 5, NULL);
183+
184+
bsp_display_backlight_on();
185+
186+
while (1) {
187+
vTaskDelay(pdMS_TO_TICKS(1000));
188+
}
189+
}

main/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ idf_component_register(SRCS
88
lvgl_port_create_c_image("../resources/images/icon_tic_tac_toe.png" "images/gen/" "ARGB8888" "NONE")
99
lvgl_port_create_c_image("../resources/images/icon_wifi_list.png" "images/gen/" "ARGB8888" "NONE")
1010
lvgl_port_create_c_image("../resources/images/icon_calculator.png" "images/gen/" "ARGB8888" "NONE")
11+
lvgl_port_create_c_image("../resources/images/icon_synth_piano.png" "images/gen/" "ARGB8888" "NONE")
1112
lvgl_port_add_images(${COMPONENT_LIB} "images/gen/")

main/bootloader_ui.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ static lv_obj_t *g_group_list[3] = {0};
4242
LV_IMG_DECLARE(icon_tic_tac_toe)
4343
LV_IMG_DECLARE(icon_wifi_list)
4444
LV_IMG_DECLARE(icon_calculator)
45-
// LV_IMG_DECLARE(icon_app4)
45+
LV_IMG_DECLARE(icon_synth_piano)
4646
// LV_IMG_DECLARE(icon_app5)
4747

4848
void ui_app1_start(void (*fn)(void));
@@ -55,7 +55,7 @@ static item_desc_t item[] = {
5555
{ "Tic-Tac-Toe", (void *) &icon_tic_tac_toe, ui_app1_start, NULL},
5656
{ "Wi-Fi List", (void *) &icon_wifi_list, ui_app2_start, NULL},
5757
{ "Calculator", (void *) &icon_calculator, ui_app3_start, NULL},
58-
{ "App4", (void *) &icon_tic_tac_toe, ui_app4_start, NULL},
58+
{ "Piano", (void *) &icon_synth_piano, ui_app4_start, NULL},
5959
{ "App5", (void *) &icon_tic_tac_toe, ui_app5_start, NULL},
6060
};
6161

350 Bytes
Loading

0 commit comments

Comments
 (0)