Skip to content

Commit

Permalink
Add audio utilities (audio.as).
Browse files Browse the repository at this point in the history
  • Loading branch information
m1maker committed Dec 29, 2024
1 parent 6348f5b commit 15a5627
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 5 deletions.
158 changes: 158 additions & 0 deletions Release/Include/audio.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#include "hip.as" // clamp

string pack_audio(array<float>@ data)
{
string result;
for (uint i = 0; i < data.length(); ++i)
{
result += float_to_bytes(data[i]);
}
return result;
}

array<float>@ unpack_audio(const string &in data)
{
array<float> result;

uint numFloats = data.length() / sizeof(0.0f);
result.resize(numFloats);

for (uint i = 0; i < numFloats; ++i)
{
string floatBytes = data.substr(i * sizeof(0.0f), sizeof(0.0f));
result[i] = bytes_to_float(floatBytes);
}

return result;
}


array<float>@ mix_audio(const array<float>@ audio1, const array<float>@ audio2, bool avoid_clip = true)
{
uint maxLength = audio1.length() > audio2.length() ? audio1.length() : audio2.length();
array<float> mixedAudio;
mixedAudio.resize(maxLength);

for (uint i = 0; i < maxLength; ++i)
{
float sample1 = (i < audio1.length()) ? audio1[i] : 0.0f;
float sample2 = (i < audio2.length()) ? audio2[i] : 0.0f;

mixedAudio[i] = sample1 + sample2;
if (avoid_clip)
{
if (mixedAudio[i] > 1.0f) mixedAudio[i] = 1.0f;
else if (mixedAudio[i] < -1.0f) mixedAudio[i] = -1.0f;
}
}
return mixedAudio;
}


array<float>@ generate_sine(int freq, int channels, int sample_rate, int samples) {
array<float> @waveform = array<float>(samples * channels);
float increment = (2.0 * 3.14159265 * freq) / sample_rate;

for (int i = 0; i < samples; ++i) {
float sample = sin(increment * i);
for (int ch = 0; ch < channels; ++ch) {
waveform[i * channels + ch] = sample;
}
}

return waveform;
}


array<float>@ generate_square(int freq, int channels, int sample_rate, int samples) {
array<float> @waveform = array<float>(samples * channels);
float period = sample_rate / freq;

for (int i = 0; i < samples; ++i) {
float sample = (i % period < period / 2) ? 1.0 : -1.0;
for (int ch = 0; ch < channels; ++ch) {
waveform[i * channels + ch] = sample;
}
}

return waveform;
}


array<float>@ generate_sawtooth(int freq, int channels, int sample_rate, int samples) {
array<float> @waveform = array<float>(samples * channels);
float increment = (2.0 / sample_rate) * freq;

for (int i = 0; i < samples; ++i) {
float sample = (2.0 * (i * increment - floor(0.5 + i * increment))) - 1.0;
for (int ch = 0; ch < channels; ++ch) {
waveform[i * channels + ch] = sample;
}
}

return waveform;
}


array<float>@ generate_triangle(int freq, int channels, int sample_rate, int samples) {
array<float> @waveform = array<float>(samples * channels);
float increment = (2.0 / sample_rate) * freq;

for (int i = 0; i < samples; ++i) {
float sample = 2.0 * abs(2.0 * ((i * increment) - floor((i * increment) + 0.5))) - 1.0;
for (int ch = 0; ch < channels; ++ch) {
waveform[i * channels + ch] = sample;
}
}

return waveform;
}

array<float>@ generate_noise(int channels, int samples) {
array<float> @waveform = array<float>(samples * channels);
for (int i = 0; i < samples; ++i) {
float sample = random(0, 1);
for (int ch = 0; ch < channels; ++ch) {
waveform[i * channels + ch] = sample;
}
}
return waveform;
}

array<float>@ generate_silence(int channels, int samples) {
array<float> @waveform = array<float>(samples * channels);
for (int i = 0; i < samples * channels; ++i) {
waveform[i] = 0.0f; // Silence
}
return waveform;
}

array<float>@ apply_envelope(const array<float>@ audio, float attack, float decay, float sustain, float release, int sample_rate) {
array<float> @envelopedAudio = array<float>(audio.length());
int totalSamples = audio.length();
int attackSamples = int(sample_rate * attack);
int decaySamples = int(sample_rate * decay);
int releaseSamples = int(sample_rate * release);

// Attack phase
for (int i = 0; i < attackSamples && i < totalSamples; ++i) {
envelopedAudio[i] = audio[i] * (i / float(attackSamples));
}

// Decay phase
for (int i = 0; i < decaySamples && (attackSamples + i) < totalSamples; ++i) {
envelopedAudio[attackSamples + i] = audio[attackSamples + i] * (1.0f - (i / float(decaySamples))) + sustain * (i / float(decaySamples));
}

// Sustain phase
for (int i = 0; i < (totalSamples - attackSamples - decaySamples) && (attackSamples + decaySamples + i) < totalSamples; ++i) {
envelopedAudio[attackSamples + decaySamples + i] = audio[attackSamples + decaySamples + i] * sustain;
}

// Release phase
for (int i = 0; i < releaseSamples && (totalSamples - releaseSamples + i) < totalSamples; ++i) {
envelopedAudio[totalSamples - releaseSamples + i] = audio[totalSamples - releaseSamples + i] * (1.0f - (i / float(releaseSamples)));
}

return envelopedAudio;
}
26 changes: 26 additions & 0 deletions Release/Tests/audio.ngt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "../include/audio.as"
void main() {
int sampleRate = 44100;
int samples = 44100; // 1 second of audio

// Generate a sine wave
array<float>@ sineWave = generate_sine(440, 2, sampleRate, samples);

// Generate noise
array<float>@ noiseWave = generate_noise(2, samples);

// Mix the sine wave and noise
array<float>@ mixedAudio = mix_audio(sineWave, noiseWave);

// Apply an envelope to the mixed audio
array<float>@ finalAudio = apply_envelope(mixedAudio, 0.1f, 0.1f, 0.7f, 0.2f, sampleRate);

// Pack the final audio data for output
string packedData = pack_audio(finalAudio);

sound s;
s.load_pcm(packedData, samples, 2, sampleRate, 32);
s.play_wait();
s.close();
}

12 changes: 8 additions & 4 deletions Release/Tests/pcm.ngt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ void main(){
sound s;
tts_voice v;
size_t size;
string pcm = v.speak_to_memory("Test", size);
string pcm = v.speak_to_memory("S", size);
show_window("Test");
v.speak("Are you ready?");
wait(1500);
v.speak_interrupt("Let's go");
wait(500);
thread_func@ func = function(dictionary@ args){
tts_voice@ v = cast<tts_voice@>(args[0]);
while (!quit_requested){
v.speak(random_bool() ? "Bla" : "Meh");
wait(1000);
v.speak("Bla");
}
};
dictionary args;
Expand All @@ -20,12 +24,12 @@ s.load_pcm(pcm, size/2, 1, 16000, 16);
bool result = random_bool();
if (result){
s.pitch = random(90, 100);
s.set_fx("reverb");
s.set_fx(random_bool() ? "reverb" : "delay");
s.set_reverb_parameters(1.0f, 0.5f, 1.0f, 0, 0);
}
s.hrtf = true;
s.set_position(0, 0, 0, random(-10, 10), random(-10, 10), 00);
s.play();
s.play_looped();
wait(result ? 500 : 250);
s.close();
}
Expand Down
19 changes: 18 additions & 1 deletion SRC/ngt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,23 @@ CScriptArray* keys_repeat() {
}
return array;
}
// Sam Tupy NVGT's functions
std::string float_to_bytes(float f) {
return std::string((char*)&f, 4);
}
float bytes_to_float(const std::string& s) {
if (s.size() != 4) return 0;
return *((float*)&s[0]);
}
std::string double_to_bytes(double d) {
return std::string((char*)&d, 8);
}
double bytes_to_double(const std::string& s) {
if (s.size() != 8) return 0;
return *((double*)&s[0]);
}


string number_to_words(uint64_t num, bool include_and)
{
vector<char> buf(1024);
Expand Down Expand Up @@ -746,7 +763,7 @@ return"";
}
return result;
#endif
}
}
bool key_pressed(int key_code)
{
if (keys[key_code].isDown == true and keys[key_code].isPressed == false)
Expand Down
5 changes: 5 additions & 0 deletions SRC/ngt.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ CScriptArray* keys_pressed();
CScriptArray* keys_released();
CScriptArray* keys_down();
CScriptArray* keys_repeat();
std::string float_to_bytes(float f);
float bytes_to_float(const std::string& s);
std::string double_to_bytes(double d);
double bytes_to_double(const std::string& s);

string string_encrypt(const string& str, string encryption_key);
string string_decrypt(const string& str, string encryption_key);
string url_decode(const string& url);
Expand Down
5 changes: 5 additions & 0 deletions SRC/ngtreg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,11 @@ void RegisterFunctions(asIScriptEngine* engine)
engine->RegisterGlobalFunction("string url_get(const string &in url)", asFUNCTION(url_get), asCALL_CDECL);
engine->RegisterGlobalFunction("string url_post(const string &in url, const string &in parameters)", asFUNCTION(url_post), asCALL_CDECL);
AS_END(engine);
engine->RegisterGlobalFunction(_O("string float_to_bytes(float number)"), asFUNCTION(float_to_bytes), asCALL_CDECL);
engine->RegisterGlobalFunction(_O("float bytes_to_float(const string&in data)"), asFUNCTION(bytes_to_float), asCALL_CDECL);
engine->RegisterGlobalFunction(_O("string double_to_bytes(double number)"), asFUNCTION(double_to_bytes), asCALL_CDECL);
engine->RegisterGlobalFunction(_O("double bytes_to_double(const string&in data)"), asFUNCTION(bytes_to_double), asCALL_CDECL);

engine->RegisterGlobalFunction("string ascii_to_character(int ascii)", asFUNCTION(ascii_to_character), asCALL_CDECL);
engine->RegisterGlobalFunction("int character_to_ascii(const string &in character)", asFUNCTION(character_to_ascii), asCALL_CDECL);
engine->RegisterGlobalFunction("string hex_to_string(const string&in the_hexadecimal_sequence)", asFUNCTION(hex_to_string), asCALL_CDECL);
Expand Down

0 comments on commit 15a5627

Please sign in to comment.