Voilà toutes les fonctions dont j'ai besoin pour jouer un "bip":
alGetError()
alcGetString()
alcOpenDevice()
alcCreateContext()
alGenBuffers()
alBufferData()
alGenSources()
alSourcei()
alSourcePlay()
alGetSourcei()
Je me donne environ un mois pour essayer d'y comprendre quelque chose.
Interet de multiplier les topics sans aucune plus-value ?
Je vais essayer de comprendre quelque chose à ce tutoriel.
https://www.youtube.com/watch?v=BR8KjNkYURk
Donc pour gérer la stereo il me faut cette fonction là en plus:
alSource3f()
Et pour le volume:
alSourcef()
Maintenant il faut que je repère quand est-ce que les sourceID ne sont plus valides car le son a fini de jouer.
bon je suppose que ces trucs là doivent être appelés sur les audiothreads
J'ai trouvé un sample sur stackoverflow je le tripote pour essayer de comprendre comment ça marche.
#include <al.h>
#include <alc.h>
#include <stdio.h>
#include <stdlib.h> // gives malloc
#include <math.h>
#include <unistd.h> // gives sleep
ALCdevice * openal_output_device;
ALCcontext * openal_output_context;
ALuint internal_buffer;
ALuint streaming_source[1];
int al_check_error(const char * given_label) {
ALenum al_error;
al_error = alGetError();
if(AL_NO_ERROR != al_error) {
printf("ERROR - %s (%s)\n", alGetString(al_error), given_label);
return al_error;
}
return 0;
}
void MM_init_al() {
const char * defname = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
openal_output_device = alcOpenDevice(defname);
openal_output_context = alcCreateContext(openal_output_device, NULL);
alcMakeContextCurrent(openal_output_context);
// setup buffer and source
alGenBuffers(1, & internal_buffer);
al_check_error("failed call to alGenBuffers");
}
void MM_exit_al() {
ALenum errorCode = 0;
// Stop the sources
alSourceStopv(1, & streaming_source[0]); // streaming_source
int ii;
for (ii = 0; ii < 1; ++ii) {
alSourcei(streaming_source[ii], AL_BUFFER, 0);
}
// Clean-up
alDeleteSources(1, &streaming_source[0]);
alDeleteBuffers(16, &streaming_source[0]);
errorCode = alGetError();
alcMakeContextCurrent(NULL);
errorCode = alGetError();
alcDestroyContext(openal_output_context);
alcCloseDevice(openal_output_device);
}
void MM_render_one_buffer() {
int seconds = 1;
// unsigned sample_rate = 22050;
unsigned sample_rate = 44100;
size_t buf_size = seconds * sample_rate;
// allocate PCM audio buffer
float * samples = (float*)malloc(sizeof(float) * buf_size);
int i=0;
for(; i<buf_size; ++i) {
samples[i] = ( ( i % 100 )*2.0-1.0 ) ;
}
/* upload buffer to OpenAL */
alBufferData( internal_buffer, AL_FORMAT_MONO16, samples, buf_size, sample_rate);
al_check_error("populating alBufferData");
free(samples);
/* Set-up sound source and play buffer */
// ALuint src = 0;
// alGenSources(1, &src);
// alSourcei(src, AL_BUFFER, internal_buffer);
alGenSources(1, & streaming_source[0]);
alSourcei(streaming_source[0], AL_BUFFER, internal_buffer);
// alSourcePlay(src);
alSourcePlay(streaming_source[0]);
// ---------------------
ALenum current_playing_state;
alGetSourcei(streaming_source[0], AL_SOURCE_STATE, & current_playing_state);
al_check_error("alGetSourcei AL_SOURCE_STATE");
while (AL_PLAYING == current_playing_state) {
printf("still playing ... so sleep\n");
sleep(1); // should use a thread sleep NOT sleep() for a more responsive finish
alGetSourcei(streaming_source[0], AL_SOURCE_STATE, & current_playing_state);
al_check_error("alGetSourcei AL_SOURCE_STATE");
}
printf("end of playing\n");
} // MM_render_one_buffer
int main() {
MM_init_al();
MM_render_one_buffer();
/* Dealloc OpenAL */
MM_exit_al();
}
bon alors son pseudo-thread se trouve dans cette boucle:
ALenum current_playing_state;
alGetSourcei(streaming_source[0], AL_SOURCE_STATE, & current_playing_state);
al_check_error("alGetSourcei AL_SOURCE_STATE");
while (AL_PLAYING == current_playing_state) {
printf("still playing ... so sleep\n");
sleep(1); // should use a thread sleep NOT sleep() for a more responsive finish
alGetSourcei(streaming_source[0], AL_SOURCE_STATE, & current_playing_state);
al_check_error("alGetSourcei AL_SOURCE_STATE");
}
il faut faire le test == AL_PLAYING ailleurs que sur un sleep, je suppose que ça doit être placé bêtement dans la routine vidéo.
bon donc tout va se passer dans la gestion d'un tableau de "voix", je crois que le plus simple c'est de le gérer en pooling
bon je verrai plus tard le pooling de tracks car là c'est de l'algo,
voici le test final de "pouet" horrible qui se déplace de droite à gauche:
#include "Module.h"
#include "AL_Entry.h"
#include <al.h>
#include <alc.h>
#include <stdio.h>
#include <stdlib.h> // gives malloc
#include <math.h>
#include <unistd.h> // gives sleep
#include <iostream>
using namespace std;
ALCdevice * openal_output_device;
ALCcontext * openal_output_context;
ALuint internal_buffer;
ALuint streaming_source[1];
int al_check_error(const char * given_label) {
ALenum al_error;
al_error = alGetError();
if(AL_NO_ERROR != al_error) {
printf("ERROR - %s (%s)\n", alGetString(al_error), given_label);
return al_error;
}
return 0;
}
void MM_init_al() {
const char * defname = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
openal_output_device = alcOpenDevice(defname);
openal_output_context = alcCreateContext(openal_output_device, NULL);
alcMakeContextCurrent(openal_output_context);
// setup buffer and source
alGenBuffers(1, & internal_buffer);
al_check_error("failed call to alGenBuffers");
cout << "id buffer: " << internal_buffer << endl;
}
void MM_exit_al() {
ALenum errorCode = 0;
// Stop the sources
alSourceStopv(1, & streaming_source[0]); // streaming_source
int ii;
for (ii = 0; ii < 1; ++ii) {
alSourcei(streaming_source[ii], AL_BUFFER, 0);
}
// Clean-up
alDeleteSources(1, &streaming_source[0]);
alDeleteBuffers(16, &streaming_source[0]);
errorCode = alGetError();
alcMakeContextCurrent(NULL);
errorCode = alGetError();
alcDestroyContext(openal_output_context);
alcCloseDevice(openal_output_device);
}
void MM_render_one_buffer() {
int seconds = 1;
unsigned sample_rate = 44100;
size_t buf_size = seconds * sample_rate;
// allocate PCM audio buffer
short * samples = (short*)malloc(sizeof(short) * buf_size);
int i=0;
float vol=1.0;
for(; i<buf_size; ++i) {
samples[i] = ( (i%128)/64.0 - 1.0 ) * (float)(1<<14) * vol ;
vol *= .9999;
}
// update into buffer
alBufferData( internal_buffer, AL_FORMAT_MONO16, samples, buf_size, sample_rate);
al_check_error("populating alBufferData");
free(samples);
}
void MM_prepare_source()
{
alGenSources(1, & streaming_source[0]);
cout << "id stream source: " << streaming_source[0] << endl;
}
void MM_play_one_buffer()
{
alSourcei(streaming_source[0], AL_BUFFER, internal_buffer);
alSourcePlay(streaming_source[0]);
}
// check playing routine
bool MM_is_buffer_playing()
{
ALenum current_playing_state;
alGetSourcei(streaming_source[0], AL_SOURCE_STATE, & current_playing_state);
al_check_error("alGetSourcei AL_SOURCE_STATE");
return (AL_PLAYING == current_playing_state);
}
// set pan
void MM_setPanVol( float pan, float vol )
{
alSource3f( streaming_source[0], AL_POSITION, pan, 0, 4 );
alSourcef( streaming_source[0], AL_GAIN, vol );
}
int main() {
float vol = 1.0;
float pan = -1.0;
Module::Init();
MM_init_al();
// prepare wave
MM_render_one_buffer();
// prepare source
MM_prepare_source();
// play wave
MM_play_one_buffer();
// loop
int STEP = 0;
while ( STEP < 60*10 )
{
pan = cos( (double)STEP / 120.0 * M_PI ) * 14;
STEP ++;
if ( MM_is_buffer_playing() )
{
MM_setPanVol( pan, vol );
}
else
{
MM_play_one_buffer();
}
cout << "." ;
usleep( 1000 * 16 ); // microseconds
}
cout << endl;
/* Dealloc OpenAL */
MM_exit_al();
Module::ShutDown();
}
pour vous faire partager mes acouphènes je l'ai traduit en javascript:
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
// Create an empty three-second stereo buffer at the sample rate of the AudioContext
const sampleRate = 44100;
const myArrayBuffer_1 = audioCtx.createBuffer(
1,
sampleRate * .5,
sampleRate,
);
// Fill the buffer with white noise;
// just random values between -1.0 and 1.0
for (let channel = 0; channel < myArrayBuffer_1.numberOfChannels; channel++) {
const nowBuffering = myArrayBuffer_1.getChannelData(channel);
let vol = 1.0;
for (let i = 0; i < myArrayBuffer_1.length; i++) {
nowBuffering[i] = vol*( (i%128)/64.0 - 1 );
vol *= .9999;
}
}
var PAN = -1;
var PAN_NODE = null, GAIN_NODE = null;
var PLAYING = false;
function SoundTest( buffer )
{
const source = audioCtx.createBufferSource();
source.buffer = buffer;
// end event
source.onended = function( e )
{
PLAYING = false;
};
// pan-vol nodes
PAN_NODE = audioCtx.createPanner();
GAIN_NODE = audioCtx.createGain();
GAIN_NODE.gain.setValueAtTime( 1, audioCtx.currentTime );
// connect the AudioBufferSourceNode to the
// destination so we can hear the sound
source.connect(PAN_NODE);
PAN_NODE.connect(GAIN_NODE);
GAIN_NODE.connect(audioCtx.destination);
// start the source playing
//source.loop = true;
source.start();
PLAYING = true;
}
var STEP = 0;
function VideoRoutine( dt )
{
if ( ! PLAYING )
{
SoundTest( myArrayBuffer_1 );
}
STEP ++ ;
// test vol-pan
const pan = Math.cos( STEP / 120.0 * Math.PI ) * 16;
PAN_NODE.positionX.setValueAtTime( pan, audioCtx.currentTime);
PAN_NODE.positionY.setValueAtTime( 0, audioCtx.currentTime);
PAN_NODE.positionZ.setValueAtTime( 4, audioCtx.currentTime);
GAIN_NODE.gain.setValueAtTime( 1, audioCtx.currentTime );
window.requestAnimationFrame(VideoRoutine);
}
const BTN_FX1 = document.getElementById("fx1");
BTN_FX1.onmousedown = function()
{
VideoRoutine(16);
};
il semble que le panner node javascript a le même effet que AL_POSITION,
j'ai choisi amplitude -16 / 16 sur X, et profondeur 4 sur Z pour cet exemple