> For the complete documentation index, see [llms.txt](https://docs.aic-eec.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.aic-eec.com/interfacing-with-infineon-psoc-tm-edge/hmi-development/spectrum-analyzer/ux-ui-design/audio-waveform.md).

# 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="https://1856353139-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MClo3nC-1US0rbK8Qau%2Fuploads%2FvQUwzCn9mswgrUJJzc3k%2Fweek5_ex3_audio_waveform.gif?alt=media&amp;token=27a1022b-ea93-4949-9253-7e61efe7d73b" 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)

***
