Open leoderja opened 1 year ago
Cool idea! Not something I’d think of immediately but why not 😄 not sure we will add it anytime soon, but open to PRs for this!
I would love something like this in an upcoming project but I don't have a clue where to start
In our project, we also needed mock audio but for recorder control, and we used the following approach: SineGenerator helper to generate audio tone at runtime:
public class SineGenerator
{
public static short[] GenerateSine(double frequency, uint sampleRate, int milliSeconds)
{
var secondsInLength = milliSeconds / 1000d;
var bufferSize = (ushort)(sampleRate * secondsInLength);
var dataBuffer = new short[bufferSize];
const int amplitude = 32760;
double timePeriod = Math.PI * 2 * frequency / sampleRate;
for (uint index = 0; index < bufferSize - 1; index++)
{
dataBuffer[index] = Convert.ToInt16(amplitude * Math.Sin(timePeriod * index));
}
return dataBuffer;
}
}
AudioDataChunk to store generated tone and write it to the buffer later:
public class AudioDataChunk
{
public uint ChunkSize { get; set; }
public short[] WaveData { get; private set; }
public byte[] WaveDataBytes { get; private set; }
public AudioDataChunk()
{
ChunkSize = 0;
WaveData = Array.Empty<short>();
}
public void AddSampleData(short[] leftChannelBuffer, short[] rightChannelBuffer)
{
WaveData = new short[leftChannelBuffer.Length + rightChannelBuffer.Length];
int bufferOffset = 0;
for (int index = 0; index < WaveData.Length; index += 2)
{
WaveData[index] = leftChannelBuffer[bufferOffset];
WaveData[index + 1] = rightChannelBuffer[bufferOffset];
bufferOffset++;
}
ChunkSize = (uint)WaveData.Length * 2;
WaveDataBytes = WaveData.SelectMany(BitConverter.GetBytes).ToArray();
}
}
How to use it?
_sineToneChunk = new AudioDataChunk();
_sineToneChunk.AddSampleData(
leftChannelBuffer: SineGenerator.GenerateSine(697.0f, (uint)AudioStreamDetails.SampleRate, ToneLengthMs),
rightChannelBuffer: SineGenerator.GenerateSine(1209.0f, (uint)AudioStreamDetails.SampleRate, ToneLengthMs));
_binaryWriter = new BinaryWriter(_streamWriter.BaseStream, System.Text.Encoding.UTF8);
_binaryWriter.Write(_sineToneChunk.WaveDataBytes);
This is solution is based on: https://www.codeguru.com/dotnet/making-sounds-with-waves-using-c/ I hope this would be a good starting point for you.
I found the comment by @Xo3-ToBapbl to be very helpful, as well as the codeguru link that he provided. However, they were still not 100% effective for me.
The code at the codeguru website worked well, even without changes. But I wanted a couple of extra pieces of functionality:
I was able to do this by making some small changes to the DataChunk.cs
class at the codeguru website. I also created a new class called SilenceGenerator
.
For those interested in my solution, the complete set of classes can be found here:
So basically there are 5 classes:
Here is an example of how I have used them:
List<byte> temp_bytes = new List<byte>();
WaveHeader header = new WaveHeader();
FormatChunk format = new FormatChunk();
DataChunk data_chunk = new DataChunk();
SineGenerator signal_data = new SineGenerator(440, 44100, 400);
SilenceGenerator silence_data = new SilenceGenerator(44100, 500);
data_chunk.AddSampleData(signal_data.Data, signal_data.Data);
data_chunk.AddSampleData(silence_data.Data, silence_data.Data);
data_chunk.AddSampleData(signal_data.Data, signal_data.Data);
data_chunk.AddSampleData(silence_data.Data, silence_data.Data);
header.FileLength += format.Length() + data_chunk.Length();
temp_bytes.AddRange(header.GetBytes());
temp_bytes.AddRange(format.GetBytes());
temp_bytes.AddRange(data_chunk.GetBytes());
var byte_array = temp_bytes.ToArray();
MemoryStream memory_stream = new MemoryStream(byte_array);
var current_audio_player = AudioManager.Current.CreatePlayer(memory_stream);
current_audio_player.Play();
This is awesome! Thank you for sharing the code.
I've got a project that I need to dust off soon that I wanted to achieve something like this.
This is awesome! Thank you for sharing the code.
I've got a project that I need to dust off soon that I wanted to achieve something like this.
I also just made one more improvement to my code (still available at the above link from my previous post). I improved the SineGenerator class so that it allows ramping up/down of the signal, thus removing any pops/clicks you can get at the beginning or ending of a sound.
If this would be implemented, could there also be a method that reads in some standard audio files (that could be overloaded for more needed types by the user) to then write those sound waves that can be played by the audioplayer?
I guess that is kind of what the proposed code already does, but I am not sure as I am really new to Audio files.
The issue where I am coming from is that I have ogg files (receiving them via backend from WhatsApp cloud api audio messages) and they work fine on android but I can’t find a way to play them on iOS as they’re not supported OOTB.
It would be great if there was a way to simply generate a tone with .Net Maui, just indicating frequency, volume, duration, (waveform?), etc, without using an audio file.
Do you think it is possible to add this functionality to Plugin.Maui.Audio?
Thank you a lot!