使用 Naudio 复制 Wave 文件 - Copy/Append 最新可用字节

Replicate Wave file using Naudio - Copy/Append Latest Available bytes

我在 Source 文件夹中有一个 Active wave 录音 wave-file.wav。 我需要使用新名称 wave-file-copy.wav.

将此文件复制到 Destination 文件夹

记录和复制应该并行进行。 我已经实施了一个计划作业,每 10 分钟 运行 并将 source 文件复制到 destination.

private static void CopyWaveFile(string destinationFile, string sourceFile){
        using (var fs = File.Open(sourceFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
            using (var reader = new WaveFileReader(fs)){
                using (var writer = new WaveFileWriter(destinationFile, reader.WaveFormat)){
                    reader.Position = 0;
                    var endPos = (int)reader.Length;
                    var buffer = new byte[1024];
                    while (reader.Position < endPos){
                        var bytesRequired = (int)(endPos - reader.Position);
                        if (bytesRequired <= 0) continue;
                        var bytesToRead = Math.Min(bytesRequired, buffer.Length);
                        var bytesRead = reader.Read(buffer, 0, bytesToRead);
                        if (bytesRead > 0){
                            writer.Write(buffer, 0, bytesRead);
                        }
                    }
                }
            }
        }
    }

复制操作工作正常,即使源文件正在不断更新。

复制操作所用的时间呈线性增加,因为我每次都复制整个文件。

我正在尝试实现一个新功能 ConcatenateWavFiles(),它应该使用源记录的最新可用字节更新目标文件的内容。

我尝试了一些示例代码 - 我使用的方法是:

  1. 读取目标文件元信息,并获取长度。
  2. 将目标文件的 length 设置为源文件的 reader.Position waveReader
  3. 从位置开始读取源文件直到结束

    public static void ConcatenateWavFiles(string destinationFile, string sourceFile){  
    
        WaveFileWriter waveFileWriter = null;
        var sourceReadOffset = GetWaveFileSize(destinationFile);
    
        try{
            using (var fs = File.Open(sourceFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                using (var reader = new WaveFileReader(fs))
                {
                    waveFileWriter = new WaveFileWriter(destinationFile, reader.WaveFormat);
                    if (!reader.WaveFormat.Equals(waveFileWriter.WaveFormat)){
                        throw new InvalidOperationException(
                            "Can't append WAV Files that don't share the same format");
                    }
    
                    var startPos = sourceReadOffset - sourceReadOffset % reader.WaveFormat.BlockAlign;
                    var endPos = (int) reader.Length;
                    reader.Position = startPos;
                    var bytesRequired = (int)(endPos - reader.Position);
                    var buffer = new byte[bytesRequired];
                    if (bytesRequired > 0)
                    {
                        var bytesToRead = Math.Min(bytesRequired, buffer.Length);
                        var bytesRead = reader.Read(buffer, 0, bytesToRead);
    
                        if (bytesRead > 0)
                        {
                            waveFileWriter.Write(buffer, startPos, bytesRead);
                        }
                    }
                }
            }
        }
        finally{
            if (waveFileWriter != null){
                waveFileWriter.Dispose();
            }
        }
    }
    

我能够获取新内容。

是否可以将最新内容附加到现有目标文件?

如果可能的话我在代码中做错了什么?

我的代码抛出以下异常 - Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.

尝试在源文件实际结束前一两个 Wav 块读取源文件。

这种情况可能是代码判断源文件的末尾太接近了。

我找不到使用 NAudio 库复制 wave 音频文件的解决方案。

但我已经使用 C# MemoryStreams 和 FileStreams 实现了一个解决方案。

  1. 如果目标文件不存在,则将源文件复制到目标文件。
  2. 将所有最新的字节(在最后一次操作后记录)附加到目标文件。
  3. 将 Wave 文件头修改为以反映最后附加的字节。 (否则文件时长不会更新,只会增加文件大小。
  4. 定期重复此追加操作。

    public void ReplicateFile(string destinationFile, string sourceFile){
    
        if (!Directory.Exists(GetRoutePathFromFile(sourceFile)))
            return;           
    
        if (!File.Exists(sourceFile))                
            return;           
    
        if (Directory.Exists(GetRoutePathFromFile(destinationFile))){
            if (File.Exists(destinationFile)){                                         
                UpdateLatestWaveFileContent(destinationFile, sourceFile);
            }else{                 
                CopyWaveFile(destinationFile, sourceFile);
            }
        }else{                        
            Directory.CreateDirectory(GetRoutePathFromFile(destinationFile));
            CopyWaveFile(destinationFile, sourceFile);
        }
    }
    
    
    
    
    private static string GetRoutePathFromFile(string file){
        var rootPath = Directory.GetParent(file);
        return rootPath.FullName;
    }
    
    
    
    
    
    private static void CopyWaveFile(string destination, string source){
        var sourceMemoryStream = new MemoryStream();
        using (var fs = File.Open(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
            fs.CopyTo(sourceMemoryStream);
        }            
        using (var fs = new FileStream(destination, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite)){
            sourceMemoryStream.WriteTo(fs);               
        }
    }
    
    
    
    
    private static void UpdateLatestWaveFileContent(string destinationFile, string sourceFile){
        var sourceMemoryStream = new MemoryStream();
        long offset = 0;
    
        using (var fs = File.Open(destinationFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
            offset = fs.Length;
        }
    
        using (var fs = File.Open(sourceFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
            fs.CopyTo(sourceMemoryStream);
        }
    
        var length = sourceMemoryStream.Length - offset;            
    
        var buffer = sourceMemoryStream.GetBuffer();
        using (var fs = new FileStream(destinationFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)){                
            fs.Write(buffer, (int)offset, (int)length);
        }
    
    
        var bytes = new byte[45];
    
        for (var i = 0; i < 45; i++){
            bytes[i] = buffer[i];
        }
    
        ModifyHeaderDataLength(destinationFile, 0, bytes);
    }
    
    
    
    private static void ModifyHeaderDataLength(string filename, int position, byte[] data){
        using (Stream stream = File.Open(filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))
        {
            stream.Position = position;
            stream.Write(data, 0, data.Length);
        }
    }