# Audio Waveform

## Lab 3: Audio Waveform Display

### Part 3 - Oscilloscope & Signal Processing

***

### 1. โครงสร้างภาพรวมของ Lab

#### Why? - ทำไมต้องเรียนรู้เรื่องนี้

* **Harmonics in Power Systems**: ในระบบไฟฟ้า Harmonics (ฮาร์มอนิกส์) คือปัญหาสำคัญ สัญญาณ 50 Hz อุดมคติจะถูกบิดเบี้ยวด้วย harmonic ที่ 3rd (150Hz), 5th (250Hz), 7th (350Hz) ทำให้เกิดปัญหา power quality
* **Sound & Music**: เสียงเป็น waveform ที่ซับซ้อน เกิดจากการรวม harmonics หลายตัว ความแตกต่างของเสียงเครื่องดนตรีแต่ละชิ้น (Timbre) มาจากสัดส่วน harmonics ที่ต่างกัน
* **Fourier Series**: ทุก periodic waveform สามารถสลายเป็นผลรวมของ sine waves (harmonics) ได้ นี่คือพื้นฐานของ Fourier Analysis ที่ใช้ในงาน EE ทุกสาขา
* **EE Application**: วิเคราะห์ THD (Total Harmonic Distortion) ในระบบไฟฟ้า, ออกแบบ Active Harmonic Filter, Audio Equalizer

#### What? - จะได้เรียนรู้อะไร

1. **Musical Frequencies**: ความถี่โน้ตดนตรี (A4=440Hz, C4=261.6Hz)
2. **Harmonics Theory**: Fundamental + 2nd + 3rd harmonics และผลต่อ waveform shape
3. **Waveform Synthesis**: สร้าง waveform ซับซ้อนจาก sine waves หลายตัว
4. **Timbre Concept**: ทำไมเสียงเปียโนกับไวโอลินถึงต่างกันที่ความถี่เดียวกัน
5. **AM Modulation**: Amplitude Modulation พื้นฐาน (carrier \* modulating)

#### How? - จะทำอย่างไร

1. สร้างปุ่มโน้ตดนตรี C, D, E, F, G, A, B
2. แต่ละปุ่มสร้าง sine wave ที่ความถี่ของโน้ตนั้น
3. เพิ่ม slider ปรับระดับ Harmonic (2nd, 3rd)
4. แสดง waveform ที่เปลี่ยนรูปร่างตาม harmonic content
5. สังเกตความสัมพันธ์ระหว่าง harmonic content กับ waveform shape

<figure><img src="/files/ENJZMAmULhv24uUNna0a" alt=""><figcaption></figcaption></figure>

***

### 2. หลักการทำงานและ Flowchart

#### 2.1 Musical Note Frequencies

```
┌─────────────────────────────────────────────────────────────────┐
│                MUSICAL NOTE FREQUENCIES                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Octave 4 (Middle):                                             │
│  ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┐             │
│  │  C4  │  D4  │  E4  │  F4  │  G4  │  A4  │  B4  │             │
│  │261.6 │293.7 │329.6 │349.2 │392.0 │440.0 │493.9 │ Hz          │
│  └──────┴──────┴──────┴──────┴──────┴──────┴──────┘             │
│                                                                 │
│  ความสัมพันธ์:                                                     │
│  - Octave ขึ้น = frequency x 2  (A4=440 -> A5=880 Hz)             │
│  - Octave ลง = frequency / 2   (A4=440 -> A3=220 Hz)            │
│  - Semitone ratio = 2^(1/12) = 1.05946                          │
│                                                                 │
│  Standard Tuning: A4 = 440 Hz (ISO 16)                          │
│  ไฟฟ้าไทย:       50 Hz (ต่ำกว่า G1 = 49.0 Hz เล็กน้อย)               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

***

### 3. ฟังก์ชันสำคัญ

#### 3.1 Audio Constants

<table><thead><tr><th width="138.77984619140625">Constant</th><th width="179.9268798828125">Value</th><th>Description</th></tr></thead><tbody><tr><td><code>NOTE_C4</code></td><td>262</td><td>โน้ต C (โด)</td></tr><tr><td><code>NOTE_D4</code></td><td>294</td><td>โน้ต D (เร)</td></tr><tr><td><code>NOTE_E4</code></td><td>330</td><td>โน้ต E (มี)</td></tr><tr><td><code>NOTE_F4</code></td><td>349</td><td>โน้ต F (ฟา)</td></tr><tr><td><code>NOTE_G4</code></td><td>392</td><td>โน้ต G (ซอล)</td></tr><tr><td><code>NOTE_A4</code></td><td>440</td><td>โน้ต A (ลา) - Standard Tuning</td></tr><tr><td><code>NOTE_B4</code></td><td>494</td><td>โน้ต B (ที)</td></tr></tbody></table>

#### 3.2 Synthesis Functions

<table><thead><tr><th width="304.22723388671875">Function</th><th>Description</th></tr></thead><tbody><tr><td><code>generate_with_harmonics()</code></td><td>สร้าง waveform พร้อม harmonics</td></tr><tr><td><code>add_harmonic()</code></td><td>เพิ่ม harmonic ลงใน buffer</td></tr><tr><td><code>update_waveform_display()</code></td><td>อัพเดท chart</td></tr></tbody></table>

***

### 4. Code เต็ม

#### 4.1 Constants and Global Variables

```c
#include "lvgl.h"
#include <math.h>
#include <stdlib.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#define BUFFER_SIZE     512
#define DISPLAY_POINTS  200
#define SAMPLE_RATE     44100
#define NUM_NOTES       7

/* Musical note frequencies (Octave 4) */
static const uint32_t note_freqs[NUM_NOTES] = {
    262, 294, 330, 349, 392, 440, 494
};
static const char *note_names[NUM_NOTES] = {
    "C4", "D4", "E4", "F4", "G4", "A4", "B4"
};

/* Global variables */
static int16_t audio_buffer[BUFFER_SIZE];
static lv_obj_t *audio_chart = NULL;
static lv_chart_series_t *audio_series = NULL;
static lv_obj_t *note_label = NULL;
static lv_obj_t *harm2_label = NULL;
static lv_obj_t *harm3_label = NULL;
static uint32_t current_note_freq = 440;
static uint8_t current_note_idx = 5;  /* A4 */
static float harm2_level = 0.0f;  /* 2nd harmonic (0.0 - 1.0) */
static float harm3_level = 0.0f;  /* 3rd harmonic (0.0 - 1.0) */
```

#### 4.2 Waveform Synthesis with Harmonics

```c
/* สร้าง waveform พร้อม harmonics */
static void generate_with_harmonics(int16_t *buf, uint16_t count,
                                      uint32_t freq, uint32_t fs,
                                      float h2, float h3)
{
    /* Normalization factor: ป้องกัน clipping เมื่อรวม harmonics */
    float total_amp = 1.0f + fabsf(h2) + fabsf(h3);

    for (uint16_t i = 0; i < count; i++) {
        double t = (double)i / (double)fs;

        /* Fundamental (1st harmonic) */
        double val = sin(2.0 * M_PI * freq * t);

        /* 2nd harmonic (2 * frequency) */
        val += h2 * sin(2.0 * M_PI * (2 * freq) * t);

        /* 3rd harmonic (3 * frequency) */
        val += h3 * sin(2.0 * M_PI * (3 * freq) * t);

        /* Normalize และ scale ไปที่ int16_t range */
        val = val / total_amp;
        buf[i] = (int16_t)(val * 1000);
    }
}

/* อัพเดท chart display */
static void update_waveform_display(void)
{
    generate_with_harmonics(audio_buffer, BUFFER_SIZE,
                            current_note_freq, SAMPLE_RATE,
                            harm2_level, harm3_level);

    if (!audio_chart || !audio_series) return;

    for (int i = 0; i < DISPLAY_POINTS; i++) {
        /* ใช้ stride เพื่อแสดง waveform ที่ดีขึ้น */
        int idx = i * BUFFER_SIZE / DISPLAY_POINTS;
        if (idx >= BUFFER_SIZE) idx = BUFFER_SIZE - 1;

        int32_t val = ((int32_t)audio_buffer[idx] + 1000) * 100 / 2000;
        if (val < 0) val = 0;
        if (val > 100) val = 100;
        lv_chart_set_value_by_id(audio_chart, audio_series, i, val);
    }
    lv_chart_refresh(audio_chart);

    /* อัพเดท note label */
    if (note_label) {
        lv_label_set_text_fmt(note_label, "%s = %u Hz",
                              note_names[current_note_idx],
                              (unsigned int)current_note_freq);
    }
}
```

#### 4.3 Event Callbacks

```c
/* Piano key button callback */
static void note_btn_cb(lv_event_t *e)
{
    if (lv_event_get_code(e) != LV_EVENT_CLICKED) return;
    lv_obj_t *btn = lv_event_get_target(e);
    uint32_t idx = (uint32_t)(uintptr_t)lv_obj_get_user_data(btn);

    if (idx < NUM_NOTES) {
        current_note_idx = idx;
        current_note_freq = note_freqs[idx];
        update_waveform_display();

        /* Highlight selected button */
        lv_obj_t *parent = lv_obj_get_parent(btn);
        for (int i = 0; i < (int)lv_obj_get_child_count(parent); i++) {
            lv_obj_t *child = lv_obj_get_child(parent, i);
            lv_obj_set_style_bg_color(child, lv_color_hex(0x333355), 0);
        }
        lv_obj_set_style_bg_color(btn, lv_color_hex(0x00aa66), 0);
    }
}

/* 2nd Harmonic slider callback */
static void harm2_slider_cb(lv_event_t *e)
{
    if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return;
    lv_obj_t *slider = lv_event_get_target(e);
    int32_t pct = lv_slider_get_value(slider);
    harm2_level = (float)pct / 100.0f;
    lv_label_set_text_fmt(harm2_label, "2nd: %d%%", (int)pct);
    update_waveform_display();
}

/* 3rd Harmonic slider callback */
static void harm3_slider_cb(lv_event_t *e)
{
    if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return;
    lv_obj_t *slider = lv_event_get_target(e);
    int32_t pct = lv_slider_get_value(slider);
    harm3_level = (float)pct / 100.0f;
    lv_label_set_text_fmt(harm3_label, "3rd: %d%%", (int)pct);
    update_waveform_display();
}
```

#### 4.4 Main Function

```c
void part3_ex3_audio_waveform(void)
{
    lv_obj_t *scr = lv_screen_active();
    lv_obj_set_style_bg_color(scr, lv_color_hex(0x0a0a1e), 0);

    /* ===== Title ===== */
    lv_obj_t *title = lv_label_create(scr);
    lv_label_set_text(title, "Part 3 Ex3: Audio Waveform & Harmonics");
    lv_obj_set_style_text_color(title, lv_color_hex(0x00ff88), 0);
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 5);

    /* ===== Waveform Chart ===== */
    audio_chart = lv_chart_create(scr);
    lv_obj_set_size(audio_chart, 400, 130);
    lv_obj_align(audio_chart, LV_ALIGN_TOP_MID, 0, 28);
    lv_chart_set_type(audio_chart, LV_CHART_TYPE_LINE);
    lv_chart_set_point_count(audio_chart, DISPLAY_POINTS);
    lv_chart_set_range(audio_chart, LV_CHART_AXIS_PRIMARY_Y, 0, 100);
    lv_chart_set_div_line_count(audio_chart, 4, 5);

    lv_obj_set_style_bg_color(audio_chart, lv_color_hex(0x001a00), 0);
    lv_obj_set_style_line_color(audio_chart, lv_color_hex(0x003300),
                                LV_PART_MAIN);
    lv_obj_set_style_border_color(audio_chart, lv_color_hex(0x006600), 0);
    lv_obj_set_style_border_width(audio_chart, 1, 0);
    lv_obj_set_style_size(audio_chart, 0, 0, LV_PART_INDICATOR);

    audio_series = lv_chart_add_series(audio_chart,
                       lv_color_hex(0x00ff00), LV_CHART_AXIS_PRIMARY_Y);

    /* ===== Piano Key Buttons ===== */
    /* Container สำหรับปุ่มโน้ต */
    lv_obj_t *key_cont = lv_obj_create(scr);
    lv_obj_set_size(key_cont, 400, 45);
    lv_obj_align(key_cont, LV_ALIGN_CENTER, 0, 20);
    lv_obj_set_style_bg_opa(key_cont, LV_OPA_TRANSP, 0);
    lv_obj_set_style_border_width(key_cont, 0, 0);
    lv_obj_set_style_pad_all(key_cont, 2, 0);
    lv_obj_set_flex_flow(key_cont, LV_FLEX_FLOW_ROW);
    lv_obj_set_flex_align(key_cont, LV_FLEX_ALIGN_SPACE_EVENLY,
                          LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_remove_flag(key_cont, LV_OBJ_FLAG_SCROLLABLE);

    const char *key_labels[] = {"C", "D", "E", "F", "G", "A", "B"};
    for (int i = 0; i < NUM_NOTES; i++) {
        lv_obj_t *btn = lv_btn_create(key_cont);
        lv_obj_set_size(btn, 48, 35);
        lv_obj_set_style_bg_color(btn, lv_color_hex(0x333355), 0);
        lv_obj_set_style_radius(btn, 4, 0);
        lv_obj_set_user_data(btn, (void*)(uintptr_t)i);
        lv_obj_add_event_cb(btn, note_btn_cb, LV_EVENT_CLICKED, NULL);

        lv_obj_t *lbl = lv_label_create(btn);
        lv_label_set_text(lbl, key_labels[i]);
        lv_obj_center(lbl);

        /* Highlight A4 (default) */
        if (i == 5) {
            lv_obj_set_style_bg_color(btn, lv_color_hex(0x00aa66), 0);
        }
    }

    /* ===== Harmonic Sliders ===== */
    /* 2nd Harmonic */
    lv_obj_t *h2_title = lv_label_create(scr);
    lv_label_set_text(h2_title, "2nd Harm:");
    lv_obj_set_style_text_color(h2_title, lv_color_hex(0xaaaaaa), 0);
    lv_obj_align(h2_title, LV_ALIGN_BOTTOM_LEFT, 10, -45);

    lv_obj_t *h2_slider = lv_slider_create(scr);
    lv_slider_set_range(h2_slider, 0, 100);
    lv_slider_set_value(h2_slider, 0, LV_ANIM_OFF);
    lv_obj_set_width(h2_slider, 160);
    lv_obj_align(h2_slider, LV_ALIGN_BOTTOM_LEFT, 100, -45);
    lv_obj_add_event_cb(h2_slider, harm2_slider_cb,
                        LV_EVENT_VALUE_CHANGED, NULL);

    harm2_label = lv_label_create(scr);
    lv_label_set_text(harm2_label, "2nd: 0%");
    lv_obj_set_style_text_color(harm2_label, lv_color_hex(0xffaa00), 0);
    lv_obj_align(harm2_label, LV_ALIGN_BOTTOM_LEFT, 270, -45);

    /* 3rd Harmonic */
    lv_obj_t *h3_title = lv_label_create(scr);
    lv_label_set_text(h3_title, "3rd Harm:");
    lv_obj_set_style_text_color(h3_title, lv_color_hex(0xaaaaaa), 0);
    lv_obj_align(h3_title, LV_ALIGN_BOTTOM_LEFT, 10, -20);

    lv_obj_t *h3_slider = lv_slider_create(scr);
    lv_slider_set_range(h3_slider, 0, 100);
    lv_slider_set_value(h3_slider, 0, LV_ANIM_OFF);
    lv_obj_set_width(h3_slider, 160);
    lv_obj_align(h3_slider, LV_ALIGN_BOTTOM_LEFT, 100, -20);
    lv_obj_add_event_cb(h3_slider, harm3_slider_cb,
                        LV_EVENT_VALUE_CHANGED, NULL);

    harm3_label = lv_label_create(scr);
    lv_label_set_text(harm3_label, "3rd: 0%");
    lv_obj_set_style_text_color(harm3_label, lv_color_hex(0xff6600), 0);
    lv_obj_align(harm3_label, LV_ALIGN_BOTTOM_LEFT, 270, -20);

    /* ===== Note Info Label ===== */
    note_label = lv_label_create(scr);
    lv_obj_set_style_text_color(note_label, lv_color_hex(0x00ffff), 0);
    lv_obj_set_style_text_font(note_label, &lv_font_montserrat_16, 0);
    lv_obj_align(note_label, LV_ALIGN_BOTTOM_RIGHT, -15, -45);

    /* แสดง waveform แรก (A4 pure sine) */
    update_waveform_display();
}
```

***

### 5. องค์ความรู้และเทคนิค

#### 5.1 Fourier Series - ผลรวมของ Harmonics

```c
/* ทุก Periodic waveform สามารถเขียนเป็น Fourier Series:
 *
 * x(t) = a0/2 + SUM(an*cos(n*w*t) + bn*sin(n*w*t))
 *
 * สำหรับ Square wave (odd harmonics only):
 * x(t) = (4/PI) * [sin(wt) + sin(3wt)/3 + sin(5wt)/5 + ...]
 *
 * สำหรับ Triangle wave:
 * x(t) = (8/PI^2) * [sin(wt) - sin(3wt)/9 + sin(5wt)/25 - ...]
 *
 * สำหรับ Sawtooth wave (all harmonics):
 * x(t) = (2/PI) * [sin(wt) - sin(2wt)/2 + sin(3wt)/3 - ...]
 *
 * ยิ่งเพิ่ม harmonics มากขึ้น waveform ยิ่งเข้าใกล้รูปร่างจริง
 */
```

#### 5.2 Harmonic Distortion ในระบบไฟฟ้า

```c
/* THD (Total Harmonic Distortion):
 *
 * THD% = sqrt(V2^2 + V3^2 + V4^2 + ...) / V1 * 100
 *
 * มาตรฐาน IEEE 519:
 * - THD < 5% สำหรับระบบจ่ายไฟทั่วไป
 * - THD < 3% สำหรับโรงพยาบาล
 *
 * สาเหตุของ Harmonics ในระบบไฟฟ้า:
 * - Rectifier (3rd, 5th, 7th, 11th, 13th)
 * - VFD (Variable Frequency Drive)
 * - LED Driver
 * - Switching Power Supply
 *
 * ผลกระทบ:
 * - สายไฟร้อนเกิน (Neutral conductor)
 * - หม้อแปลงร้อนเกิน (Eddy current loss)
 * - Capacitor bank เสียหาย (Resonance)
 */
```

#### 5.3 Normalization เพื่อป้องกัน Clipping

```c
/* เมื่อรวม harmonics หลายตัว ค่ารวมอาจเกิน int16_t range
 * ต้อง normalize ก่อน scale
 *
 * total_amplitude = |A1| + |A2| + |A3| + ...
 * normalized_val = val / total_amplitude
 *
 * ตัวอย่าง:
 * Fundamental = 1.0, 2nd = 0.5, 3rd = 0.33
 * total = 1.0 + 0.5 + 0.33 = 1.83
 * Max possible = 1.0 (guaranteed not clipping)
 */
float total_amp = 1.0f + fabsf(h2) + fabsf(h3);
val = val / total_amp;  /* Now -1.0 to +1.0 */
```

#### 5.4 Display Stride สำหรับ High Sample Rate

```c
/* เมื่อ sample_rate สูง (44100 Hz) แต่ display_points น้อย (200)
 * ต้องใช้ stride เพื่อแสดงจำนวนรอบที่เหมาะสม
 *
 * BUFFER_SIZE = 512 samples at 44100 Hz = 11.6 ms
 * A4 (440 Hz): 440 * 0.0116 = 5.1 cycles ใน buffer
 *
 * stride = BUFFER_SIZE / DISPLAY_POINTS = 512/200 = 2.56
 * แสดงทุก ~2.5 sample
 */
int idx = i * BUFFER_SIZE / DISPLAY_POINTS;
```

***

### 6. แบบฝึกหัด

#### Exercise 1: Piano Key Display (แสดง Waveform ตามปุ่มกด)

**โจทย์:** ปรับปรุง Piano keys ให้มีฟีเจอร์เพิ่มเติม:

* แสดง Frequency spectrum bar ข้างๆ chart (Fundamental + Harmonics แต่ละตัว)
* เพิ่มโน้ต sharp/flat (#/b) ระหว่างปุ่ม (C#, D#, F#, G#, A#) เป็นปุ่มสีดำ
* เมื่อกดปุ่มโน้ต ให้แสดง period (ms) และ wavelength (m) ด้วย
* Wavelength = speed\_of\_sound / frequency (speed\_of\_sound = 343 m/s ที่ 20C)

**คำใบ้:**

```c
/* Sharp note frequencies */
static const uint32_t sharp_freqs[] = {
    277, 311, 370, 415, 466  /* C#4, D#4, F#4, G#4, A#4 */
};

/* Wavelength calculation */
float wavelength_m = 343.0f / (float)frequency;
lv_label_set_text_fmt(info, "T=%.2f ms  L=%.2f m",
    1000.0/(double)freq, 343.0/(double)freq);
```

#### Exercise 2: AM Modulation (Amplitude Modulation)

**โจทย์:** สร้าง AM Modulation visualization:

* Carrier: sine wave ที่ 1000 Hz (adjustable)
* Modulating signal: sine wave ที่ 50 Hz (adjustable)
* AM formula: `output = (1 + m * mod_signal) * carrier`
* เพิ่ม Slider ปรับ modulation depth (m = 0 to 1)
* แสดง 2 charts: ด้านบน = modulated signal, ด้านล่าง = modulating signal (envelope)
* แสดงค่า Modulation Index (%)

**คำใบ้:**

```c
/* AM Modulation */
for (int i = 0; i < BUFFER_SIZE; i++) {
    double t = (double)i / SAMPLE_RATE;
    double carrier = sin(2.0 * M_PI * carrier_freq * t);
    double modulating = sin(2.0 * M_PI * mod_freq * t);
    double am_signal = (1.0 + mod_depth * modulating) * carrier;
    buf[i] = (int16_t)(am_signal * 500);
}
```

***

### 7. References

* [I2S Audio Interface](https://en.wikipedia.org/wiki/I%C2%B2S)
* [Musical Note Frequencies](https://pages.mtu.edu/~suits/notefreqs.html)

***


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aic-eec.com/interfacing-with-infineon-psoc-tm-edge/hmi-development/spectrum-analyzer/ux-ui-design/audio-waveform.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
