双缓冲方法实现waveOut的连续无卡顿播放以及即收即放

article/2025/11/9 14:40:02

1. waveOut基本使用方法

waveOut是一套历史悠久的Windows音频API,虽然古老,但至今仍运行良好,且支持老旧系统(原生支持Windows XP)。

waveOut虽然不像DirectSound那样自带混音功能,但也可以通过同时开多个播放线程实现同时播放多个声音的目的,达到事实上的混音效果。(关于waveOut混音效果可以看我的一个视频演示:FC音乐格式nsf多线程播放程序演示)

waveOut的使用一般遵循Open - Prepare - Write - Reset - Unprepare - Close的步骤。本文提到的函数均省略前缀waveOut,例如Write实际上指的是waveOutWrite函数。
在这里插入图片描述
如上图所示。waveOut在播放音频期间不能调用Unprepare, Write等函数,如要暂停播放可使用Pause函数,或者Reset函数。播放期间调用Reset会使声音立即停止,接着调用Prepare, Write函数,可以再次播放。

通过此法可以实现《帝国时代2》中“伐伐伐伐伐木工”的效果。

WaveOutEffect.h:

#pragma once#include <Windows.h>
#include <mmsystem.h>class WaveOutEffect
{
public:WaveOutEffect(PWAVEFORMATEX pWaveformat, int buf_ms = 80);~WaveOutEffect();void PlayAudio(char* in_buf, unsigned int in_bufsize);
private:char* buf;unsigned int buf_size;WAVEFORMATEX m_Waveformat;WAVEHDR wavehdr;HWAVEOUT	m_hWaveOut;MMRESULT mRet;void Open();void Reset();
};

WaveOutEffect.cpp:

#include "WaveOutEffect.h"#include "tstring.h"#pragma comment(lib,"winmm.lib")#define _PRINT
#include <stdexcept>using namespace std;WaveOutEffect::WaveOutEffect(PWAVEFORMATEX pWaveformat, int buf_ms) :m_hWaveOut(0)
{//初始化音频格式memcpy(&m_Waveformat, pWaveformat, sizeof(WAVEFORMATEX));m_Waveformat.nBlockAlign = (m_Waveformat.wBitsPerSample * m_Waveformat.nChannels) >> 3;m_Waveformat.nAvgBytesPerSec = m_Waveformat.nBlockAlign * m_Waveformat.nSamplesPerSec;//分配缓冲区buf_size = buf_ms * m_Waveformat.nSamplesPerSec * m_Waveformat.nBlockAlign / 1000;buf = new char[buf_size];//清空WAVEHDRZeroMemory(&wavehdr, sizeof(WAVEHDR));//设置WAVEHDRwavehdr.lpData = buf;wavehdr.dwBufferLength = buf_size;Open();
}WaveOutEffect::~WaveOutEffect()
{Reset();mRet = waveOutUnprepareHeader(m_hWaveOut, &wavehdr, sizeof(WAVEHDR));if (mRet != MMSYSERR_NOERROR){TCHAR info[260];waveOutGetErrorText(mRet, info, 260);throw runtime_error(to_string(info));}mRet = waveOutClose(m_hWaveOut);if (mRet != MMSYSERR_NOERROR){throw runtime_error("waveOutClose fail");}delete[] buf;
}void WaveOutEffect::PlayAudio(char* in_buf, unsigned int in_bufsize)
{if (in_bufsize > buf_size)//传入buf大于内置缓冲区,抛出异常{throw runtime_error("input buffer size is bigger than self");}if (in_bufsize <= buf_size){wavehdr.dwBufferLength = in_bufsize;}memcpy(buf, in_buf, in_bufsize);Reset();mRet = waveOutPrepareHeader(m_hWaveOut, &wavehdr, sizeof(WAVEHDR));if (mRet != MMSYSERR_NOERROR){TCHAR info[260];waveOutGetErrorText(mRet, info, 260);throw runtime_error(to_string(info));}mRet = waveOutWrite(m_hWaveOut, &wavehdr, sizeof(WAVEHDR));if (mRet != MMSYSERR_NOERROR){TCHAR info[260];waveOutGetErrorText(mRet, info, 260);throw runtime_error(to_string(info));}
}void WaveOutEffect::Open()
{mRet = waveOutOpen(&m_hWaveOut, WAVE_MAPPER, &m_Waveformat, NULL, 0, CALLBACK_NULL);if (mRet != MMSYSERR_NOERROR){throw runtime_error("waveOutOpen fail");}
}void WaveOutEffect::Reset()
{mRet = waveOutReset(m_hWaveOut);if (mRet != MMSYSERR_NOERROR){TCHAR info[260];waveOutGetErrorText(mRet, info, 260);throw runtime_error(to_string(info));}
}

2. 双缓冲实现waveOut的即取即放

如果我们要播放一大段音频怎么办?或者音频数据是以流的方式获得,比如网络中接收到的,怎么办?

一大段音频倒是可以一次性分配几十MB内存,全部加载,一股脑地都用waveOutWrite调用了。但音频流就没有办法了。

网络上流传很广的一篇,由英文翻译得到的waveOut教程,上面使用了20个数组轮流送进waveOut的方法,我个人觉得很不直观,又繁琐。

但waveOut的线程控制较为复杂,稍不注意就会触发死锁。这方面更深入的剖析可以看参考文献2。直到我看到参考文献1,才得到真正的解决方法。

2.1 播放

在这里插入图片描述

如上图所示,我首先使用了两个WAVEHDR,分别掌管2个缓冲区。然后开辟了一个线程,每当一个缓冲区播放完成时,线程2就将该缓冲区标记为“未播放”,每次调用PlayAudio时,都先寻找空闲的缓冲区,没有则阻塞,直到空闲缓冲区出现,则使用Write将当前音频片段送入缓冲待播放。因为两个WAVEHDR绑定到同一个HWAVEOUT,所以系统内核在一个缓冲播放完成时会自动接着放另一个,达到时间上的连续播放。

2.2 停止

在这里插入图片描述
停止功能如上图所示。首先调用Reset,让内核强制停止。然后阻塞等待playing1和playing2全部为false,确保没有音频正在播放。然后发送WM_QUIT至线程2,再阻塞等待线程2退出。之后就可以调用Unprepare和Close正常结束了。

在使用时,由于PlayAudio内部会自动阻塞,所以只要单独开一个线程,无脑地往PlayAudio里怼 读取/解码/接收 到的任何音频数据就可以了。机制会保证播放的连续性,不会出现“伐伐伐木工”这种情况。

下附代码。
WaveOut.h:

#pragma once#include <Windows.h>
#include <mmsystem.h>class WaveOut
{
public:WaveOut(PWAVEFORMATEX pWaveformat,int buf_ms=80);~WaveOut();//open & prepareHeadervoid Start();//填入数据,若当前没有空余缓冲区,则先阻塞,填充后返回//writevoid PlayAudio(char* buf, unsigned int nSize);//reset -> 等待线程播放完成并退出 -> unprepare -> closevoid Stop();
private:char* buf1,*buf2;unsigned int buf_size;bool isplaying1, isplaying2;bool thread_is_running;HANDLE		m_hThread;DWORD		m_ThreadID;BOOL		m_bDevOpen;HWAVEOUT	m_hWaveOut;int			m_BufferQueue;WAVEFORMATEX m_Waveformat;WAVEHDR wavehdr1,wavehdr2;CRITICAL_SECTION m_Lock;static DWORD WINAPI ThreadProc(LPVOID lpParameter);void StartThread();void StopThread();void Open();//unprepare & closevoid Close();inline void WaitForPlayingEnd();inline void SetThreadSymbol(bool running);//若传入的指针指向wavehdr1,则将isplaying1设为falseinline void SetFinishSymbol(PWAVEHDR pWaveHdr);
};

WaveOut.cpp:

#include "WaveOut.h"
#include "tstring.h"#pragma comment(lib,"winmm.lib")#define _PRINT
#include <stdexcept>using namespace std;WaveOut::WaveOut(PWAVEFORMATEX pWaveformat, int buf_ms) :thread_is_running(false), m_hThread(0), m_ThreadID(0), m_bDevOpen(false), m_hWaveOut(0), m_BufferQueue(0), isplaying1(false), isplaying2(false)
{//初始化音频格式memcpy(&m_Waveformat, pWaveformat, sizeof(WAVEFORMATEX));m_Waveformat.nBlockAlign = (m_Waveformat.wBitsPerSample * m_Waveformat.nChannels) >> 3;m_Waveformat.nAvgBytesPerSec = m_Waveformat.nBlockAlign * m_Waveformat.nSamplesPerSec;//分配缓冲区buf_size = buf_ms * m_Waveformat.nSamplesPerSec * m_Waveformat.nBlockAlign / 1000;buf1 = new char[buf_size];buf2 = new char[buf_size];//清空WAVEHDRZeroMemory(&wavehdr1, sizeof(WAVEHDR));ZeroMemory(&wavehdr2, sizeof(WAVEHDR));//设置WAVEHDRwavehdr1.lpData = buf1;wavehdr1.dwBufferLength = buf_size;wavehdr2.lpData = buf2;wavehdr2.dwBufferLength = buf_size;InitializeCriticalSection(&m_Lock);}WaveOut::~WaveOut()
{WaitForPlayingEnd();StopThread();Close();delete[] buf1;delete[] buf2;DeleteCriticalSection(&m_Lock);
}void WaveOut::Start()
{StartThread();try{Open();}catch (runtime_error e){StopThread();throw e;}
}void WaveOut::PlayAudio(char* in_buf, unsigned int in_size)
{if (!m_bDevOpen){throw runtime_error("waveOut has not been opened");}//等待出现可写入缓存while (1){if (isplaying1 && isplaying2)//都不可写入,继续等待{Sleep(10);
#ifdef _PRINTprintf("PlayAudio::waitting\n");
#endifcontinue;}else{//一旦出现任意一个可写入,则break
#ifdef _PRINTprintf("PlayAudio::break\n");
#endifbreak;}}//将没有在播放的hdr设为当前hdrchar* now_buf=nullptr;WAVEHDR* now_wavehdr = nullptr;bool* now_playing = nullptr;if (isplaying1 == false){now_buf = buf1;now_wavehdr = &wavehdr1;now_playing = &isplaying1;}if (isplaying2 == false){now_buf = buf2;now_wavehdr = &wavehdr2;now_playing = &isplaying2;}if (in_size > buf_size)//传入buf大于内置缓冲区,抛出异常{throw runtime_error("input buffer size is bigger than self");}if (in_size <= buf_size){now_wavehdr->dwBufferLength = in_size;}memcpy(now_buf, in_buf, in_size);if (waveOutWrite(m_hWaveOut, now_wavehdr, sizeof(WAVEHDR)) != MMSYSERR_NOERROR){throw runtime_error("waveOutWrite fail");}EnterCriticalSection(&m_Lock);*now_playing = true;LeaveCriticalSection(&m_Lock);}DWORD __stdcall WaveOut::ThreadProc(LPVOID lpParameter)
{
#ifdef _PRINTprintf("ThreadProc::enter\n");
#endifWaveOut* pWaveOut = (WaveOut*)lpParameter;pWaveOut->SetThreadSymbol(true);MSG msg;while (GetMessage(&msg, 0, 0, 0)){switch (msg.message){case WOM_OPEN:break;case WOM_CLOSE:break;case WOM_DONE://标记完成符号WAVEHDR* pWaveHdr = (WAVEHDR*)msg.lParam;pWaveOut->SetFinishSymbol(pWaveHdr);break;}}pWaveOut->SetThreadSymbol(false);
#ifdef _PRINTprintf("ThreadProc::exit\n");
#endifreturn msg.wParam;
}void WaveOut::StartThread()
{if (thread_is_running){throw runtime_error("thread has been running");}m_hThread = CreateThread(0, 0, ThreadProc, this, 0, &m_ThreadID);if (!m_hThread){throw runtime_error("CreateThread fail");}
}void WaveOut::StopThread()
{if (!thread_is_running){return;}if (m_hThread){PostThreadMessage(m_ThreadID, WM_QUIT, 0, 0);while (1){if (thread_is_running){
#ifdef _PRINTprintf("StopThread::waiting\n");
#endifSleep(1);}else{
#ifdef _PRINTprintf("StopThread::break\n");
#endifbreak;}}TerminateThread(m_hThread, 0);m_hThread = 0;}
}void WaveOut::Open()
{if (m_bDevOpen){throw runtime_error("waveOut has been opened");}//lphWaveOut: PHWaveOut;   {用于返回设备句柄的指针; 如果 dwFlags=WAVE_FORMAT_QUERY, 这里应是 nil}//uDeviceID: UINT;         {设备ID; 可以指定为: WAVE_MAPPER, 这样函数会根据给定的波形格式选择合适的设备}//lpFormat: PWaveFormatEx; {TWaveFormat 结构的指针; TWaveFormat 包含要申请的波形格式}//dwCallback: DWORD        {回调函数地址或窗口句柄; 若不使用回调机制, 设为 nil}//dwInstance: DWORD        {给回调函数的实例数据; 不用于窗口}//dwFlags: DWORD           {打开选项}// long120823MMRESULT mRet;mRet = waveOutOpen(0, WAVE_MAPPER, &m_Waveformat, 0, 0, WAVE_FORMAT_QUERY);if (mRet != MMSYSERR_NOERROR){throw runtime_error("waveOutOpen fail");}mRet = waveOutOpen(&m_hWaveOut, WAVE_MAPPER, &m_Waveformat, m_ThreadID, 0, CALLBACK_THREAD);if (mRet != MMSYSERR_NOERROR){throw runtime_error("waveOutOpen fail");}if (waveOutPrepareHeader(m_hWaveOut, &wavehdr1, sizeof(WAVEHDR)) != MMSYSERR_NOERROR){throw runtime_error("waveOutPrepareHeader fail");}if (waveOutPrepareHeader(m_hWaveOut, &wavehdr2, sizeof(WAVEHDR)) != MMSYSERR_NOERROR){throw runtime_error("waveOutPrepareHeader fail");}m_bDevOpen = TRUE;
}void WaveOut::Close()
{if (!m_bDevOpen){return;}if (!m_hWaveOut){return;}MMRESULT mRet;if ((mRet = waveOutUnprepareHeader(m_hWaveOut, &wavehdr1, sizeof(WAVEHDR))) != MMSYSERR_NOERROR){TCHAR info[260];waveOutGetErrorText(mRet, info, 260);throw runtime_error(to_string(info));}if ((mRet = waveOutUnprepareHeader(m_hWaveOut, &wavehdr2, sizeof(WAVEHDR))) != MMSYSERR_NOERROR){TCHAR info[260];waveOutGetErrorText(mRet, info, 260);throw runtime_error(to_string(info));}mRet = waveOutClose(m_hWaveOut);if (mRet != MMSYSERR_NOERROR){throw runtime_error("waveOutClose fail");}m_hWaveOut = 0;m_bDevOpen = FALSE;
}inline void WaveOut::WaitForPlayingEnd()
{while (1){if (isplaying1 || isplaying2){
#ifdef _PRINTprintf("Stop::waitting\n");
#endifSleep(1);}else{
#ifdef _PRINTprintf("Stop::break\n");
#endifbreak;}}
}void WaveOut::Stop()
{//先resetMMRESULT mRet;if ((mRet = waveOutReset(m_hWaveOut)) != MMSYSERR_NOERROR){TCHAR info[260];waveOutGetErrorText(mRet, info, 260);throw runtime_error(to_string(info));}//等待播放完成WaitForPlayingEnd();//向线程发送关闭信号,阻塞直到线程退出StopThread();Close();
}inline void WaveOut::SetThreadSymbol(bool running)
{EnterCriticalSection(&m_Lock);thread_is_running = running;LeaveCriticalSection(&m_Lock);
}inline void WaveOut::SetFinishSymbol(PWAVEHDR pWaveHdr)
{EnterCriticalSection(&m_Lock);if (pWaveHdr == &wavehdr1){isplaying1 = false;
#ifdef _PRINTprintf("1 is finished.\n");
#endif}else{isplaying2 = false;
#ifdef _PRINTprintf("2 is finished.\n");
#endif}LeaveCriticalSection(&m_Lock);
}

本文代码一部分参考自文献1。
代码中的tstring.h是我自己写的一个文件,主要内容是兼容Unicode和ANSI的映射宏。读者删掉对应#define行,TCHAR改成char,to_string去掉,就可以在ANSI编码上正常使用了。

参考

[1] 打造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
[2] waveOutReset的N种死法, 及其解决方案


http://chatgpt.dhexx.cn/article/FGwIUrgY.shtml

相关文章

清华牛逼!

如果让你用尽可能多的词语形容一名女子的美貌&#xff0c;你会怎么说&#xff1f; “这女孩太美了&#xff0c;闭月羞花、沉鱼落雁不足以形容起美貌。那身段令貂婵蒙羞&#xff0c;那脸蛋令月亮失色。那眼睛&#xff0c;那鼻子&#xff0c;那小嘴&#xff0c;那胸脯&#xff0c…

博士申请 | 上海交通大学叶南阳老师招收计算机视觉/机器学习方向博士生

合适的工作难找&#xff1f;最新的招聘信息也不知道&#xff1f; AI 求职为大家精选人工智能领域最新鲜的招聘信息&#xff0c;助你先人一步投递&#xff0c;快人一步入职&#xff01; 上海交通大学 上海交通大学约翰霍普克罗夫特计算机科学中心于 2017 年 1 月正式成立&#x…

硬核实力!西安交大人工智能学科到底有多强?

人工智能 是时下最火的科技词汇之一 人工智能 作为引领未来的战略性技术 正默默改变着人类的生产生活 西安交大人工智能研究是国内高校的先驱 拥有强大的学术实力 科研成绩斐然 斩获国内外多项荣誉 今天 跟着小编一起走进交大人工智能 漫步科学殿堂 西安交通大学人工智能学科发…

清华大学交叉信息研究院姚班修改培养模式:姚班、智班、量信班全合并

机器之心报道 编辑&#xff1a;杜伟、泽南 拓宽优秀学生的选择方向。 清华大学交叉信息研究院宣布了优化本科培养模式的新计划&#xff1a; 为进一步拓宽学生基础学养&#xff0c;并优化专业方向选择&#xff0c;特调整本科生培养模式&#xff0c;具体要点如下&#xff1a; 1、…

我的2020推免之路:清叉、TBSI、贵系、中山、国防科大、自动化所

我的2020推免之路&#xff1a;清叉、TBSI、贵系、中山、国防科大、自动化所 一.前言二.个人情况三.夏令营清华大学交叉信息学院&#xff08;6.13~6.14&#xff09;清华-伯克利深圳学院&#xff08;7.2~7.9&#xff09;清华大学计算机系&#xff08;7.8~7.9&#xff09;中山大学…

清北浙交大比拼,南大强势上榜,AI到底哪家强?

作者 | 阿司匹林 编者注&#xff1a;近日&#xff0c;上海交通大学和武书连编制的两份比较有影响力的大学排行榜先后发布。这两份榜单的最终排名虽然存在一定的差异&#xff0c;但是都将清华大学、北京大学、浙江大学、上海交通大学、以及复旦大学排在了前五&#xff08;先后顺…

2022 ACM杰出会员揭榜:清华黄隆波、刘世霞,北大郝丹、刘譞哲等23位华人学者入选!...

来源&#xff1a;新智元 2022年度ACM杰出会员&#xff08;Distinguished Member&#xff09;名单公布&#xff01; 本次评选设有三个奖项&#xff0c;分别表彰在计算机领域做出的教育贡献、工程贡献和科学贡献。 今年共有67名会员入选。其中有23位是华人&#xff08;中国大陆6人…

科研实习 | 清华大学交叉信息研究院弋力老师课题组招收科研实习生

合适的工作难找&#xff1f;最新的招聘信息也不知道&#xff1f; AI 求职为大家精选人工智能领域最新鲜的招聘信息&#xff0c;助你先人一步投递&#xff0c;快人一步入职&#xff01; 清华大学 弋力博士于2021年7月加入清华大学交叉信息研究院任助理教授&#xff0c;博士生导师…

数说CS | 北大叉院推免生源大起底!

写在前面 北京大学前沿交叉学科研究院是怎样的一个存在&#xff1f;研究方向与其他学院有什么不同&#xff1f;学术氛围如何&#xff1f; 今天&#xff0c;岛主将会带领各位一同了解北大叉院&#xff0c;从学院介绍、培养特色、优营生源分析等板块为大家深入剖析&#xff01;…

北理工通报方岱宁院士处理结果

点击进入—>3D视觉工坊学习交流群 12月7日&#xff0c;北京理工大学发布《情况通报》&#xff0c;通报“教师方岱宁相关视频事件”处理结果。 据通报&#xff0c;经查核&#xff0c;视频内容是方岱宁今年7月初在线参加学术会议时发生的事情&#xff0c;因其行为失范&#xf…

2020cs线上保研之路 清华北大交大复旦自动化所计算所

2020计算机线上保研 清华北大交大复旦自动化所计算所 无聊在飞机上编辑的这几个月来的经历吧。我真的感觉好累&#xff0c;巨累。该死的疫情。 今年保研&#xff0c;往届的学长学姐的去路都没有什么参考性了&#xff0c;一个原因在于cs内卷更严重了&#xff0c;另一个因为疫情…

直播预告 | 清华叉院助理教授赵行、在读博士生孙桥:自动驾驶中的行为交互

本期为TechBeat人工智能社区第421期线上Talk&#xff0c;响应大家希望实时交流的需求&#xff0c;本次Talk是&#xff01;直&#xff01;播&#xff01; 北京时间7月13日(周三)20:00&#xff0c;我们很开心邀请到老朋友——清华叉院助理教授赵行老师和他的学生孙桥&#xff0c;…

清华大学交叉信息研究院招收计算机图形学、机器学习方向博士

来源&#xff1a;机器之心 课题组简介杜韬博士于 2022 年秋季加入清华大学交叉信息研究院担任助理教授&#xff0c;领导课题组开展计算机图形学和机器学习相关方向的研究。课题组常年招收博士生、科研助理、实习生、访问学生、助理研究员等&#xff0c;部分职位工作地点可选北京…

生源985占比100%,北大叉院这个专业本校学生也心仪~

1、院校介绍 北京大学前沿交叉学科研究院下的大数据研究中心是中国交叉学科的先锋&#xff0c;主要学科为数据科学&#xff0c;现设数据挖掘方法、大数据软件技术、大数据安全技术、健康医疗大数据以及交通大数据等9个研究方向。 北大叉院大数据研究中心科研实力强大&#xf…

【2019保研经验】清华贵系、清华软院、北大叉院、中科院自动化所等

2019年9月29日23点00分&#xff0c;我收到了清华大学计算机科学与技术系的待录取通知&#xff0c;也意味着我几个月的保研工作彻底结束&#xff0c;并且取得了我所能取得的最好成绩。如今&#xff0c;全部的保研工作已全部结束&#xff0c;我也是时候要摆脱懒惰的借口&#xff…

清华数据女神评选结果:第一竟然是叉院大神...?

在刚刚过去的3月7日女生节&#xff0c;清华学大数据的男生们发起了一个数据女神评选活动&#xff0c;希望在大家的参与下评选出数据女神&#xff0c;送上女生节的礼物。 今天是公布评选结果的时刻&#xff1a; 恭喜来自交叉信息研究院的陈立杰和来自美术学院的张玉萍同学成为我…

清华叉院弋力:从谷歌研究科学家到清华任教,我想看远一点

原来他们是这样走过来的&#xff01; 【AI红人荟】——这里是TechBeat人工智能社区为优秀的AI工作者开设的人物专访栏目。从膜拜“红人”到成为“红人”&#xff0c;TechBeat与你一起&#xff0c;在AI进阶之路上&#xff0c;升级打怪、完美通关~ 本篇人物——来自清华交叉信息研…

青源LIVE第29期|清华叉院高阳:使用1/500数据掌控Atari游戏-EfficientZero算法详解

当前强化学习已在许多应用中取得了巨大成功。但样本效率仍是强化学习中一个重大挑战&#xff0c;重要的方法需要数百万&#xff08;甚至数十亿&#xff09;的环境步骤来训练。虽然&#xff0c;当前在基于图像的样本高效RL算法方面取得了重大进展&#xff1b;但是&#xff0c;在…

修改windows系统的host文件

修改windows系统的host文件 最近要使用一款免费软件&#xff0c;所以用到修改该文件这一项。 遇到的问题 真正的修改是要对这个文件赋予高的权限&#xff0c;路径里面查找到该文件&#xff0c;C:\WINDOWS\system32\drivers\etc&#xff0c;点击host文件&#xff0c;点击属性…

win7 host文件的位置

文件在 C:\Windows\System32\drivers\etc 文件内容如下&#xff1a; # Copyright (c) 1993-1999 Microsoft Corp. # # This is a sample HOSTS file used by Microsoft TCP/IP for Windows. # # This file contains the mappings of IP addresses to host names. Each # en…