浏览代码

Play a Mario tune

Thomas Chef 4 月之前
父节点
当前提交
94fc196d40
共有 3 个文件被更改,包括 235 次插入46 次删除
  1. 94 0
      import_midi.py
  2. 2 2
      main/CMakeLists.txt
  3. 139 44
      main/sound.c

+ 94 - 0
import_midi.py

@@ -0,0 +1,94 @@
+import struct
+
+# Constants for MIDI to Frequency conversion
+A4_MIDI = 69
+A4_FREQ = 440.0
+
+def midi_to_freq(note):
+    return A4_FREQ * (2 ** ((note - A4_MIDI) / 12.0))
+
+def read_variable_length(data, index):
+    value = 0
+    while True:
+        byte = data[index]
+        index += 1
+        value = (value << 7) | (byte & 0x7F)
+        if not (byte & 0x80):
+            break
+    return value, index
+
+def parse_midi(filename):
+    with open(filename, 'rb') as f:
+        data = f.read()
+    
+    # Check MIDI header
+    if data[:4] != b'MThd':
+        raise ValueError("Invalid MIDI file")
+    
+    # Read header values
+    header_size = struct.unpack('>I', data[4:8])[0]
+    format_type, num_tracks, division = struct.unpack('>HHH', data[8:14])
+    index = 14 + (header_size - 6)
+
+    print("Header size:", header_size)
+    print("Format type:", format_type)
+    print("Number of tracks:", num_tracks)
+    print("Division:", division)
+
+    
+    notes = []
+    active_notes = {}
+    last_time = 0
+    
+    while index < len(data):
+        if data[index:index+4] == b'MTrk':
+            index += 4
+            track_length = struct.unpack('>I', data[index:index+4])[0]
+            index += 4
+            track_end = index + track_length
+            
+            while index < track_end:
+                delta_time, index = read_variable_length(data, index)
+                last_time += delta_time
+                event = data[index]
+                index += 1
+                
+                if event == 0xFF:  # Meta event
+                    meta_type = data[index]
+                    index += 1
+                    length, index = read_variable_length(data, index)
+                    index += length  # Skip meta event data
+                elif event >= 0x80:  # MIDI event
+                    status = event & 0xF0
+                    channel = event & 0x0F
+                    
+                    if status in (0x90, 0x80):  # Note On/Off
+                        note = data[index]
+                        velocity = data[index + 1]
+                        index += 2
+                        
+                        if status == 0x90 and velocity > 0:  # Note On
+                            active_notes[note] = last_time
+                        elif status == 0x80 or (status == 0x90 and velocity == 0):  # Note Off
+                            if note in active_notes:
+                                start_time = active_notes.pop(note)
+                                duration = last_time - start_time
+                                pause_duration = 0 if not notes else start_time - (notes[-1][1] + notes[-1][2])
+                                notes.append((midi_to_freq(note), int(duration), int(pause_duration)))
+                    else:
+                        index += 1  # Skip unknown event
+    
+    return notes
+
+def generate_c_array(notes):
+    c_code = "const struct Note notes[] = {\n"
+    for freq, duration, pause in notes:
+        c_code += f"    {{{freq:.2f}, {duration}, {pause}}},\n"
+    c_code += "};\n"
+    return c_code
+
+# Example usage
+midi_file = "track_1.mid"  # Replace with your MIDI file
+notes = parse_midi(midi_file)
+c_array = generate_c_array(notes)
+print(c_array)

+ 2 - 2
main/CMakeLists.txt

@@ -1,4 +1,4 @@
 idf_component_register(SRCS "sound.c" "main.c"
 idf_component_register(SRCS "sound.c" "main.c"
-
-                    INCLUDE_DIRS ".")
+                    INCLUDE_DIRS "."
+                    )
 
 

+ 139 - 44
main/sound.c

@@ -2,38 +2,152 @@
 #include <stdio.h>
 #include <stdio.h>
 #include "freertos/FreeRTOS.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
 #include "freertos/task.h"
-#include "driver/gpio.h"
+//#include "driver/gpio.h"
 #include "esp_log.h"
 #include "esp_log.h"
 #include "sdkconfig.h"
 #include "sdkconfig.h"
-#include "driver/ledc.h"
+#include "driver/dac.h"
+#include "math.h"
 
 
-#define PWM_PIN       GPIO_NUM_19       // GPIO pin for PWM output
-#define PWM_FREQ      262               // Frequency for C4 (Middle C)
+#define DAC_CHANNEL       1
+#define DAC_PIN           GPIO_NUM_25   // GPIO pin for DAC output GPIO25=Channel 1
+#define TONE_FREQ         262               // Frequency for C4 (Middle C)
 #define PWM_CHANNEL   LEDC_CHANNEL_0
 #define PWM_CHANNEL   LEDC_CHANNEL_0
 #define PWM_TIMER     LEDC_TIMER_0
 #define PWM_TIMER     LEDC_TIMER_0
 #define PWM_RES       LEDC_TIMER_8_BIT  // 8-bit resolution (0-255)
 #define PWM_RES       LEDC_TIMER_8_BIT  // 8-bit resolution (0-255)
 
 
+typedef struct {
+    int frequency;      // Frequency in Hz
+    int duration;       // Duration in ms
+    int quietDuration;  // The quiet duration after each note
+} Note;
+
+// Super Mario Bros. short intro melody
+/*Note melody[] = {
+    {660, 100}, {0, 150}, {660, 100}, {0, 300}, {660, 100}, {0, 300}, {510, 100}, {660, 100}, {770, 100}, {0, 550}, 
+    {380, 100}, {0, 500}, {510, 100}, {0, 300}, {380, 100}, {0, 300}, {320, 100}, {0, 700}  // End of melody
+};*/
+/*Note melody[] = {
+    {440, 500}, {440, 500}, {440, 500}, {349, 350}, {523, 150}, {440, 500}, {349, 350}, {523, 150}, {440, 1000},
+    {659, 500}, {659, 500}, {659, 500}, {698, 350}, {523, 150}, {415, 500}, {349, 350}, {523, 150}, {440, 1000}, 
+};*/
+/*Note melody[] = {
+    {1318, 150}, {2637, 150}, {2093, 150}, {2349, 150}, {3136, 300}
+};*/
+
+/*Note melody[] = {
+    {320, 200, 200},{320, 200, 200},{320, 200, 200},{320, 200, 200},{320, 200, 200},{320, 200, 200},{320, 200, 200},
+    
+    {660, 100, 150}, {660, 100, 300}, {660, 100, 300}, {510, 100, 100}, {660, 100, 300}, {770, 100, 550}, {380, 100, 575},
+    {510, 100, 450}, {380, 100, 400}, {320, 100, 500}, {440, 100, 300}, {480, 80, 330}, {450, 100, 150}, {430, 100, 300},
+    {380, 100, 200}, {660, 80, 200}, {760, 50, 150}, {860, 100, 300}, {700, 80, 150}, {760, 50, 350}, {660, 80, 300},
+    {520, 80, 150}, {580, 80, 150}, {480, 80, 500},
+
+    {510, 100, 450}, {380, 100, 400}, {320, 100, 500}, {440, 100, 300}, {480, 80, 330}, {450, 100, 150}, {430, 100, 300},
+    {380, 100, 200}, {660, 80, 200}, {760, 50, 150}, {860, 100, 300}, {700, 80, 150}, {760, 50, 350}, {660, 80, 300},
+    {520, 80, 150}, {580, 80, 150}, {480, 80, 500},
+
+    {500, 100, 300}, {760, 100, 100}, {720, 100, 150}, {680, 100, 150}, {620, 150, 300}, {650, 150, 300}, {380, 100, 150},
+    {430, 100, 150}, {500, 100, 300}, {430, 100, 150}, {500, 100, 100}, {570, 100, 220}, {500, 100, 300}, {760, 100, 100},
+    {720, 100, 150}, {680, 100, 150}, {620, 150, 300}, {650, 200, 300},
+
+    {1020, 80, 300}, {1020, 80, 150}, {1020, 80, 300}, {380, 100, 300}, {500, 100, 300}, {760, 100, 100}, {720, 100, 150},
+    {680, 100, 150}, {620, 150, 300}, {650, 150, 300}, {380, 100, 150}, {430, 100, 150}, {500, 100, 300}, {430, 100, 150},
+    {500, 100, 100}, {570, 100, 420}, {585, 100, 450}, {550, 100, 420}, {500, 100, 360}, {380, 100, 300}, {500, 100, 300},
+
+    {500, 100, 150}, {500, 100, 300}, {500, 100, 300}, {760, 100, 100}, {720, 100, 150}, {680, 100, 150}, {620, 150, 300},
+    {650, 150, 300}, {380, 100, 150}, {430, 100, 150}, {500, 100, 300}, {430, 100, 150}, {500, 100, 100}, {570, 100, 220},
+    {500, 100, 300}, {760, 100, 100}, {720, 100, 150}, {680, 100, 150}, {620, 150, 300}, {650, 200, 300},
+
+    {1020, 80, 300}, {1020, 80, 150}, {1020, 80, 300}, {380, 100, 300}, {500, 100, 300}, {760, 100, 100}, {720, 100, 150},
+    {680, 100, 150}, {620, 150, 300}, {650, 150, 300}, {380, 100, 150}, {430, 100, 150}, {500, 100, 300}, {430, 100, 150},
+    {500, 100, 100}, {570, 100, 420}, {585, 100, 450}, {550, 100, 420}, {500, 100, 360}, {380, 100, 300}, {500, 100, 300},
+
+    {500, 60, 150}, {500, 80, 300}, {500, 60, 350}, {500, 80, 150}, {580, 80, 350}, {660, 80, 150}, {500, 80, 300},
+    {430, 80, 150}, {380, 80, 600}, {500, 60, 150}, {500, 80, 300}, {500, 60, 350}, {500, 80, 150}, {580, 80, 150},
+    {660, 80, 550}, {870, 80, 325}, {760, 80, 600},
+
+    {500, 60, 150}, {500, 80, 300}, {500, 60, 350}, {500, 80, 150}, {580, 80, 350}, {660, 80, 150}, {500, 80, 300},
+    {430, 80, 150}, {380, 80, 600}, {660, 100, 150}, {660, 100, 300}, {660, 100, 300}, {510, 100, 100}, {660, 100, 300},
+    {770, 100, 550}, {380, 100, 575}
+};*/
+
+const Note melody[] = {
+    {196.00, 152, 0},
+    {261.63, 152, 39},
+    {329.63, 151, 160},
+    {392.00, 152, 201},
+    {523.25, 152, 318},
+    {659.26, 151, 361},
+    {783.99, 456, 480},
+    {659.26, 456, 536},
+    {207.65, 152, 960},
+    {261.63, 152, 999},
+    {311.13, 151, 1120},
+    {415.30, 152, 1161},
+    {523.25, 152, 1278},
+    {622.25, 151, 1321},
+    {830.61, 456, 1440},
+    {622.25, 456, 1496},
+    {233.08, 152, 1920},
+    {293.66, 152, 1959},
+    {349.23, 151, 2080},
+    {466.16, 152, 2121},
+    {587.33, 152, 2238},
+    {698.46, 151, 2281},
+    {932.33, 456, 2400},
+    {932.33, 152, 2456},
+    {932.33, 152, 2863},
+    {932.33, 151, 2616},
+    {659.26, 456, 3025},
+};
+
+int melody_size = sizeof(melody) / sizeof(melody[0]);
+
+static void play_tone(int frequency, float duration, int quietDuration) {
+
+    int semitones = 2;
+
+    int t_freq = frequency * pow(2.0, semitones / 12.0);
+
+    int8_t cw_offset = 0;
+    dac_cw_config_t cosineConf = {DAC_CHANNEL_1, DAC_CW_SCALE_8, DAC_CW_PHASE_0, t_freq, cw_offset};
+
+    if (frequency == 0) {
+        dac_output_disable(DAC_CHANNEL_1);
+    } else {
+        dac_cw_generator_config(&cosineConf);
+        dac_output_enable(DAC_CHANNEL_1);
+    }
+    vTaskDelay(pdMS_TO_TICKS(duration));
+    //dac_output_disable(DAC_CHANNEL_1);
+    //vTaskDelay(pdMS_TO_TICKS(quietDuration));
+}
+
+void play_melody() {
+    for (int i = 0; i < melody_size; i++) {
+        play_tone(melody[i].frequency, melody[i].duration, melody[i].quietDuration);
+    }
+    dac_output_disable(DAC_CHANNEL_1);
+}
+
 static void setupSound() {
 static void setupSound() {
 
 
-    // Configure LEDC PWM Timer
-    ledc_timer_config_t timer_conf = {
-        .speed_mode = LEDC_LOW_SPEED_MODE,
-        .duty_resolution = PWM_RES,
-        .timer_num  = PWM_TIMER,
-        .freq_hz    = PWM_FREQ,
-        .clk_cfg    = LEDC_AUTO_CLK,
-    };
-    ledc_timer_config(&timer_conf);
-
-    // Configure LEDC PWM Channel
-    ledc_channel_config_t channel_conf = {
-        .gpio_num   = PWM_PIN,
-        .speed_mode = LEDC_LOW_SPEED_MODE,
-        .channel    = PWM_CHANNEL,
-        .timer_sel  = PWM_TIMER,
-        .duty       = 0,  // Start at 0% volume
-    };
-    ledc_channel_config(&channel_conf);
+    int8_t cw_offset = 0;
+    dac_cw_config_t cosineConf = {DAC_CHANNEL_1, DAC_CW_SCALE_8, DAC_CW_PHASE_0, 440, cw_offset};
+
+
+    dac_cw_generator_config(&cosineConf);
+    dac_cw_generator_enable();
+    //dac_output_enable(DAC_CHANNEL_1);
+
+    //vTaskDelay(pdMS_TO_TICKS(1000)); // Delay 1 second
+
+    dac_output_disable(DAC_CHANNEL_1);
+    
+
+    
+
+    
 
 
     ESP_LOGI("SOUND", "Sound setup");
     ESP_LOGI("SOUND", "Sound setup");
 }
 }
@@ -44,30 +158,11 @@ static void sound_task(void *pvParameters) {
 
 
     while (1) {
     while (1) {
 
 
-        ledc_set_duty(LEDC_LOW_SPEED_MODE, PWM_CHANNEL, 30);
-        ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_CHANNEL);
-
-        /*// Gradually increase volume (fade-in)
-        for (int duty = 10; duty <= 127; duty += 5) {
-            ledc_set_duty(LEDC_LOW_SPEED_MODE, PWM_CHANNEL, duty);
-            ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_CHANNEL);
-            vTaskDelay(pdMS_TO_TICKS(50));
-        }*/
-
-        vTaskDelay(pdMS_TO_TICKS(1000));  // Keep sound for 500ms
-
-        ledc_set_duty(LEDC_LOW_SPEED_MODE, PWM_CHANNEL, 10);
-        ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_CHANNEL);
 
 
-        /*// Gradually decrease volume (fade-out)
-        for (int duty = 127; duty >= 0; duty -= 5) {
-            ledc_set_duty(LEDC_LOW_SPEED_MODE, PWM_CHANNEL, duty);
-            ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_CHANNEL);
-            vTaskDelay(pdMS_TO_TICKS(50));
-        }*/
 
 
 
 
-        vTaskDelay(pdMS_TO_TICKS(1000)); // Delay 1 second
+        vTaskDelay(pdMS_TO_TICKS(5000));
+        play_melody();
     }
     }
 
 
     ESP_LOGI("SOUND", "Sound task starts....");
     ESP_LOGI("SOUND", "Sound task starts....");