using Amphora; using System; using System.IO; using System.Reflection; namespace Susurrus.WebAudio.Testbed { /// /// Sounds the testbed makes up, so it needs no audio files of its own beyond the one Ogg. /// public static class Tones { public const int SampleRate = 48000; public static DecodedSound Sine( double hertz, double amplitude, int frameCount, int sampleRate = SampleRate, int channelCount = 1) { short[] samples = new short[frameCount * channelCount]; for (int frame = 0; frame < frameCount; ++frame) { short sample = (short)(amplitude * short.MaxValue * Math.Sin(2 * Math.PI * hertz * frame / sampleRate)); for (int channel = 0; channel < channelCount; ++channel) { samples[frame * channelCount + channel] = sample; } } byte[] bytes = new byte[samples.Length * 2]; Buffer.BlockCopy(samples, 0, bytes, 0, bytes.Length); return new DecodedSound(new ByteView(bytes), sampleRate, channelCount, SampleFormat.Pcm16); } /// /// Makes a stereo sound with a different pitch in each ear. /// public static DecodedSound Chord(double leftHertz, double rightHertz, double amplitude, int frameCount) { short[] samples = new short[frameCount * 2]; for (int frame = 0; frame < frameCount; ++frame) { double time = frame / (double)SampleRate; double peak = amplitude * short.MaxValue; samples[frame * 2] = (short)(peak * Math.Sin(2 * Math.PI * leftHertz * time)); samples[frame * 2 + 1] = (short)(peak * Math.Sin(2 * Math.PI * rightHertz * time)); } byte[] bytes = new byte[samples.Length * 2]; Buffer.BlockCopy(samples, 0, bytes, 0, bytes.Length); return new DecodedSound(new ByteView(bytes), SampleRate, channelCount: 2, SampleFormat.Pcm16); } /// /// Makes a mono sound of 32-bit floats whose pitch rises from 300 to 1200 hertz. /// public static DecodedSound RisingSweepOfFloats(double amplitude, int frameCount) { float[] samples = new float[frameCount]; double phase = 0; for (int frame = 0; frame < frameCount; ++frame) { double hertz = 300 + 900 * frame / (double)frameCount; phase += 2 * Math.PI * hertz / SampleRate; samples[frame] = (float)(amplitude * Math.Sin(phase)); } byte[] bytes = new byte[samples.Length * 4]; Buffer.BlockCopy(samples, 0, bytes, 0, bytes.Length); return new DecodedSound(new ByteView(bytes), SampleRate, channelCount: 1, SampleFormat.Float32); } /// /// Returns a WAV file of a 16-bit sound. /// public static byte[] Wav(DecodedSound sound) { int dataBytes = sound.Samples.Length; byte[] returnMe = new byte[44 + dataBytes]; Put(returnMe, 0, "RIFF"); Put(returnMe, 4, 36 + dataBytes); Put(returnMe, 8, "WAVE"); Put(returnMe, 12, "fmt "); Put(returnMe, 16, 16); Put(returnMe, 20, (short)1); Put(returnMe, 22, (short)sound.ChannelCount); Put(returnMe, 24, sound.SampleRate); Put(returnMe, 28, sound.SampleRate * sound.ChannelCount * 2); Put(returnMe, 32, (short)(sound.ChannelCount * 2)); Put(returnMe, 34, (short)16); Put(returnMe, 36, "data"); Put(returnMe, 40, dataBytes); sound.Samples.CopyTo(returnMe, 44); return returnMe; } private static void Put(byte[] bytes, int index, string text) { System.Text.Encoding.ASCII.GetBytes(text, 0, text.Length, bytes, index); } private static void Put(byte[] bytes, int index, int value) { BitConverter.GetBytes(value).CopyTo(bytes, index); } private static void Put(byte[] bytes, int index, short value) { BitConverter.GetBytes(value).CopyTo(bytes, index); } public static byte[] OggFile() { using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream( "Susurrus.WebAudio.Testbed.Mono441Hz.ogg")) using (MemoryStream copy = new MemoryStream()) { stream.CopyTo(copy); return copy.ToArray(); } } } }