多通道波形文件存储

多通道波形文件存储


最近在做一个多通道信号数据采集的项目,遇到这样的问题“在一个文件中如何去存储多个通道的实时波形数据”,初步定义了如下方式:将“一定时间”内不同通道采样点数据集合划分为一个数据块,“一定时间”在文件头信息中有定义(即数据块持续时间),采样数据集合点数将在每个通道头中有定义,以上两要素可以直接被采样率所影响。如此便可在一个文件中存储不同采样率多通道的波形数据,也可以保证不同通道间的数据在时间序列上的同步。

文件结构

这里写图片描述

文件头和通道头定义

struct  File_Head_Info    {
    char    szTotal_Head_Size[8];           /// 文件头总字节数(文件头+通道头)
    char    szReserved_Data[44];            /// 保留的字节数
    char    szTotal_Data_Block[8];          /// 数据块总数
    char    szData_Block_Duration[8];       /// 以秒为单位的一个数据块持续时间
    char    szSignal_Number [4];            /// 数据块中的通道数
};
struct  Channel_Info    {
        char    szSamples_In_A_Record[8];       /// 一个数据记录块中的采样数据数
        char    szMaintain[32];                 /// 保留值,共32字节,必须填为0
};

文件读取伪代码

short *Format::ReadChannelData(int channel, int &smpcount, int&samplerate)
{
    smpcount = 0;
    //通道数超过了存储的通道数
    if(channel >= m__Head.lSignal_Number)
    {
        smpcount = 0;
        samplerate = 0;
        return NULL;
    }

    ///1.------------------  获取要使用到的基本信息. ----------------------------
    int     iSignal_Num     = (int)m__Head.lSignal_Number;      /// 信号数
    int     iTotal_Block    = (int)m__Head.lTotal_Data_Block;   /// 总的数据块数
    int     iHead_Size      = (int)m__Head.lTotal_Head_Size;        /// 文件头大小(基本信息头+通道信息头)  

    ///1.1 ----------------  计算1个数据块内所有通道的采样点总字节数. ------------
    long    lData_In_A_Record   = 0;
    for( int i=0; i<iSignal_Num; i++ )
    {
        lData_In_A_Record   += m_Cha_Info[i].lSamples_In_A_Record * sizeof( short );
    }

    samplerate = m_Cha_Info[channel].fSample_Rate;

    ////开始解析数据的位置
    char*   pStart          = m_DataSource + iHead_Size;

    ////分配一个通道的内存
    long lTotleLen = m_Cha_Info[channel].lSamples_In_A_Record * iTotal_Block;
    short* relBuff = new short[lTotleLen];

    ////读取所有的块数据
    for ( int i=0; i<iTotal_Block; i++ )
    {
        for( int j=0; j<iSignal_Num; j++ )
        {
            ///2.1 --------  通道数相同,提取数据. -----------------------------
            if( channel == j )
            {
               int size     = (m_Cha_Info[j].lSamples_In_A_Record) * sizeof(short);
                memcpy( (char*)( relBuff + smpcount), pStart, size );
                smpcount    += m_Cha_Info[j].lSamples_In_A_Record;
                pStart      += size;
            }
            ///2.2 --------  通道数不同跳过数据. -----------------------------
            else
            {
                pStart += m_Cha_Info[j].lSamples_In_A_Record * sizeof( short );
            }
        }
    }
    return relBuff;
}

数据写入伪代码

int FormatPlat::Write2File(int chanle, short *data, int sampleCount)
{
    //判断通道个数
    if(chanle >= m__Head.lSignal_Number || sampleCount <= 0 )
    {
        return 0;
    }

    int     iSignal_Num     = (int)m__Head.lSignal_Number;      /// 信号数
    int     iTotal_Block    = (int)m__Head.lTotal_Data_Block;   /// 总的数据块数
    int     iHead_Size      = (int)m__Head.lTotal_Head_Size;        /// 文件头大小(基本信息头+通道信息头)

    ///1.1 ----------------  计算1个数据块内所有通道的采样点总字节数. ------------
    long    lData_In_A_Record   = 0;
    for( int i=0; i<iSignal_Num; i++ )
    {
        lData_In_A_Record   += m_Cha_Info[i].lSamples_In_A_Record * sizeof( short );
    }

    //定位当前块
   long long lCurBlock =  m_iSeqSumNum[chanle] / m_Cha_Info[chanle].lSamples_In_A_Record;
   //在当前block的偏移位置
   long long lseek = m_iSeqSumNum[chanle] % m_Cha_Info[chanle].lSamples_In_A_Record;
   //数据块内剩余的点数
    long surplus = m_Cha_Info[chanle].lSamples_In_A_Record - lseek;

   //偏移到对应的通道的起始位置
   char*    pStart          = m_DataSource + iHead_Size;
   pStart += lCurBlock * lData_In_A_Record;
   for( int i=0; i<chanle; i++ )
   {
       pStart   += m_Cha_Info[i].lSamples_In_A_Record * sizeof( short );
   }
   pStart +=  lseek * sizeof(short);


   //剩余未写入的数据
   long lSurCount = sampleCount;
   while(lSurCount > 0)
   {
       //当块内部小于剩余数据的长度
        if(surplus < lSurCount)
        {
             memcpy(pStart , data, sizeof(short) * surplus);
             data += surplus;

             //移动到下一数据块的起始位置
             pStart += lData_In_A_Record - (m_Cha_Info[chanle].lSamples_In_A_Record -surplus) * sizeof(short);
             lSurCount -= surplus;
             surplus = m_Cha_Info[chanle].lSamples_In_A_Record;
        }
        else
        {
            memcpy(pStart, data, lSurCount * sizeof(short));
            lSurCount = 0;
        }
   }

   m_iSeqSumNum[chanle] += sampleCount;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的PYQT程序,用于显示wav文件波形和裁剪波形后保存波形文件。它使用Python的wave和pyqtgraph库。 ```python import sys import wave import pyqtgraph as pg from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QPushButton from PyQt5.QtCore import Qt class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Waveform Viewer") self.setGeometry(100, 100, 800, 600) self.filepath = None self.initUI() def initUI(self): # 选择文件按钮 btn_open = QPushButton('Open', self) btn_open.move(10, 10) btn_open.clicked.connect(self.open_file) # 保存按钮 btn_save = QPushButton('Save', self) btn_save.move(100, 10) btn_save.clicked.connect(self.save_file) btn_save.setDisabled(True) # 显示波形图 self.plot_widget = pg.PlotWidget(self) self.plot_widget.setGeometry(10, 50, 780, 540) def open_file(self): # 打开文件对话框选择wav音频文件 self.filepath, _ = QFileDialog.getOpenFileName(self, 'Open file', '', 'WAV files (*.wav)') if self.filepath: self.load_waveform() self.statusBar().showMessage(f"Loaded {self.filepath}") self.findChild(QPushButton, 'Save').setEnabled(True) def load_waveform(self): # 从wav文件中加载波形数据 with wave.open(self.filepath, 'r') as wf: num_channels = wf.getnchannels() num_frames = wf.getnframes() sample_rate = wf.getframerate() sample_width = wf.getsampwidth() duration = num_frames / sample_rate data = wf.readframes(num_frames) # 将二进制数据转换为整数数组 if sample_width == 1: data = pg.Byte(data) elif sample_width == 2: data = pg.Short(data) else: raise ValueError("Unsupported sample width") # 将多通道数据转换为二维数组 if num_channels > 1: data = data.reshape((num_frames, num_channels)) self.plot_widget.plot(data[:, 0], pen='b') self.plot_widget.plot(data[:, 1], pen='g') else: self.plot_widget.plot(data, pen='b') # 设置坐标轴范围和标签 self.plot_widget.setXRange(0, duration, padding=0) self.plot_widget.getAxis('bottom').setLabel("Time (s)") self.plot_widget.getAxis('left').setLabel("Amplitude") def save_file(self): # 打开文件对话框选择保存路径和文件名 save_path, _ = QFileDialog.getSaveFileName(self, 'Save file', '', 'WAV files (*.wav)') if save_path: # 截取波形数据 region = self.plot_widget.getViewBox().viewRange()[0] start_time = region[0] end_time = region[1] with wave.open(self.filepath, 'r') as wf: num_channels = wf.getnchannels() num_frames = wf.getnframes() sample_rate = wf.getframerate() sample_width = wf.getsampwidth() data = wf.readframes(num_frames) # 计算截取开始和结束的帧数 start_frame = int(start_time * sample_rate) end_frame = int(end_time * sample_rate) if end_frame > num_frames: end_frame = num_frames # 将二进制数据转换为整数数组 if sample_width == 1: data = pg.Byte(data) elif sample_width == 2: data = pg.Short(data) else: raise ValueError("Unsupported sample width") # 将多通道数据转换为二维数组 if num_channels > 1: data = data.reshape((num_frames, num_channels)) # 保存截取后的波形数据到新的wav文件中 with wave.open(save_path, 'w') as wf: wf.setnchannels(num_channels) wf.setsampwidth(sample_width) wf.setframerate(sample_rate) wf.writeframes(data[start_frame:end_frame].tostring()) self.statusBar().showMessage(f"Saved {save_path}") if __name__ == '__main__': app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) ``` 该程序包括三个主要部分: 1. 初始化用户界面:创建一个窗口,包括一个“打开”按钮、一个“保存”按钮和一个用于显示波形图的绘图小部件。 2. 打开文件:当用户点击打开按钮时,程序打开一个文件对话框,让用户选择一个.wav音频文件。然后,程序加载该文件波形数据,并在绘图小部件中显示它。 3. 保存文件:当用户点击保存按钮时,程序打开一个文件对话框,让用户选择保存路径和文件名。然后,程序截取用户在绘图小部件中选择的时间范围,并将截取后的波形数据保存到新的.wav文件中。 注意:该程序只支持单声道和双声道16位或8位WAV文件。如果需要支持其他类型的音频文件,需要进行一些修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值