public AudioEngine(string audioDevice = null, AudioDriver driver = AudioDriver.Default, int sampleRate = 44100, int bufferLength = 1024, ResampleQuality resampleQuality = ResampleQuality.High) { this.resampleQuality = resampleQuality; SDL.SDL_InitSubSystem(SDL.SDL_INIT_AUDIO); audioCallback = AudioCallback; SDL.SDL_AudioSpec desired = new SDL.SDL_AudioSpec() { freq = sampleRate, format = SDL.AUDIO_S16, channels = 2, samples = (ushort)bufferLength, callback = audioCallback, }; if (driver == AudioDriver.File) { audioSpec = desired; audioDriver = driver; } else { string[] audioDrivers = new string[SDL.SDL_GetNumAudioDrivers()]; for (int i = 0; i < audioDrivers.Length; i++) { audioDrivers[i] = SDL.SDL_GetAudioDriver(i); } string driverName = audioDrivers[0]; string driverFallbackName = audioDrivers.Length > 1 ? audioDrivers[1] : null; if (driver != AudioDriver.Default) { driverName = driver.ToString(); } int init = SDL.SDL_AudioInit(driverName.ToLower()); if (init != 0 && driverName == AudioDriver.XAudio2.ToString().ToLower() && driverFallbackName != null) { // supplied SDL.dll does not support XAudio2, fallback to next driver driverName = driverFallbackName; init = SDL.SDL_AudioInit(driverName.ToLower()); } if (init != 0) { throw new ApplicationException("Failed to initialize audio driver " + driverName + ": " + SDL.SDL_GetError()); } Enum.TryParse(driverName, true, out audioDriver); if (audioDevice == null) { string[] audioDevices = new string[SDL.SDL_GetNumAudioDevices(0)]; for (int i = 0; i < audioDevices.Length; i++) { audioDevices[i] = SDL.SDL_GetAudioDeviceName(i, 0); } audioDevice = audioDevices.Length > 0 ? audioDevices[0] : null; } outputDevice = SDL.SDL_OpenAudioDevice(audioDevice, 0, ref desired, out audioSpec, 0); if (outputDevice == 0) { throw new ApplicationException("Failed to open audio device " + audioDevice + ": " + SDL.SDL_GetError()); } } if (audioSpec.format == SDL.AUDIO_S32) { bytesPerSample = (uint)audioSpec.channels * 4; } else if (audioSpec.format == SDL.AUDIO_S16 || audioSpec.format == SDL.AUDIO_F32) { bytesPerSample = (uint)audioSpec.channels * 2; } else if (audioSpec.format == SDL.AUDIO_S8 || audioSpec.format == SDL.AUDIO_U8) { bytesPerSample = (uint)audioSpec.channels * 1; } if (audioSpec.size == 0) { audioSpec.size = audioSpec.samples * bytesPerSample; } emptyBuffer = new byte[audioSpec.size]; audioBuffer = new byte[audioSpec.size]; lastCallback = 0.0; bufferTimer = Stopwatch.StartNew(); if (outputDevice != 0) { SDL.SDL_PauseAudioDevice(outputDevice, 0); } }
public static byte[] SoundFromFileResample(string path, int sampleRate, int channels, ushort sampleFormatSDL, ResampleQuality resampleQuality = ResampleQuality.High) { AVSampleFormat targetFormat2; switch (sampleFormatSDL) { case SDL.AUDIO_S16: targetFormat2 = AVSampleFormat.AV_SAMPLE_FMT_S16; break; case SDL.AUDIO_F32: targetFormat2 = AVSampleFormat.AV_SAMPLE_FMT_FLT; break; case SDL.AUDIO_S32: targetFormat2 = AVSampleFormat.AV_SAMPLE_FMT_S32; break; default: throw new ApplicationException("Could not map SDL audio format to AVSampleFormat: " + sampleFormatSDL.ToString()); } using (FFmpegContext ffContext = FFmpegContext.Read(path)) { ffContext.SelectStream(AVMediaType.AVMEDIA_TYPE_AUDIO); // setup resamplers and other format converters if needed ffContext.ConvertToFormat(targetFormat2, sampleRate, channels, resampleQuality); // FFmpeg only approximates stream durations but is // usually not far from the real duration. byte[] bytes = new byte[ffContext.audioBytesTotal]; // read all data from frames long offset = 0; while (ffContext.ReadFrame()) { long frameSize = ffContext.GetFrameBufferSize(); if (offset + frameSize > bytes.Length) { Array.Resize(ref bytes, (int)(offset + frameSize)); } offset += ffContext.GetFrameData(ref bytes, (int)offset); } return(bytes); } }
public void ConvertToFormat(AVSampleFormat sampleFormat, int sampleRate, int channels, ResampleQuality resampleQuality = ResampleQuality.High) { if (format == (int)sampleFormat && this.sampleRate == sampleRate && this.channels == channels) { return; } format = (int)sampleFormat; this.sampleRate = sampleRate; this.channels = channels; int channelLayout = (int)ffmpeg.av_get_default_channel_layout(channels); swrContext = ffmpeg.swr_alloc(); ffmpeg.av_opt_set_int(swrContext, "in_channel_layout", (int)codecContext->channel_layout, 0); ffmpeg.av_opt_set_int(swrContext, "out_channel_layout", channelLayout, 0); ffmpeg.av_opt_set_int(swrContext, "in_channel_count", codecContext->channels, 0); ffmpeg.av_opt_set_int(swrContext, "out_channel_count", channels, 0); ffmpeg.av_opt_set_int(swrContext, "in_sample_rate", codecContext->sample_rate, 0); ffmpeg.av_opt_set_int(swrContext, "out_sample_rate", sampleRate, 0); ffmpeg.av_opt_set_sample_fmt(swrContext, "in_sample_fmt", codecContext->sample_fmt, 0); ffmpeg.av_opt_set_sample_fmt(swrContext, "out_sample_fmt", sampleFormat, 0); switch (resampleQuality) { case ResampleQuality.Low: ffmpeg.av_opt_set_int(swrContext, "filter_size", 0, 0); ffmpeg.av_opt_set_int(swrContext, "phase_shift", 0, 0); break; case ResampleQuality.Medium: // default ffmpeg settings break; case ResampleQuality.High: ffmpeg.av_opt_set_int(swrContext, "filter_size", 128, 0); ffmpeg.av_opt_set_double(swrContext, "cutoff", 1.0, 0); break; case ResampleQuality.Highest: ffmpeg.av_opt_set_int(swrContext, "filter_size", 256, 0); ffmpeg.av_opt_set_double(swrContext, "cutoff", 1.0, 0); break; } if (ffmpeg.swr_init(swrContext) != 0) { throw new ApplicationException("Failed init SwrContext: " + FFmpegHelper.logLastLine); } }