#include <Bela.h>
#include <libraries/Scope/Scope.h>
#include <vector>
#include <fstream>
#include <string>
/**
* 实验:同步播放二进制信号并录音
* 采样率要求:96kHz (请在 IDE Settings 中设置)
*/
// --- 参数设置 ---
std::string gInputFile = "zc_signal_525_30.bin"; // MATLAB 生成的二进制文件
std::string gOutputFile = "9.wav"; // 录音保存的文件名
float gScopeGain = 0.1f; // 示波器显示增益
// --- 全局变量 ---
Scope scope;
std::vector<float> gPlayBuffer; // 播放缓冲区
std::vector<float> gRecordBuffer; // 录制缓冲区
unsigned int gReadPtr = 0; // 播放指针
unsigned int gWritePtr = 0; // 录制指针
unsigned int gTotalFrames = 0; // 总帧数
bool gIsRunning = false;
// WAV 头写入函数
void writeWavHeader(std::ofstream& out, int sr, int ch, int samples) {
int ds = samples * ch * 2; int fs = 36 + ds;
out.write("RIFF", 4); out.write((char*)&fs, 4); out.write("WAVEfmt ", 8);
int s1 = 16; out.write((char*)&s1, 4); short af = 1; out.write((char*)&af, 2);
short c = ch; out.write((char*)&c, 2); out.write((char*)&sr, 4);
int br = sr * ch * 2; out.write((char*)&br, 4);
short ba = ch * 2; out.write((char*)&ba, 2); short bps = 16; out.write((char*)&bps, 2);
out.write("data", 4); out.write((char*)&ds, 4);
}
bool setup(BelaContext *context, void *userData) {
// 打印当前系统识别到的音频输入通道总数
rt_printf("audioInChannels=%d\n",
context->audioInChannels);
rt_printf("audioOutChannels=%d\n",
context->audioOutChannels);
rt_printf("analogInChannels=%d\n",
context->analogInChannels);
// 1. 检查硬件采样率是否为 96kHz
if(context->audioSampleRate != 96000) {
rt_printf("❌ 错误:当前硬件采样率为 %.0f,请在 Settings 中改为 96000!\n", context->audioSampleRate);
return false;
}
// 2. 读取二进制文件到内存
std::ifstream infile(gInputFile, std::ios::binary | std::ios::ate);
if (!infile) {
rt_printf("❌ 无法打开文件 %s。请确认已上传到 IDE。\n", gInputFile.c_str());
return false;
}
std::streamsize size = infile.tellg();
infile.seekg(0, std::ios::beg);
gTotalFrames = size / sizeof(float);
gPlayBuffer.resize(gTotalFrames);
if (!infile.read((char*)gPlayBuffer.data(), size)) {
rt_printf("❌ 文件读取失败。\n");
return false;
}
rt_printf("✅ 信号加载成功!长度: %d 采样点 (%.2f 秒)\n", gTotalFrames, (float)gTotalFrames/96000.0);
// 3. 初始化录制缓冲区和示波器
gRecordBuffer.resize(gTotalFrames);
scope.setup(1, context->audioSampleRate);
gReadPtr = 0; gWritePtr = 0; gIsRunning = true;
return true;
}
void render(BelaContext *context, void *userData) {
for(unsigned int n = 0; n < context->audioFrames; n++) {
float out = 0;
// --- 播放部分 ---
if(gReadPtr < gTotalFrames) {
out = gPlayBuffer[gReadPtr];
gReadPtr++;
}
// 发送到所有通道,确保音箱发声
for(unsigned int ch = 0; ch < context->audioOutChannels; ch++) {
audioWrite(context, n, ch, out);
}
// --- 录制部分
float in = audioRead(context, n, 0); // 读取 Audio In 0
if(gWritePtr < gRecordBuffer.size()) {
gRecordBuffer[gWritePtr] = in;
gWritePtr++;
// 实时示波器查看
scope.log(in * gScopeGain);
} else if(gIsRunning) {
gIsRunning = false;
rt_printf("✅ 实验完成,正在保存文件...\n");
Bela_requestStop();
}
}
}
void cleanup(BelaContext *context, void *userData) {
if(gWritePtr > 0) {
std::ofstream out(gOutputFile, std::ios::binary);
if(out) {
writeWavHeader(out, (int)context->audioSampleRate, 1, gWritePtr);
for(unsigned int i = 0; i < gWritePtr; i++) {
float s = gRecordBuffer[i];
if(s > 1.0f) s = 1.0f; if(s < -1.0f) s = -1.0f;
short pcm = (short)(s * 32767.0f);
out.write((char*)&pcm, 2);
}
out.close();
rt_printf("🎉 录音已保存: %s。请下载后用 MATLAB 分析。\n", gOutputFile.c_str());
}
}
}


`