string FindFontFileFromFontName(string fontname, string style) { string s, e; ExternalTool.Run("/bin/bash", string.Format("-c \"fc-match -f '%{{file}}:%{{family}}\\n' '{0}:style={1}'\"", fontname, style), out s, out e); s = s.Trim(); var split = s.Split(':'); //check font family, fontconfig might return a fallback if (split [1].Contains(",")) //this file defines multiple family names { var families = split [1].Split(','); foreach (var f in families) { if (f.ToLowerInvariant() == fontname.ToLowerInvariant()) { return(split [0]); } } //didn't find it return(String.Empty); } else { if (split [1].ToLowerInvariant() != fontname.ToLowerInvariant()) { return(String.Empty); } } return(split [0]); }
/// <summary> /// Processes the string representation of the specified effect into /// a platform-specific binary format using the specified context. /// </summary> /// <param name="input">The effect string to be processed.</param> /// <param name="context">Context for the specified processor.</param> /// <returns>A platform-specific compiled binary effect.</returns> /// <remarks> /// If you get an error during processing, compilation stops immediately. /// The effect processor displays an error message. /// Once you fix the current error, it is possible you may get more errors on subsequent compilation attempts. /// </remarks> public override CompiledEffectContent Process(EffectContent input, ContentProcessorContext context) { var mgfxc = Path.Combine(Path.GetDirectoryName(typeof(EffectProcessor).Assembly.Location), "mgfxc.dll"); var sourceFile = input.Identity.SourceFilename; var destFile = Path.GetTempFileName(); var arguments = "\"" + mgfxc + "\" \"" + sourceFile + "\" \"" + destFile + "\" /Profile:" + GetProfileForPlatform(context.TargetPlatform); if (DebugMode == EffectProcessorDebugMode.Debug) { arguments += " /Debug"; } if (!string.IsNullOrWhiteSpace(Defines)) { arguments += " \"/Defines:" + Defines + "\""; } var success = ExternalTool.Run("dotnet", arguments, out var stdout, out var stderr) == 0; var ret = success ? new CompiledEffectContent(File.ReadAllBytes(destFile)) : null; File.Delete(destFile); var stdOutLines = stdout.ToString().Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); foreach (var line in stdOutLines) { if (line.StartsWith("Dependency:") && line.Length > 12) { context.AddDependency(line[12..]);
/// <summary> /// Выполнить внешнее ПО /// </summary> /// <param name="Program"></param> /// <param name="ProgArgs"></param> /// <param name="Args"></param> /// <param name="Rewrite"></param> /// <returns></returns> public static ExternalToolResult Exec(string Program, string ProgArgs, ArgumentList Args, OptRewrite[] Rewrite) { var CmdPath = ProcessCommand(Program, Args, Rewrite); var CmdArgs = ProcessCommand(ProgArgs, Args, Rewrite); string Dir = Args.Get("$dir"); return(ExternalTool.Run(CmdPath, CmdArgs, Dir)); }
private string FindFont(string name, string style) { if (CurrentPlatform.OS == OS.Windows) { var fontDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Fonts"); var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", false); foreach (var font in key.GetValueNames().OrderBy(x => x)) { if (font.StartsWith(name, StringComparison.OrdinalIgnoreCase)) { var fontPath = key.GetValue(font).ToString(); return(Path.IsPathRooted(fontPath) ? fontPath : Path.Combine(fontDirectory, fontPath)); } } } else if (CurrentPlatform.OS == OS.Linux) { string s, e; ExternalTool.Run("/bin/bash", string.Format("-c \"fc-match -f '%{{file}}:%{{family}}\\n' '{0}:style={1}'\"", name, style), out s, out e); s = s.Trim(); var split = s.Split(':'); if (split.Length < 2) { return(string.Empty); } // check font family, fontconfig might return a fallback if (split[1].Contains(",")) { // this file defines multiple family names var families = split[1].Split(','); foreach (var f in families) { if (f.ToLowerInvariant() == name.ToLowerInvariant()) { return(split[0]); } } // didn't find it return(string.Empty); } else { if (split[1].ToLowerInvariant() != name.ToLowerInvariant()) { return(string.Empty); } } return(split[0]); } return(String.Empty); }
public static void WritePcmFile(AudioContent content, string saveToFile) { string ffmpegStdout, ffmpegStderr; var ffmpegExitCode = ExternalTool.Run( "ffmpeg", string.Format( "-y -i \"{0}\" -vn -c:a pcm_s16le -b:a 192000 -f:a wav -strict experimental \"{1}\"", content.FileName, saveToFile), out ffmpegStdout, out ffmpegStderr); if (ffmpegExitCode != 0) { throw new InvalidOperationException("ffmpeg exited with non-zero exit code: \n" + ffmpegStdout + "\n" + ffmpegStderr); } }
public static void ProbeFormat(string sourceFile, out AudioFileType audioFileType, out AudioFormat audioFormat, out TimeSpan duration, out int loopStart, out int loopLength) { string ffprobeStdout, ffprobeStderr; var ffprobeExitCode = ExternalTool.Run( "ffprobe", string.Format("-i \"{0}\" -show_format -show_entries streams -v quiet -of flat", sourceFile), out ffprobeStdout, out ffprobeStderr); if (ffprobeExitCode != 0) { throw new InvalidOperationException("ffprobe exited with non-zero exit code."); } // Set default values if information is not available. int averageBytesPerSecond = 0; int bitsPerSample = 0; int blockAlign = 0; int channelCount = 0; int sampleRate = 0; int format = 0; string sampleFormat = null; double durationInSeconds = 0; var formatName = string.Empty; try { var numberFormat = CultureInfo.InvariantCulture.NumberFormat; foreach (var line in ffprobeStdout.Split(new[] { '\r', '\n', '\0' }, StringSplitOptions.RemoveEmptyEntries)) { var kv = line.Split(new[] { '=' }, 2); switch (kv[0]) { case "streams.stream.0.sample_rate": sampleRate = int.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.bits_per_sample": bitsPerSample = int.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.start_time": { double seconds; if (double.TryParse(kv[1].Trim('"'), NumberStyles.Any, numberFormat, out seconds)) { durationInSeconds += seconds; } break; } case "streams.stream.0.duration": durationInSeconds += double.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.channels": channelCount = int.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.sample_fmt": sampleFormat = kv[1].Trim('"').ToLowerInvariant(); break; case "streams.stream.0.bit_rate": averageBytesPerSecond = (int.Parse(kv[1].Trim('"'), numberFormat) / 8); break; case "format.format_name": formatName = kv[1].Trim('"').ToLowerInvariant(); break; case "streams.stream.0.codec_tag": { var hex = kv[1].Substring(3, kv[1].Length - 4); format = int.Parse(hex, NumberStyles.HexNumber); break; } } } } catch (Exception ex) { throw new InvalidOperationException("Failed to parse ffprobe output.", ex); } // XNA seems to use the sample format for the bits per sample // in the case of non-PCM formats like MP3 and WMA. if (bitsPerSample == 0 && sampleFormat != null) { switch (sampleFormat) { case "u8": case "u8p": bitsPerSample = 8; break; case "s16": case "s16p": bitsPerSample = 16; break; case "s32": case "s32p": case "flt": case "fltp": bitsPerSample = 32; break; case "dbl": case "dblp": bitsPerSample = 64; break; } } // Figure out the file type. var durationMs = (int)Math.Floor(durationInSeconds * 1000.0); if (formatName == "wav") { audioFileType = AudioFileType.Wav; } else if (formatName == "mp3") { audioFileType = AudioFileType.Mp3; format = 1; durationMs = (int)Math.Ceiling(durationInSeconds * 1000.0); bitsPerSample = Math.Min(bitsPerSample, 16); } else if (formatName == "wma" || formatName == "asf") { audioFileType = AudioFileType.Wma; format = 1; durationMs = (int)Math.Ceiling(durationInSeconds * 1000.0); bitsPerSample = Math.Min(bitsPerSample, 16); } else if (formatName == "ogg") { audioFileType = AudioFileType.Ogg; format = 1; durationMs = (int)Math.Ceiling(durationInSeconds * 1000.0); bitsPerSample = Math.Min(bitsPerSample, 16); } else { audioFileType = (AudioFileType)(-1); } // XNA seems to calculate the block alignment directly from // the bits per sample and channel count regardless of the // format of the audio data. // ffprobe doesn't report blockAlign for ADPCM and we cannot calculate it like this if (bitsPerSample > 0 && (format != 2 && format != 17)) { blockAlign = (bitsPerSample * channelCount) / 8; } // XNA seems to only be accurate to the millisecond. duration = TimeSpan.FromMilliseconds(durationMs); // Looks like XNA calculates the average bps from // the sample rate and block alignment. if (blockAlign > 0) { averageBytesPerSecond = sampleRate * blockAlign; } audioFormat = new AudioFormat( averageBytesPerSecond, bitsPerSample, blockAlign, channelCount, format, sampleRate); // Loop start and length in number of samples. For some // reason XNA doesn't report loop length for non-WAV sources. loopStart = 0; if (audioFileType != AudioFileType.Wav) { loopLength = 0; } else { loopLength = (int)Math.Floor(sampleRate * durationInSeconds); } }
public static ConversionQuality ConvertToFormat(AudioContent content, ConversionFormat formatType, ConversionQuality quality, string saveToFile) { var temporaryOutput = Path.GetTempFileName(); try { string ffmpegCodecName, ffmpegMuxerName; //int format; switch (formatType) { case ConversionFormat.Adpcm: // ADPCM Microsoft ffmpegCodecName = "adpcm_ms"; ffmpegMuxerName = "wav"; //format = 0x0002; /* WAVE_FORMAT_ADPCM */ break; case ConversionFormat.Pcm: // XNA seems to preserve the bit size of the input // format when converting to PCM. if (content.Format.BitsPerSample == 8) { ffmpegCodecName = "pcm_u8"; } else if (content.Format.BitsPerSample == 32 && content.Format.Format == 3) { ffmpegCodecName = "pcm_f32le"; } else { ffmpegCodecName = "pcm_s16le"; } ffmpegMuxerName = "wav"; //format = 0x0001; /* WAVE_FORMAT_PCM */ break; case ConversionFormat.WindowsMedia: // Windows Media Audio 2 ffmpegCodecName = "wmav2"; ffmpegMuxerName = "asf"; //format = 0x0161; /* WAVE_FORMAT_WMAUDIO2 */ break; case ConversionFormat.Xma: throw new NotSupportedException( "XMA is not a supported encoding format. It is specific to the Xbox 360."); case ConversionFormat.ImaAdpcm: // ADPCM IMA WAV ffmpegCodecName = "adpcm_ima_wav"; ffmpegMuxerName = "wav"; //format = 0x0011; /* WAVE_FORMAT_IMA_ADPCM */ break; case ConversionFormat.Aac: // AAC (Advanced Audio Coding) // Requires -strict experimental ffmpegCodecName = "aac"; ffmpegMuxerName = "ipod"; //format = 0x0000; /* WAVE_FORMAT_UNKNOWN */ break; case ConversionFormat.Vorbis: // Vorbis ffmpegCodecName = "libvorbis"; ffmpegMuxerName = "ogg"; //format = 0x0000; /* WAVE_FORMAT_UNKNOWN */ break; default: // Unknown format throw new NotSupportedException(); } string ffmpegStdout, ffmpegStderr; int ffmpegExitCode; do { ffmpegExitCode = ExternalTool.Run( "ffmpeg", string.Format( "-y -i \"{0}\" -vn -c:a {1} -b:a {2} -ar {3} -f:a {4} -strict experimental \"{5}\"", content.FileName, ffmpegCodecName, QualityToBitRate(quality), QualityToSampleRate(quality, content.Format.SampleRate), ffmpegMuxerName, temporaryOutput), out ffmpegStdout, out ffmpegStderr); if (ffmpegExitCode != 0) { quality--; } } while (quality >= 0 && ffmpegExitCode != 0); if (ffmpegExitCode != 0) { throw new InvalidOperationException("ffmpeg exited with non-zero exit code: \n" + ffmpegStdout + "\n" + ffmpegStderr); } byte[] rawData; using (var fs = new FileStream(temporaryOutput, FileMode.Open, FileAccess.Read)) { rawData = new byte[fs.Length]; fs.Read(rawData, 0, rawData.Length); } if (saveToFile != null) { using (var fs = new FileStream(saveToFile, FileMode.Create, FileAccess.Write)) fs.Write(rawData, 0, rawData.Length); } // Use probe to get the final format and information on the converted file. AudioFileType audioFileType; AudioFormat audioFormat; TimeSpan duration; int loopStart, loopLength; ProbeFormat(temporaryOutput, out audioFileType, out audioFormat, out duration, out loopStart, out loopLength); AudioFormat riffAudioFormat; byte[] data = StripRiffWaveHeader(rawData, out riffAudioFormat); // deal with adpcm if (audioFormat.Format == 2 || audioFormat.Format == 17) { // riff contains correct blockAlign audioFormat = riffAudioFormat; // fix loopLength -> has to be multiple of sample per block // see https://msdn.microsoft.com/de-de/library/windows/desktop/ee415711(v=vs.85).aspx int samplesPerBlock = SampleAlignment(audioFormat); loopLength = (int)(audioFormat.SampleRate * duration.TotalSeconds); int remainder = loopLength % samplesPerBlock; loopLength += samplesPerBlock - remainder; } content.SetData(data, audioFormat, duration, loopStart, loopLength); } finally { ExternalTool.DeleteFile(temporaryOutput); } return(quality); }
/// <summary> /// Transcodes the source audio to the target format and quality. /// </summary> /// <param name="formatType">Format to convert this audio to.</param> /// <param name="quality">Quality of the processed output audio. For streaming formats, it can be one of the following: Low (96 kbps), Medium (128 kbps), Best (192 kbps). For WAV formats, it can be one of the following: Low (11kHz ADPCM), Medium (22kHz ADPCM), Best (44kHz PCM)</param> /// <param name="saveToFile"> /// The name of the file that the converted audio should be saved into. This is used for SongContent, where /// the audio is stored external to the XNB file. If this is null, then the converted audio is stored in /// the Data property. /// </param> public void ConvertFormat(ConversionFormat formatType, ConversionQuality quality, string saveToFile) { var temporarySource = Path.GetTempFileName(); var temporaryOutput = Path.GetTempFileName(); try { using (var fs = new FileStream(temporarySource, FileMode.Create, FileAccess.Write)) { var dataBytes = this.data.ToArray(); fs.Write(dataBytes, 0, dataBytes.Length); } string ffmpegCodecName, ffmpegMuxerName; int format; switch (formatType) { case ConversionFormat.Adpcm: // ADPCM Microsoft ffmpegCodecName = "adpcm_ms"; ffmpegMuxerName = "wav"; format = 0x0002; /* WAVE_FORMAT_ADPCM */ break; case ConversionFormat.Pcm: // PCM signed 16-bit little-endian ffmpegCodecName = "pcm_s16le"; ffmpegMuxerName = "wav"; format = 0x0001; /* WAVE_FORMAT_PCM */ break; case ConversionFormat.WindowsMedia: // Windows Media Audio 2 ffmpegCodecName = "wmav2"; ffmpegMuxerName = "asf"; format = 0x0161; /* WAVE_FORMAT_WMAUDIO2 */ break; case ConversionFormat.Xma: throw new NotSupportedException( "XMA is not a supported encoding format. It is specific to the Xbox 360."); case ConversionFormat.ImaAdpcm: // ADPCM IMA WAV ffmpegCodecName = "adpcm_ima_wav"; ffmpegMuxerName = "wav"; format = 0x0011; /* WAVE_FORMAT_IMA_ADPCM */ break; case ConversionFormat.Aac: // AAC (Advanced Audio Coding) // Requires -strict experimental ffmpegCodecName = "aac"; ffmpegMuxerName = "ipod"; format = 0x0000; /* WAVE_FORMAT_UNKNOWN */ break; case ConversionFormat.Vorbis: // Vorbis ffmpegCodecName = "libvorbis"; ffmpegMuxerName = "ogg"; format = 0x0000; /* WAVE_FORMAT_UNKNOWN */ break; default: // Unknown format throw new NotSupportedException(); } string ffmpegStdout, ffmpegStderr; var ffmpegExitCode = ExternalTool.Run( "ffmpeg", string.Format( "-y -i \"{0}\" -vn -c:a {1} -b:a {2} -f:a {3} -strict experimental \"{4}\"", temporarySource, ffmpegCodecName, QualityToBitRate(quality), ffmpegMuxerName, temporaryOutput), out ffmpegStdout, out ffmpegStderr); if (ffmpegExitCode != 0) { throw new InvalidOperationException("ffmpeg exited with non-zero exit code: \n" + ffmpegStdout + "\n" + ffmpegStderr); } byte[] rawData; using (var fs = new FileStream(temporaryOutput, FileMode.Open, FileAccess.Read)) { rawData = new byte[fs.Length]; fs.Read(rawData, 0, rawData.Length); } if (saveToFile != null) { using (var fs = new FileStream(saveToFile, FileMode.Create, FileAccess.Write)) { fs.Write(rawData, 0, rawData.Length); } this.data = null; } else { this.data = rawData.ToList(); } // Get the audio metadata from the output file string ffprobeStdout, ffprobeStderr; var ffprobeExitCode = ExternalTool.Run( "ffprobe", string.Format("-i \"{0}\" -show_entries streams -v quiet -of flat", temporaryOutput), out ffprobeStdout, out ffprobeStderr); if (ffprobeExitCode != 0) { throw new InvalidOperationException("ffprobe exited with non-zero exit code."); } // Set default values if information is not available. int averageBytesPerSecond = 0; int bitsPerSample = 0; int blockAlign = 0; int channelCount = 0; int sampleRate = 0; double durationInSeconds = 0; var numberFormat = System.Globalization.CultureInfo.InvariantCulture.NumberFormat; foreach (var line in ffprobeStdout.Split(new[] { '\r', '\n', '\0' }, StringSplitOptions.RemoveEmptyEntries)) { var kv = line.Split(new[] { '=' }, 2); switch (kv[0]) { case "streams.stream.0.sample_rate": sampleRate = int.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.bits_per_sample": bitsPerSample = int.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.duration": durationInSeconds = double.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.channels": channelCount = int.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.bit_rate": averageBytesPerSecond = (int.Parse(kv[1].Trim('"'), numberFormat) / 8); break; } } // Calculate blockAlign. switch (formatType) { case ConversionFormat.Adpcm: case ConversionFormat.ImaAdpcm: case ConversionFormat.Pcm: // Block alignment value is the number of bytes in an atomic unit (that is, a block) of audio for a particular format. For Pulse Code Modulation (PCM) formats, the formula for calculating block alignment is as follows: // • Block Alignment = Bytes per Sample x Number of Channels // For example, the block alignment value for 16-bit PCM format mono audio is 2 (2 bytes per sample x 1 channel). For 16-bit PCM format stereo audio, the block alignment value is 4. // https://msdn.microsoft.com/en-us/library/system.speech.audioformat.speechaudioformatinfo.blockalign(v=vs.110).aspx // Get the raw PCM from the output WAV file using (var reader = new BinaryReader(new MemoryStream(rawData))) { data = GetRawWavData(reader, ref blockAlign).ToList(); } break; default: // blockAlign is not available from ffprobe (and may or may not // be relevant for non-PCM formats anyway) break; } this.duration = TimeSpan.FromSeconds(durationInSeconds); this.format = new AudioFormat( averageBytesPerSecond, bitsPerSample, blockAlign, channelCount, format, sampleRate); // Loop start and length in number of samples. Defaults to entire sound loopStart = 0; if (data != null && bitsPerSample > 0 && channelCount > 0) { loopLength = data.Count / ((bitsPerSample / 8) * channelCount); } else { loopLength = 0; } } finally { File.Delete(temporarySource); File.Delete(temporaryOutput); } }
public static void ProbeFormat( string sourceFile, out AudioFileType audioFileType, out AudioFormat audioFormat, out TimeSpan duration, out int loopStart, out int loopLength) { string ffprobeArguments = string.Format( "-i \"{0}\" -show_format -show_entries streams -v quiet -of flat", sourceFile); int ffprobeExitCode = ExternalTool.Run( "ffprobe", ffprobeArguments, out var ffprobeStdout, out var ffprobeStderr); if (ffprobeExitCode != 0) { throw new InvalidOperationException( "ffprobe exited with non-zero exit code.", new Exception(ffprobeStderr.ToString())); } // Set default values if information is not available. int averageBytesPerSecond = 0; int bitsPerSample = 0; int blockAlign = 0; int channelCount = 0; int sampleRate = 0; int format = 0; string sampleFormat = null; double durationSeconds = 0; var formatName = string.Empty; try { var numberFormat = CultureInfo.InvariantCulture.NumberFormat; foreach (var line in ffprobeStdout.ToString() .Split(_newlineChars, StringSplitOptions.RemoveEmptyEntries)) { string[] kv = line.Split(new[] { '=' }, 2); switch (kv[0]) { case "streams.stream.0.sample_rate": sampleRate = int.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.bits_per_sample": bitsPerSample = int.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.start_time": if (double.TryParse( kv[1].Trim('"'), NumberStyles.Any, numberFormat, out double seconds)) { durationSeconds += seconds; } break; case "streams.stream.0.duration": durationSeconds += double.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.channels": channelCount = int.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.sample_fmt": sampleFormat = kv[1].Trim('"').ToUpperInvariant(); break; case "streams.stream.0.bit_rate": if (averageBytesPerSecond != 0) { break; } if (int.TryParse( kv[1].Trim('"'), NumberStyles.Integer, numberFormat, out int bitsPerSec)) { averageBytesPerSecond = bitsPerSec / 8; } break; case "streams.stream.0.codec_tag": string hex = kv[1][3..^ 1];
/// <summary> /// Transcodes the source audio to the target format and quality. /// </summary> /// <param name="formatType">Format to convert this audio to.</param> /// <param name="quality">Quality of the processed output audio. For streaming formats, it can be one of the following: Low (96 kbps), Medium (128 kbps), Best (192 kbps). For WAV formats, it can be one of the following: Low (11kHz ADPCM), Medium (22kHz ADPCM), Best (44kHz PCM)</param> /// <param name="saveToFile"> /// The name of the file that the converted audio should be saved into. This is used for SongContent, where /// the audio is stored external to the XNB file. If this is null, then the converted audio is stored in /// the Data property. /// </param> public void ConvertFormat(ConversionFormat formatType, ConversionQuality quality, string saveToFile) { if (disposed) { throw new ObjectDisposedException("AudioContent"); } var temporarySource = Path.GetTempFileName(); var temporaryOutput = Path.GetTempFileName(); try { using (var fs = new FileStream(temporarySource, FileMode.Create, FileAccess.Write)) { var dataBytes = this.data.ToArray(); fs.Write(dataBytes, 0, dataBytes.Length); } string ffmpegCodecName, ffmpegMuxerName; int format; switch (formatType) { case ConversionFormat.Adpcm: // ADPCM Microsoft ffmpegCodecName = "adpcm_ms"; ffmpegMuxerName = "wav"; format = 0x0002; /* WAVE_FORMAT_ADPCM */ break; case ConversionFormat.Pcm: // PCM signed 16-bit little-endian ffmpegCodecName = "pcm_s16le"; ffmpegMuxerName = "s16le"; format = 0x0001; /* WAVE_FORMAT_PCM */ break; case ConversionFormat.WindowsMedia: // Windows Media Audio 2 ffmpegCodecName = "wmav2"; ffmpegMuxerName = "asf"; format = 0x0161; /* WAVE_FORMAT_WMAUDIO2 */ break; case ConversionFormat.Xma: throw new NotSupportedException( "XMA is not a supported encoding format. It is specific to the Xbox 360."); case ConversionFormat.ImaAdpcm: // ADPCM IMA WAV ffmpegCodecName = "adpcm_ima_wav"; ffmpegMuxerName = "wav"; format = 0x0011; /* WAVE_FORMAT_IMA_ADPCM */ break; case ConversionFormat.Aac: // AAC (Advanced Audio Coding) // Requires -strict experimental ffmpegCodecName = "aac"; ffmpegMuxerName = "ipod"; format = 0x0000; /* WAVE_FORMAT_UNKNOWN */ break; case ConversionFormat.Vorbis: // Vorbis ffmpegCodecName = "libvorbis"; ffmpegMuxerName = "ogg"; format = 0x0000; /* WAVE_FORMAT_UNKNOWN */ break; default: // Unknown format throw new NotSupportedException(); } string ffmpegStdout, ffmpegStderr; var ffmpegExitCode = ExternalTool.Run( "ffmpeg", string.Format( "-y -i \"{0}\" -vn -c:a {1} -b:a {2} -f:a {3} -strict experimental \"{4}\"", temporarySource, ffmpegCodecName, QualityToBitRate(quality), ffmpegMuxerName, temporaryOutput), out ffmpegStdout, out ffmpegStderr); if (ffmpegExitCode != 0) { throw new InvalidOperationException("ffmpeg exited with non-zero exit code: \n" + ffmpegStdout + "\n" + ffmpegStderr); } byte[] rawData; using (var fs = new FileStream(temporaryOutput, FileMode.Open, FileAccess.Read)) { rawData = new byte[fs.Length]; fs.Read(rawData, 0, rawData.Length); } if (saveToFile != null) { using (var fs = new FileStream(saveToFile, FileMode.Create, FileAccess.Write)) { fs.Write(rawData, 0, rawData.Length); } this.data = null; } else { this.data = rawData.ToList(); } string ffprobeStdout, ffprobeStderr; var ffprobeExitCode = ExternalTool.Run( "ffprobe", string.Format("-i \"{0}\" -show_entries streams -v quiet -of flat", temporarySource), out ffprobeStdout, out ffprobeStderr); if (ffprobeExitCode != 0) { throw new InvalidOperationException("ffprobe exited with non-zero exit code."); } // Set default values if information is not available. int averageBytesPerSecond = 0; int bitsPerSample = 0; int blockAlign = 0; int channelCount = 0; int sampleRate = 0; double durationInSeconds = 0; var numberFormat = System.Globalization.CultureInfo.InvariantCulture.NumberFormat; foreach (var line in ffprobeStdout.Split(new[] { '\r', '\n', '\0' }, StringSplitOptions.RemoveEmptyEntries)) { var kv = line.Split(new[] { '=' }, 2); switch (kv[0]) { case "streams.stream.0.sample_rate": sampleRate = int.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.bits_per_sample": bitsPerSample = int.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.duration": durationInSeconds = double.Parse(kv[1].Trim('"'), numberFormat); break; case "streams.stream.0.channels": channelCount = int.Parse(kv[1].Trim('"'), numberFormat); break; } } // This information is not available from ffprobe (and may or may not // be relevant for non-PCM formats anyway): // // * averageBytesPerSecond // * blockAlign this.duration = TimeSpan.FromSeconds(durationInSeconds); this.format = new AudioFormat( averageBytesPerSecond, bitsPerSample, blockAlign, channelCount, format, sampleRate); } finally { File.Delete(temporarySource); File.Delete(temporaryOutput); } }