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();
}
}
}
}