wav音頻轉C語言樣點數組

WAV to C Header Converter

將WAV音頻文件轉換為C語言頭文件的Python腳本,支持將音頻數據嵌入到C/C++項目中。

功能特性

音頻格式支持

  • PCM格式:支持8位、16位、24位、32位PCM音頻
  • IEEE Float格式:支持32位浮點音頻
  • 多聲道:支持單聲道、立體聲及多聲道音頻
  • 自動格式檢測:自動識別WAV文件的編碼格式

智能數組類型選擇

  • 16位及以下WAV → 生成 int16_t 數組(緊湊存儲)
  • 32位WAV → 生成 int32_t 數組(保持完整精度)

生成的頭文件包含

  • 包含防護宏定義
  • 音頻參數宏定義(采樣率、聲道數、采樣點數)
  • 格式化的音頻數據數組
  • 符合C語言標準的變量命名

使用方法

基本用法

  1. 將WAV文件放在腳本同一目錄下
  2. 運行腳本:
python convert_wav_to_int16_h.py

腳本會自動掃描當前目錄下的所有 .wav 文件并轉換。

輸出示例

對于16位WAV文件 audio.wav,會生成 audio.h

#ifndef AUDIO_H_
#define AUDIO_H_#include <stdint.h>#define AUDIO_SAMPLE_RATE 44100
#define AUDIO_NUM_CHANNELS 2
#define AUDIO_NUM_SAMPLES 88200const int16_t audio_samples[AUDIO_NUM_SAMPLES] = {1234, -5678, 2345, -6789, 3456, -7890, 4567, -8901,5678, -9012, 6789, -1023, 7890, -2134, 8901, -3245,// ... 更多數據
};#endif

對于32位WAV文件,會生成 int32_t 數組:

const int32_t audio_samples[AUDIO_NUM_SAMPLES] = {123456789, -987654321, 234567890, -876543210,// ... 更多數據
};

支持的音頻格式

位深度格式類型輸出數組類型說明
8位PCMint16_t無符號轉有符號,縮放到16位
16位PCMint16_t直接使用原始值
24位PCMint16_t縮放到16位(截斷低8位)
32位PCMint32_t保持完整32位精度
32位IEEE Floatint32_t浮點轉整數,縮放到32位范圍

文件命名規則

  • 輸出文件名:原文件名.h
  • 變量名:基于文件名生成合法的C標識符
  • 宏定義:變量名轉大寫

命名示例

  • my-audio.wav → 變量名:my_audio_samples,宏前綴:MY_AUDIO_
  • 123sound.wav → 變量名:wav_123sound_samples,宏前綴:WAV_123SOUND_

應用場景

  • 嵌入式系統:將音效直接編譯到固件中
  • 游戲開發:嵌入音效資源,避免文件IO
  • 音頻處理:將測試音頻數據編譯到程序中
  • 實時系統:避免運行時文件加載延遲

技術細節

數據轉換

  • 所有音頻數據按小端序處理
  • 浮點數據范圍限制在 [-1.0, 1.0]
  • 超出范圍的整數數據會被裁剪
  • NaN浮點值會被轉換為0

內存效率

  • 使用生成器避免大文件內存占用
  • 分塊讀取音頻數據(4096幀/塊)
  • 16位數組每行16個數據,32位數組每行8個數據

錯誤處理

  • 跳過不支持的音頻格式
  • 顯示詳細的轉換狀態信息
  • 繼續處理其他文件即使某個文件出錯

輸出信息

腳本運行時會顯示每個文件的處理狀態:

[ok] audio.wav -> audio.h | fmt=PCM, ch=2, sr=44100, width=2 bytes, samples=88200, array_type=int16_t
[skip] unsupported.wav: unsupported sample width 5 bytes
[error] corrupted.wav: Invalid WAV file format

依賴要求

  • Python 3.6+
  • 標準庫模塊:os, struct, wave

無需安裝額外的第三方庫。

python代碼

import os
import struct
import wavedef sanitize_name(name: str) -> str:base = ''.join(c if (c.isalnum() or c == '_') else '_' for c in name)if not base or base[0].isdigit():base = 'wav_' + basereturn basedef to_macro(name: str) -> str:return sanitize_name(name).upper()def detect_wav_format(path: str):# Returns ("PCM" or "FLOAT")try:with open(path, 'rb') as f:if f.read(4) != b'RIFF':return "PCM"f.read(4)  # sizeif f.read(4) != b'WAVE':return "PCM"# iterate chunkswhile True:hdr = f.read(8)if len(hdr) < 8:breakchunk_id, chunk_size = hdr[:4], struct.unpack('<I', hdr[4:8])[0]if chunk_id == b'fmt ':fmt_data = f.read(chunk_size)if len(fmt_data) < 16:return "PCM"audio_format = struct.unpack('<H', fmt_data[0:2])[0]if audio_format == 1:return "PCM"if audio_format == 3:return "FLOAT"if audio_format == 0xFFFE and len(fmt_data) >= 40:# WAVE_FORMAT_EXTENSIBLE: subformat at offset 24 (16 bytes)subformat = fmt_data[24:40]# First 4 bytes little-endian correspond to PCM(1) or IEEE_FLOAT(3)code = struct.unpack('<I', subformat[0:4])[0]if code == 1:return "PCM"if code == 3:return "FLOAT"return "PCM"else:# skip chunk (with padding byte if size is odd)skip = chunk_size + (chunk_size & 1)f.seek(skip, 1)except Exception:return "PCM"return "PCM"def clip_int16(v: int) -> int:if v > 32767:return 32767if v < -32768:return -32768return vdef convert_sample(sample_bytes: bytes, fmt: str, sampwidth: int, target_width: int) -> int:# Returns int16 or int32 value based on target_widthif sampwidth == 1:# 8-bit unsigned PCM: 0..255 -> -128..127, then scaleu = sample_bytes[0]s = u - 128if target_width == 2:return s << 8  # scale to int16else:return s << 24  # scale to int32elif sampwidth == 2:# 16-bit signed little-endianval = struct.unpack('<h', sample_bytes)[0]if target_width == 2:return valelse:return val << 16  # scale to int32elif sampwidth == 3:# 24-bit signed little-endianb0, b1, b2 = sample_bytes[0], sample_bytes[1], sample_bytes[2]val = b0 | (b1 << 8) | (b2 << 16)if b2 & 0x80:val -= 1 << 24if target_width == 2:# Scale down to 16-bitreturn clip_int16(val >> 8)else:return val << 8  # scale to int32elif sampwidth == 4:if fmt == "PCM":# 32-bit signed little-endianval = struct.unpack('<i', sample_bytes)[0]if target_width == 2:return clip_int16(val >> 16)else:return valelse:# 32-bit IEEE floatf = struct.unpack('<f', sample_bytes)[0]if f != f:  # NaNf = 0.0if f > 1.0:f = 1.0elif f < -1.0:f = -1.0if target_width == 2:return clip_int16(int(round(f * 32767.0)))else:return int(round(f * 2147483647.0))  # scale to int32else:# Unsupported width: fallback zeroreturn 0def write_header_start(fp, guard: str, var_base: str, num_samples: int, sample_rate: int, num_channels: int):fp.write(f"#ifndef {guard}\n")fp.write(f"#define {guard}\n\n")fp.write("#include <stdint.h>\n\n")macro_base = to_macro(var_base)fp.write(f"#define {macro_base}_SAMPLE_RATE {sample_rate}\n")fp.write(f"#define {macro_base}_NUM_CHANNELS {num_channels}\n")fp.write(f"#define {macro_base}_NUM_SAMPLES {num_samples}\n\n")fp.write(f"extern const int16_t {var_base}_samples[{macro_base}_NUM_SAMPLES];\n\n")fp.write("#endif\n")def write_header_with_array(path_h: str, var_base: str, sample_iter, total_samples: int, sample_rate: int, num_channels: int, sampwidth: int):guard = to_macro(var_base) + "_H_"# Determine array type based on sample widthif sampwidth == 4:array_type = "int32_t"per_line = 8  # fewer numbers per line for int32else:array_type = "int16_t"per_line = 16with open(path_h, "w", encoding="utf-8") as fp:fp.write(f"#ifndef {guard}\n")fp.write(f"#define {guard}\n\n")fp.write("#include <stdint.h>\n\n")macro_base = to_macro(var_base)fp.write(f"#define {macro_base}_SAMPLE_RATE {sample_rate}\n")fp.write(f"#define {macro_base}_NUM_CHANNELS {num_channels}\n")fp.write(f"#define {macro_base}_NUM_SAMPLES {total_samples}\n\n")fp.write(f"const {array_type} {var_base}_samples[{macro_base}_NUM_SAMPLES] = {{\n  ")count = 0first = Truefor s in sample_iter:if not first:fp.write(", ")else:first = Falsefp.write(str(s))count += 1if (count % per_line) == 0:fp.write("\n  ")fp.write("\n};\n\n")fp.write("#endif\n")def convert_wav_to_h(path_wav: str):base_name = os.path.splitext(os.path.basename(path_wav))[0]var_base = sanitize_name(base_name)out_h = f"{base_name}.h"fmt = detect_wav_format(path_wav)with wave.open(path_wav, 'rb') as wf:nch = wf.getnchannels()sampwidth = wf.getsampwidth()  # bytes per samplefr = wf.getframerate()nframes = wf.getnframes()if sampwidth not in (1, 2, 3, 4):print(f"[skip] {path_wav}: unsupported sample width {sampwidth} bytes")return# Determine target width: 32-bit WAV -> int32, others -> int16target_width = 4 if sampwidth == 4 else 2array_type = "int32_t" if target_width == 4 else "int16_t"total_samples = nframes * nchdef sample_generator():chunk_frames = 4096while True:frames = wf.readframes(chunk_frames)if not frames:breakmv = memoryview(frames)step = sampwidthfor off in range(0, len(mv), step):# mv[off:off+step] is a memoryview, convert to bytes for struct/unpacksb = mv[off:off + step].tobytes()yield convert_sample(sb, fmt, sampwidth, target_width)write_header_with_array(out_h, var_base, sample_generator(), total_samples, fr, nch, sampwidth)print(f"[ok] {path_wav} -> {out_h} | fmt={fmt}, ch={nch}, sr={fr}, width={sampwidth} bytes, samples={total_samples}, array_type={array_type}")def main():wavs = [f for f in os.listdir('.') if f.lower().endswith('.wav')]if not wavs:print("No .wav files found in current directory.")returnwavs.sort()for w in wavs:try:convert_wav_to_h(w)except Exception as e:print(f"[error] {w}: {e}")if __name__ == "__main__":main()

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/94930.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/94930.shtml
英文地址,請注明出處:http://en.pswp.cn/web/94930.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

01.《基礎入門:了解網絡的基本概念》

網絡基礎 文章目錄網絡基礎網絡通信核心原理網絡通信定義信息傳遞過程關鍵術語解釋網絡的分類網絡參考模型OSI 參考模型各層核心工作分層核心原則TCP/IP 參考模型&#xff08;4 層 / 5 層&#xff0c;實際應用模型&#xff09;TCP/IP 與 OSI 模型的對應關系傳輸層核心協議&…

基于vue駕校管理系統的設計與實現5hl93(程序+源碼+數據庫+調試部署+開發環境)帶論文文檔1萬字以上,文末可獲取,系統界面在最后面。

系統程序文件列表&#xff1a;項目功能&#xff1a;學員,教練,教練信息,預約信息,場地信息,時間安排,車輛信息,預約練車,時間段,駕校場地信息,駕校車輛信息,預約報名開題報告內容&#xff1a;一、選題背景與意義背景隨著汽車保有量持續增長&#xff0c;駕校行業規模不斷擴大&am…

灰度思維:解鎖世界原有本色的密碼

摘要本文深入探討灰度思維的概念內涵及其在處理他人評價中的應用價值。研究指出&#xff0c;灰度思維作為一種超越非黑即白的思維方式&#xff0c;能夠幫助個體以更客觀、全面的態度接受他人評價的片面性&#xff0c;從而促進個人成長和人際關系和諧。文章分析了他人評價片面性…

動態規劃--Day03--打家劫舍--198. 打家劫舍,213. 打家劫舍 II,2320. 統計放置房子的方式數

動態規劃–Day03–打家劫舍–198. 打家劫舍&#xff0c;213. 打家劫舍 II&#xff0c;2320. 統計放置房子的方式數 今天要訓練的題目類型是&#xff1a;【打家劫舍】&#xff0c;題單來自靈艾山茶府。 掌握動態規劃&#xff08;DP&#xff09;是沒有捷徑的&#xff0c;咱們唯一…

Nuxt.js@4 中管理 HTML <head> 標簽

可以在 nuxt.config.ts 中配置全局的 HTML 標簽&#xff0c;也可以在指定 index.vue 頁面中配置指定的 HTML 標簽。 在 nuxt.config.ts 中配置 HTML 標簽 export default defineNuxtConfig({compatibilityDate: 2025-07-15,devtools: { enabled: true },app: {head: {charse…

UCIE Specification詳解(十)

文章目錄4.5.3.7 PHYRETRAIN&#xff08;物理層重訓練&#xff09;4.5.3.7.1 Adapter initiated PHY retrain4.5.3.7.2 PHY initiated PHY retrain4.5.3.7.3 Remote Die requested PHY retrain4.5.3.8 TRAIN ERROR4.5.3.9 L1/L24.6 Runtime Recalibration4.7 Multi-module Link…

電商數據的獲取方式:API、爬蟲、第三方服務及更多

在競爭激烈的電商領域&#xff0c;數據是驅動業務增長的關鍵。準確、及時地獲取電商數據&#xff0c;并進行深入分析&#xff0c;能夠幫助企業洞察市場趨勢、優化運營策略、提升用戶體驗。本文將全面介紹電商數據的獲取方式&#xff0c;涵蓋API接口、網絡爬蟲技術、第三方數據服…

《WINDOWS 環境下32位匯編語言程序設計》第8章 通用對話框

Windows操作系統為一些常用功能提供了一些通用對話框&#xff08;Common Dialog Box&#xff09;&#xff0c;比如&#xff0c;在不同的應用程序中進行打開文件、選擇字體、選擇顏色等操作時&#xff0c;不同程序顯示的對話框的模樣都是一樣的。另外&#xff0c;把同樣的應用程…

SOME/IP-SD協議中組播IP地址和端口號應從何處獲取、由誰設置?

<摘要> AUTOSAR SOME/IP-SD協議中組播通信參數的核心配置規則明確規定了在服務端傳輸&#xff08;Server-Transmits&#xff09;和客戶端傳輸&#xff08;Client-Transmits&#xff09;兩種模式下&#xff0c;組播IP地址和端口號應從何處獲取、由誰設置&#xff0c;從而確…

DAY49打卡

追到第45天內容浙大疏錦行

十四、測試 (Testing)

Rust內置了強大的測試框架,使得編寫和運行測試變得非常簡單。Rust的測試系統主要包括單元測試、集成測試和文檔測試。 1. 單元測試 單元測試通常放在與被測試代碼相同的文件中,使用#[cfg(test)]模塊和#[test]屬性標記。 1.1 基本測試結構 // 在src/lib.rs或任何模塊中pub…

LeetCode 刷題【56. 合并區間】

56. 合并區間 自己做 解&#xff1a;排序合并 class Solution { public:static bool compare(const vector<int> &p1, const vector<int> &p2){ //按第一個數排序return p1[0] < p2[0]; }vector<vector<int>> merge(ve…

DistributedLock 實現.Net分布式鎖

在分布式系統中&#xff0c;經常會遇到多個實例同時訪問同一份資源的情況&#xff0c;例如&#xff1a; ? 多個服務節點同時寫入數據庫同一行數據? 定時任務在多個節點上同時運行&#xff0c;導致重復執行? 多實例寫緩存時出現數據覆蓋問題 為了解決 并發沖突 和 數據一致…

Flutter:ios打包ipa,證書申請,Xcode打包,完整流程

步驟1 - 5 為 申請ios的簽名文件&#xff0c;App ID&#xff0c;證書&#xff0c;描述文件&#xff0c;并添加測試打包設備。 步驟1&#xff1a;生成證書簽名文件&#xff08;打開鑰匙串訪問>證書助理>從證書頒發機構請求證書&#xff09; 存儲后得到了一個簽名文件&…

Shell 秘典(卷二)——號令延展秘術 與 流程掌控心法?if 天機判語篇精解

文章目錄前言一、命令擴展詳解1.1 邏輯運算符1.1.1 邏輯與運算符&#xff08;&&&#xff09;1.1.2 邏輯或運算符&#xff08;||&#xff09;1.1.3 組合使用注意事項1.2 echo 命令1.2.1 基本用法1.2.2 輸出到標準錯誤&#xff08;stderr&#xff09;1.3 標準文件描述符&…

Agent實戰教程:深度解析async異步編程在Langgraph中的性能優化

在現代Python開發中&#xff0c;異步編程已經成為提高程序性能的重要手段&#xff0c;特別是在處理網絡請求、數據庫操作或AI模型調用等耗時操作時。本文將通過實際的LangGraph 示例&#xff0c;深入解析async的真正作用&#xff0c;并揭示一個常見誤區&#xff1a;為什么異步順…

coalesce在sql中什么作用

COALESCE?是SQL中的一個函數&#xff0c;用于返回參數列表中的第一個非空值&#xff0c;若所有參數均為NULL則返回NULL&#xff0c;常用于處理數據中的空值情況。 ?核心功能與語法? COALESCE函數的基本語法為&#xff1a;COALESCE(expression1, expression2, ..., express…

【Rust】 6. 字符串學習筆記

一、Rust 字符串概述 Rust 字符串是 UTF-8 編碼的文本序列&#xff0c;提供兩種主要類型&#xff1a; &str - 字符串切片&#xff08;通常作為引用出現&#xff09;String - 動態可變的、擁有所有權的字符串 二、字符串字面量 (&str) 編譯時已知大小&#xff0c;靜態分…

達夢數據庫-數據文件 (二)

達夢數據庫-數據文件&#xff08;二&#xff09;-自動監控達夢數據庫表空間使用率的 Shell 腳本 自動監控達夢數據庫表空間使用率的 Shell 腳本&#xff0c;支持&#xff1a; ? 實時計算每個表空間的使用率? 設置閾值告警&#xff08;如 >80%&#xff09;? 支持郵件告警&…

如何用 Android 平臺開發第一個 Kotlin 小程序

安裝開發環境下載并安裝最新版 Android Studio&#xff08;官方 IDE&#xff09;&#xff0c;安裝時勾選 Kotlin 插件。確保 JDK 版本為 11 或更高。創建新項目打開 Android Studio 選擇 File > New > New Project&#xff0c;選擇 Empty Activity 模板。在配置界面中&am…