/// <summary> /// 语音【文件、上传、解码、保存(WAV)】 /// </summary> [DeveloperEx("Liwei:秘书语音需求单")] public class AudioController : ClubBaseController { #region Android和IOS的一些音频参数 /**************** //格式 #define NAOMI_SPEEX_FORMAT kAudioFormatLinearPCM //采样 #define NAOMI_SPEEX_RATE 8000 //声道 #define NAOMI_SPEEX_NUMBER_CHANNEL 1 //采样位数 #define NAOMI_SPEEX_BITDEPTH 16 private static final int FREQUENCY = 8000; private static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; private static final int CHANNEL = AudioFormat.CHANNEL_IN_MONO;//0x10 ***************/ #endregion private ILogProvider log = LogFactory.Create(); private FileStream fileStream = null; private BinaryWriter binaryWriter = null; /// <summary> /// PCM To WAV /// 添加Wav头文件 /// </summary> [NonAction] private void CreateSoundFile(string path) { fileStream = new FileStream(path, FileMode.Create); binaryWriter = new BinaryWriter(fileStream); //Set up file with RIFF chunk info. 每个WAVE文件的头四个字节便是“RIFF”。 char[] ChunkRiff = { 'R', 'I', 'F', 'F' }; char[] ChunkType = { 'W', 'A', 'V', 'E' }; char[] ChunkFmt = { 'f', 'm', 't', ' ' }; char[] ChunkData = { 'd', 'a', 't', 'a' }; short shPad = 1; // File padding int nFormatChunkLength = 0x10; // Format chunk length. int nLength = 0; // File length, minus first 8 bytes of RIFF description. This will be filled in later. short bitsPerSample = 16; //每个采样需要的bit数 //short khCaiYang = 8000; //16KHz 采样频率 //short bitSecondRate = 16000; //-每秒所需字节数 short channels = 1; //声道数目,1-- 单声道;2-- 双声道 short shBytesPerSample = 2; //一个样本点的字节数目 //------- RIFF 块 ------- binaryWriter.Write(ChunkRiff); binaryWriter.Write(nLength); binaryWriter.Write(ChunkType); //------- WAVE块 --------- binaryWriter.Write(ChunkFmt); binaryWriter.Write(nFormatChunkLength); binaryWriter.Write(shPad); binaryWriter.Write(channels); //Mono,声道数目,1-- 单声道;2-- 双声道 binaryWriter.Write(8000); //16KHz 采样频率 binaryWriter.Write(16000); //每秒所需字节数 binaryWriter.Write(shBytesPerSample); //数据块对齐单位(每个采样需要的字节数) binaryWriter.Write(bitsPerSample); //16Bit,每个采样需要的bit数 //------- 数据块 --------- binaryWriter.Write(ChunkData); binaryWriter.Write((int)0); // The sample length will be written in later. } /// <summary> ///【上传、保存、PCM源数据文件】 /// </summary> [AllowAnonymous] public ResponseModel UploadAudio() { try { //-------上传文件--------- var hash = CommonUpload("/UploadAudio/", (string i) => { i = Guid.NewGuid().ToString("n"); return i; }, isFile: true); if (hash["retInt"].Equals("1")) { string uploadPcmFile = hash["retSrc"].ToString(); //--------获取pcm的文件名------------ string pcmFileName = uploadPcmFile.Substring(0, uploadPcmFile.IndexOf(Path.GetExtension(uploadPcmFile))); string wavFile = pcmFileName + ".wav"; string physicPCMPath = hash["retSrcDirPath"].ToString(); string tempWavPath = Path.Combine(HttpContext.Current.Server.MapPath(physicPCMPath), wavFile); //--------添加wav文件头----- CreateSoundFile(tempWavPath); #region 读取上传的PCM源文件 string fileName = Path.Combine(HttpContext.Current.Server.MapPath(physicPCMPath), uploadPcmFile); FileInfo fileinfo = new FileInfo(fileName); FileStream fs = fileinfo.OpenRead(); int length = (int)fs.Length; byte[] bytes = new byte[length]; fs.Read(bytes, 0, length); fs.Close(); fs.Dispose(); #endregion #region 向WAV音频中写入数据 binaryWriter.Write(bytes, 0, bytes.Length); binaryWriter.Seek(4, SeekOrigin.Begin); binaryWriter.Write((int)(bytes.Length + 36)); // 写文件长度 binaryWriter.Seek(40, SeekOrigin.Begin); binaryWriter.Write(bytes.Length); fileStream.Close(); #endregion //----------删除用户PCM的源文件--------------- if (System.IO.File.Exists(fileName)) { FileInfo fi = new FileInfo(fileName); if (fi.Attributes.ToString().IndexOf("ReadOnly") != -1) fi.Attributes = FileAttributes.Normal; System.IO.File.Delete(fileName); } return SetOfMessage(data: new { filename = base.domainSite + physicPCMPath + wavFile }); } else { return SetOfMessage(status: 0, message: hash["retMsg"].ToString()); } } catch (Exception ex) { log.Log(LogLevel.Info, "上传语音出错误了!", ex.Message); //--------记录日志------------- return SetOfMessage(data: null, message: "语音上传出现错误了!", status: 0); ; } finally { if (fileStream != null) { fileStream.Close(); } } }