【VS开发】【智能语音处理】Windows下麦克风语音采集
简介
这是我很早以前的大学毕业设计,忽然间找到贴出来以纪念自己的纯真年代...但是因为CSDN不给面子所以导致短短的一篇文章贴了足足7次..他老提时说文章超过了64K,老大,拜托,那是算上了里面的图片大小吧...:-(
本文简单介绍了声卡的工作原理 , 录音的原理以及数字音频的基本知识并且利用 Windows 提供的 Waveform
Aduio APIs 以及 Multimedia File I/O APIs 实现一个 Windows 环境下的麦克风录音以及将录音文件保存成 .wav 文件的简单系统 .
关键字
Waveform Aduio APIs, Multimedia File I/O APIs,waveInXXX,
mmioXXX, 麦克风 , 录音 , 波形文件 ,VC6++
要深入的了解麦克风录音的实现我们必须了解声卡的工作原理 ,麦克风录音的原理以及了解相关的编程接口,下面我们将慢慢道来…
1. 声卡的工作原理
声卡的工作原理其实很简单 ,我们知道,麦克风和喇叭所用的都是模拟信号,而电脑所能处理的都是数字信号,两者不能混用,声卡的作用就是实现两者的转换。从结构上分,声卡可分为模数转换电路和数模转换电路两部分,模数转换电路负责将麦克风等声音输入设备采到的模拟声音信号转换为电脑能处理的数字信号,而数模转换电路负责将电脑使用的数字声音信号转换为喇叭等设备能使用的模拟信号,就这么简单 。
上图就是一块典型的声卡 , Mic 插口 用于连接麦克风 , 通过它可以录制外界的声音
2. 数字音频基础知识
麦克风录音的过程其实就是将模拟信号转化成数字信号的过程 , 其中涉及的一些概念如下 :
1. 采样率 (Sampling Rate)
采样率指声卡在一秒之中对声音 ( 波形 ) 作记录的次数 , 根据研究声音播出时的质量常常只能达到采样率的一半 , 因此必须采取双倍的采样率才能将声音标准重现 . 也就是只要采样率大于原始信号频率的两倍以上即可减低错误 , 达到和原始声音差不多的质量 . 人的听力大概是 20KHZ, 所以高品质的采样率应为其两倍以上 .
当声音来源为音乐时 , 因为它所横跨的频率变化极为宽广 , 通常以 44.1KHZ 的频率为 CD 音乐采样率的标准 . 但是若以语言为主由于人说话的语音大概是 10KHZ, 因此加倍采样 , 只取 22KHZ 即可 , 采样率越高所记录下来的音质就越清晰 , 当然 , 越高的采样所记录下的文件就越大 .
2. 采样位
解析度决定了采样的音波是否能保持原来的形状 , 越接近原型则需解析度越高 , 若以 8 位来采样的话其能表达的组合种类是 2 的 8 次方 , 即 256, 表示用 8 位的采样大小能分辨出 256 个层次的声音 , 若用 16 位来采样 , 则能分辨的差异将高达 2 的 16 次方 , 为 65536, 其精度自然大为提高 .16 位 ,8 位采样的差别在于动态范围的宽窄 , 动态范围宽广 , 音量起伏的大小变化就能够更精细的被记录下来 , 如此一来不论是细微的声音或是强烈的动感震撼 , 都可以表现的淋漓尽致 , 而 CD 音质的采样规格正式 16 位采样的规格 .
3. 量化误差 (Quantization error)
在采样的过程中 , 不断连续变化的模拟信号要用数字化的数值来表示 , 这样的过程就会发生所谓的量化误差 (Quantization error). 所谓的量化误差指的是实际的信号的振幅 (smplitude) 和数字化之后所的数字之间的差异 . 如果用将数字信号还原成模拟信号的角度看 , 量化误差就是失真 (Distortion). 我们可以用增加采样大小的方式来降低量化误差 , 也就是更多的位 (bits) 来表示一个采样信号 , 这样可以提高精度 .
4. 量化 (Quantization), 线性量化法 (Linear quantization) 和非线性量化法 (Nonlinear
quantization)
所谓的量化 (Quantization) 就是将模拟信号所代表的连续范围分成一段一段的区间 (Interval), 每一段区间我们定义一个数字化的值 . 区间的数目是跟采样大小有关 , 举例来说 , 有一种最简单的量化法称为 ” 线性量化法 ”(Linear quantization), 这种量化法采用等距离的间隔空间 , 架设一个讯号它的最大值是 5.0, 采样大小为 3 位 , 则每个量化区间就时 5.0/2^3, 也就是 0.625 单位 . 另外一种相反的量化方法就是 ” 非线性量化法 ”(Nonlinear
quantization), 这种量化法采用不同的间隔空间 . 以 ” 对数量化法 ”(Logarithm quantization) 为例 . 低振幅范围的量化区间就比高振幅的范围的区间较为接近 , 用这种量化的法产生的结果就是在低振幅时我们会得到佳好的效果 . 通常如果使用同样的采样大小 , 非线性量化法会比线性量化法得到更好的声音品质 . 但是如果是要对声音做滤波 (filtered) 或一些运算的时候 , 使用线性量化法会比较容易处理 .
5. 声音强度
波形振幅的平方.两个声音强度上的差常以分贝 (db) 为单位来度量 ,计算公式如下:
20*log(A1/A2) 分贝 , A1,A2 为两个声音的振幅 .
a. 如果采样大小为 8 位 ,则采样的动态范围为 20*log(256) 分贝 =48db;
b. 如果样本大小为 16 位 ,则采样动态范围为 20*log(65536) 大约是 96 分贝 ,接近了人听觉极限和痛苦极限,是再线音乐的理想范围, windows 同时支持8 位和 16 位的采样大小 .
6. 音频编码方法
目前已经发展了许多音频编码的方法用以减少存储量或是传输的时间 , 以下所列为两种较普遍的编码方法 :
a.PCM(Pulse code modulation);
脉冲编码调制 ,即对波形按照固定周期频率采样。为了保证采样后数据质量,采样频率必须是样本声音最高频率的两倍,这就是 Nyquist 频率 .
b.ADPCM(Adaptive delta pulse modulation).
3. RIFF 文件结构和 WAVE 文件格式
Windows 支持两种 RIFF(Resource Interchange File Format," 资源交互文件格式 ") 格式的音频文件: MIDI 的 RMID 文件和波形音频文件格式WAVE 文件,其中在计算机领域最常用的数字化声音文件格式是后者,它是微软专门为 Windows 系统定义的波形文件格式( Waveform Audio ),由于其扩展名为 "*.wav" ,因而该类文件也被称为 WAVE 文件。 为了突出重点,有的放矢,本文涉及到的声音文件所指的就是 WAVE 文件。常见的 WAVE 语音文件主要有两种,分别对应于单声道( 11.025KHz 采样率、 8Bit 的采样值)和双声道( 44.1KHz 采样率、 16Bit 的采样值)。这里的采样率是指声音信号在进行 " 模→数 " 转换过程中单位时间内采样的次数。采样值是指每一次采样周期内声音模拟信号的积分值。对于单声道声音文件,采样数据为八位的短整数(short
int 00H-FFH );而对于双声道立体声声音文件,每次采样数据为一个 16 位的整数( int ),高八位和低八位分别代表左右两个声道。 WAVE 文件数据块包含以脉冲编码调制( PCM )格式表示的样本。在进行声音编程处理以前,首先让我们来了解一下 RIFF 文件和 WAVE 文件格式。
RIFF 文件结构可以看作是树状结构,其基本构成是称为 " 块 " ( Chunk )的单元,每个块有 " 标志符 " 、 " 数据大小 " 及 " 数据 " 所组成,块的结构如图 2 所示:
块的标志符( 4BYTES ) |
数据大小 ( 4BYTES ) |
数据 |
图 2
从上图可以看出,其中 " 标志符 " 为 4 个字符所组成的代码,如 "RIFF" , "LIST" 等,指定块的标志 ID ;数据大小用来指定块的数据域大小,它的尺寸也为 4 个字符;数据用来描述具体的声音信号,它可以由若干个子块构成,一般情况下块与块是平行的,不能相互嵌套,但是有两种类型的块可以嵌套子块,他们是 "RIFF" 或 "LIST" 标志的块,其中 RIFF 块的级别最高,它可以包括 LIST 块。另外, RIFF 块和 LIST 块与其他块不同, RIFF 块的数据总是以一个指定文件中数据存储格式的四个字符码(称为格式类型)开始,如 WAVE 文件有一个 "WAVE" 的格式类型。 LIST 块的数据总是以一个指定列表内容的 4 个字符码(称为列表类型)开始,例如扩展名为 ".AVI" 的视频文件就有一个 "strl" 的列表类型。 RIFF 和 LIST 的块结构如下:
RIFF/LIST 标志符 |
|
数据 1 大小 |
|
数据 1 |
格式 / 列表类型 |
数据 |
图 3
WAVE 文件是非常简单的一种 RIFF 文件,它的格式类型为 "WAVE" 。 RIFF 块包含两个子块,这两个子块的 ID 分别是 "fmt" 和 "data", 其中 "fmt" 子块由结构 PCMWAVEFORMAT 所组成,其子块的大小就是 sizeofof(PCMWAVEFORMAT), 数据组成就是 PCMWAVEFORMAT 结构中的数据。 WAVE 文件的结构如下图 4 所示:
标志符( RIFF ) |
数据大小 |
格式类型( "WAVE" ) |
"fmt" |
Sizeof(PCMWAVEFORMAT) |
PCMWAVEFORMAT |
"data" |
声音数据大小 |
声音数据 |
图 4
PCMWAVEFORMAT 结构定义如下:
typedef struct
16 位双声道:
图 5 4. 硬件抽象层 ( HAL, Hardware Abstraction Layer ) HAL 是一个可加载的核心模块 (HAL.dll) ,它为运行在 Windows NT 架构 ( 包括 WindowsNT4.0,Windows2000,WindowsXP) 上 的硬件平台提供低级接口 , HAL 隐藏各种与硬件有关的细节 ,例如: I/O 接口 ,中断控制器,声卡…这样的话如果用户需要访问声卡硬件的话只能通过该声卡的驱动程序来实现,声卡驱动程序再调用 HAL 中的相应例程来实现 ,下图显示了 HAL ,声卡驱动程序, Waveform Audio APIs ,我们的麦克录音程序之间的关系: |
5. Waveform Audio
Waveform Audio APIs 是 Microsoft 提供给广大 Win32 程序员用来给自己的应用程序添加声音支持的一套强大的 API, 它提供的功能如下:
1. 打开 / 关闭 / 查询声音设备 ;
2. 播放波形文件 ;
3. 设置播放速度 ;
4. 播放进度控制 ;
5. 录音 ;
6. 得到当前的播放位置 ;
7. 调节音量 .
下面简单介绍一下这套 API 提供的主要函数 :
- 打开录音设备函数
MMRESULT waveInOpen(
LPHWAVEIN phwi, // 输入设备句柄
UINT uDeviceID, // 输入设备 ID
LPWAVEFORMATEX pwfx, // 录音格式指针
DWORD dwCallback, // 处理 MM_WIM_*** 消息的回调函数或窗
// 口句柄,线程 ID
DWORD dwCallbackInstance,
DWORD fdwOpen // 处理消息方式的符号位
);
- 为录音设备准备缓存函数
MMRESULT waveInPrepareHeader( HWAVEIN hwi,
LPWAVEHDR pwh,
UINT bwh );
- 给输入设备增加一个缓存
MMRESULT waveInAddBuffer( HWAVEIN hwi,
LPWAVEHDR pwh,
UINT cbwh );
- 开始录音
MMRESULT waveInStart( HWAVEIN hwi );
- 清除缓存
MMRESULT waveInUnprepareHeader( HWAVEIN hwi,
LPWAVEHDR pwh,
UINT cbwh);
- 停止录音
MMRESULT waveInReset( HWAVEIN hwi );
- 关闭录音设备
MMRESULT waveInClose( HWAVEIN hwi );
- Wave_audio 数据格式
typedef struct {
WORD wFormatTag; // 数据格式,一般为 WAVE_FORMAT_PCM 即
// 脉冲编码
WORD nChannels; // 声道
DWORD nSamplesPerSec; // 采样频率
DWORD nAvgBytesPerSec; // 每秒数据量
WORD nBlockAlign;
WORD wBitsPerSample; // 样本大小
WORD cbSize;
} WAVEFORMATEX;
- waveform-audio 缓存格式
typedef struct {
LPSTR lpData; // 内存指针
DWORD dwBufferLength; // 长度
DWORD dwBytesRecorded; // 已录音的字节长度
DWORD dwUser;
DWORD dwFlags;
DWORD dwLoops; // 循环次数
struct wavehdr_tag * lpNext;
DWORD reserved;
} WAVEHDR;
- 相关消息
MM_WIM_OPEN: 打开设备时消息,在此期间我们可以进行一些初始化工作
MM_WIM_DATA: 当缓存已满或者停止录音时的消息,处理这个消息可以对
缓存进行重新分配,实现不限长度录音
MM_WIM_CLOSE :关闭录音设备时的消息。
5. Multimedia File I/O
Multimedia File I/O APIs 是 Microsoft 提供的另外一套强大的针对媒体文件 I/O 的 API, 我们知道许多像 MediaPlay,RealOne 这样的多媒体程序对媒体文件的读写性能要求很高 , 它们几乎要求实时的将磁盘上的媒体文件以流的形式读入 , 但是对于一般的文件 I/O 形式如图 1:
1.文件从磁盘上被读入操作系统的File I/O的buffer;
2. 然后拷贝到应用程序自己的 buffer 中 ;
3. 应用程序这时候才能读取文件内容 .
上述的过程对于多媒体应用程序来说是低效的而且浪费宝贵的内存资源,如果文件和大的话势必还要采取分段读取等机制, Multimedia File I/O 采取了一种直接存取机制 ( 如图 2), 使得应用程序可以直接读取操作系统的 File I/O buffer, 大大提高了效率 . 后面我们会利用此套 API实现录音文件的存储 .
6. 麦克录音系统简介
本文实现的麦克录音系统将具备以下功能 :
1. 录制用户通过麦克风发出的声音 ;
这将利用到 Waveform APIs, 流程如下 :
a. 打开录音设备 waveInOpen;
b. 准备 wave 数据头 waveInPrepareHeader;
c. 准备数据块 waveInAddBuffer;
d. 开始录音 waveInStart;
e. 停止录音 (waveInReset);
f. 关闭录音设备 (waveInClose);
g. 当开始录音后当 buffer 已满时 , 将收到 MM_WIM_DATA 消息 , 处理该
消息可以保存已录好数据 .
2. 根据用户的声音强弱动态显示声音波形 ;
这主要通过 GDI 函数来实现 .
3. 将用户通过麦克风发出的声音录制成 wav 文件保存 .
这将利用到 Multimedia file I/O APIs.
a .调用 mminoOpen 函数来打开 WAVE 文件 , 获取 HMMIO 类型的文件句柄 ;
b .根据 WAVE 文件的结构 , 调用 mmioRead 、 mmioWrite 和 mmioSeek 函数实现文件的读、写和定位操作 ;
c .调用 mmioClose 函数来关闭 WAVE 文件 .
7. 麦克录音系统的实现 (MicDemo)
下面是该系统的界面 :
namespace perdubug { // prevent the name-space pollution
class CSoundIn
{
public :
BOOL __initMic(); // get the best wave format supported by your sound card
// and then i will use the format to capture sound.
void __closeMic();
BOOL __openMic(); // open device and begin to capture with the best format(when
// invoke __initMic function then you will get the best format
// supported by host's sound card
//
// if your want to capture sound and export into a wav file please invoke this function
// to tell me the full path then i will create the wav file.
//
void __createOutputWaveFile( const TCHAR * lpszFileName);
// if you invoke any member function return error/false please
// use this function to get the result
DWORD __getLastError();
//
// when the capture buffer is filled please invoke this function to 'add buffer'(Actually
// you should create two-circular buffers,when 1st buffer is filled then switch to 2st,1st
// buffer will be wrote into wav file.
//
void AddBuffer();
virtual ~ CSoundIn();
friend CSoundIn & theSoundCapture();
private :
BOOL GetBestWaveFormat(WAVEFORMATEX & waveFormatEx);
// because sound card is one and only so i must limit the number of CSoundIn object,
// but how to limit the class object nums?maybe put constructor into private scope is
// a good idea,:-)
CSoundIn();
private :
WAVEINCAPS m_WaveInDevCaps;
HWAVEIN m_WaveIn;
WAVEHDR m_WaveHeader;
WAVEFORMATEX m_WaveFormat;
UINT m_WaveInSampleRate;
int m_NbMaxSamples;
UINT m_SizeRecord;
DWORD m_dwLastError;
enum { MAX_SIZE_INPUT_BUFFER = 1 * 2 * 1024 } ; // samples * voie * size_samples
public :
SHORT InputBuffer[MAX_SIZE_INPUT_BUFFER]; // used for int FFT,many GUI application
// want to display sound peak so..
BOOL m_bTerminateThread; // to 'kill' waveCallback function
BOOL m_bImportToWaveFile;
CWaveFile m_waveFile;
} ;
} // end namespace perdubug
对于将录音保存在WAV文件的工作主要是由CwaveFile类来完成.下面是该类的定义:
//
// Encapsulates reading or writing sound data to or from a wave file
// -----------------------------------------------------------------------------
class CWaveFile
{
public :
WAVEFORMATEX * m_pwfx; // Pointer to WAVEFORMATEX structure
HMMIO m_hmmio; // MM I/O handle for the WAVE
MMCKINFO m_ck; // Multimedia RIFF chunk
MMCKINFO m_ckRiff; // Use in opening a WAVE file
DWORD m_dwSize; // The size of the wave file
MMIOINFO m_mmioinfoOut;
DWORD m_dwFlags;
BOOL m_bIsReadingFromMemory;
BYTE * m_pbData;
BYTE * m_pbDataCur;
ULONG m_ulDataSize;
CHAR * m_pResourceBuffer;
protected :
HRESULT ReadMMIO();
HRESULT WriteMMIO( WAVEFORMATEX * pwfxDest );
public :
CWaveFile();
~ CWaveFile();
HRESULT Open( LPCTSTR strFileName, WAVEFORMATEX * pwfx, DWORD dwFlags );
HRESULT OpenFromMemory( BYTE * pbData, ULONG ulDataSize,
WAVEFORMATEX * pwfx, DWORD dwFlags );
HRESULT Close();
HRESULT Read( BYTE * pBuffer, DWORD dwSizeToRead, DWORD * pdwSizeRead );
HRESULT Write( UINT nSizeToWrite, BYTE * pbData, UINT * pnSizeWrote );
DWORD GetSize();
HRESULT ResetFile();
WAVEFORMATEX * GetFormat() { return m_pwfx; } ;
} ;
1 .用VC6 ++ 建立一个MFC基于对话框的工程:MicDemo;
2 .添加我们的两个类CSoundIn,CwaveFile;
3 .当我们点击开始(Start)按钮的时候我们就要开始录音了…
void CMicDemoDlg::OnStart()
{
m_btnStart.EnableWindow(FALSE);
if (theSoundCapture().__initMic())
{
m_filePath.SetWindowText(_T( " yangchen.wav. " ));
theSoundCapture().__createOutputWaveFile(_T( " yangchen.wav " ));
if ( ! theSoundCapture().__openMic())
{
::MessageBox( this -> m_hWnd,_T( " Can not open microphone! " ), _T( " Error " ),MB_OK | MB_ICONERROR);
return ;
}
}
m_btnStop.EnableWindow(TRUE);
// 设置定时器是为了画波形用的
SetTimer( 1 , 200 /* start slow */ , NULL);
}
4 .在定时器的回调函数中画波形.
void CMicDemoDlg::OnTimer(UINT nIDEvent)
{
if (nIDEvent == 1 )
{
static const int xCon = 13 ;
static const int yCon = 13 ;
static const int wCon = 623 ;
static const int hCon = 80 ;
CClientDC dc( this );
CBitmap Bitmap;
CBitmap * pbmOld = NULL;
CDC dcMem;
dcMem.CreateCompatibleDC( & dc);
Bitmap.CreateCompatibleBitmap( & dc,wCon,hCon);
pbmOld = dcMem.SelectObject( & Bitmap);
dcMem.PatBlt( 0 , 0 ,wCon,hCon, WHITENESS);
dcMem.MoveTo( 0 ,hCon / 2 );
//
// display incomming signal--key idea!
//
for ( int x = 0 ; x < wCon; x ++ ) // display Input
{
dcMem.LineTo(x,(hCon >> 1 ) - (theSoundCapture().InputBuffer[x] >> 7 ));
}
dc.BitBlt(xCon,yCon,wCon,hCon, & dcMem, 0 , 0 , SRCCOPY);
dcMem.SelectObject(pbmOld);
dcMem.DeleteDC();
}
else
CDialog::OnTimer(nIDEvent);
}
5 .点击停止(Stop)按钮的时候停止录音和写WAV文件
void CMicDemoDlg::OnStop()
{
m_btnStop.EnableWindow(FALSE);
theSoundCapture().__closeMic();
m_btnStart.EnableWindow(TRUE);
}
看完整段代码你可能会很奇怪怎么在CmicDemoDlg中居然都没有定义一个CSoundIn对象 ?? 呵呵,原因很简单,因为设备的独占性所以在一个时刻只能有一个CSoundIn对象存在(因为CSoundIn对象需要占据录音设备),所以我们必须限制程序员生成CSoundIn对象的数量,怎么限制呢 ? 那就是把CSoundIn的构造函数放在private区域里面:
private :
BOOL GetBestWaveFormat(WAVEFORMATEX & waveFormatEx);
// because sound card is one and only so i must limit the number of CSoundIn object,
// but how to limit the class object nums?maybe put constructor into private scope is
// a good idea,:-)
CSoundIn();
这样的话就根本无法声明一个CSoundIn对象,不信你试一下在你的代码中写上:
CSoundIn soundInObj;
能编译通过吗 ? 肯定是不能,那如何调用CSoundIn的成员函数呢 ? 答案是通过一个全局函数:
// global function,:-(
// client can only through this function to use CSoundIn object
CSoundIn & theSoundCapture()
{
static CSoundIn p;
return p;
}
这时候你应该明白了为什么上面的代码中调用CSoundIn的成员函数的时候都是用theSoundCapture来做的原因了吧.
【VS开发】【智能语音处理】Windows下麦克风语音采集的更多相关文章
- 团队软件开发_基于windows下截屏软件关于NABC框架的特点
经过我们小组数次的激烈讨论,就自己的能力和时间而言,我们小组的初步的计划是开发一款基于windows下的截图软件. 关于这个软件的功能,我们初步的想法如下: 1.能在windows下后台运行,有相应的 ...
- cocos2d-x 3.0rc开发指南:Windows下Android环境搭建
安装工具 1. 配置JDK JDK下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html 本人的系统是Win7 ...
- C++开发安卓、windows下搭建Android NDK开发环境
1. NDK(Native Development Kit) 1.1 NDK简介 Android NDK是一套允许开发人员使用本地代码(如C/C++)进行Android APP功能开发的工具,通过这个 ...
- [转帖]cocos2d-x 3.0rc开发指南:Windows下Android环境搭建
原文请看:http://blog.csdn.net/linzhengqun/article/details/21663341 鲜红字体请注意:文中红色字体乃是本文博主阳光下的蒲公英添加.红色字体部分造 ...
- Mqtt开发笔记:windows下C++ ActiveMQ客户端介绍、编译和使用
前话 项目需求,需要使用到mqtt协议,之前编译QtMqtt库,不支持队列模式queue(点对点),只支持订阅/发布者模式.,所以使用C++ ActiveMQ实现. MQTT协议 简介 M ...
- 【开发工具】- Windows下多个jdk版本切换
一.直接安装jdk,如图我安装了JDK6.JDK7和JDK8三个版本: 二.在安装JDK8后需要在 C:\Windows\System32 该目录下删除 java.exe 和 javaw.exe两个文 ...
- 【VS开发】【数据库开发】libevent windows下基于VS2010的编译
libevent是一个常用的网络库,下面就看看在windows下面编译测试的过程吧. 一 环境 系统:win8.1编译器:VS2013官方下载地址:http://libevent.org/版本:2.0 ...
- Android React Native 开发环境搭建---windows下
环境搭建 环境搭建可以参考RN官网,也可以参考中文版本:http://reactnative.cn/docs/0.45/getting-started.html 如果你希望可以看到原版的安装流程,可以 ...
- Windows 下最佳的 C++ 开发的 IDE 是什么?
作者:渡世白玉链接:https://www.zhihu.com/question/19589089/answer/30312199来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...
随机推荐
- OSM全球地图MBTiles,非postgresql方式。
介绍: https://www.cnblogs.com/i-gps/p/3919475.html 下载和使用: https://openmaptiles.org/ OSM pbf转换: https:/ ...
- let 命令
let命令取代并扩展了expr命令的整数算数符号. let除了支持5中基础的运算符. 还支持+=,-=,*=,.-,%= 自变运算符. 以及**幂次运算符. 在变量计算中不需要加上$来表示变量. [c ...
- 简述Hibernate常见优化策略
①制定合理的缓存策略 ② 采用合理的Session管理机制 ③ 尽量使用延迟加载特性 ④如果可以, 选用基于version的乐观锁替代悲观锁 ⑤在开发过程中, 开启hibernate.show_sql ...
- 【MongoDB系列】简介、安装、基本操作命令
文章内容概述: 1.MongoDB介绍 2.MongoDB安装(windows及Linux) 3.MongoDB基本操作命令 MongoDB介绍: MongoDB 是一个基于分布式文件存储的数据库.由 ...
- Ubuntu镜像的MD5校验
造冰箱的大熊猫@cnblogs 2018/9/7 1.在Ubuntu终端中,按照以下格式输入命令计算镜像文件ubuntu-xxx.iso的MD5校验和. md5sum ubuntu-xxx.iso 2 ...
- Centos 下硬盘分区的最佳方案
Centos7从零开始]Centos 下硬盘分区的最佳方案 2016年12月25日 10:09:02 浮華的滄桑 阅读数 41971 在对硬盘进行分区前,应该先弄清楚计算机担负的工作及硬盘的容量有 ...
- JDK_API剖析之java.util包
Java的实用工具类库java.util包.在这个包中,Java提供了一些实用的方法和数据结构. 一.接口 1.Collection<E> 接口 自1.2开始有 继承Iterable< ...
- Jmeter连接Redis服务缓存
1.添加线程组->Sampler->BeanShell Sampler,加入以下内容: import redis.clients.jedis.Jedis; import org.apach ...
- LeetCode 17. 电话号码的字母组合(Letter Combinations of a Phone Number)
题目描述 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合. 给出数字到字母的映射如下(与电话按键相同).注意 1 不对应任何字母. 示例: 输入:"23" 输出: ...
- 后盾网lavarel视频项目---lavarel用户认证实例
后盾网lavarel视频项目---lavarel用户认证实例 一.总结 一句话总结: 主要是用的Auth认证,所以配置是配置的auth(config/auth.php),控制器中调用也是用的Auth( ...