![]() |
|
Audio seems overly complicated :( |
Edgar Reynaldo
Major Reynaldo
May 2007
![]() |
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? My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
bamccaig
Member #7,536
July 2006
![]() |
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. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Elias
Member #358
May 2000
|
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 -- |
Edgar Reynaldo
Major Reynaldo
May 2007
![]() |
I thought the number you passed to al_reserve_samples divided the volume of each attached sample instance? My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
bamccaig
Member #7,536
July 2006
![]() |
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. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
roger levy
Member #2,513
July 2002
|
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
Major Reynaldo
May 2007
![]() |
@bambams 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 said: 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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Elias
Member #358
May 2000
|
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
Major Reynaldo
May 2007
![]() |
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 My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
bamccaig
Member #7,536
July 2006
![]() |
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. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Edgar Reynaldo
Major Reynaldo
May 2007
![]() |
I'll try out your program a little later, if I don't fall asleep first. What you're doing sounds reasonable enough. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Izual
Member #2,756
September 2002
![]() |
After reading this thread i just had to try the allegro 5 "complex" audio addon that everyone is talking about here. I find the audio addon very well made, and really easy to use.
|
Edgar Reynaldo
Major Reynaldo
May 2007
![]() |
This ^. Thank you Izual. That's what I've been trying to say. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Neil Roy
Member #2,229
April 2002
![]() |
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
Major Reynaldo
May 2007
![]() |
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! My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
roger levy
Member #2,513
July 2002
|
@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
Major Reynaldo
May 2007
![]() |
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. roger levy said: 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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
roger levy
Member #2,513
July 2002
|
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
Member #7,536
July 2006
![]() |
"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"} -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Neil Roy
Member #2,229
April 2002
![]() |
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
Major Reynaldo
May 2007
![]() |
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 EDIT 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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
bamccaig
Member #7,536
July 2006
![]() |
Edgar Reynaldo said:
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. 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. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Edgar Reynaldo
Major Reynaldo
May 2007
![]() |
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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
bamccaig
Member #7,536
July 2006
![]() |
Edgar Reynaldo said: 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? 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. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Edgar Reynaldo
Major Reynaldo
May 2007
![]() |
Quote: bamccaig said:
Edgar Reynaldo 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? 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:
Edgar Reynaldo 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. 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 Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
|
|