Skip to content

Latest commit

 

History

History
291 lines (252 loc) · 10 KB

File metadata and controls

291 lines (252 loc) · 10 KB
Error in user YAML: (<unknown>): found a tab character that violate indentation while scanning a plain scalar at line 3 column 3
---
- oeasy Python 0758
- 这是 oeasy 系统化 Python 教程,从基础一步步讲,扎实、完整、不跳步。愿意花时间学,就能真正学会。
- 本教程同步发布在: 
	- 个人网站: `https://oeasy.org` 
	- 蓝桥云课: `https://www.lanqiao.cn/courses/3584` 
	- GitHub: `https://github.com/overmind1980/oeasy-python-tutorial` 
	- Gitee: `https://gitee.com/overmind1980/oeasypython` 
---
普通话的阴阳上去的调值变化,如果是其他方言呢?做成相应字符串的列表。

图片描述

# 纯本地方言声调生成工具 - 无任何第三方库依赖

import sys
import os
import numpy as np
import wave
import struct
from datetime import datetime

# 定义各地方言的阴平、阳平、上声、去声调值(五度标记法)
dialect_tone_values = {
    '北京话': {
        '阴平': '55',
        '阳平': '35',
        '上声': '214',
        '去声': '51'
    },
    '上海话': {
        '阴平': '53',
        '阳平': '24',
        '上声': '34',
        '去声': '22'
    },
    '广州话': {
        '阴平': '55',
        '阳平': '21',
        '上声': '35',
        '去声': '22'
    },
    '成都话': {
        '阴平': '55',
        '阳平': '41',
        '上声': '53',
        '去声': '13'
    },
    '武汉话': {
        '阴平': '44',
        '阳平': '213',
        '上声': '42',
        '去声': '35'
    },
    '南京话': {
        '阴平': '31',
        '阳平': '13',
        '上声': '22',
        '去声': '44'
    },
    '西安话': {
        '阴平': '21',
        '阳平': '24',
        '上声': '53',
        '去声': '44'
    },
    '天津话': {
        '阴平': '11',
        '阳平': '45',
        '上声': '214',
        '去声': '53'
    }
}

# 音频参数配置
DEFAULT_SAMPLE_RATE = 44100
DEFAULT_DURATION = 1.0  # 每个音的持续时间,单位秒
DEFAULT_FREQ = 440.0  # 基础频率,对应A4音
DEFAULT_VOLUME = 0.5  # 音量,0.0-1.0

# 将五度标记法转换为频率调整因子
def tone_value_to_frequency_adjustment(tone_value, base_freq=DEFAULT_FREQ):
    """将五度标记法转换为对应的频率调整因子"""
    # 五度对应的相对频率(1-5度)
    pitch_levels = {
        1: 0.5946,  # 降低一个半音
        2: 0.7071,  # 保持较低
        3: 1.0000,  # 基准音高
        4: 1.4142,  # 保持较高
        5: 1.6818   # 升高一个半音
    }
    
    # 解析调值(可能是两位或三位数字)
    digits = [int(d) for d in str(tone_value)]
    
    if len(digits) == 2:
        # 两值调:开始值和结束值
        start_level, end_level = digits
        # 生成平滑过渡的频率序列
        num_steps = int(DEFAULT_SAMPLE_RATE * DEFAULT_DURATION)
        start_freq = base_freq * pitch_levels[start_level]
        end_freq = base_freq * pitch_levels[end_level]
        
        # 线性插值生成频率数组
        freq_adjustments = np.linspace(start_freq, end_freq, num_steps)
        
    elif len(digits) == 3:
        # 三值调:开始值、中间值和结束值
        start_level, mid_level, end_level = digits
        # 分两段过渡
        half_steps = int(DEFAULT_SAMPLE_RATE * DEFAULT_DURATION / 2)
        start_freq = base_freq * pitch_levels[start_level]
        mid_freq = base_freq * pitch_levels[mid_level]
        end_freq = base_freq * pitch_levels[end_level]
        
        # 生成两段插值
        first_segment = np.linspace(start_freq, mid_freq, half_steps)
        second_segment = np.linspace(mid_freq, end_freq, half_steps)
        freq_adjustments = np.concatenate((first_segment, second_segment))
    else:
        # 单值调:保持固定频率
        level = digits[0]
        num_steps = int(DEFAULT_SAMPLE_RATE * DEFAULT_DURATION)
        freq_adjustments = np.array([base_freq * pitch_levels[level]] * num_steps)
    
    return freq_adjustments

# 生成正弦波数据
def generate_sine_wave(frequency, duration, volume=DEFAULT_VOLUME, sample_rate=DEFAULT_SAMPLE_RATE):
    """生成正弦波数据"""
    num_samples = int(sample_rate * duration)
    x = np.arange(num_samples)
    # 生成正弦波
    sine_wave = volume * np.sin(2 * np.pi * frequency * x / sample_rate)
    # 转换为16位整数
    return np.int16(sine_wave * 32767)

# 生成频率变化的音频数据
def generate_variable_frequency_audio(freq_adjustments, volume=DEFAULT_VOLUME, sample_rate=DEFAULT_SAMPLE_RATE):
    """根据频率调整序列生成音频数据"""
    num_samples = len(freq_adjustments)
    audio_data = np.zeros(num_samples, dtype=np.int16)
    
    # 为每个样本点生成对应的正弦波值
    for i in range(num_samples):
        t = i / sample_rate  # 当前时间
        # 生成当前频率的正弦波值
        audio_data[i] = np.int16(volume * 32767 * np.sin(2 * np.pi * freq_adjustments[i] * t))
    
    return audio_data

# 保存音频数据为WAV文件
def save_audio_to_wav(audio_data, filename, sample_rate=DEFAULT_SAMPLE_RATE):
    """将音频数据保存为WAV文件"""
    with wave.open(filename, 'w') as wav_file:
        # 设置WAV文件参数
        wav_file.setnchannels(1)  # 单声道
        wav_file.setsampwidth(2)  # 16位
        wav_file.setframerate(sample_rate)
        # 写入音频数据
        for sample in audio_data:
            wav_file.writeframes(struct.pack('<h', sample))

# 主函数:生成拼音的四种声调音频
def generate_pinyin_tones(pinyin, dialect):
    """纯本地生成给定拼音在指定方言下的四种声调音频"""
    if dialect not in dialect_tone_values:
        print(f"未找到方言: {dialect}")
        available_dialects = ', '.join(dialect_tone_values.keys())
        print(f"可用方言: {available_dialects}")
        return False
    
    print(f"正在纯本地生成{dialect}中拼音'{pinyin}'的四种声调音频...")
    
    # 创建输出目录
    output_dir = f"{pinyin}_{dialect}_pure_local_tones_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
    os.makedirs(output_dir, exist_ok=True)
    
    # 首先生成阴平(55)的基础音频
    print("  首先生成阴平(55)的基础音频...")
    yinping_freqs = tone_value_to_frequency_adjustment('55')
    yinping_audio = generate_variable_frequency_audio(yinping_freqs)
    
    # 保存阴平音频
    yinping_file = os.path.join(output_dir, f"{pinyin}_阴平_55.wav")
    save_audio_to_wav(yinping_audio, yinping_file)
    print(f"  阴平音频已保存: {yinping_file}")
    
    # 创建配置文件,记录声调信息
    config_data = {
        "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        "pinyin": pinyin,
        "dialect": dialect,
        "base_tone": "阴平(55)",
        "tones": {}
    }
    
    # 然后根据阴平音频调整生成其他三个声调
    for tone_type in ['阳平', '上声', '去声']:
        tone_value = dialect_tone_values[dialect][tone_type]
        config_data["tones"][tone_type] = tone_value
        
        print(f"  根据阴平基础音频调整生成{tone_type}({tone_value})音频...")
        
        # 计算该声调的频率调整
        tone_freqs = tone_value_to_frequency_adjustment(tone_value)
        
        # 生成该声调的音频
        tone_audio = generate_variable_frequency_audio(tone_freqs)
        
        # 保存音频文件
        output_file = os.path.join(output_dir, f"{pinyin}_{tone_type}_{tone_value}.wav")
        save_audio_to_wav(tone_audio, output_file)
        print(f"  {tone_type}音频已保存: {output_file}")
    
    # 保存配置文件
    config_file = os.path.join(output_dir, "tone_config.json")
    import json
    with open(config_file, 'w', encoding='utf-8') as f:
        json.dump(config_data, f, ensure_ascii=False, indent=2)
    
    print(f"\n所有音频生成完成!")
    print(f"保存在目录: {output_dir}")
    print(f"配置文件已保存: {config_file}")
    print("\n提示:")
    print("1. 这是纯本地生成的WAV格式音频,使用Python内置模块,无需任何第三方库")
    print("2. 实现了先生成阴平(55)基础音频,再根据方言特点调整生成其他三个声调的要求")
    print("3. 音频基于五度标记法进行频率调整,模拟方言声调特点")
    print("4. 您可以调整代码中的DEFAULT_FREQ和DEFAULT_DURATION参数来获得不同效果")
    return True

# 交互式菜单
def interactive_menu():
    """交互式菜单功能"""
    while True:
        print("\n===== 纯本地方言声调生成工具 ======")
        print("1. 生成拼音的四种声调音频")
        print("2. 查看支持的方言列表")
        print("3. 退出")
        
        choice = input("请选择操作 (1-3): ")
        
        if choice == '1':
            pinyin = input("请输入要生成音频的拼音: ")
            print("支持的方言:", ', '.join(dialect_tone_values.keys()))
            dialect = input("请输入方言名称: ")
            generate_pinyin_tones(pinyin, dialect)
        elif choice == '2':
            print("\n支持的方言列表:")
            for dialect, tones in dialect_tone_values.items():
                print(f"\n{dialect}:")
                for tone_type, tone_value in tones.items():
                    print(f"  {tone_type}: {tone_value}")
        elif choice == '3':
            print("感谢使用纯本地方言声调生成工具,再见!")
            break
        else:
            print("无效的选择,请重新输入")

# 主程序入口
if __name__ == "__main__":
    # 检查是否以命令行参数模式运行
    if len(sys.argv) == 3:
        # 参数1是拼音,参数2是方言
        pinyin = sys.argv[1]
        dialect = sys.argv[2]
        generate_pinyin_tones(pinyin, dialect)
    else:
        # 交互式模式
        print("欢迎使用纯本地方言声调生成工具!")
        print("本工具无需网络连接,不依赖任何第三方库,完全在本地生成方言声调音频")
        interactive_menu()
  • 调用
python3 pure_local_dialect_tool.py ma 天津话
  • 观察频率


  • 本文来自 oeasy Python 系统教程。
  • 想完整、扎实学 Python,
  • 搜索 oeasy 即可。