如果你是一名音乐发烧友, 那么应该知道 Flac 这种常见的无损音乐格式. Flac 音乐文件支持 metadata, 用户可以编辑 metadata, 让音乐文件带有艺术家, 所属专辑, 音轨等等信息. 通常来说, metadata 和音频数据并不相关, 修改 metadata 并不会影响音频本身. 但是, 近日微软官方公布了 Win10 中存在一个 Bug, 在 Win10 中用资源管理器修改 Flac 文件的 metadata, 竟会导致音频的损坏!
根据 Windows Latest 的报道, 微软最新发布的一份支持文件披露, 如果在 Win10 的 2004 或者更高版本中, 使用文件资源管理器修改 Flac 音乐文件的 metadata, 就会损耗 Flac 音频文件. 这个 Bug 在 Win10 专业版, 家庭版, 企业版, 工作站版乃至其他版本的 Win10 中均有出现.
根据微软本月早些时候发布的支持文件, Win10 的文件资源管理器导致了这个错误, 它破坏了 Flac 文件头包含的 ID3 框架也就是 metadata, 而这个 ID3 框架负责存储音频的注释, 例如音乐标题, 艺术家, 专辑, 曲目编号等. 在 Win10 上, Flac 的处理程序忽视了 ID3 框架, 该程序认为 Flac 文件在使用 4 字节的文件头, 当 Flac 文件被 Win10 编辑的时候, ID3 框架被覆盖了, 导致没有了开始代码, 导致了音乐播放器无法识别被修改后的文件.
因此, 在 Win10 中, 如果你直接用文件资源管理器修改 Flac 音乐文件的标题, 艺术家等 metadata, 会导致该文件无法播放.
幸运的是, 微软已经确定了 Bug 的根本原因, 用户可以通过 Windows Update 升级 KB5003214 补丁进行修复.
在 KB5003214 补丁中, 微软确认了上文提到的错误已经被修复, 修改了 Flac 的标题, 艺术家等 metadata 后, Flac 不会再变得无法播放. 而对于已经损坏了的 Flac 文件, 微软则发布了一个 PowerShell 脚本来进行修复, 运行该脚本后 Flac 文件即可重新播放, 不过已经从 ID3 框架中丢失了的 metadata 信息并不能恢复.
下面是利用 PowerShell 脚本修复 Flac 文件的具体方法.
1, 开启记事本;
2, 复制以下字符, 粘贴到记事本中:
- # Copyright 2021 Microsoft
- # This script will repair a FLAC file that has been corrupted by Media Foundation in reference to KB5003430.
- # Refer to KB5003430 for further information
- param(
- [parameter(Mandatory=$true,
- HelpMessage="The path to the FLAC file that has been corrupted by Media Foundation",
- ValueFromRemainingArguments=$true)]
- [ValidateScript({ -not [String]::IsNullOrEmpty($_) -and (Test-Path $_) })]
- [String]$File
- )
- # We need to back up the current file incase we have any errors
- $FileDirectory = Split-Path -Resolve $File
- $Filename = Split-Path -Leaf -Resolve $File
- $FullPath = Join-Path -Resolve $FileDirectory $Filename
- $Filename = [String]::Format("Backup_{0:yyyyMMdd_hhmmss}_{1}", [DateTime]::Now, $Filename)
- $BackupLocation = Join-Path $FileDirectory $Filename
- Write-Output "Microsoft FLAC Repair Tool. This tool will repair a FLAC audio file that was corrupted when editing its details."
- Write-Output "Affected File: $FullPath"
- Write-Output "A backup of the file will be made: $BackupLocation"
- Write-Output "Do you wish to continue?"
- $choice=$host.ui.PromptForChoice("Fixing FLAC Script", "Do you wish to continue", ('&Yes', '&No'), 1)
- function ParseStreamInfoMetadataBlock([System.IO.FileStream]$stream)
- {
- $blockType = $stream.ReadByte()
- $lastBlock = ($blockType -shr 7) -ne 0
- $blockType = $blockType -band 0x7F
- if ($blockType -ne 0)
- {
- return $false
- }
- $blockSize = (($stream.ReadByte() -shl 16) -bor ($stream.ReadByte() -shl 8) -bor $stream.ReadByte())
- if ($blockSize -lt 34)
- {
- return $false
- }
- $minAudioBlockSize = ($stream.ReadByte() -shl 8) -bor $stream.ReadByte()
- $maxAudioBlockSize = ($stream.ReadByte() -shl 8) -bor $stream.ReadByte()
- if ($minAudioBlockSize -lt 16 -or $maxAudioBlockSize -lt 16)
- {
- return $false
- }
- $minFrameSize = (($stream.ReadByte() -shl 16) -bor ($stream.ReadByte() -shl 8) -bor $stream.ReadByte())
- $maxFrameSize = (($stream.ReadByte() -shl 16) -bor ($stream.ReadByte() -shl 8) -bor $stream.ReadByte())
- $sampleInfo = (($stream.ReadByte() -shl 24) -bor ($stream.ReadByte() -shl 16) -bor ($stream.ReadByte() -shl 8) -bor $stream.ReadByte())
- $sampleRate = $sampleInfo -shr 12
- $channelCount = (($sampleInfo -shr 9) -band 0x7) + 1
- $bitsPerSample = (($sampleInfo -shr 4) -band 0x1F) + 1
- [UInt64]$sampleCount = (($stream.ReadByte() -shl 24) -bor ($stream.ReadByte() -shl 16) -bor ($stream.ReadByte() -shl 8) -bor $stream.ReadByte())
- $sampleCount = (([UInt64]$sampleInfo -band 0xF) -shl 32) -bor $sampleCount
- $MD5HashBytes = New-Object byte[] 16
- $stream.Read($MD5HashBytes, 0, $MD5HashBytes.Length)
- $MD5Hash = [Guid]($MD5HashBytes)
- if ($sampleRate -eq 0)
- {
- return $false
- }
- # Passing these checks means that we likely have a stream info header and can rebuild the file
- Write-Output "File Stream Information"
- Write-Output "Sample Rate: $sampleRate"
- Write-Output "Audio Channels: $channelCount"
- Write-Output "Sample Depth: $bitsPerSample"
- Write-Output "MD5 Audio Sample Hash: $MD5Hash"
- return $true
- }
- if ($choice -eq 0)
- {
- Copy-Item $FullPath -Destination $BackupLocation -Force
- $stream = [System.IO.File]::Open($FullPath, [System.IO.FileMode]::Open)
- $stream.Seek(4, [System.IO.SeekOrigin]::Begin)
- while ($stream.ReadByte() -eq 0) {}
- # We now need to figure out where a valid FLAC metadata frame begins
- # We are likely pointing to the last byte of the size member so we'll seek back 4 bytes and retry
- $flacDataStartPosition = $stream.Position - 4
- $stream.Seek($flacDataStartPosition, [System.IO.SeekOrigin]::Begin)
- while (-not(ParseStreamInfoMetadataBlock($stream)))
- {
- $flacDataStartPosition = $flacDataStartPosition + 1
- $stream.Seek($flacDataStartPosition, [System.IO.SeekOrigin]::Begin)
- }
- # Insert the start code
- $stream.Seek($flacDataStartPosition, [System.IO.SeekOrigin]::Begin)
- if (Test-Path "$FullPath.tmp")
- {
- Remove-Item "$FullPath.tmp"
- }
- $fixedStream = [System.IO.File]::Open("$FullPath.tmp", [System.IO.FileMode]::CreateNew)
- [byte[]]$startCode = [char[]]('f', 'L', 'a', 'C');
- $fixedStream.Write($startCode, 0, $startCode.Length)
- $stream.CopyTo($fixedStream)
- $stream.Close()
- $fixedStream.Close()
- Move-Item -Force "$FullPath.tmp" $FullPath
- }
3, 保存文件, 在 "另存为" 对话框中, 将目录定位到你想要保存 PowerShell 脚本的位置;
4, 在文件名输入框中, 输入 "FixFlacFiles.ps1", 将另存为文件的类型更改为 Text Documents (*.txt);
5, 进入到你保存该 PowerShell 脚本的目录;
6, 右键点击刚刚保存的脚本, 然后选择 "使用 PowerShell 运行";
7, 出现提示时, 输入无法播放的 Flac 文件的文件名, 然后按下回车键.
微软建议大家安装本月推送的可选累积更新, 以避免修改 Flac 文件 metadata 出现的问题.
鸿蒙官方战略合作共建 --HarmonyOS 技术社区
来源: http://os.51cto.com/art/202106/665149.htm