ต้องไปหัดพูดให้มันฟังทุกวันมันจะฝึกพูดเอง
import numpy as np
import time
# =====================================================================
# ส่วนที่ 1: ตัวกรองเสียงและแปลงข้อมูล (Audio Processing -> .npy)
# แก้ไข: LUFS normalization ทำบน amplitude ถูก domain แล้ว
# =====================================================================
class AudioProcessor:
def __init__(self, chunk_duration_sec=0.5):
self.LOW_CUT = 20
self.HIGH_CUT = 20000
self.MALE_RANGE = (85, 180)
self.FEMALE_RANGE = (165, 255)
self.TARGET_LUFS = -20
# แก้ไข: คำนวณ silence limit จาก chunk_duration แทน hardcode
self.chunk_duration_sec = chunk_duration_sec
self.silence_limit_sec = 3.0 # ตัดเสียงหลังเงียบ 3 วินาที
self.silence_limit_chunks = max(1, int(self.silence_limit_sec / self.chunk_duration_sec))
print(f"ℹ️ [Config] ตัดเสียงหลังเงียบ {self.silence_limit_sec}s = {self.silence_limit_chunks} chunks")
def filter_and_convert_to_npy(self, raw_microphone_data):
"""
กรองขยะแล้วแปลงเป็นตัวเลข FQ, db, Time ทันที
แก้ไข: LUFS normalization ทำบน amplitude (linear) ไม่ใช่ค่า dB โดยตรง
"""
# 1. Low-cut / High-cut Filter
is_valid_freq = (raw_microphone_data['freq'] >= self.LOW_CUT) & \
(raw_microphone_data['freq'] <= self.HIGH_CUT)
if not np.any(is_valid_freq):
return None
# 2. แก้ไข LUFS Normalization:
# - แปลง dB -> amplitude (linear) ก่อน
# - คำนวณ RMS บน amplitude
# - หาร scale_factor แล้วแปลงกลับเป็น dB
db_values = raw_microphone_data['db']
amplitude_linear = 10 ** (db_values / 20.0) # dB -> linear amplitude
current_rms = np.sqrt(np.mean(amplitude_linear ** 2)) # RMS บน linear ถูกต้อง
target_amplitude = 10 ** (self.TARGET_LUFS / 20.0) # -20 LUFS -> linear
if current_rms > 0:
scale_factor = target_amplitude / current_rms
normalized_amplitude = amplitude_linear * scale_factor
else:
normalized_amplitude = amplitude_linear
# แปลงกลับเป็น dB เพื่อเก็บใน matrix
normalized_db = 20 * np.log10(np.maximum(normalized_amplitude, 1e-10))
# 3. สร้าง Matrix แล้ว Return ออกมาเป็น .npy
npy_matrix = np.column_stack((
raw_microphone_data['freq'][is_valid_freq],
normalized_db[is_valid_freq],
raw_microphone_data['time'][is_valid_freq]
))
return npy_matrix
def is_human_speaking(self, npy_matrix):
"""ตรวจสอบว่าเป็นเสียงมนุษย์ไหม (ตรวจจากช่วง FQ เพศชาย/หญิง)"""
if npy_matrix is None:
return False
avg_fq = np.mean(npy_matrix[:, 0])
if (self.MALE_RANGE[0] <= avg_fq <= self.MALE_RANGE[1]) or \
(self.FEMALE_RANGE[0] <= avg_fq <= self.FEMALE_RANGE[1]):
return True
return False
# =====================================================================
# ส่วนที่ 2: สมอง AI ภาษาไทยโดยตรง + สร้างเสียงจากคลื่นหลายคลื่น
# แก้ไข: think_thai_text เทียบด้วยค่าเฉลี่ย ป้องกัน shape mismatch
# =====================================================================
class ThaiBrain:
def __init__(self):
# สมองจำความถี่หลายๆ คลื่น (Harmonics) เพื่อจะได้สร้างเสียงพูดได้ ไม่ใช่เสียงติ๊งเดียว
self.memory = {
"สวัสดี": np.array([[150.0, -20.0, 0.1], [800.0, -25.0, 0.15], [160.0, -20.0, 0.2]]),
"ปูมมะรุม": np.array([[120.0, -20.0, 0.5], [600.0, -28.0, 0.55], [130.0, -20.0, 0.6]]),
}
self.thinking_weights = np.ones(3) # เริ่มต้นด้วย 1 ก่อน ไม่ random เพื่อความเสถียร
def _get_feature_vector(self, npy_matrix):
"""
แก้ไข: แปลง matrix ขนาดไม่แน่นอน -> feature vector ขนาดคงที่ (3,)
โดยเอาค่าเฉลี่ยของ FQ, db, Time ซึ่ง shape ตรงกันเสมอ
"""
return np.mean(npy_matrix, axis=0) # shape: (3,) เสมอ
def think_thai_text(self, list_of_npy_chunks):
"""
คิดเป็น txt ภาษาไทยจากก้อนเสียงทั้งหมดที่พูด
แก้ไข: เทียบด้วย feature vector ขนาดคงที่ ป้องกัน shape mismatch
"""
if not list_of_npy_chunks:
return "???"
full_sound_matrix = np.vstack(list_of_npy_chunks)
input_vector = self._get_feature_vector(full_sound_matrix) # (3,)
best_word = "???"
min_error = float('inf')
for thai_txt, sound_vectors in self.memory.items():
memory_vector = self._get_feature_vector(sound_vectors) # (3,) เสมอ
# ตอนนี้ทั้งคู่ shape (3,) เปรียบเทียบได้แน่นอน
error = np.sum(np.abs(memory_vector - (input_vector * self.thinking_weights)))
if error < min_error:
min_error = error
best_word = thai_txt
return best_word
def learn_and_remember(self, list_of_npy_chunks, correct_thai_txt):
"""มนุษย์แก้ผิด -> AI เลียนแบบและจดจำเสียงคำนั้น"""
full_sound_matrix = np.vstack(list_of_npy_chunks)
self.memory[correct_thai_txt] = full_sound_matrix
# ปรับน้ำหนักสมองเล็กน้อย (learning rate 0.01 ป้องกันไม่ให้บวมเกินไป)
self.thinking_weights = np.clip(self.thinking_weights + 0.01, 0.1, 10.0)
print("🧠 [สมอง]: จดจำการออกเสียงนี้ในภาษาไทยแล้วครับ")
def speak_sine_wave(self, thai_txt):
"""ดึงเลข FQ, db มาสร้างคลื่นเสียงใหม่หลายคลื่นบวกซ้อนกัน (Superposition)"""
if thai_txt not in self.memory:
return None
sound_data = self.memory[thai_txt]
sample_rate = 48000
duration = 0.3
t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
final_wave = np.zeros_like(t)
# วนลูปสร้างคลื่นจากทุกๆ FQ ที่จดจำไว้แล้วบวกกัน
for i in range(len(sound_data)):
target_FQ = sound_data[i, 0]
target_db = sound_data[i, 1]
single_sine = np.sin(2 * np.pi * target_FQ * t)
amplitude = 10 ** (target_db / 20.0)
final_wave += single_sine * amplitude
# Normalize ไม่ให้ clip เกิน (ป้องกันเสียงแตก)
max_val = np.max(np.abs(final_wave))
if max_val > 0:
final_wave = final_wave / max_val * 0.8
output_wave_16bit = np.int16(final_wave * 32767)
return output_wave_16bit
# =====================================================================
# ส่วนที่ 3: ระบบ UI + ตัดเสียงอัตโนมัติ (ไม่ตัดตอนกำลังพูด)
# แก้ไข: silence_limit_chunks ดึงมาจาก AudioProcessor โดยตรง
# =====================================================================
class SmartAudioUI:
def __init__(self, chunk_duration_sec=0.5):
self.processor = AudioProcessor(chunk_duration_sec=chunk_duration_sec)
self.brain = ThaiBrain()
# ดึงค่า limit จาก processor (ไม่ hardcode ซ้ำ)
self.silence_limit_chunks = self.processor.silence_limit_chunks
self.silence_counter = 0
self.is_recording = False
self.temp_npy_buffer = []
def run(self):
print("=" * 60)
print("🎤 ระบบ AI ฟัง-พูด ภาษาไทย (Auto-Cut Active)")
print("ℹ️ ระบบจะบันทึกเมื่อมีคนพูด และตัดอัตโนมัติเมื่อเงียบ")
print("=" * 60)
# =====================================================
# จำลอง Stream จากไมค์ (chunk ละ 0.5 วินาที)
# ในความเป็นจริง: แทนด้วย loop อ่าน PyAudio stream
# silence_limit_chunks = 3 chunks * 0.5s = 1.5s (demo)
# หรือจะตั้ง chunk_duration_sec=0.5, silence_limit_sec=3.0 = 6 chunks
# =====================================================
fake_mic_stream = [
# chunk 1: เงียบ (ขยะใต้ 20Hz)
{'freq': np.array([15.0, 10.0]), 'db': np.array([-5.0, -8.0]), 'time': np.array([0.0, 0.1])},
# chunk 2: เริ่มพูด (150Hz + Harmonic 800Hz)
{'freq': np.array([150.0, 800.0]), 'db': np.array([-10.0, -15.0]), 'time': np.array([0.2, 0.3])},
# chunk 3: พูดต่อ
{'freq': np.array([155.0, 810.0]), 'db': np.array([-12.0, -16.0]), 'time': np.array([0.4, 0.5])},
# chunk 4: หยุดพูด (เงียบ #1)
{'freq': np.array([20.0, 25.0]), 'db': np.array([-40.0, -42.0]), 'time': np.array([0.6, 0.7])},
# chunk 5: เงียบ #2
{'freq': np.array([18.0, 22.0]), 'db': np.array([-41.0, -43.0]), 'time': np.array([0.8, 0.9])},
# chunk 6: เงียบ #3 -> ตัดเสียง (3 chunks * 0.5s = 1.5s)
{'freq': np.array([19.0, 21.0]), 'db': np.array([-42.0, -44.0]), 'time': np.array([1.0, 1.1])},
# chunk 7: เงียบ #4 (หลังตัดแล้ว ระบบรอคนพูดใหม่)
{'freq': np.array([10.0, 12.0]), 'db': np.array([-50.0, -50.0]), 'time': np.array([1.2, 1.3])},
]
for chunk_idx, raw_chunk in enumerate(fake_mic_stream):
print(f"\n[Stream chunk {chunk_idx + 1}/{len(fake_mic_stream)}]")
# 1. แปลงเป็น .npy
npy_data = self.processor.filter_and_convert_to_npy(raw_chunk)
# 2. ตรวจว่าเป็นเสียงมนุษย์ไหม
has_human_voice = self.processor.is_human_speaking(npy_data)
# --------------------------------------------------
# กรณีที่ 1: ไม่มีเสียงคนพูด
# --------------------------------------------------
if not has_human_voice:
if not self.is_recording:
# ยังไม่เคยเริ่มบันทึก -> รอเฉยๆ
print("🚫 ไม่มีเสียงคนพูด รอ...")
continue
else:
# กำลังบันทึกอยู่แล้วพึ่งเงียบ -> นับ
self.silence_counter += 1
print(f"⏸️ หยุดพูดชั่วคราว... นับเงียบ {self.silence_counter}/{self.silence_limit_chunks}")
if self.silence_counter >= self.silence_limit_chunks:
print("\n" + "=" * 40)
print("✂️ [ตัดเสียงอัตโนมัติ!] เงียบเกินเกณฑ์")
self.proces
โค้ดสร้าง Ai เองภาษาไทย
import numpy as np
import time
# =====================================================================
# ส่วนที่ 1: ตัวกรองเสียงและแปลงข้อมูล (Audio Processing -> .npy)
# แก้ไข: LUFS normalization ทำบน amplitude ถูก domain แล้ว
# =====================================================================
class AudioProcessor:
def __init__(self, chunk_duration_sec=0.5):
self.LOW_CUT = 20
self.HIGH_CUT = 20000
self.MALE_RANGE = (85, 180)
self.FEMALE_RANGE = (165, 255)
self.TARGET_LUFS = -20
# แก้ไข: คำนวณ silence limit จาก chunk_duration แทน hardcode
self.chunk_duration_sec = chunk_duration_sec
self.silence_limit_sec = 3.0 # ตัดเสียงหลังเงียบ 3 วินาที
self.silence_limit_chunks = max(1, int(self.silence_limit_sec / self.chunk_duration_sec))
print(f"ℹ️ [Config] ตัดเสียงหลังเงียบ {self.silence_limit_sec}s = {self.silence_limit_chunks} chunks")
def filter_and_convert_to_npy(self, raw_microphone_data):
"""
กรองขยะแล้วแปลงเป็นตัวเลข FQ, db, Time ทันที
แก้ไข: LUFS normalization ทำบน amplitude (linear) ไม่ใช่ค่า dB โดยตรง
"""
# 1. Low-cut / High-cut Filter
is_valid_freq = (raw_microphone_data['freq'] >= self.LOW_CUT) & \
(raw_microphone_data['freq'] <= self.HIGH_CUT)
if not np.any(is_valid_freq):
return None
# 2. แก้ไข LUFS Normalization:
# - แปลง dB -> amplitude (linear) ก่อน
# - คำนวณ RMS บน amplitude
# - หาร scale_factor แล้วแปลงกลับเป็น dB
db_values = raw_microphone_data['db']
amplitude_linear = 10 ** (db_values / 20.0) # dB -> linear amplitude
current_rms = np.sqrt(np.mean(amplitude_linear ** 2)) # RMS บน linear ถูกต้อง
target_amplitude = 10 ** (self.TARGET_LUFS / 20.0) # -20 LUFS -> linear
if current_rms > 0:
scale_factor = target_amplitude / current_rms
normalized_amplitude = amplitude_linear * scale_factor
else:
normalized_amplitude = amplitude_linear
# แปลงกลับเป็น dB เพื่อเก็บใน matrix
normalized_db = 20 * np.log10(np.maximum(normalized_amplitude, 1e-10))
# 3. สร้าง Matrix แล้ว Return ออกมาเป็น .npy
npy_matrix = np.column_stack((
raw_microphone_data['freq'][is_valid_freq],
normalized_db[is_valid_freq],
raw_microphone_data['time'][is_valid_freq]
))
return npy_matrix
def is_human_speaking(self, npy_matrix):
"""ตรวจสอบว่าเป็นเสียงมนุษย์ไหม (ตรวจจากช่วง FQ เพศชาย/หญิง)"""
if npy_matrix is None:
return False
avg_fq = np.mean(npy_matrix[:, 0])
if (self.MALE_RANGE[0] <= avg_fq <= self.MALE_RANGE[1]) or \
(self.FEMALE_RANGE[0] <= avg_fq <= self.FEMALE_RANGE[1]):
return True
return False
# =====================================================================
# ส่วนที่ 2: สมอง AI ภาษาไทยโดยตรง + สร้างเสียงจากคลื่นหลายคลื่น
# แก้ไข: think_thai_text เทียบด้วยค่าเฉลี่ย ป้องกัน shape mismatch
# =====================================================================
class ThaiBrain:
def __init__(self):
# สมองจำความถี่หลายๆ คลื่น (Harmonics) เพื่อจะได้สร้างเสียงพูดได้ ไม่ใช่เสียงติ๊งเดียว
self.memory = {
"สวัสดี": np.array([[150.0, -20.0, 0.1], [800.0, -25.0, 0.15], [160.0, -20.0, 0.2]]),
"ปูมมะรุม": np.array([[120.0, -20.0, 0.5], [600.0, -28.0, 0.55], [130.0, -20.0, 0.6]]),
}
self.thinking_weights = np.ones(3) # เริ่มต้นด้วย 1 ก่อน ไม่ random เพื่อความเสถียร
def _get_feature_vector(self, npy_matrix):
"""
แก้ไข: แปลง matrix ขนาดไม่แน่นอน -> feature vector ขนาดคงที่ (3,)
โดยเอาค่าเฉลี่ยของ FQ, db, Time ซึ่ง shape ตรงกันเสมอ
"""
return np.mean(npy_matrix, axis=0) # shape: (3,) เสมอ
def think_thai_text(self, list_of_npy_chunks):
"""
คิดเป็น txt ภาษาไทยจากก้อนเสียงทั้งหมดที่พูด
แก้ไข: เทียบด้วย feature vector ขนาดคงที่ ป้องกัน shape mismatch
"""
if not list_of_npy_chunks:
return "???"
full_sound_matrix = np.vstack(list_of_npy_chunks)
input_vector = self._get_feature_vector(full_sound_matrix) # (3,)
best_word = "???"
min_error = float('inf')
for thai_txt, sound_vectors in self.memory.items():
memory_vector = self._get_feature_vector(sound_vectors) # (3,) เสมอ
# ตอนนี้ทั้งคู่ shape (3,) เปรียบเทียบได้แน่นอน
error = np.sum(np.abs(memory_vector - (input_vector * self.thinking_weights)))
if error < min_error:
min_error = error
best_word = thai_txt
return best_word
def learn_and_remember(self, list_of_npy_chunks, correct_thai_txt):
"""มนุษย์แก้ผิด -> AI เลียนแบบและจดจำเสียงคำนั้น"""
full_sound_matrix = np.vstack(list_of_npy_chunks)
self.memory[correct_thai_txt] = full_sound_matrix
# ปรับน้ำหนักสมองเล็กน้อย (learning rate 0.01 ป้องกันไม่ให้บวมเกินไป)
self.thinking_weights = np.clip(self.thinking_weights + 0.01, 0.1, 10.0)
print("🧠 [สมอง]: จดจำการออกเสียงนี้ในภาษาไทยแล้วครับ")
def speak_sine_wave(self, thai_txt):
"""ดึงเลข FQ, db มาสร้างคลื่นเสียงใหม่หลายคลื่นบวกซ้อนกัน (Superposition)"""
if thai_txt not in self.memory:
return None
sound_data = self.memory[thai_txt]
sample_rate = 48000
duration = 0.3
t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
final_wave = np.zeros_like(t)
# วนลูปสร้างคลื่นจากทุกๆ FQ ที่จดจำไว้แล้วบวกกัน
for i in range(len(sound_data)):
target_FQ = sound_data[i, 0]
target_db = sound_data[i, 1]
single_sine = np.sin(2 * np.pi * target_FQ * t)
amplitude = 10 ** (target_db / 20.0)
final_wave += single_sine * amplitude
# Normalize ไม่ให้ clip เกิน (ป้องกันเสียงแตก)
max_val = np.max(np.abs(final_wave))
if max_val > 0:
final_wave = final_wave / max_val * 0.8
output_wave_16bit = np.int16(final_wave * 32767)
return output_wave_16bit
# =====================================================================
# ส่วนที่ 3: ระบบ UI + ตัดเสียงอัตโนมัติ (ไม่ตัดตอนกำลังพูด)
# แก้ไข: silence_limit_chunks ดึงมาจาก AudioProcessor โดยตรง
# =====================================================================
class SmartAudioUI:
def __init__(self, chunk_duration_sec=0.5):
self.processor = AudioProcessor(chunk_duration_sec=chunk_duration_sec)
self.brain = ThaiBrain()
# ดึงค่า limit จาก processor (ไม่ hardcode ซ้ำ)
self.silence_limit_chunks = self.processor.silence_limit_chunks
self.silence_counter = 0
self.is_recording = False
self.temp_npy_buffer = []
def run(self):
print("=" * 60)
print("🎤 ระบบ AI ฟัง-พูด ภาษาไทย (Auto-Cut Active)")
print("ℹ️ ระบบจะบันทึกเมื่อมีคนพูด และตัดอัตโนมัติเมื่อเงียบ")
print("=" * 60)
# =====================================================
# จำลอง Stream จากไมค์ (chunk ละ 0.5 วินาที)
# ในความเป็นจริง: แทนด้วย loop อ่าน PyAudio stream
# silence_limit_chunks = 3 chunks * 0.5s = 1.5s (demo)
# หรือจะตั้ง chunk_duration_sec=0.5, silence_limit_sec=3.0 = 6 chunks
# =====================================================
fake_mic_stream = [
# chunk 1: เงียบ (ขยะใต้ 20Hz)
{'freq': np.array([15.0, 10.0]), 'db': np.array([-5.0, -8.0]), 'time': np.array([0.0, 0.1])},
# chunk 2: เริ่มพูด (150Hz + Harmonic 800Hz)
{'freq': np.array([150.0, 800.0]), 'db': np.array([-10.0, -15.0]), 'time': np.array([0.2, 0.3])},
# chunk 3: พูดต่อ
{'freq': np.array([155.0, 810.0]), 'db': np.array([-12.0, -16.0]), 'time': np.array([0.4, 0.5])},
# chunk 4: หยุดพูด (เงียบ #1)
{'freq': np.array([20.0, 25.0]), 'db': np.array([-40.0, -42.0]), 'time': np.array([0.6, 0.7])},
# chunk 5: เงียบ #2
{'freq': np.array([18.0, 22.0]), 'db': np.array([-41.0, -43.0]), 'time': np.array([0.8, 0.9])},
# chunk 6: เงียบ #3 -> ตัดเสียง (3 chunks * 0.5s = 1.5s)
{'freq': np.array([19.0, 21.0]), 'db': np.array([-42.0, -44.0]), 'time': np.array([1.0, 1.1])},
# chunk 7: เงียบ #4 (หลังตัดแล้ว ระบบรอคนพูดใหม่)
{'freq': np.array([10.0, 12.0]), 'db': np.array([-50.0, -50.0]), 'time': np.array([1.2, 1.3])},
]
for chunk_idx, raw_chunk in enumerate(fake_mic_stream):
print(f"\n[Stream chunk {chunk_idx + 1}/{len(fake_mic_stream)}]")
# 1. แปลงเป็น .npy
npy_data = self.processor.filter_and_convert_to_npy(raw_chunk)
# 2. ตรวจว่าเป็นเสียงมนุษย์ไหม
has_human_voice = self.processor.is_human_speaking(npy_data)
# --------------------------------------------------
# กรณีที่ 1: ไม่มีเสียงคนพูด
# --------------------------------------------------
if not has_human_voice:
if not self.is_recording:
# ยังไม่เคยเริ่มบันทึก -> รอเฉยๆ
print("🚫 ไม่มีเสียงคนพูด รอ...")
continue
else:
# กำลังบันทึกอยู่แล้วพึ่งเงียบ -> นับ
self.silence_counter += 1
print(f"⏸️ หยุดพูดชั่วคราว... นับเงียบ {self.silence_counter}/{self.silence_limit_chunks}")
if self.silence_counter >= self.silence_limit_chunks:
print("\n" + "=" * 40)
print("✂️ [ตัดเสียงอัตโนมัติ!] เงียบเกินเกณฑ์")
self.proces