C++ C++;在后台读取文件

C++ C++;在后台读取文件,c++,multithreading,file,audio,C++,Multithreading,File,Audio,我正在实现一个音频播放器应用程序,该应用程序预缓冲一小部分音频数据,并在需要时读取其余数据,例如播放命令到达时。这是一个实时应用程序,在播放命令和开始播放之间有接近零的延迟非常重要 示例:我的音频流为10 Mb,当选择文件时,我读取了部分音频流,并开始创建如下缓冲区: // Stuff to do as soon as the file is selected // Allocate new memory for the current sample // contains sample le

我正在实现一个音频播放器应用程序,该应用程序预缓冲一小部分音频数据,并在需要时读取其余数据,例如播放命令到达时。这是一个实时应用程序,在播放命令和开始播放之间有接近零的延迟非常重要

示例:我的音频流为10 Mb,当选择文件时,我读取了部分音频流,并开始创建如下缓冲区:

// Stuff to do as soon as the file is selected
// Allocate new memory for the current sample

// contains sample length in number of samples
sampleSize = SampleLib.smp.len; 

// assume it's a 16-bit audio file, each sample is 2 bytes long
byteSize = sampleSize * sizeof(short); 

// Allow 10 extra samples and fill with zeroes
SampleData = new short[sampleSize + 10]();

// PRELOAD_BYTESIZE is set to 65535 bytes
preloadByteSize = byteSize > PRELOAD_BYTESIZE ? PRELOAD_BYTESIZE : byteSize;

// Set pointer in file - WavePointer contains the exact location where the sample data starts in file
fseek(inFile, WavePointer, SEEK_SET); 

// read preloadByteSize from inFile into SampleData
fread(SampleData, 1, preloadByteSize, inFile);
此时,我的缓冲区
SampleData
仅包含一部分音频数据,以便在播放命令到达时立即开始播放。同时,程序应填满缓冲区的其余部分,并继续播放,直到音频采样结束,不得中断

// Stuff to do as soon the playback starts
// Load the rest of the sample data

// If file is already in memory, avoid reading it again
if (preloadByteSize < ByteSize) 
{
    // Set pointer in file at stample start + preload size
    fseek(fpFile, WavePointers + preloadByteSize, SEEK_SET);

    // read the remaining bytes from inFile and fill the empty part of the buffer
    fread(SampleData + preloadByteSize / sizeof(short), 1, ByteSize - preloadByteSize, inFile);

    // remember the number of loaded bytes
    preloadByteSize = ByteSize;
}
//播放一开始就要做的事情
//加载其余的示例数据
//如果文件已在内存中,请避免再次读取
if(预加载字节大小<字节大小)
{
//将文件中的指针设置为stample start+预加载大小
fseek(fpFile,WavePointers+preforebytesize,SEEK_SET);
//从infle读取剩余字节并填充缓冲区的空白部分
fread(SampleData+preload字节大小/sizeof(short),1,字节大小-preload字节大小,infle);
//记住加载的字节数
preload字节大小=字节大小;
}
我希望代码的第二部分在文件回放时在后台执行,但实际上这都是串行处理,因此在加载缓冲区的其余部分之前不会开始回放,从而延迟了回放。
我可以有一个后台线程加载文件数据而不干扰音频任务吗?我可以用OpenMP做这件事吗?

BackgroundWorker类

这里有一些很好的例子:


您可能可以使用OpenMP实现这一点,但这涉及的是并发性而不是并行性,因此我将介绍pthreads或C++11线程:

  • pthreads
  • C++11线程

在这里,我使用pthread启动了三个线程。它可能会给你一些工作的东西。。。享受:

// g++ -o  audio  *.cpp ../common/*.cpp  -std=c++11  -lm -lpthread

#include "cpp_openal_opengl_dna.h"

#include <thread>
#include <exception>
#include <mutex>

void launch_producer(Circular_Buffer * given_circular_buffer,
        struct_sample_specs * ptr_struct_sample_specs, std::string chosen_file) {
}
void launch_mvc_visualization(Audio_Model * given_audio_model) {
}

void launch_audio_playback(Circular_Buffer * given_circular_buffer, Audio_Model * given_audio_model) {
}

int main() {

    std::cout << "hello Corinde" << std::endl; // prints hello Corinde

    // here we launch three threads

    // thread t1 reads an input file to populate audio buffer
    // notice the first parameter is the function above followed by its input parms

    std::thread t1(launch_producer, circular_buffer, ptr_struct_sample_specs,
            all_file_names[WHICH_FILE_INPUT]);

    Audio_Model * audio_model = new Audio_Model(MAX_SIZE_CIRCULAR_BUFFER);

    // thread t2 does real time OpenGL visualization of audio buffer data

    std::thread t2(launch_mvc_visualization, audio_model);  // OpenGL graphics visualization

    // thread t3 renders the audio buffers as sound to your speakers

    std::thread t3(launch_audio_playback, circular_buffer, audio_model);

    // -------------------------

    std::cout << "all three threads now launched" << std::endl;

    t1.join();
    t2.join();
    t3.join();

    std::cout << "processing is complete" << std::endl;

    // ----------

    return 0;
}
//g++-o audio*.cpp../common/*.cpp-std=c++11-lm-lpthread
#包括“cpp_openal_opengl_dna.h”
#包括
#包括
#包括
无效启动生产者(循环缓冲区*给定循环缓冲区,
结构样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本样本{
}
无效启动\u mvc\u可视化(音频模型*给定音频模型){
}
无效启动音频播放(循环缓冲区*给定循环缓冲区,音频模型*给定音频模型){
}
int main(){

std::cout我想我刚刚用
std::thread
方法
detach()
解决了这个问题

为此,每次必须从文件中加载新样本数据时,我都必须重新打开该文件,这样我现在就有了一个全局变量,用于存储文件名并以这种方式调用函数:

// The loading function that will be executed in a new thread
void continuePreload(unsigned long ByteSize)
{
    // Re-open the file 'openFile'
    FILE *fpFile = fopen(openFile, "rb");

    // Set pointer in file at stample start + preload size
    fseek(fpFile, WavePointers + preloadByteSize, SEEK_SET);

    // Read the remaining bytes
    fread(SampleData + preloadByteSize / sizeof(short), 1, ByteSize - preloadByteSize, fpFile);

    // Close file
    fclose(fpFile);

    // Remember how many bytes we loaded
    preloadByteSize = ByteSize;
}
在播放事件功能中

// Get the size in bytes
const unsigned long ByteSize = SampleLib.smp.len * sizeof(short);

if (preloadByteSize < ByteSize)
{
    std::thread loadSample(&myClass::continuePreload, this, ByteSize);
    loadSample.detach();
}
//以字节为单位获取大小
const unsigned long ByteSize=SampleLib.smp.len*sizeof(short);
if(预加载字节大小<字节大小)
{
std::threadloadsample(&myClass::continuePreload,this,ByteSize);
loadSample.detach();
}
该程序现在正按照我预期的方式运行:每当播放事件到达时,它就开始使用先前预加载的内容从示例缓冲区播放音频,同时一个新线程完成加载文件的其余部分并完全填充缓冲区


只要从磁盘加载的速度比音频播放的速度快,我们就没有竞争条件。如果加载速度太慢,我仍然可以增加预加载大小,使初始加载时间稍微慢一点。

我没有指定平台,因为我假设我的代码必须是多平台的。我认为你的建议仅适用于Windows,是吗不是吗?你在用什么操作系统?Windows,Linux,一些嵌入式环境?Windows和Linux。这段代码应该是多平台的。我现在用
std::thread
测试
join()
我正在使用Microsoft ProcessMonitor查看引擎盖下发生了什么。显然,每次必须加载新样本时,我都会创建一个新线程,但使用的是
join()
将阻止主执行,使整个进程成为串行而非并行。这不是我的目标…我希望在主线程回放示例缓冲区时在后台完成加载。您只需调用t1、t2、t3以上的线程一次…然后它会为每个线程生成并行执行的子线程…连接只是位于等待对正在运行的线程没有影响的线程完成