Audio seems overly complicated :(
nshade

In my game I have music that plays independent of sound effects. However, I need to the ability start and stop each on their own channel. I'm trying to set up mixers, but for some reason the sound effects don't work most of the time. It's just silent. Also if I monkey with things all of the sudden my music stops working as well. The example code is kind of big, so I'll post it to a pastebin.

It just seem way over kill to load up samples, set every one to an instance and then hook every one to a mixer. Can you tell me why my audio just stops working. Do I have to hook up all these mixers? (I want a separate volume control for music and sound)

also it looks like I have to make a stop routine for each sound effect? I think I made this very over complicated and there has to be an easier way...

The pastebin example is here
https://pastebin.com/yayDRCFB

The function order in main(is the order things are called in the code. I don't have control over function names or call order because I'm writing a allegro wrapper for these functions.

Edgar Reynaldo

101 - have you ever heard of enums and arrays?

I took a quick glance, and I only saw where you hooked up the master mixer. You didn't attach the other mixers to a voice.

It goes Sound->Voice->Mixer->SampleInstance

And you can also attach mixers to mixers. I assume there's some global gain setting function for a mixer that would allow you to have your separate sound and music volume controls.

In your case I would have :

                       SFX Mixer - sfx sample instances
                     /
Voice -> Master Mixer
                     \ 
                      Music Mixer - music sample on loop or whatever

nshade

music_mixer is created in SoundInit() line 192
sfx_mixer created in SoundInit() line 193
master_mixer is created in SoundInit() line 194
voice created in SoundInit() line 195
master_mixer is attached to voice in SoundInit() line 196

This music_mixer is attached to master_mixer at MusicInit() line 95
The sfx_mixer is attached master_mixer at the bottom of LoadSound() line 266

sample instances are attached when they are loaded. The order appears to be exactly how you described.

Edgar Reynaldo

Do you call SoundInit before MusicInit?

Neil Roy

My biggest complaint about Allegro 5 was how the sound was set up. It was far simpler in Allegro 4.

Edgar Reynaldo

And could do far less. :/

nshade

yup You can see the function order under main()

Edgar Reynaldo

Your al_reserve_samples(1) looks suspiciious. Let me try something out.

Why do you mix audio init across different functions? Its confusing.

nshade

The reason why they are stretched out between different functions is because the game I'm porting calls these functions. I'm replacing the soundblaster library with one that uses allegro calls in it's place.

Edgar Reynaldo

I've got a working example with two mixers and two sample instances, one for music and one for sound. Their volume is independent. Next I'm going to try with a master mixer and a voice instead of the default voice and mixer, as you have it set up. Then I'll try multiple sample instances on the sound mixer and see if it still works.

I can't test it yet though, because al_create_voice returns NULL. I have to investigate a bit more.

Neil Roy

And could do far less. :/

I could gradually change the pitch in Allegro 4 with minimal fuss. It is FAR more complicated in Allegro 5.

In my Deluxe Pacman 1 game (Allegro 4), listen to the siren in the background. As you eat the dots, the pitch gradually goes up. Because it was simple to code.

Now listen to the same sound in Deluxe Pacman 2 (Allegro 5). You will hear the pitch suddenly change, because I could not gradually change it over time without a whole lot of headache. So instead I change it every so many pills which was easier to do.

I could do more with Allegro 4, at least what I wanted to do. Allegro 5 seemed to forget that the library was supposed to make life easier, and for the most part is does, but the sound system is horribad. The function to change the pitch in Allegro 4 is straight forward: adjust_sample() You can't do that in Allegro 5.

Edgar Reynaldo

Neil Roy said:

I could gradually change the pitch in Allegro 4 with minimal fuss. It is FAR more complicated in Allegro 5.

Really?

al_set_sample_instance_speed

Neil Roy

Really. I had this conversation years ago when working on DP2 and it was more complicated. There's more to it than just that one function, but hey, have at it. I'm not working on my Allegro games anymore anyhow. Maybe they have improved it since then, I certainly hope so because it used to be a pain in the ass, and still looks like it is, at least with the audio.

I was able to play music and sound samples and do periodic speed changes, but gradual changes while you played was more involved. I don't recall how now, and the problem wasn't resolved in these forums at the time. I really don't care to revisit it except to agree with the original poster.

Telling people they are wrong about it being overly complicated when you have several that tell you that it is, is burying your head in the sand and ignoring the problem.

Niunio

I agree with Neil: Allegro 5 sound subsystem is way too much complexest. And documentation doesn't help a lot. For example, I have no idea why I can't use more than 5 channels in my engine.

Anyway, this is a discussion for the development mailing lists. And I hope they discuss it. ::)

Edgar Reynaldo
Neil Roy said:

Really. I had this conversation years ago when working on DP2 and it was more complicated

You're living in the past. A lot has happened in Allegro 5 in the last few years.

Neil Roy said:

I was able to play music and sound samples and do periodic speed changes, but gradual changes while you played was more involved. I don't recall how now, and the problem wasn't resolved in these forums at the time. I really don't care to revisit it except to agree with the original poster.

Well that was then and this is now. One function is all it takes to change the speed of the sample instance.

Allegro 4 has MIDI. That's about all that Allegro 5 doesn't have. Allegro 4 had one mixer and one voice. Allegro 5 lets you create your own mixer setup.

Allegro 5 is more flexible and gives you both a simple API and a complicated API for those who need to exercise more control over their sound.

Audio init aside, it's the same as it was in allegro 4. If all you want is normal sound then you call load sample and play sample. This is not complicated.

If you need control over your sound, you use an ALLEGRO_SAMPLE_INSTANCE*. That's the only difference.

Neil Roy said:

Telling people they are wrong about it being overly complicated when you have several that tell you that it is, is burying your head in the sand and ignoring the problem.

I'm just going to quote Elias's sig and Evert here.

Evert said:

Either help out or stop whining.

Niunio said:

I agree with Neil: Allegro 5 sound subsystem is way too much complexest. And documentation doesn't help a lot. For example, I have no idea why I can't use more than 5 channels in my engine.

Do you really need 7 channel sound? What, 5 isn't enough? I doubt you could tell the difference between the two. What is your target audience? If you really need 7 (or more?) channels, your best bed is FMOD, which is non-free.

Niunio said:

Anyway, this is a discussion for the development mailing lists. And I hope they discuss it. ::)

There are few active devs left these days, and I doubt an overhaul of the audio API is something they want to spend their time on. If you have valid concerns other than just "omg its so complicated" then by all means start a discussion on the mailing list. And if you think the docs are lacking important information then make a patch and submit it.

I've barely used the sound API in Allegro at all, but it didn't take me very long to get a working example. As for why I can't create a voice I'm going to debug that now. Had to rebuild Allegro so I could hack on it. Took me two hours to get all the dumb dependencies built. Did I mention how much I hate autotools? CMake is far superior and much easier to work with.

nshade

So did I hit on a bug and that's why it's not playing the audio correctly?

Neil Roy

I'm just going to quote Elias's sig and Evert here.

I'm not whining, I am agreeing with the original poster. He finds it overly complicated as well as others.

I could care less anymore as I can do what I want with SDL2. You may not like to hear that, and I hate to have to convert to another library when I have always loved Allegro, but after years of being told to put up or shut up by people like you, I have had it.

If I could program it myself, I wouldn't be using this or any library (actually,. I have done it myself with some straight Windows programming and no libraries), so expecting people to "help out or shut up" is very rude and is what drives people away.

It's more than that one simple function to change it as I recall it from the past, but you need more before you use it in place which is where the pain the buttocks comes into play. I don't have time to reprogram a library and debate this, if it doesn't work easy for me, like Allegro 4 could, than I will find a library that will do it. And I am not alone in this thinking.

But hey, keep telling everyone they are wrong about this. I liked the simplicity of Allegro 4, that's what attracted most people to it. And I like most of Allegro 5, and yes, I can play sounds with it and music via OGG files which is fine. Most of the time that is enough, but for a few things, it can be a pain where Allegro 4 was not. I feel that Allegro 5 provides more flexibility to do things that the vast majority don't want or need while sacrificing the ease of use where it is important.

Edgar Reynaldo

@Neil
Your argument is without merit. You haven't provided any evidence that you can't do audio with Allegro 5 simply. Saying it's complicated over and over again without providing any proof whatsoever is pointless and doesn't help anyone fix anything. And yes, I would immensely prefer that people help improve allegro over endlessly saying it's not good enough. All you do anymore is complain about Allegro, and I've had it with you.

It's not that complicated, regardless of how you are perceiving it. It's just different from Allegro 4, and because of that you complain and bitch and moan, without helping in any constructive way whatsoever. If you love SDL2 so much, what are you still doing here? I also find it funny that you say "Allegro 5 can't do what I want" when all it takes is a simple glance through the manual to prove you're wrong. RTFM noob. And if the manual isn't good enough, then help improve it. You don't like Allegro's audio? Then fork allegro and fix it. I will say it again, "Either help out, or quit whining". You don't like something about Allegro? Then fix it and submit a patch. No one's stopping you. Patches and pull requests have always been welcome. But you'd rather complain. I don't want to hear it anymore.

nshade said:

So did I hit on a bug and that's why it's not playing the audio correctly?

That remains to be seen.

I figured out why al_create_voice was failing. ALLEGRO_AUDIO_DEPTH_FLOAT32 is not supported for voices. Only ALLEGRO_AUDIO_DEPTH_UINT8 and ALLEGRO_AUDIO_DEPTH_INT16 are supported by DirectSound for voices.

So, my example now works to play music and a sound, using a created voice, a master mixer, and two mixers one each for music and sound. The volume can be manipulated independently for music and sound.

Next step is to try multiple sounds at once.

Can you try my example program and see if it works for you?

Mixer app (static win32 binary and source)

Press M and S to increase music or sound volume. Hold shift with M and S to decrease it. Press Control with M or S to start or stop playing the sound or music.

nshade

The example worked "YAAAAAAAY!" :P

I changed my settings to ALLEGRO_AUDIO_DEPTH_INT16, bit the sounds were still hit and miss though.

Neil Roy

I was just agreeing with nshade, I couldn't give a shit what you think about my opinion Edgar. :)

Chris Katko

Sigh. ::)

Edgar Reynaldo

@Neil - Cool Story Bro 8-)

Do you have anything constructive to add?

Meanwhile, our super heroes are off fighting evil...

EDIT1

Reading The Fine Manual said:

In order to just play some samples, here's how to quick start with Allegro's audio addon: Call al_reserve_samples with the number of samples you'd like to be able to play simultaneously (don't forget to call al_install_audio beforehand). If these succeed, you can now call al_play_sample, with data obtained by al_load_sample, for example (don't forget to initialize the acodec addon). You don't need to worry about voices, mixers or sample instances when using this approach. In order to stop samples, you can use the ALLEGRO_SAMPLE_ID that al_play_sample returns.

That's pretty simple. Just like Allegro 4.

Reed Teh F'in Manuel said:

al_lock_sample_id
ALLEGRO_SAMPLE_INSTANCE* al_lock_sample_id(ALLEGRO_SAMPLE_ID *spl_id)
Source Code

Locks a ALLEGRO_SAMPLE_ID, returning the underlying ALLEGRO_SAMPLE_INSTANCE. This allows you to adjust the various properties of the instance (such as volume, pan, etc) while the sound is playing.

This function will return NULL if the sound corresponding to the id is no longer playing.

While locked, ALLEGRO_SAMPLE_ID will be unavailable to additional calls to al_play_sample, even if the sound stops while locked. To put the ALLEGRO_SAMPLE_ID back into the pool for reuse, make sure to call al_unlock_sample_id when you're done with the instance.

See also: al_play_sample, al_unlock_sample_id

Now you don't need to create your own sample instances and you can modify them on the fly. Take that, bihatchian

@nshade
The mixers can handle FLOAT32 fine, just not the voice, your code was fine.

The only difference then, is the number of sample instances you load and then attach to a mixer.

You also call al_reserve_samples before creating your voice and master mixer and attaching the two. I think that may be part of it. Try moving al_reserve_samples until after they are both created and attached.

The real question is, how many instances are reserved for a mixer? Does it add them as you go, or do you have to set it somehow using al_reserve_samples? But that would only affect the current mixer? I don't really understand this bit

Audric

I also think that A4 had some very user-friendly shortcuts that I don't see in A5.
For example I would use release_voice()(*) as soon as a game element gets destroyed/de-allocated, and A4 would take care of not cutting any remaining sound it was emitting ("Aaaaarggg-")
I don't see any equivalent in A5, and I think re-implementing it outside allegro is more than a few lines of code :
- Keeping track of 'to be deleted resources' in a collection that allows removal in any order (doubly linked list)
- Periodically check for completed sounds to clean up their resources. It can be implemented by a timer and its own event queue, but host program still has to call the cleanup function a few times per second.

(*) https://www.allegro.cc/manual/4/api/digital-sample-routines/voice-control/release_voice

nshade

The code from my game was pasted almost exactly as it is in the pastebin (With a little sugar to make it compile on it's own)

I have 1 theme song and 20 samples that are played in the game. I just think it's strange as most games have more than just one or two sound effects.

I just realized it's only playing the first two sounds I attached to the mixer. I upped al_reserve_samples to 20 and it's still just playing the first two sounds attached. The code in the pastebin compiles so you can try it for yourself (changing the sample filenames of course)

I'm attaching every sample to an instance and then each instance to the mixer. I just think that was a little redundant if I just want to play 20 sounds. I can imagine a game have several hundred sounds so having to plug in so many things is strange. I was wondering if I was approaching it correctly, or a simpler way of doing it. For example:

play("sound.ogg")

loadsample("sound.ogg",sample); play(sample);

I can see the need for mixers and such, but why load each sample and then create an instance and then attach that instance to a mixer. Why I can't I just load the sample and then just play it, or attach the sample to the mixer directly? I'm only going to have one sound play at a time, so why instantiate it at all?

EDIT

OHMYGOD I FOUND THE BUG!

smpl_hits = al_load_sample("res\hits.ogg"); <--- did not work
smpl_hits = al_load_sample("res/hits.ogg"); <--- did work

I look in the allegro logs and there was no logging the it was failing to load the file... You would think it would at least that. Oh well, my fault for not checking "NULL" on sample load :/

I'm moving this all to ALLEGRO_PATH so I don't have to deal with those stupid backslashes anymore.

Audric

A good compiler would have told you "unknown escape sequence '\h'" :)
(My first compiler was not gcc-based, and I made the same mistake several times)

Niunio

Do you really need 7 channel sound?

I've wrote a PSG for my engine, and the only way I've found to do it easily was using a channel per wave generator. So if I want to emulate (say) MSX sound then I have no problem (3 channels). I can deal with NES sound too (5 channels). But I want to use PC-MIDI sound too (+16 channels) and I have no idea how to do that.

May be it is possible to do it differently (binding more than one stream per channel?) but I haven't found the way by just reading the documentation. May be I didn't understood it correctly. ???

Edgar Reynaldo
nshade said:

OHMYGOD I FOUND THE BUG!

Surely you compiled in debug mode? Why didn't it fail any asserts? Also, if you look at the syntax highlighting on your pastebin code in the mentioned loading section, you can see several escape sequences. Yes, always use forward slashes, or ALLEGRO_NATIVE_PATH_SEP.

Neil Roy

That's pretty simple. Just like Allegro 4.

Yuppers, I did that in Deluxe Pacman 2. Play the game, it's 100% Allegro 5, uses sound etc... just certain things are more involved than they were in Allegro 4. Needlessly more involved in order to add in more features that most people just do not need.

But anyhow, I was able to make my game with it anyhow and I hacked my way around the A5 limitations.

Why comment? Because if this was fixed, and from what I remember from this conversation YEARS ago, it would not be a difficult thing to add in some defaults which would have remedied the problems I experienced. It would probably make Allegro 5 more attractive to those who just want to make a game up quickly without jumping through hoops to do simple things. Now as stated, I found another library that can do it easily, so why bother coming to these forums? Because I prefer Allegro, I have a long history with it. But there is also a long history of rude responses like what you give to anyone who dares to criticize something about it.

I would rather see the library improved and the complexities removed. It would help it gain in popularity again I think. If I had the skill to work on it myself I would. But one look at that source was scary. ;)

Chris Katko
Neil Roy said:

I would rather see the library improved and the complexities removed. It would help it gain in popularity again I think. If I had the skill to work on it myself I would. But one look at that source was scary. ;)

So write it and submit a pull request and we'll compare the two.

Quote:

But one look at that source was scary. ;)

Honestly, it's not so bad. You just keep staring and building notes and any large codebase eventually starts sinking in. Especially ones that weren't clobbered together by non-programmers / super-fast for a past-due job.

Edgar Reynaldo

Basic structure of allegro is like this :

1. There's a system driver, determined at compile time.
2. There's different sub system drivers, again limited to those with support compiled in.

Each driver is basically a vtable, or a set of function pointers. The allegro functions call the system drivers vtable functions, so this allows for dynamic behavior, across platforms, and at run time if desired.

The best way to understand allegro is to use gdb and set a breakpoint on a function your curious about. It's as simple as 'break al_create_voice', then you step through the code with 'next' and 'step' to step into function calls. It tells you where you are as you go, listing source code line numbers. You can 'list' code, and you can 'print $VAR' variables to examine things. When you get something interesting like a system driver function call you can print the pointer to the system driver, and then print the function pointer it refers to to see which function it is actually calling. Then you can set a breakpoint inside that function, run the program again, and repeat until you find out what's wrong.

EDIT

I will say this about the audio API, it sucks that there is no companion function to al_play_sample for sample instances. You have to set all the things manually. al_play_sample_instance does not do the same thing, and doesn't allow you to modify the sound as it's created. I think it should create a new sample instance and set it's parameters and start it playing and return bool on success.

But it only took 5 secs to write one so, ... ;

ALLEGRO_SAMPLE_INSTANCE* Sound::Play(ALLEGRO_MIXER* mixer , float gain , float pan , float speed , ALLEGRO_PLAYMODE mode) {
   ALLEGRO_SAMPLE_INSTANCE* instance = al_create_sample_instance(sample);
   al_attach_sample_instance_to_mixer(instance , mixer);
   al_set_sample_instance_gain(instance , gain);
   al_set_sample_instance_pan(instance , pan);
   al_set_sample_instance_speed(instance , speed);
   al_set_sample_instance_playmode(instance , mode);
   al_set_sample_instance_playing(instance , true);
   instances.push_back(instance);
   return instance;
}

nshade

I don't have any asserts in my code as I'm dealing with 23 year old C code and I'm compiling in VS2015. It's not the first time My compiler silently just let it go. It also tends to gloss over functions with the wrong number of arguments too.

Edgar Reynaldo

See my edit.

As for your compiler, you should still be compiling against the debugging libs so that asserts in allegro will fail. That's what I was talking about.

EDIT
Here's a new demo. Same controls as before but now keys 1 thru 9 play a sound. Press repeatedly to spawn new instances.

Sound.zip (win32 binary + src + data)

Chris Katko
nshade said:

I don't have any asserts in my code as I'm dealing with 23 year old C code

Start adding them!!! Any of your assumptions for how code should work. If it constantly fails, it's either a bug... or... a mistaken assumption!

Polybios

Just want to drop in to emphasize that al_reserve_samples is "the easy audio route" together with al_play_sample. See:
http://liballeg.org/a5docs/trunk/audio.html#al_reserve_samples
http://liballeg.org/a5docs/trunk/audio.html#al_play_sample

The former keeps a set of sample instances attached to the default mixer (attached to the default voice) that it reuses when you call the latter.

I don't think that mixing this with an individual complex setup is a good idea.
IIRC, the way to do it is to manage sample instances yourself and attaching them to a mixer.

I think I've contributed some of the introductory paragraphs (minus the nice illustration) to the manual for the audio addon. If there's something unclear or confusing (in the manual), please report.

Edgar Reynaldo

I was trying to note earlier that you can still modify the sample instances at runtime, even if you use al_play_sample. You get back an ALLEGRO_SAMPLE_ID that you can use with al_lock_sample_id to get back an ALLEGRO_SAMPLE_INSTANCE*.

bamccaig

I don't have much to contribute here, other than to agree with the OP. I have never programmed audio into a program before (unless you count messing with text-to-speech in JavaScript) and I recall a few months ago I was trying to figure out how to generate sound in Allegro 5. I was completely lost and the documentation wasn't very helpful at all. I'm sure it's very useful to somebody that already knows how to do it, but that's kind of useless to the majority of people that are going to be reading it. The documentation for a library with the history of Allegro should be written for the complete beginner that has no idea what any of these things are, and just wants to make sounds come out of his headphones, whether it's from existing recordings or raw.

Neil Roy

In my Deluxe Pacman 2 game, which uses Allegro 5, playing sound effects and music; I use OGGs for both sound effects and music, so the only difference is I flag the sound samples to only be played once, and I flag the music to be looped. It's all pretty simple. My only complaint was that in order to do more, like gradually change the pitch of a looping sound sample, it was much more involved.

As it is, this is basically all you need to do in Allegro 5 to simply play a sound sample, whether once or looped. It IS very simple, for just this. It's just that if you want to do more, you will need to set up a lot more. Too much I felt when you consider that I could do the same in Allegro 4 with the gradual pitch change without any extra effort compared to Allegro 5.

But to be fair, here's all you need to do to play basic sounds and music...
(note: the a5_error() and shut_down() are my own personal functions I created)

#SelectExpand
1// Initialize audio 2if(!al_install_audio()) { 3 a5_error(AT, setting.screen, "al_install_audio() failed."); 4 shut_down(); 5 exit(1); 6} 7if(!al_init_acodec_addon()) { 8 a5_error(AT, setting.screen, "al_init_acodec_addon() failed."); 9 shut_down(); 10 exit(1); 11} 12 13if(!al_reserve_samples(16)) { 14 a5_error(AT, setting.screen, "al_reserve_samples() failed."); 15 shut_down(); 16 exit(1); 17} 18 19// Load in a sound sample 20ALLEGRO_SAMPLE *sfx_pill = NULL; 21sfx_pill = al_load_sample("Sound/Pill.ogg"); 22if(!sfx_pill) { 23 a5_error(AT, setting.screen, "Failed to load Pill.ogg"); 24 shut_down(); 25 exit(1); 26} 27 28// Play sound sample once 29al_play_sample(sfx_pill, setting.sound_volume, 0, 1, ALLEGRO_PLAYMODE_ONCE, NULL); 30 31 32// When done, clean up and shut it all down 33al_stop_samples(); 34 35al_destroy_sample(sfx_pill); 36sfx_pill = NULL; 37 38al_uninstall_audio();

...this was pulled out of my DP2 game.

The pitch does change at regular intervals in my game, but not constantly like DP1. To do this, I regularly check the number of pills left and based on the percentage, I will actually stop the background sample from playing and restart it at a new pitch. This was my work around as I could not change it while it played without doing a lot more, and while I probably could have done it, I felt it was a lot of extra work for JUST changing the pitch.

Now it was pointed out that I could have "simply" used al_set_sample_instance_speed().

But that requires ALLEGRO_SAMPLE_INSTANCE.

To quote the docs:
“To be played, an ALLEGRO_SAMPLE_INSTANCE object must be attached to an ALLEGRO_VOICE object, or to an ALLEGRO_MIXER object which is itself attached to an ALLEGRO_VOICE object (or to another ALLEGRO_MIXER object which is attached to an ALLEGRO_VOICE object, etc).”

When all I wanted to do was change the pitch on one sample, you can see why I didn't want to go through setting all the rest of that up JUST to change the pitch. I opted instead to change it less frequently, basically it changes in DP2 when 75%, 50%, and 25% of the pills are left. As stated, I shut down the sample and restart it with the new pitch setting. Which ironically, is more like how the original arcade game does it.

When searching for some information about this, I found in mildly amusing that I came across this post from Edgar back in 2015 when he had problems with Allegro's audio in t his thread...

https://www.allegro.cc/forums/thread/615723

His final post in that thread was: "Yeah, that's pretty confusing" :) No offence Edgar but even you found all of it confusing when you tried it.

bamccaig

This is the program that I tried.

src/main.c#SelectExpand
1#include <allegro5/allegro.h> 2#include <allegro5/allegro_acodec.h> 3#include <allegro5/allegro_audio.h> 4#include <allegro5/allegro_font.h> 5#include <allegro5/allegro_ttf.h> 6#include <stdio.h> 7#include <stdlib.h> 8 9#define notes_length (sizeof(notes) / sizeof(ALLEGRO_SAMPLE *)) 10 11#define say_center(w, h, text) \ 12 (al_draw_text(musicals, white, w, h, ALLEGRO_ALIGN_CENTRE, text)) 13 14#define play_note(i) \ 15 (al_play_sample( \ 16 notes[i], \ 17 3.0, 0.0, 1.0, \ 18 ALLEGRO_PLAYMODE_ONCE, \ 19 &sample_ids[i])) 20 21static const char abcdefg[] = "abcdefg"; 22 23int main(int argc, char * argv[]) 24{ 25 char tmp[8] = " "; 26 int i = 0, l = -1; 27 int playing[7] = {0}; 28 int status = 0; 29 ALLEGRO_COLOR black, white; 30 ALLEGRO_DISPLAY * display = NULL; 31 ALLEGRO_EVENT_QUEUE * event_queue = NULL; 32 ALLEGRO_FONT * musicals = NULL; 33 ALLEGRO_SAMPLE * notes[7] = {0}; 34 ALLEGRO_SAMPLE_ID sample_ids[7] = {0}; 35 ALLEGRO_TIMEOUT timeout; 36 37 if (!al_init()) { 38 fprintf(stderr, "al_init failed\n"); 39 goto error; 40 } 41 42 if (!al_install_keyboard()) { 43 fprintf(stderr, "al_install_keyboard failed\n"); 44 goto error; 45 } 46 47 if (!al_install_audio()) { 48 fprintf(stderr, "al_install_audio failed\n"); 49 goto error; 50 } 51 52 if (!al_init_acodec_addon()) { 53 fprintf(stderr, "al_init_acodec_addon failed\n"); 54 goto error; 55 } 56 57 if (!al_init_font_addon()) { 58 fprintf(stderr, "al_init_font_addon failed\n"); 59 goto error; 60 } 61 62 if (!al_init_ttf_addon()) { 63 fprintf(stderr, "al_init_ttf_addon failed\n"); 64 goto error; 65 } 66 67 display = al_create_display(1440, 900); 68 69 if (display == NULL) { 70 fprintf(stderr, "al_create_display failed\n"); 71 goto error; 72 } 73 74 event_queue = al_create_event_queue(); 75 76 if (event_queue == NULL) { 77 fprintf(stderr, "al_create_event_queue failed\n"); 78 goto error; 79 } 80 81 if (!al_reserve_samples(7 * 7)) { 82 fprintf(stderr, "al_reserve_samples failed\n"); 83 goto error; 84 } 85 86 for (i=0,l=notes_length; i<l; i++) { 87 char filename[22] = "media/Piano.pp.A3.ogg"; 88 89 filename[15] = 'A' + i; 90 91 if (filename[15] > 'B') { 92 filename[16] = '4'; 93 } 94 95 notes[i] = al_load_sample(filename); 96 97 if (notes[i] == NULL) { 98 fprintf(stderr, "al_load_sample %s failed\n", filename); 99 goto error; 100 } 101 } 102 103 musicals = al_load_ttf_font("musicals.ttf", 72, 0); 104 105 if (musicals == NULL) { 106 fprintf(stderr, "al_load_ttf_font failed\n"); 107 goto error; 108 } 109 110 al_register_event_source(event_queue, 111 al_get_display_event_source(display)); 112 113 al_register_event_source(event_queue, 114 al_get_keyboard_event_source()); 115 116 black = al_map_rgb(0, 0, 0); 117 white = al_map_rgb(255, 255, 255); 118 119 al_init_timeout(&timeout, 0.06); 120 121 while (1) { 122 int w = al_get_display_width(display); 123 int h = al_get_display_height(display); 124 ALLEGRO_EVENT ev; 125 126 if (al_wait_for_event_until(event_queue, &ev, &timeout)) { 127 switch (ev.type) { 128 case ALLEGRO_EVENT_DISPLAY_CLOSE: 129 goto clean; 130 break; 131 case ALLEGRO_EVENT_KEY_DOWN: 132 if (ev.keyboard.keycode >= ALLEGRO_KEY_A && 133 ev.keyboard.keycode <= ALLEGRO_KEY_G) { 134 i = ev.keyboard.keycode - ALLEGRO_KEY_A; 135 tmp[i] = abcdefg[i]; 136 137 if (playing[i] == 0) { 138 playing[i] = play_note(i); 139 } 140 } else { 141 switch (ev.keyboard.keycode) { 142 case ALLEGRO_KEY_ESCAPE: 143 case ALLEGRO_KEY_Q: 144 goto clean; 145 } 146 } 147 148 break; 149 case ALLEGRO_EVENT_KEY_UP: 150 if (ev.keyboard.keycode >= ALLEGRO_KEY_A && 151 ev.keyboard.keycode <= ALLEGRO_KEY_G) { 152 i = ev.keyboard.keycode - ALLEGRO_KEY_A; 153 tmp[i] = ' '; 154 155 if (playing[i] != 0) { 156 al_stop_sample(&sample_ids[i]); 157 playing[i] = 0; 158 } 159 } 160 break; 161 } 162 } 163 164 al_clear_to_color(black); 165 166 say_center(w / 2.0, h / 2.0, "abcdefg ABCDEFG"); 167 say_center(w / 2.0, h / 2.0 + 100, tmp); 168 169 al_flip_display(); 170 } 171 172clean: 173 for (i=0,l=notes_length; i<l; i++) { 174 if (notes[i]) al_destroy_sample(notes[i]); 175 } 176 if (musicals) al_destroy_font(musicals); 177 if (event_queue) al_destroy_event_queue(event_queue); 178 if (display) al_destroy_display(display); 179 180 return status; 181 182error: 183 status = 1; 184 goto clean; 185}

I don't remember where I got the sound files from, but Google suggested here. That seems likely. At the very least, it'll probably suffice. That appears to be MIT licensed.

The font appears to be available from here (but you can probably substitute any other font). It should be free for personal use.

Anyway, the initial intent was a sort of QWERTY piano. This was the first experiment. What I found is that typing doesn't produce consistent sounds. It seems very limited in what it can do and I couldn't figure out why. Can you spot my errors?

Neil Roy

I found my old post from 2013 where I tried to figure this out. Read Matthew's reply and you will see why I didn't bother. When you wish to loop your sound, or adjust it, you have to manage every aspect of it playing yourself, even loops which is nuts.

It's been five years, but after reading his reply to me back then, it all came back to me. It was more than simply adjusting the pitch, it was also looping a sound. If you want to play a sample more than once, you have to manage that on your own which seems to me to be a tad much. And back then this was not documented and I still don't think it is from what I have seen.

https://www.allegro.cc/forums/thread/613153

bamccaig said:

Can you spot my errors?

This is all you need to play a sample: al_play_sample(sample, volume, 0, 1, ALLEGRO_PLAYMODE_ONCE, NULL);

I noticed you store a sample id, but if you're only playing it once, there's no need, just use NULL as I have above. Unless there is something you wish to track about it (like in my game I used the id so I could stop the sample later on and change the pitch then restart it, otherwise I didn't need an id). Your code looks fine honestly. I reserve 16 samples in my own code and that works, not sure why you have 7*7? I don't know what the maximums are, but if that passes okay...

I honestly don't know what your problem could be. :/

bamccaig

It actually sounds like your thread from 5 years ago is needed for my purpose. From the sounds of it, you cannot replay a sample until it stops unless you instantiate many sample "instances" off of it and play those instead... But to do that I have to define a voice and a mixer myself? I have a confession to make. I have no idea what a voice or a mixer is. ;) The docs don't help me with that problem. Here's the thing: TFM doesn't seem to say anything about audio unless I'm bypassing the wordy bit. It's just a list of symbols. That's useless to everybody that didn't write the addon and has never used it before.

From the sounds of it a "voice" is supposed to represent the hardware or a virtual device provided by the OS, and a "mixer" is a mechanism to combine several buffers into a single buffer. This is starting to almost make sense, no thanks to TFM, but I haven't actually attempted to translate this so called knowledge into code yet. That's usually the hard part.

Append:

I get lost on the "frequency" hint that you're supposed to pass to a voice/mixer. I'm not a sound engineer. I can attempt to lookup frequencies for the various notes I intend my program to support, but why does the "device" or "buffer combiner" need or want to know about what frequencies I'm going to play? Is that for surround sound setups or what? Supposing the user did have a 7.1 surround system does that mean my program needs to be different for them than for me using just headphones? This is the complexity I think we're talking about. I shouldn't need to think about the hardware here. I don't think I should need to think about what sounds I'll be playing. I should just be able to play sounds, goddamnit.

Audric

This frequency indicates the sound quality that this mixer will operate. 44100 will make this mixer output 44100 values per second, the same as a CD.

Edgar Reynaldo

It really isn't hard to get your sound to pan and change pitch and all that. All you need is an ALLEGRO_SAMPLE_INSTANCE*. I showed you a function to easily create a sound and attach it to a mixer earlier :

ALLEGRO_SAMPLE_INSTANCE* PlaySample(ALLEGRO_SAMPLE* sample , ALLEGRO_MIXER* mixer , float gain , float pan , float speed , ALLEGRO_PLAYMODE mode) {
   ALLEGRO_SAMPLE_INSTANCE* instance = al_create_sample_instance(sample);
   al_attach_sample_instance_to_mixer(instance , mixer);
   al_set_sample_instance_gain(instance , gain);
   al_set_sample_instance_pan(instance , pan);
   al_set_sample_instance_speed(instance , speed);
   al_set_sample_instance_playmode(instance , mode);
   al_set_sample_instance_playing(instance , true);
   return instance;
}

I made a simple example as well that demonstrates changing the pan and speed of the sound using the mouse here :

Mixer2.zip (win32 src + binary + data)

As for bambams, al_reserve_samples takes the maximum number of samples that you want to play at a time. You'd probably be best off with 4, or 8 if you don't want to manage sample instances yourself. Using a large number here makes your sounds quieter since they all have to be mixed together.

Also, as I noted earlier, you can get an ALLEGRO_SAMPLE_INSTANCE* from an ALLEGRO_SAMPLE_ID, and adjust playing sounds.

Neil Roy

I made a simple example as well that demonstrates changing the pan and speed of the sound using the mouse here

I'll take a look at that.

Edgar Reynaldo

bambams, look at liballeg.org for the latest Allegro manual

bamccaig

Thanks, Edgar, the documentation is much better on liballeg.org. :)

Niunio
bamccaig said:

I don't have much to contribute here, other than to agree with the OP. I have never programmed audio into a program before (unless you count messing with text-to-speech in JavaScript) and I recall a few months ago I was trying to figure out how to generate sound in Allegro 5. I was completely lost and the documentation wasn't very helpful at all. I'm sure it's very useful to somebody that already knows how to do it, but that's kind of useless to the majority of people that are going to be reading it. The documentation for a library with the history of Allegro should be written for the complete beginner that has no idea what any of these things are, and just wants to make sounds come out of his headphones, whether it's from existing recordings or raw.

Thank you! :-*

I hope somebody updated the documentation and adds all this seemingly* useful information of yours.
______________________

* I didn't test it yet and not sure I've understood it.

Edgar Reynaldo

He was also reading documentation from over 3 years ago. :P

Niunio said:

I hope somebody updated the documentation and adds all this seemingly* useful information of yours.

This is why nothing ever gets done. I hope somebody updated the docs for me...

If there are changes you want to see made to the documentation, post them! If you have questions, ask them! I am more than glad to make the corresponding edits for you, and submit a pull request, but you have to give me content!!!

So this doesn't degenerate into a "it's too complicated" "no, it's not" again, for all of those who find the manual or the audio system in allegro to be too complicated, post specific concerns, and I will address them one by one. You have to be specific though.

I will start.

1. I don't understand how sample instances are 'reserved'. Sure, al_reserve_samples will generate empty sample instances and attach them to the default mixer, but what about mixers that you create yourself? How are the number of sample instances 'attached' to a mixer? Does the mixer scale the sound automatically based on how many sample instances are attached? Or do we need to manage that ourselves? Is there something akin to al_reserve_samples that we need to do for custom mixers?

Audric

I'm a bit surprised that the recommended setup is a single voice, because if I understand correctly, it means the mixing is made in software. But soundcards have dozens of channels dedicated to hardware mixing (151 on the venerable Sounblaster Live) ?
I understand that on a multitasking system I can't count on grabbing all hardware resources, but 8 or 16 sound channels on a "main task" videogame doesn't sound overkill.

Edgar Reynaldo

I'm not a sound guru so I don't really know how these things work. It definitely seems to me that you should be able to create multiple voices, or install sound across multiple devices, but I have no idea how hard that is. The windows audio interface is not the best anymore. I used to be able to record sound directly from hardware, so I could record a song playing live via the radio and a double ended stereo plug. I don't know how to do that anymore. I used it to great effect to dub some of my old cassette tapes onto the HDD.

Elias

Sure, al_reserve_samples will generate empty sample instances and attach them to the default mixer, but what about mixers that you create yourself?

There is basically two sound APIs, the al_reserve_samples/al_play_sample one and the complicated one. In the former one you use al_install_audio(), al_reserve_samples(), al_load_sample() and al_play_sample() for playing samples. You cannot use anything else. You would especially not use any mixer or voice functions at all. As simple as it gets really. al_play_sample() allows you to set volume, pan and speed for each sample so there even is some limited control.

Most games also need to play music, so to do that in addition to the above you would use al_create_mixer(), al_load_audio_stream(), al_attach_audio_stream_to_mixer() and al_attach_mixer_to_mixer. This will give you music which can play in parallel to your samples above and with a separate volume control for just the music.

For anything else, like using samples on a mixer other than the default mixer, you would not use al_reserve_samples() or al_play_sample() at all. Those two functions are unnecessary and are basically a separate audio addon which was added much later because the normal audio is way too complicated :P

Edgar Reynaldo

Okay. What if I wanted to use both? Can't I use the default voice, default mixer, and then al_reserve_samples(2) and then attach two mixers to the default mixer, one for music, and one for sound?

bamccaig

What I want to try to figure out is a dumb way to automatically wire this up as the user adds more samples to the system. Basically, create a simple API over the complicated API... I'm curious if something like that is easy to accomplish, or would be a brainfuck to get working. We could call it, allegro_audio_unfucked.h. :P

Elias

You don't have to worry about the user adding more samples. This is exactly why someone already added al_reserve_samples and al_play_sample - it takes care of everything else for you. The parameter to al_reserve_samples really doesn't matter all that much, just set it to 100 or something. If you play more than 100 sounds at the same time then you're doing something else wrong :P

Edgar Reynaldo

I thought the number you passed to al_reserve_samples divided the volume of each attached sample instance?

bamccaig

Basically what I'm trying to build is a sort of virtual "piano". The user can spam a key that corresponds to a note and it should sound similar to if they did the same with a piano key. If I just reserve samples and play the sample each time the user presses the key (and stop the samples when the user releases the key) then the sounds lag and then stop all together. Presumably, because the lower-level system has already allocated all of those "instances" and I cannot play more. Another thing that I'm envisioning is if somebody attempted to develop a music mixing program with Allegro, where the user can select any number of sound files from their computer or possibly generate them using the program, and then freely play them back any number of times in any order, sequence, at any time. I'm trying to envision a library that can do that simply without the programmer having to think too hard. :P

roger levy

I gave up on Allegro 5 audio in favor of FMOD despite the license restrictions.

I sweated for days trying to figure it out, but could not get it to behave in a predictable way. The amount of setup, guesswork, and gotchas is unreasonable.

This is not a war between the past and the future, there is no reason why there can't be an easy layer on top that is as useful as the old one. The current "easy" API doesn't let you modify channel properties after they have started playing. I couldn't even figure out the best way to detect that anything has stopped playing in order to free up channels in my own implementation, which is in my opinion suboptimal that we should have to do that in the first place. Unless a lot has changed in the past few months, to me, the API is far from usable.

With FMOD on the other hand I was up and running within an hour and am not wanting for functionality. What's Allegro's excuse?

Edgar Reynaldo

@bambams
It would be easier to use a sample instance. Then you can adjust it's looping and playing much easier.

There should be one sample for each note, as well as one sample instance. When the key is down, set the sample instance playing to true and false when up. Keep the sound on a loop so it doesn't stop. Then each sample instance will mix with the others currently playing. It should come out like a piano.

You will probably run into hardware keyboard limitations though.

EDIT
@roger levy

The current "easy" API doesn't let you modify channel properties after they have started playing.

That's not true. al_play_sample gives you back an ALLEGRO_SAMPLE_ID, which you can al_lock_sample_id to get an ALLEGRO_SAMPLE_INSTANCE*, which you can then adjust to your hearts content.

roger levy said:

I couldn't even figure out the best way to detect that anything has stopped playing

al_get_sample_instance_playing does exactly what you want.

Elias
bamccaig said:

I'm trying to envision a library that can do that simply without the programmer having to think too hard.

Well, if al_reserve_samples(100) is not enough try al_reserve_samples(200). A piano only has 88 keys - and if you press the same key again it will interrupt the previous sound it made.

Edgar Reynaldo

elias - I think you're mistaken about this but my tests don't seem to disagree.

I tried out using a default mixer with 1, 10, and 100 samples reserved. Nothing seems to change, despite playing multiple sounds repeatedly. Some distortion is noticeable if you play too many instances, but the volume per sample seemed to stay constant.

This is actually easier than I thought it was. I thought I would have to manage the per sample volume, but apparently not so.

EDIT
Nevermind, the number of samples passed to al_reserve_samples is a hard maximum. I was attaching sample instances directly to the default mixer, which works without limitation. Why does the default mixer have a maximum number of sounds anyway? If it's a regular mixer, then attaching sample instances to it shouldn't matter.

bamccaig
Elias said:

Well, if al_reserve_samples(100) is not enough try al_reserve_samples(200). A piano only has 88 keys - and if you press the same key again it will interrupt the previous sound it made.

I posted the program above I think. I'm reserving 49 (7 * 7) samples, and the program only supports 7 notes at the moment. When a key down event is seen I begin playing the sounds, and when a key up event is seen I stop the sample using the ID I was given. Can you see a reason why that program won't work? If I can avoid having to setup mixers and instances and such myself that would be great, but from the sounds of it, I need to dig into this lower-level if I want sounds to sound predictable/as instructed.

Edgar Reynaldo

I'll try out your program a little later, if I don't fall asleep first. What you're doing sounds reasonable enough.

Izual

After reading this thread i just had to try the allegro 5 "complex" audio addon that everyone is talking about here.
I played with it for about an hour or two ( including recompiling allegro to enable audio and browsing the web for samples ) and found out that it really is not "complex".
Just a little bit more involved on the side of initial setup. But it is a very small price to pay for the amount of control you will gain over your samples.
I had no issue playing samples in any way i liked, modify them on the fly, or play whatever amount of them i wanted.
But when i was playing too many samples i had to use ALLEGRO_AUDIO_DEPTH_FLOAT32 for voice and mixers because with the ALLEGRO_AUDIO_DEPTH_INT16 the sound was getting a bit distorted.

I find the audio addon very well made, and really easy to use.

Edgar Reynaldo

This ^. Thank you Izual. That's what I've been trying to say.

Neil Roy
Izual said:

I find the audio addon very well made, and really easy to use.

Yeah, next time try figuring it out without the help of a thread like this. I got no easy help like this years ago when I asked the same questions. And the official documentation has STILL not been updated.

Even Edgar got confused about it all a few years back if you search. So don't give me this bullshit.

Edgar Reynaldo

The passage you're quoting me from as saying "it's confusing" was referring to the documentation at the time. That was years ago. It's improved by leaps and bounds.

Like I said, if you have specific recommendations to make things easier, speak up!

roger levy

@Edgar

I'll admit I haven't used the API in at least a year. I threw up my hands the last time I tried to use it because it's so complex, but also because previously it was buggy and I needed to do a lot of workarounds.

The al_get_sample_instance_playing doesn't distinguish if the sample has stopped playing because it is finished or if you paused playback. Unless that has changed. Also IIRC it didn't work for one of the playable sound types and I had to code a workaround. This was years ago though. An event or callback would be better, then you are not constantly having to poll every sound.

I am glad you can now change properties of playing sounds. I will have to take another look. It'll be preferable to not need FMOD. But I still think Allegro 5 is too fussy and a simpler API similar to FMOD would be nice. Not everyone has the brain capacity and time to deal with a more powerful, more complicated API.

Last time I brought up that old shortcoming I was told that al_play_sample wasn't meant for advanced use and I needed to use the more lowlevel functions so I assumed it wasn't going to change any time soon.

(I had to implement my own dynamic channel system for The Lady, but if it's not needed anymore then al_get_sample_instance_playing is moot now.)

I just want to point out that defending things with your opinion, or "taking sides" when it comes to tools isn't helpful. Everybody is different, so a little more listening and a little less rhetoric would be good for everyone.

Edgar Reynaldo

Hey roger,

I get what you're saying. At least you have specific concerns and not just general complaints. That is much easier to work with.

The al_get_sample_instance_playing doesn't distinguish if the sample has stopped playing because it is finished or if you paused playback. Unless that has changed. Also IIRC it didn't work for one of the playable sound types and I had to code a workaround. This was years ago though. An event or callback would be better, then you are not constantly having to poll every sound.

Yes. The audio API needs to be hooked up to the event system. However, that would mean you would need to register an ALLEGRO_EVENT_SOURCE attached to a sample instance. This might take some work, and I don't know myself how to do it straight away.

roger levy said:

I just want to point out that defending things with your opinion, or "taking sides" when it comes to tools isn't helpful. Everybody is different, so a little more listening and a little less rhetoric would be good for everyone.

Maybe so, but at the same time when people just repeat "it's too complicated" over and over without anything constructive to add, it gets tiresome. I'm listening for sure, but unless people make less vague suggestions as to what to do to improve the API, it's kind of pointless.

roger levy

Right, people need to recognize that this is a free open-source project, so actually proposing ideas for solutions is way more constructive than just complaining.

My suggestion is pretty straightforward - provide a layer as easy as FMOD! :)

bamccaig

"The audio API is too complicated," is constructive.

"Fuck this shit library," for example, would be unconstructive.

{"name":"f9e.gif","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/b\/fb04f0c4bec3923359d4e1bb014af4e5.gif","w":305,"h":185,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/b\/fb04f0c4bec3923359d4e1bb014af4e5"}f9e.gif

Neil Roy

I will give credit where credit is due. Edgar's example he shared in here was really excellent and helped a lot. So koodos for that.

Edgar Reynaldo

Thanks Neil. I try to be helpful when I can.

bamccaig said:

"The audio API is too complicated," is constructive.

That statement is vague and useless without qualifying it.

Take a second and think about the API. Then make a concrete suggestion as to what could be improved. Saying "it could be simpler" is also vague and pointless.

If you want things to change, then you need to go deeper.

Is the documentation for a certain function lacking? What does it lack?

Do we need more functions? Or just different ones? Without specifics, there's really no point discussing it, as nothing will ever change.

What is it that is complicated? Sample instances? They seem straight forward enough for me. You have to create one from a sample, and then you have to attach it to a mixer. Okay, that's easy enough.

If all you want to do is play sound, then al_play_sample does the trick. If you want to change sound characteristics during playback, then you need a sample instance, and not a sample, because that's just static data. The instance is what tells the mixer how to play it. Keeping samples and instances separate is the way to go, as you can share the same data between several instances. This allows you to play multiple simultaneous occurrences of the same audio file, instead of loading several copies of the same data. Instances make sense. To me at least. ;)

@bamccaig
I'm trying out your program now.

EDIT
One thing I see is that you call al_play_sample with a gain of 3.0. This is bound to cause you problems. Normal gain is 1.0, which is full volume. You were also using the 'pp' media piano sounds, which means 'pianissimo', which is soft. I used the 'ff' which is forte, which is loud. Another problem is the delayed attack on the piano notes, so it doesn't coincide with the key press. You also can't press more than two or three keys at once without KEY_DOWN events getting lost. These are the main issues I see with your piano program.

I got your program to work though, with some slight modifications. Attached is Piano.zip (win32 binary and src and data)

To make a better piano program, you need better samples. Ones that start right away, and loop cleanly. The hardware keyboard limitation is gonna get you though.

bamccaig

If you want to change sound characteristics during playback, then you need a sample instance, and not a sample, because that's just static data. The instance is what tells the mixer how to play it. Keeping samples and instances separate is the way to go, as you can share the same data between several instances. This allows you to play multiple simultaneous occurrences of the same audio file, instead of loading several copies of the same data. Instances make sense. To me at least. ;)

This is a good explanation, but I think that the instances are really implementation details. Instead, al_play_sample() could accept a pointer to an ALLEGRO_SAMPLE_PARAMETERS or something like that which can override the play settings. Under the hood, Allegro may wish to track it in an instance (needs to copy it anyway so the user can free it), but the user will just get an ID back that it can use to further manipulate playback. The ID can be used to pause or possibly readjust parameters if that's possible while an instance is already playing (e.g., al_tune_playback(id, &settings) or al_stop_playback(id)).

All the user has is an ALLEGRO_SAMPLE for the data. If necessary, they can allocate a temporarily ALLEGRO_SAMPLE_PARAMETERS on the stack to tune the playback, and if they want to be able to control playback after it has started then they will want to store a simple integer ID too.

Allegro should do whatever it needs to under the hood to make this work: play the sound as instructed, and do its best to make it sound as the caller would expect. If this means it needs to wire up mixers and voices and whatnot then it should do it on the fly, but as an implementation detail the user doesn't have to care about. If it's resource intensive and might be too slow to do while the game is running, then Allegro should try to allocate a complex enough hierarchy to do anything that could be asked of it when the addon is initialized (hopefully just a voice and a couple of mixers or something).

Even the user facing streams could probably be eliminated and just make samples themselves streamable. If a sample is streamed then the data would not be loaded fully when the file is opened, and it would transparently act like a stream instead when played. If file-based then Allegro will load up chunks of the file when needed. Or if the user is generating the stream data on the fly then it'll just respond to an event the same way, but be filling in a sample id instead something like al_stream_playback(id, buffer, start, count) (instead of the current API of getting a buffer and then setting it again, which doesn't make much sense).

Append:

I finally took a stab at rewriting my program to use the lower-level API.

#SelectExpand
1#include <allegro5/allegro.h> 2#include <allegro5/allegro_acodec.h> 3#include <allegro5/allegro_audio.h> 4#include <allegro5/allegro_font.h> 5#include <allegro5/allegro_ttf.h> 6#include <stdio.h> 7#include <stdlib.h> 8 9const int AUDIO_FREQUENCY = 44100; 10const ALLEGRO_AUDIO_DEPTH AUDIO_DEPTH = ALLEGRO_AUDIO_DEPTH_FLOAT32; 11const ALLEGRO_CHANNEL_CONF CHANNEL_CONF = ALLEGRO_CHANNEL_CONF_2; 12const int NUM_INSTANCES = 10000; 13 14#define notes_length (sizeof(notes) / sizeof(ALLEGRO_SAMPLE *)) 15 16#define say_center(w, h, text) \ 17 (al_draw_text(musicals, white, w, h, ALLEGRO_ALIGN_CENTRE, text)) 18 19static const char abcdefg[] = "abcdefg"; 20 21int get_available_instance(int, ALLEGRO_SAMPLE_INSTANCE * [7][10000]); 22bool play_note(int, ALLEGRO_SAMPLE_INSTANCE * [7][10000]); 23 24int main(int argc, char * argv[]) 25{ 26 char tmp[8] = " "; 27 int i = 0, il = -1, j = 0, jl = -1; 28 int status = 0; 29 ALLEGRO_COLOR black, white; 30 ALLEGRO_DISPLAY * display = NULL; 31 ALLEGRO_EVENT_QUEUE * event_queue = NULL; 32 ALLEGRO_FONT * musicals = NULL; 33 ALLEGRO_MIXER * mixer = NULL; 34 ALLEGRO_SAMPLE * notes[7] = {0}; 35 ALLEGRO_SAMPLE_INSTANCE * instances[7][10000] = {0}; 36 ALLEGRO_TIMEOUT timeout; 37 ALLEGRO_VOICE * voice = NULL; 38 39 if (!al_init()) { 40 fprintf(stderr, "al_init failed\n"); 41 goto error; 42 } 43 44 if (!al_install_keyboard()) { 45 fprintf(stderr, "al_install_keyboard failed\n"); 46 goto error; 47 } 48 49 if (!al_install_audio()) { 50 fprintf(stderr, "al_install_audio failed\n"); 51 goto error; 52 } 53 54 if (!al_init_acodec_addon()) { 55 fprintf(stderr, "al_init_acodec_addon failed\n"); 56 goto error; 57 } 58 59 if (!al_init_font_addon()) { 60 fprintf(stderr, "al_init_font_addon failed\n"); 61 goto error; 62 } 63 64 if (!al_init_ttf_addon()) { 65 fprintf(stderr, "al_init_ttf_addon failed\n"); 66 goto error; 67 } 68 69 display = al_create_display(1440, 900); 70 71 if (display == NULL) { 72 fprintf(stderr, "al_create_display failed\n"); 73 goto error; 74 } 75 76 event_queue = al_create_event_queue(); 77 78 if (event_queue == NULL) { 79 fprintf(stderr, "al_create_event_queue failed\n"); 80 goto error; 81 } 82 83 voice = al_create_voice( 84 AUDIO_FREQUENCY, 85 AUDIO_DEPTH, 86 CHANNEL_CONF); 87 88 if (voice == NULL) { 89 fprintf(stderr, "al_create_voice failed\n"); 90 goto error; 91 } 92 93 mixer = al_create_mixer( 94 AUDIO_FREQUENCY, 95 AUDIO_DEPTH, 96 CHANNEL_CONF); 97 98 if (mixer == NULL) { 99 fprintf(stderr, "al_create_mixer failed\n"); 100 goto error; 101 } 102 103 if (!al_attach_mixer_to_voice(mixer, voice)) { 104 fprintf(stderr, "al_attach_mixer_to_voice failed\n"); 105 goto error; 106 } 107 108 for (i=0,il=notes_length; i<il; i++) { 109 char filename[22] = "media/Piano.pp.A3.ogg"; 110 111 filename[15] = 'A' + i; 112 113 if (filename[15] > 'B') { 114 filename[16] = '4'; 115 } 116 117 notes[i] = al_load_sample(filename); 118 119 if (notes[i] == NULL) { 120 fprintf(stderr, "al_load_sample %s failed\n", filename); 121 goto error; 122 } else { 123 for(j=0,jl=NUM_INSTANCES; j<jl; j++) { 124 instances[i][j] = al_create_sample_instance(notes[i]); 125 126 if (instances[i][j] == NULL) { 127 fprintf(stderr, 128 "al_create_sample_instance %s[%d] failed\n", 129 filename, j); 130 goto error; 131 } 132 133 if (!al_attach_sample_instance_to_mixer(instances[i][j], mixer)) { 134 fprintf(stderr, 135 "al_attach_sample_instance_to_mixer %s[%d] failed\n", 136 filename, j); 137 goto error; 138 } 139 } 140 } 141 } 142 143 musicals = al_load_ttf_font("musicals.ttf", 72, 0); 144 145 if (musicals == NULL) { 146 fprintf(stderr, "al_load_ttf_font failed\n"); 147 goto error; 148 } 149 150 al_register_event_source(event_queue, 151 al_get_display_event_source(display)); 152 153 al_register_event_source(event_queue, 154 al_get_keyboard_event_source()); 155 156 black = al_map_rgb(0, 0, 0); 157 white = al_map_rgb(255, 255, 255); 158 159 al_init_timeout(&timeout, 0.06); 160 161 while (1) { 162 int w = al_get_display_width(display); 163 int h = al_get_display_height(display); 164 ALLEGRO_EVENT ev; 165 166 if (al_wait_for_event_until(event_queue, &ev, &timeout)) { 167 switch (ev.type) { 168 case ALLEGRO_EVENT_DISPLAY_CLOSE: 169 goto clean; 170 break; 171 case ALLEGRO_EVENT_KEY_DOWN: 172 if (ev.keyboard.keycode >= ALLEGRO_KEY_A && 173 ev.keyboard.keycode <= ALLEGRO_KEY_G) { 174 i = ev.keyboard.keycode - ALLEGRO_KEY_A; 175 tmp[i] = abcdefg[i]; 176 177 play_note(i, instances); 178 } else { 179 switch (ev.keyboard.keycode) { 180 case ALLEGRO_KEY_ESCAPE: 181 case ALLEGRO_KEY_Q: 182 goto clean; 183 } 184 } 185 186 break; 187 case ALLEGRO_EVENT_KEY_UP: 188 if (ev.keyboard.keycode >= ALLEGRO_KEY_A && 189 ev.keyboard.keycode <= ALLEGRO_KEY_G) { 190 i = ev.keyboard.keycode - ALLEGRO_KEY_A; 191 tmp[i] = ' '; 192 } 193 break; 194 } 195 } 196 197 al_clear_to_color(black); 198 199 say_center(w / 2.0, h / 2.0, "abcdefg ABCDEFG"); 200 say_center(w / 2.0, h / 2.0 + 100, tmp); 201 202 al_flip_display(); 203 } 204 205clean: 206 for (i=0,il=notes_length; i<il; i++) { 207 if (notes[i]) { 208 al_destroy_sample(notes[i]); 209 210 for (j=0,jl=NUM_INSTANCES; j<jl; j++) { 211 if (instances[i][j]) { 212 al_destroy_sample_instance(instances[i][j]); 213 } 214 } 215 } 216 } 217 if (musicals) al_destroy_font(musicals); 218 if (mixer) al_destroy_mixer(mixer); 219 if (voice) al_destroy_voice(voice); 220 if (event_queue) al_destroy_event_queue(event_queue); 221 if (display) al_destroy_display(display); 222 223 return status; 224 225error: 226 status = 1; 227 goto clean; 228} 229 230int get_available_instance( 231 int i, 232 ALLEGRO_SAMPLE_INSTANCE * instances[7][10000]) { 233 int j, l; 234 235 for (j=0, l=NUM_INSTANCES; j<l; j++) { 236 if (!al_get_sample_instance_playing(instances[i][j])) { 237 return j; 238 } 239 } 240 241 return -1; 242} 243 244bool play_note( 245 int i, 246 ALLEGRO_SAMPLE_INSTANCE * instances[7][10000]) { 247 int j = get_available_instance(i, instances); 248 249 if (j == -1) { 250 return false; 251 } 252 253 return al_set_sample_instance_playing( 254 instances[i][j], 255 true); 256}

diff --git a/src/main.c b/src/main.c
index ec60a45..e349017 100644
--- a/src/main.c
+++ b/src/main.c
@@ -6,33 +6,35 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+const int AUDIO_FREQUENCY = 44100;
+const ALLEGRO_AUDIO_DEPTH AUDIO_DEPTH = ALLEGRO_AUDIO_DEPTH_FLOAT32;
+const ALLEGRO_CHANNEL_CONF CHANNEL_CONF = ALLEGRO_CHANNEL_CONF_2;
+const int NUM_INSTANCES = 10000;
+
 #define notes_length (sizeof(notes) / sizeof(ALLEGRO_SAMPLE *))
 
 #define say_center(w, h, text) \
         (al_draw_text(musicals, white, w, h, ALLEGRO_ALIGN_CENTRE, text))
 
-#define play_note(i) \
-        (al_play_sample( \
-                 notes[i], \
-                 3.0, 0.0, 1.0, \
-                 ALLEGRO_PLAYMODE_ONCE, \
-                 &sample_ids[i]))
-
 static const char abcdefg[] = "abcdefg";
 
+int get_available_instance(int, ALLEGRO_SAMPLE_INSTANCE * [7][10000]);
+bool play_note(int, ALLEGRO_SAMPLE_INSTANCE * [7][10000]);
+
 int main(int argc, char * argv[])
 {
     char tmp[8] = "       ";
-    int i = 0, l = -1;
-    int playing[7] = {0};
+    int i = 0, il = -1, j = 0, jl = -1;
     int status = 0;
     ALLEGRO_COLOR black, white;
     ALLEGRO_DISPLAY * display = NULL;
     ALLEGRO_EVENT_QUEUE * event_queue = NULL;
     ALLEGRO_FONT * musicals = NULL;
+    ALLEGRO_MIXER * mixer = NULL;
     ALLEGRO_SAMPLE * notes[7] = {0};
-    ALLEGRO_SAMPLE_ID sample_ids[7] = {0};
+    ALLEGRO_SAMPLE_INSTANCE * instances[7][10000] = {0};
     ALLEGRO_TIMEOUT timeout;
+    ALLEGRO_VOICE * voice = NULL;
 
     if (!al_init()) {
         fprintf(stderr, "al_init failed\n");
@@ -78,12 +80,32 @@ int main(int argc, char * argv[])
         goto error;
     }
 
-    if (!al_reserve_samples(7 * 7)) {
-        fprintf(stderr, "al_reserve_samples failed\n");
+    voice = al_create_voice(
+            AUDIO_FREQUENCY,
+            AUDIO_DEPTH,
+            CHANNEL_CONF);
+
+    if (voice == NULL) {
+        fprintf(stderr, "al_create_voice failed\n");
+        goto error;
+    }
+
+    mixer = al_create_mixer(
+            AUDIO_FREQUENCY,
+            AUDIO_DEPTH,
+            CHANNEL_CONF);
+
+    if (mixer == NULL) {
+        fprintf(stderr, "al_create_mixer failed\n");
+        goto error;
+    }
+
+    if (!al_attach_mixer_to_voice(mixer, voice)) {
+        fprintf(stderr, "al_attach_mixer_to_voice failed\n");
         goto error;
     }
 
-    for (i=0,l=notes_length; i<l; i++) {
+    for (i=0,il=notes_length; i<il; i++) {
         char filename[22] = "media/Piano.pp.A3.ogg";
 
         filename[15] = 'A' + i;
@@ -97,6 +119,24 @@ int main(int argc, char * argv[])
         if (notes[i] == NULL) {
             fprintf(stderr, "al_load_sample %s failed\n", filename);
             goto error;
+        } else {
+            for(j=0,jl=NUM_INSTANCES; j<jl; j++) {
+                instances[i][j] = al_create_sample_instance(notes[i]);
+
+                if (instances[i][j] == NULL) {
+                    fprintf(stderr,
+                            "al_create_sample_instance %s[%d] failed\n",
+                            filename, j);
+                    goto error;
+                }
+
+                if (!al_attach_sample_instance_to_mixer(instances[i][j], mixer)) {
+                    fprintf(stderr,
+                            "al_attach_sample_instance_to_mixer %s[%d] failed\n",
+                            filename, j);
+                    goto error;
+                }
+            }
         }
     }
 
@@ -134,9 +174,7 @@ int main(int argc, char * argv[])
                         i = ev.keyboard.keycode - ALLEGRO_KEY_A;
                         tmp[i] = abcdefg[i];
 
-                        if (playing[i] == 0) {
-                            playing[i] = play_note(i);
-                        }
+                        play_note(i, instances);
                     } else {
                         switch (ev.keyboard.keycode) {
                             case ALLEGRO_KEY_ESCAPE:
@@ -151,11 +189,6 @@ int main(int argc, char * argv[])
                             ev.keyboard.keycode <= ALLEGRO_KEY_G) {
                         i = ev.keyboard.keycode - ALLEGRO_KEY_A;
                         tmp[i] = ' ';
-
-                        if (playing[i] != 0) {
-                            al_stop_sample(&sample_ids[i]);
-                            playing[i] = 0;
-                        }
                     }
                     break;
             }
@@ -170,10 +203,20 @@ int main(int argc, char * argv[])
     }
 
 clean:
-    for (i=0,l=notes_length; i<l; i++) {
-        if (notes[i]) al_destroy_sample(notes[i]);
+    for (i=0,il=notes_length; i<il; i++) {
+        if (notes[i]) {
+            al_destroy_sample(notes[i]);
+
+            for (j=0,jl=NUM_INSTANCES; j<jl; j++) {
+                if (instances[i][j]) {
+                    al_destroy_sample_instance(instances[i][j]);
+                }
+            }
+        }
     }
     if (musicals) al_destroy_font(musicals);
+    if (mixer) al_destroy_mixer(mixer);
+    if (voice) al_destroy_voice(voice);
     if (event_queue) al_destroy_event_queue(event_queue);
     if (display) al_destroy_display(display);
 
@@ -183,3 +226,31 @@ error:
     status = 1;
     goto clean;
 }
+
+int get_available_instance(
+        int i,
+        ALLEGRO_SAMPLE_INSTANCE * instances[7][10000]) {
+    int j, l;
+
+    for (j=0, l=NUM_INSTANCES; j<l; j++) {
+        if (!al_get_sample_instance_playing(instances[i][j])) {
+            return j;
+        }
+    }
+
+    return -1;
+}
+
+bool play_note(
+        int i,
+        ALLEGRO_SAMPLE_INSTANCE * instances[7][10000]) {
+    int j = get_available_instance(i, instances);
+
+    if (j == -1) {
+        return false;
+    }
+
+    return al_set_sample_instance_playing(
+           instances[i][j],
+           true);
+}

What I found was increased complexity having to manage a nested layer of objects. In my case, I am loading 7 "notes", and to allow notes to be repeatedly played I try to keep many samples around for each note. I started with 7, but the program quickly ran out of air again. I presume because the sample files that I happened to find vary in length up to a minute or so. It seems evident that I need to trim the files down to a shorter slice, or else find better files. That said, I tried creating 10000 instances per note (see code above for that hack) and still resulted in gittering and unpredictable behavior. Not only did the sounds eventually stop working (for a while anyway) again, but it seemed the more instances I fired up by repeatedly pressing "A" the more "pops" I'd hear. Pops that are not part of the sample data. Am I doing something wrong now with the lower level?

I hope that I'm missing something obvious here. If this is truly the direction my program has to go then I think it would illustrate why the current API is bad.

Edgar Reynaldo

Comments for your reply first :

bamccaig said:

This is a good explanation, but I think that the instances are really implementation details. Instead, al_play_sample() could accept a pointer to an ALLEGRO_SAMPLE_PARAMETERS or something like that which can override the play settings. Under the hood, Allegro may wish to track it in an instance (needs to copy it anyway so the user can free it), but the user will just get an ID back that it can use to further manipulate playback. The ID can be used to pause or possibly readjust parameters if that's possible while an instance is already playing (e.g., al_tune_playback(id, &settings) or al_stop_playback(id)).

This is exactly what Allegro does already. ;)

bamccaig said:

All the user has is an ALLEGRO_SAMPLE for the data. If necessary, they can allocate a temporarily ALLEGRO_SAMPLE_PARAMETERS on the stack to tune the playback, and if they want to be able to control playback after it has started then they will want to store a simple integer ID too.

Allegro should do whatever it needs to under the hood to make this work: play the sound as instructed, and do its best to make it sound as the caller would expect. If this means it needs to wire up mixers and voices and whatnot then it should do it on the fly, but as an implementation detail the user doesn't have to care about. If it's resource intensive and might be too slow to do while the game is running, then Allegro should try to allocate a complex enough hierarchy to do anything that could be asked of it when the addon is initialized (hopefully just a voice and a couple of mixers or something).

Again, Allegro already does this. al_reserve_samples sets up a default voice and mixer, and sample instances tell allegro how to play a sample. When you call al_play_sample, it uses one of the reserved sample instances to play your sound.

bamccaig said:

Even the user facing streams could probably be eliminated and just make samples themselves streamable. If a sample is streamed then the data would not be loaded fully when the file is opened, and it would transparently act like a stream instead when played. If file-based then Allegro will load up chunks of the file when needed. Or if the user is generating the stream data on the fly then it'll just respond to an event the same way, but be filling in a sample id instead something like al_stream_playback(id, buffer, start, count) (instead of the current API of getting a buffer and then setting it again, which doesn't make much sense).

I don't know much about ALLEGRO_AUDIO_STREAMs, but it seems silly to stream small samples. Streams only make sense to me for large files and custom audio.

bamccaig said:

I finally took a stab at rewriting my program to use the lower-level API.

After replacing the note files and changing the voice to use ALLEGRO_AUDIO_DEPTH_INT16 (it's the only supported audio depth for a DirectSound voice (I'm on Windoze) ), everything works as expected.

What OS are you on?

Also, you don't need to create your own voice and mixer. Just call al_reserve_samples(1) to create the default voice and mixer. Then attach all your sample instances to the default mixer using al_get_default_mixer(). It will still work. I didn't get any distortion or pops or anything when playing sounds with your code. So the problem may be with your audio driver. You can change the driver using allegro5.cfg. I've attached a copy from the allegro source distribution. Look in the file under the [audio] section, and try changing the audio driver. That may fix things, it may not.

bamccaig

I don't know much about ALLEGRO_AUDIO_STREAMs, but it seems silly to stream small samples. Streams only make sense to me for large files and custom audio.

I meant for large files. :)

Quote:

Again, Allegro already does this. al_reserve_samples sets up a default voice and mixer, and sample instances tell allegro how to play a sample. When you call al_play_sample, it uses one of the reserved sample instances to play your sound.

Then why doesn't my original program work? :P Did you ever get a chance to try that?

Quote:

After replacing the note files and changing the voice to use ALLEGRO_AUDIO_DEPTH_INT16 (it's the only supported audio depth for a DirectSound voice (I'm on Windoze) ), everything works as expected.

What files did you use to test? I wonder if those would have an affect? Can you share the Windows build and files for me to try in Windows? Source too please so I can compare.

Quote:

Also, you don't need to create your own voice and mixer. Just call al_reserve_samples(1) to create the default voice and mixer. Then attach all your sample instances to the default mixer using al_get_default_mixer(). It will still work.

I just figured the best way to rule out other "magical" explanations was to do it all myself. This is pretty much exploration at this point to figure out what works so that I can learn from it and understand it.

Quote:

What OS are you on?

Ubuntu 16.04 (Xenial Xerces).

Quote:

I didn't get any distortion or pops or anything when playing sounds with your code. So the problem may be with your audio driver. You can change the driver using allegro5.cfg. I've attached a copy from the allegro source distribution. Look in the file under the [audio] section, and try changing the audio driver. That may fix things, it may not.

Interesting. I'll give the different driver options a try tonight.

Edgar Reynaldo

Quote:

bamccaig said:

Again, Allegro already does this. al_reserve_samples sets up a default voice and mixer, and sample instances tell allegro how to play a sample. When you call al_play_sample, it uses one of the reserved sample instances to play your sound.

Then why doesn't my original program work? :P Did you ever get a chance to try that?

It did work, after fixing the gain and the sound files, for me on Windows at least. Piano.zip has the source, media, and binary I made that shows this.

bamccaig said:

After replacing the note files and changing the voice to use ALLEGRO_AUDIO_DEPTH_INT16 (it's the only supported audio depth for a DirectSound voice (I'm on Windoze) ), everything works as expected.

What files did you use to test? I wonder if those would have an affect? Can you share the Windows build and files for me to try in Windows? Source too please so I can compare.

Attached is your second program, with the modifications I mentioned. It works for me too.

BCPiano.zip

So it seems you can still attach sample instances to the mixer created by al_reserve_samples, regardless of the number of instances you reserve. I used al_reserve_samples(1) and attached multiple sample instances and they all played without distortion or dropping out.

bamccaig

Piano.zip has the source, media, and binary I made that shows this.

Thanks. That is working much better in Windows (assuming by Piano.zip you meant BCPiano.zip). I haven't tried playing with the driver options in Linux yet.

The source code in the zip appears to be the fancy, low-level version. Did the original version work too?

Quote:

Attached is your second program, with the modifications I mentioned.

It appears all you changed was the depth and then hard-coded filenames instead of generating them. I assume is should also work with the default voice and mixer, but did you try it?

Quote:

So it seems you can still attach sample instances to the mixer created by al_reserve_samples, regardless of the number of instances you reserve. I used al_reserve_samples(1) and attached multiple sample instances and they all played without distortion or dropping out.

My guess without trying to make sense of the code is that al_reserve_samples() is setting up a pool of a fixed size based on what you specify, and it automatically will use that to allocate instances from. As far as I've come across, I don't know of a limitation to the number of things that can be attached to a mixer. Though I'm also not sure if there are quality or performance limitations as a result. I assume there must be, or again, a "simple" API that hides all of these shenanigans should be braindead simple to wire up..

Edgar Reynaldo
bamccaig said:

assuming by Piano.zip you meant BCPiano.zip

No, I meant Piano.zip, the link was broken. It's fixed now. That contains the first modified version you used.

bamccaig said:

I assume is should also work with the default voice and mixer, but did you try it?

Yes, it worked fine.

bamccaig said:

a "simple" API that hides all of these shenanigans should be braindead simple to wire up..

I want to know what exactly you mean. Write out the function prototypes you would expect a "simple" API to have.

Izual

bamccaig, here is my edited version of your piano using VOICE / MIXER / SAMPLE INSTANCES:

ZIP with source and all files here.

#SelectExpand
1#include <allegro5/allegro.h> 2#include <allegro5/allegro_acodec.h> 3#include <allegro5/allegro_audio.h> 4#include <allegro5/allegro_font.h> 5#include <allegro5/allegro_ttf.h> 6#include <stdio.h> 7#include <stdlib.h> 8 9#define notes_length ( sizeof( note_s ) / sizeof( ALLEGRO_SAMPLE * ) ) 10 11#define say_center(w, h, text) \ 12 (al_draw_text(musicals, white, w, h, ALLEGRO_ALIGN_CENTRE, text)) 13 14void play_note( ALLEGRO_SAMPLE_INSTANCE *si ) 15{ 16 /* 17 Setting position just to skip the silence at the start of the notes. 18 Why 32000 ? Because it sounds just right to me. Try more or less and see. 19 */ 20 al_set_sample_instance_position( si, 32000 ); 21 al_set_sample_instance_playing( si, true ); 22} 23 24static const char abcdefg[] = "abcdefg"; 25 26int main(int argc, char * argv[]) 27{ 28 char tmp[8] = " "; 29 int i = 0, l = -1; 30 int status = 0; 31 ALLEGRO_COLOR black, white; 32 ALLEGRO_DISPLAY * display = NULL; 33 ALLEGRO_EVENT_QUEUE * event_queue = NULL; 34 ALLEGRO_FONT * musicals = NULL; 35 ALLEGRO_TIMEOUT timeout; 36 ALLEGRO_VOICE *voice = NULL; 37 ALLEGRO_MIXER *mixer = NULL; 38 ALLEGRO_SAMPLE *note_s[ 7 ] = { 0 }; 39 ALLEGRO_SAMPLE_INSTANCE *note_si[ 7 ] = { 0 }; 40 41 if (!al_init()) 42 { 43 fprintf(stderr, "al_init failed\n"); 44 goto error; 45 } 46 if (!al_install_keyboard()) 47 { 48 fprintf(stderr, "al_install_keyboard failed\n"); 49 goto error; 50 } 51 if (!al_install_audio()) 52 { 53 fprintf(stderr, "al_install_audio failed\n"); 54 goto error; 55 } 56 if (!al_init_acodec_addon()) 57 { 58 fprintf(stderr, "al_init_acodec_addon failed\n"); 59 goto error; 60 } 61 if (!al_init_font_addon()) 62 { 63 fprintf(stderr, "al_init_font_addon failed\n"); 64 goto error; 65 } 66 if (!al_init_ttf_addon()) 67 { 68 fprintf(stderr, "al_init_ttf_addon failed\n"); 69 goto error; 70 } 71 display = al_create_display(1440, 900); 72 if (display == NULL) 73 { 74 fprintf(stderr, "al_create_display failed\n"); 75 goto error; 76 } 77 event_queue = al_create_event_queue(); 78 if (event_queue == NULL) 79 { 80 fprintf(stderr, "al_create_event_queue failed\n"); 81 goto error; 82 } 83 84 /* Setup sound: */ 85 voice = al_create_voice( 44100, 86 ALLEGRO_AUDIO_DEPTH_INT16, 87 ALLEGRO_CHANNEL_CONF_2 ); 88 if( !voice ) 89 { 90 fprintf(stderr, "al_create_voice failed\n"); 91 goto error; 92 } 93 mixer = al_create_mixer( 44100, 94 ALLEGRO_AUDIO_DEPTH_INT16, 95 ALLEGRO_CHANNEL_CONF_2 ); 96 if( !mixer ) 97 { 98 fprintf(stderr, "al_create_mixer failed\n"); 99 goto error; 100 } 101 al_attach_mixer_to_voice( mixer, voice ); 102 103 /* Load note samples: */ 104 for( i=0, l=notes_length; i<l; i++ ) 105 { 106 char filename[22] = "media/Piano.ff.A3.ogg"; 107 filename[15] = 'A' + i; 108 if (filename[15] > 'B') 109 { 110 filename[16] = '4'; 111 } 112 note_s[ i ] = al_load_sample( filename ); 113 if( note_s[ i ] == NULL ) 114 { 115 fprintf(stderr, "al_load_sample %s failed\n", filename); 116 goto error; 117 } 118 119 /* Create our sample instances: */ 120 note_si[ i ] = al_create_sample_instance( note_s[ i ] ); 121 if( note_si[ i ] == NULL ) 122 { 123 fprintf( stderr, "al_create_sample_instance %s failed\n", filename ); 124 goto error; 125 } 126 /* And attach them to the mixer: */ 127 al_attach_sample_instance_to_mixer( note_si[ i ], mixer ); 128 } 129 130 musicals = al_load_ttf_font("musicals.ttf", 72, 0); 131 if (musicals == NULL) 132 { 133 fprintf(stderr, "al_load_ttf_font failed\n"); 134 goto error; 135 } 136 al_register_event_source(event_queue, al_get_display_event_source(display)); 137 al_register_event_source(event_queue, al_get_keyboard_event_source()); 138 black = al_map_rgb(0, 0, 0); 139 white = al_map_rgb(255, 255, 255); 140 al_init_timeout( &timeout, 0.06); 141 142 while (1) 143 { 144 int w = al_get_display_width(display); 145 int h = al_get_display_height(display); 146 ALLEGRO_EVENT ev; 147 if (al_wait_for_event_until(event_queue, &ev, &timeout) ) 148 { 149 switch (ev.type) 150 { 151 case ALLEGRO_EVENT_DISPLAY_CLOSE: 152 goto clean; 153 break; 154 case ALLEGRO_EVENT_KEY_DOWN: 155 if (ev.keyboard.keycode >= ALLEGRO_KEY_A && ev.keyboard.keycode <= ALLEGRO_KEY_G) 156 { 157 i = ev.keyboard.keycode - ALLEGRO_KEY_A; 158 tmp[ i ] = abcdefg[ i ]; 159 160 play_note( note_si[ i ] ); 161 } 162 else 163 { 164 switch (ev.keyboard.keycode) 165 { 166 case ALLEGRO_KEY_ESCAPE: 167 case ALLEGRO_KEY_Q: 168 goto clean; 169 } 170 } 171 break; 172 case ALLEGRO_EVENT_KEY_UP: 173 if (ev.keyboard.keycode >= ALLEGRO_KEY_A && ev.keyboard.keycode <= ALLEGRO_KEY_G) 174 { 175 i = ev.keyboard.keycode - ALLEGRO_KEY_A; 176 177 tmp[ i ] = ' ' ; 178 /* 179 Uncomment to make the sample instance stop playing on 180 key up event. Does not sound good to me: 181 */ 182 /* 183 al_set_sample_instance_playing( note_si[ i ], false ); 184 */ 185 } 186 break; 187 } 188 } 189 al_clear_to_color(black); 190 191 say_center(w / 2.0, h / 2.0, "abcdefg ABCDEFG"); 192 say_center(w / 2.0, h / 2.0 + 100, tmp); 193 194 al_flip_display(); 195 } 196clean: 197 for ( i=0, l=notes_length; i<l; i++ ) 198 { 199 if( note_si[ i ] ) 200 al_destroy_sample_instance( note_si[ i ] ); 201 if( note_s[ i ] ) 202 al_destroy_sample( note_s[ i ] ); 203 } 204 205 if (musicals) al_destroy_font(musicals); 206 207 if( mixer ) al_destroy_mixer( mixer ); 208 if( voice ) al_destroy_voice( voice ); 209 210 if (event_queue) al_destroy_event_queue(event_queue); 211 if (display) al_destroy_display(display); 212 return status; 213error: 214 status = 1; 215 goto clean; 216}

bamccaig

Thanks. I think Edgar and I already accomplished that. What remains is for me to test whether it's the data files or the driver causing my issue. I haven't had time to do that yet.. So bump..

Niunio

Sorry for the late answer but:

This is why nothing ever gets done. I hope somebody updated the docs for me...

You're right, but I'm Spanish and English is quite bad when I try to explain complex things (it really is); that's why I don do it.

Anyway I have to read again this thread carefully to see if I learn more.

Edgar Reynaldo

Bump so Eric can try out the piano program.

Eric Johnson

Thanks for the bump. It's a cool little piano program. I'm on Linux, so I had to run it in Wine. It's probably just a Wine issue, but it took about a second between the time I pressed a key and the time the sound began. I'll try it in Windows when I get the chance.

bamccaig

Eric, you could also just try building the program yourself in Linux. :) I would appreciate somebody else testing in Linux anyway to report their results.

Eric Johnson

Done. I built it from Piano.zip, but nothing happened when I pressed the keys. Then I tried Izual's edit and it worked. It works flawlessly without delay, so the audio issues were Wine's fault.

AramisL

Hi bamccaig, as Edgar mentioned, as my little doubt too, have you noticed that the keyboards have hardware limitations?

I mean, it depends on the builder of your keyboard HOW MANY buttons can be pressed at a time, it is called buffer keyboard and this is related with memory and bus in the hardware (and I dont know if OS too, that's why audio cards does exists, aren't they?)

my little grand of sand ;D

bamccaig

I am using a DAS KEYBOARD so my keyboard should support N-key input. :) But yes, for other keyboards it could certainly be a limitation. As far as "tracker" goes, it's just an experiment to see what I can accomplish, and gain some experience with Allegro audio. And if something fun for me comes out of it even better. I'm already surprised with the little bit of attention it has gotten so perhaps a featureful program would even attract other attention.

Izual

@Eric: The delay in the sound is propably not Wines fault. The samples contain about a second of silence at the start.

This line in my edit: al_set_sample_instance_position( si, 32000 ); skips about a second of that silence and makes it sounds about right.

And one more thing. When you use al_set_sample_instance_playing( sample_instance, true ); more then once on one sample instance, it will not start playing the sample form the start. I will just keep playing.
To achieve the effect of playing the sample again you either stop the sample first or just set the sample position to desired location. Like in my bam's piano edit.

The sample instance position is reset to the start ( 0 ) when it is finished playing or when it is stopped.

Eric Johnson
Izual said:

@Eric: The delay in the sound is propably not Wines fault. The samples contain about a second of silence at the start.

Oh that's it then. I do know I've had audio issues with Wine in the past though.

GullRaDriel

Bump 8-)

Michael Weiss

Just thought I'd add my two cents here.

I have used the old allegro 4 sound routine and have just ported to the new
allegro 5 ones.

I had no problems, except for a small misunderstanding when I tried to hook
two mixers up to a voice and only one would work. After troubleshooting and
RTFMing some more I found by design you are only supposed to connect one mixer
to a voice.

So now a have voice -> mixer -> two other mixers.

One of them I have connected to all my sound effects and the other one is
for the background music.

I personally do not think the api is overly complicated. I read the comments
from Edgar asking if anyone had any specific suggestions on how the api or
documentation could be improved, I thought about it, and it seems like
exactly what is needed to get the job done.

Just my two cents worth...

Thread #617315. Printed from Allegro.cc