{"id":23120196,"url":"https://github.com/jader/pcmtowav","last_synced_at":"2025-04-06T06:08:58.138Z","repository":{"id":56995657,"uuid":"173855128","full_name":"Jader/PcmToWav","owner":"Jader","description":":musical_note: PHP 实现 PCM 格式音波文件转 WAV 格式音频文件","archived":false,"fork":false,"pushed_at":"2024-05-31T10:35:13.000Z","size":277,"stargazers_count":216,"open_issues_count":0,"forks_count":7,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-06T06:08:52.934Z","etag":null,"topics":["audio","audio-converter","composer","pcm","php","php-library","wav"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Jader.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-03-05T02:08:35.000Z","updated_at":"2025-02-08T06:25:00.000Z","dependencies_parsed_at":"2024-11-15T21:42:03.658Z","dependency_job_id":"bd685435-4137-4aa1-bbb9-5af6424b01a8","html_url":"https://github.com/Jader/PcmToWav","commit_stats":{"total_commits":9,"total_committers":1,"mean_commits":9.0,"dds":0.0,"last_synced_commit":"686a56a00a24bab1b281329baeebfc0015ba5f70"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jader%2FPcmToWav","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jader%2FPcmToWav/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jader%2FPcmToWav/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jader%2FPcmToWav/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Jader","download_url":"https://codeload.github.com/Jader/PcmToWav/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247441052,"owners_count":20939239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio","audio-converter","composer","pcm","php","php-library","wav"],"created_at":"2024-12-17T06:09:07.105Z","updated_at":"2025-04-06T06:08:58.114Z","avatar_url":"https://github.com/Jader.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PHP 实现PCM转WAV\n\n[![License](https://img.shields.io/packagist/l/inhere/console.svg)](LICENSE)\n[![Php Version](https://img.shields.io/badge/php-%3E=5.4-brightgreen.svg)](https://packagist.org/packages/jade/pcm-to-wav)\n[![Latest Stable Version](http://img.shields.io/packagist/v/jade/pcm-to-wav.svg)](https://packagist.org/packages/jade/pcm-to-wav)\n\n此扩展能快速将PCM格式的音波文件转换成WAV格式的音频文件，目前只为公司项目提供解决方案。\n\n扩展参考于 [helviojunior/WaveGenerator](https://github.com/helviojunior/WaveGenerator) ，在此特别感谢！\n\n## 安装\n\n```bash\ncomposer require jade/pcm-to-wav\n```\n\n## 使用\n\n```bash\nuse PcmToWave\\PcmToWave;\n\n$input_file = './file/test.pcm'; // 准备输入的文件\n$output_file = './file/test.wav'; // 预计输出的文件\n$data = PcmToWave::init($pcm_file, $wav_file); // 调用转换\n\n```\n\n## 测试Demo使用\n\n```bash\n进入扩展包目录\ncd vendor/jade/pcm-to-wav\ncomposer install\ncd test\nphp Test.php\n```\n\n## 原理介绍\n\n### 什么是 `PCM` 和 `WAV` ？\n\n `PCM` ：PCM（Pulse Code Modulation----脉码调制录音)。所谓 `PCM` 录音就是将声音等模拟信号变成符号化的脉冲列，再予以记录。 `PCM` 信号是由 `1` 、 `0` 等符号构成的数字信号，而未经过任何编码和压缩处理。与模拟信号比，它不易受传送系统的杂波及失真的影响。动态范围宽，可得到音质相当好的影响效果。\n\n `WAV` ： `WAV` 是一种无损的音频文件格式， `WAV` 符合 PIFF(Resource Interchange File Format) 规范。所有的 `WAV` 都有一个文件头，这个文件头音频流的编码参数。WAV对音频流的编码没有硬性规定，除了 `PCM` 之外，还有几乎所有支持 `ACM` 规范的编码都可以为WAV的音频流进行编码。\n\n###  `PCM` 和 `WAV` 的关系\n\n简单地说,  `PCM` 是音频的原始数据,  `WAV` 则是一种封装音频数据的容器, 而且它的格式还很简单, 只是在数据开头添加一些和音频数据相关的头信息。\n\n\n首先我们看一下WAV的格式规则, 如下图\n\n![](https://cdn.jsdelivr.net/gh/jader/resource/images/2024/202405311833829.jpeg)\n\n![](https://cdn.jsdelivr.net/gh/jader/resource/images/2024/202405311834709.jpeg)\n\n了解这些规则后，我们就可以撸代码吧\n\n1、 `ChunkID` 占4byte, 固定值\"RIFF\"\n\n    $ChunkID = array(0x52, 0x49, 0x46, 0x46); // RIFF 16进制的0x52等于10进制中的82，82对应的ASCII码为R\n    \n2、 `ChunkSize` 占4byte, 值为4 + (8 + SubChunk1Size) + (8 + SubChunk2Size), 其中如果原始数据是PCM, 简化为36 + SubChunk2Size\n\n    $ChunkSize = array(0x0, 0x0, 0x0, 0x0);\n    $ChunkSize = self::getLittleEndianByteArray(36 + $dataSize);\n    \n3、 `Format` 占4byte, 固定值\"WAVE\"\n\n    $FileFormat = array(0x57, 0x41, 0x56, 0x45); // WAVE\n    \n4、 `Subchunk1ID` 占4byte, 固定值\"ftm \"(注意空格补全4位)\n\n    $Subchunk1ID = array(0x66, 0x6D, 0x74, 0x20); // fmt\n\n5、 `Subchunk1Size` 占4byte, 数据为PCM时, 值为16\n\n    $Subchunk1Size = array(0x10, 0x0, 0x0, 0x0); // 16 little endian\n\n6、 `AudioFormat` 占2byte, 数据为PCM时, 值为1, 其他值表示数据进行过某种压缩\n\n    $AudioFormat = array(0x1, 0x0); // PCM = 1 little endian\n    \n7、 `NumChannels` 占2byte, 对应AudioRecord中的channelConfig, 单声道Mono = 1, 立体声Stereo = 2\n\n    if ($numchannels == 2) {\n        $fmt-\u003eNumChannels = array(0x2, 0x0); // 立体声为2\n    } else {\n        $fmt-\u003eNumChannels = array(0x1, 0x0); // 单声道为1\n    }\n\n8、 `SampleRate` 占4byte, 对应AudioRecord中的sampleRateInHz, 即采样频率, 例如8000, 16000, 44100\n\n    $SampleRate = self::getLittleEndianByteArray($samplerate);\n\n9、 `ByteRate` 占4byte, 值为SampleRate * BlockAlign\n\n    self::getLittleEndianByteArray($samplerate * $numchannels * ($bitspersample / 8));\n\n10、 `BlockAlign` 占2byte, 值为NumChannels * BitsPerSample / 8\n\n    self::getLittleEndianByteArray($numchannels * ($bitspersample / 8), true);\n\n11、 `BitsPerSample` 占2byte, 对应AudioRecord中的audioFormat, 8bits = 8, 16bits = 16\n\n    self::getLittleEndianByteArray($bitspersample, true);\n    \n12、 `Subchunk2ID` 占4byte, 固定值\"data\", 即\n\n    $Subchunk2ID = array(0x64, 0x61, 0x74, 0x61); // data\n\n13、 `Subchunk2Size` 占4byte, 描述音频数据的长度, 就是pcm文件大小\n\n    self::getLittleEndianByteArray(filesize($filename));\n    \n14、 `Data` 占pcm文件大小个byte, 表示原始的PCM音频数据\n\n `getLittleEndianByteArray` 方法说明\n \n `getLittleEndianByteArray`主要是将传递过来的数进行处理已转换成需要使用的数据，站几字节，就返回多少长度的数组\n\n    private static function getLittleEndianByteArray($lValue, $short = false)\n        {\n            $b = array(0, 0, 0, 0);\n            $running = $lValue / pow(16, 6);\n            $b[3] = floor($running);\n            $running -= $b[3];\n            $running *= 256;\n            $b[2] = floor($running);\n            $running -= $b[2];\n            $running *= 256;\n            $b[1] = floor($running);\n            $running -= $b[1];\n            $running *= 256;\n            $b[0] = round($running);\n    \n            if ($short) { // 为 `true` 时返回长度为2的数组\n                $tmp = array_slice($b, 0, 2);\n                $b = $tmp;\n            }\n    \n            return $b;\n        }\n\n整个文件的开头44字节信息也基本说明完了，下面就说下处理类文件的实现，这边处理的逻辑先临时创建一个只有44字节的文件，然后将 `PCM` 文件的数据追加进该文件，最终根据WAV的格式规则实际计算出真实的头部44字节信息并将文件修改指针指向文件开头，然后修改为新产生的数据\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjader%2Fpcmtowav","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjader%2Fpcmtowav","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjader%2Fpcmtowav/lists"}