Y Sound Systems is a low-level and multi-client system of access to a machine's Recorder (sound card). YLib is the programming language used to write applications for that system. YLib follows the X window Systems pattern of design and is licensed under GPL (GNU Public License).
You can develop open software, free software, or even commercial (non-free) software using YLib without having to spend anything for licenses or royalties.
The YLib is intended for (but not limited to) entertainment software, any application that wants simplified, multiple, network transparent access to the Recorder much like multiple GUI applications want to output to the display, should use YLib.
YLib attempts to follow as many existing standards as possible, many of its objects contain data that are formatted after popular formats such as the DSP Sound Object capable of having raw, voc, or wav formats.
First make sure that you have access to a Y Server
(preferably on your own computer) and that YLib
(which consists of /usr/lib/libY2.so.#
and
(/usr/include/Y2/*
) is
properly installed. If not, then you should obtain the
complete package of the YIFF Sound System (both server and library)
here and install it. Get the
latest version and make sure that you build and install
all the compoents to maximize your Y Sound System capabilities.
We begin our journey into YLib with a simple hello world(tm) program
(note, the below source comes from the YIFF source package, from
the file yiffutils/helloworld.c
). Code colored
red in the below example denotes code
that is specific to the YLib language.
#include <stdio.h>
#include <Y2/Y.h> /* Basic Y types and constants. */
#include <Y2/Ylib.h> /* YLib functions and structs. */
/* Change this to the address and port of the Y server you want
* to connect to. Note that 127.0.0.1 is a symbolic address
* meaning `localhost'.
*/
#define CON_ARG "127.0.0.1:9433"
int main(int argc, char *argv[])
{
YConnection *con;
char *filename;
YEventSoundObjectAttributes sndobj_attrib;
YID play_id;
YEvent event;
/* Need atleast one argument, being the file name.
* This is so that we can play a sound object on file.
*/
if(argc < 2)
return(1);
else
filename = argv[1];
/* Connect to the Y server. We pass NULL as the start
* argument, this means the Y server will not be started if
* it was detected to be not running. The connection
* argument is CON_ARG which is defined at the beginning of
* the source. The connection argument is a string of the
* format ":".
*/
con = YOpenConnection(
NULL,
CON_ARG
);
if(con == NULL)
{
/* Failed to connect to the Y server. */
fprintf(
stderr,
"%s: Cannot connect to YIFF server.\n",
CON_ARG
);
return(1);
}
/* Check if the filename exists on the machine that
* the Y server is running on and obtain its attributes.
*/
if(YGetSoundObjectAttributes(
con,
filename,
&sndobj_attrib
))
{
/* Can't get sound object attributes. */
fprintf(
stderr,
"%s: Error: Missing or corrupt.\n",
filename
);
}
else
{
/* Start playing the sound object. */
play_id = YStartPlaySoundObjectSimple(
con,
filename
);
/* There is also a YStartPlaySoundObject()
* function which allows additional specified
* values for playing. More on that later.
*/
/* Print sound object attributes. */
switch(sndobj_attrib.format)
{
case SndObjTypeDSP:
printf(
"ID: %i Type: DSP SmpRate: %i Hz Bits: %i Ch: %i\n",
play_id,
sndobj_attrib.sample_rate,
sndobj_attrib.sample_size,
sndobj_attrib.channels
);
break;
case SndObjTypeMIDI:
printf(
"ID: %i Type: MIDI\n",
play_id
);
break;
default:
printf(
"ID: %i Type: *Unknown*\n",
play_id
);
}
/* Manage events: Wait untill audio is done playing.
* Here we call YGetNextEvent() repeatedly to get
* events from the Y server. Once we get an event
* we process it accordingly by its type.
*/
while(1)
{
/* Get the next event (if any). */
if(YGetNextEvent(
con,
&event,
False /* Nonblocking. */
) > 0)
{
/* Sound object stopped playing? */
if((event.type == YSoundObjectKill) &&
(event.kill.yid == play_id)
)
{
/* Our play has stopped. */
printf("Done playing.\n");
break;
}
/* Server disconnected us? */
else if(event.type == YDisconnect)
{
/* Got disconnected. */
printf(
"Y server disconnected us, reason %i.\n",
event.disconnect.reason
);
/* Need to close connection! */
YCloseConnection(con, False);
con = NULL;
break;
}
/* Server shutdown? */
else if(event.type == YShutdown)
{
/* Server shutdown. */
printf(
"Y server shutdown, reason %i.\n",
event.shutdown.reason
);
/* Need to close connection! */
YCloseConnection(con, False);
con = NULL;
break;
}
else
{
/* Some other Y event, ignore. */
}
}
usleep(1000); /* Don't hog the CPU. */
}
}
/* Disconnect from the Y server. We need to pass the
* original connection pointer con to close that
* connection to the Y server. Note that con may be
* NULL at this point if the Y server disconnected us
* already, passing NULL is okay.
*
* The second argument asks us do we want to leave the
* Y server up when we disconnect. If we were the
* program that started the Y erver and the second
* argument is set to False then the Y server will
* be automatically shut down. To ensure that the Y
* server stays running, you can pass True instead.
*/
YCloseConnection(con, False);
con = NULL;
return(0);
}
To compile the program, type:
cc helloworld.c -o helloworld -lY2
The -lY2
instructs the compiling procedure to link
to libY2.so.#
(where #
is the version
number of YLib.
Now before you run helloworld
, make
sure that you have:
/usr/sbin/yiff /etc/yiff/yiffrc
).
yrecinfo -m
(to get a list of Audio modes).
yset audio <audio_mode_name>
(choose an Audio mode
that best suits the sound file you are about to play).
./helloworld /home/me/helloworld.wav
(play it).
yrecinfo -m
to get a list of Audio modes) or adjust
the Y mixers (use ymixer
).
Now is a good time to review the helloworld program. You may have noticed that its code is awefully long for a `simple example', this is because Ylib is a low-level language. Low-level languages are very lengthy, however the advantage is that they provide the most functionality to the actual device you want to control.
Now don't give up just yet, there's good news ahead! You now have been exposed to all the basic and some intermediate levels of code that's required to work with Ylib. It also means that since you made it this far, you're over the first and most difficult hurdle. If you keep at it, then you are gauranteed to understand the rest of YLib!
You'll find out that the `massive lines of required code' are a blessing rather than `make work'. It allows you to control as much of the aspect of sound programming while keeping the even more low level coding hidden (yup, you don't want a back stage tour just yet!) and keeping your code portable to any platform with YLib.
Remember that any platform with BSD style networking (ie, all UNIXes) can support YLib but not always the Y server. This means you can write your programs for almost any platform and not have to worry about portability, because YLib is almost gauranteed to be portable to it.
If a Y server is not available for that platform, YLib will simply return
a NULL when you call YOpenConnection()
and your program
should be able to continue on normally without playing sound.
There is also a more advanced form of
YStartPlaySoundObjectSimple()
which allows you to
specify additional values on how your Sound Object should be
played (these values can also be adjusted while the Sound Object is being
played).
YEventSoundPlay value;
YID yid;
value.flags = YPlayValuesFlagPosition |
YPlayValuesFlagTotalRepeats |
YPlayValuesFlagVolume |
YPlayValuesFlagSampleRate;
value.position = 0;
value.total_repeats = total_repeats; /* Can be -1. */
value.left_volume = vol_left; /* 0.0 to 1.0. */
value.right_volume = vol_right; /* 0.0 to 1.0. */
value.sample_rate = sample_rate; /* In Hz, can be 0. */
yid = YStartPlaySoundObject(con, path, &value);
In the YEventSoundPlay value
the member
flags
specify which values are to be changed.
The position
specifies which byte position to start playing
at. If the Audio is in 16 bits, make sure the position is an even number.
total_repeats
specify how many times you want to repeat this
play, normally it is set to 1 or -1. -1 means to repeat infinatly.
The left_volume
and right_volume
specify the
volume of the Sound Object to be mixed into the Sound, valid values are
from 0.0 to 1.0 (inclusive). The sample_rate
specifies
the applied sample rate deviance of the Sound Object to be played at in
Hz, specifying a value less than the actual sample rate of the current
Audio has no affect (hence passing 0 is the default), specifying a value
greater than will make the Sound Object play faster (good for engine
sound simulations).
Try replacing the call to YStartPlaySoundObjectSimple()
in the sample source with the above example of
YStartPlaySoundObject()
. Try tinkering with the values
passed on to YStartPlaySoundObject()
and see what happens!
Good news, it's time to simplify things!
Now that you've seen an example program, all you need to do next is pick out the significant parts of it... that we'll be covering in this section.
YLib is a low level language because it requires the calling program to handle all the procedures (except timings to the sound driver, since YLib is async), while at the same time YLib is a high-level language because it has a transparent network layer and uses a server to do all the messy sound loading, mixing, and playing.
Let's simplify the low level part first, the following is the procedure (flow) of YLib. Note the sequence of YLib functions your program needs to call:
YOpenConnection()
this opens a connection to the Y
server
and (at your option) starts it too. You call this function once when your
program wants to connect to the Y server (usually at startup or when your
program wants to turn on sound).
YGetNextEvent()
this is considered the maintainance
function as well as the event reporter. You need to call this function
each time your program loops (note the
helloworld
example does not illustrate this well, we'll go into depth about this
function later).
YCloseConnection()
this closes the connection to the Y
server
and (at your option) if your program started the Y server, then it will
shut down
the Y server.
Let's talk about YOpenConnection()
first. This function
when called, returns a pointer that you will use as refferance to the
connection to the Y server when using most of the other YLib functions.
Later on, before your program exits you will want to call
YCloseConnection()
and pass this pointer to it. Once you do that the pointer is no longer
valid and your connection to the Y server should be considered closed.
If you want to connect to the Y server again, just call
YOpenConnection()
again and you will get a pointer to the new
connection.
Next is the more complicated function YGetNextEvent()
.
This function has two purposes, it serves as a maintainance
function
(something to allow YLib to do internal things you don't need/want to
worry
about) and fetches the latest event (if any) comming from the Y server.
It's up to your program to handle these events properly, examples about
handling events from YGetNextEvent()
will be covered later.
The important part is that your program call this function atleast once
per loop (example on that is comming, when we put it all togeather).
Lastly is YCloseConnection()
which closes the connection
to the Y server. You need to pass it the pointer that you got from
YOpenConnection()
, or if you pass NULL then it will do
nothing.
Now it's time to get a contrasting overview of how and where your program should call YLib functions (note this example may differ with the helloworld example):
#include <Y2/Y.h> /* Basic Y types and constants. */
#include <Y2/Ylib.h> /* YLib functions and structs. */
#include "myprogram.h"
YConnection *con;
int main()
{
YEvent event;
MyProgramInitialize();
con = YOpenConnection(NULL, somewhere);
if(con == NULL)
return(1);
while(1)
{
MyProgramManage();
if(YGetNextEvent(con, &event, False) > 0)
MyProgramHandleEvent(&event);
}
MyProgramShutdown();
YCloseConnection(con);
return(0);
}
The above sample code is not compilable because it is missing the functions you will have to write for your program. Instead, pay attention to the procedure and note the following:
Notice how YOpenConnection()
is called once at the very
beginning and YCloseConnection()
is called at the end.
Also notice how YGetNextEvent()
is called once per loop,
and if there was a new event then it would be passed on to be handled
by your program's function MyProgramHandleEvent()
.
Those are the key points you want to pay attention to, remember that YLib is async. It's not too important when you call the YLib function, it's important what order you call them (hence why we emphisize procedure).
Here we show you how to handle YEvents obtained from
YGetNextEvent()
by example.
Below is what your MyProgramHandleEvent()
function probably
should look like (that function came from the previous section
Putting It All Togeather).
void MyProgramHandleEvent(YEvent *event)
{
if(event == NULL)
return;
switch(event->type)
{
/* Audio mode has changed, this happens sometimes.
* usually when your program or another program has
* asked the Y server to change Audio modes.
*
* When this happens all your Sound Objects that
* you have playing (if any) will recieve
* YSoundObjectKill events.
*/
case YAudioChange:
if(event->audio.preset)
printf(
"Audio mode changed to `%s'.\n",
event->audio.mode_name
);
else
printf("Audio mode changed.\n");
printf("Sample Rate: %i Hz\n",
event->audio.sample_size
);
printf("Channels: %i Bits: %i\n",
event->audio.channels,
event->audio.sample_size
);
printf("Fragment Size: %i Bytes\n",
event->audio.fragment_size_bytes
);
break;
/* The Audio cycle interval on the Y server has changed,
* either by your program or another program.
*/
case YCycleChange:
printf("Audio cycle interval changed to: %ld ms\n",
event->cycle.cycle_us
);
break;
/* Your program has been disconnected from the Y server.
* A variety of reasons may have caused this but
* regardless of that, you must set the YConnection
* pointer you got from YOpenConnection() to NULL.
*/
case YDisconnect:
printf("You have been disconnected, reason code %i.\n",
event->disconnect.reason
);
/* Need to close connection afterwards. */
YCloseConnection(con, False);
con = NULL;
break;
/* A host has been added or removed, this isn't
* a very important event unless your program is serious
* about managing who's allowed to connect.
*/
case YSetHost:
printf("Host %i.%i.%i.%i has been %s.\n",
event->host.ip.charaddr[0],
event->host.ip.charaddr[1],
event->host.ip.charaddr[2],
event->host.ip.charaddr[3],
((event->host.op) ? "added" : "removed")
);
break;
/* A sound object (that your program instructed to
* play) has stopped playing. This may be important
* if you want to keep track of when a sound object
* has stopped playing (if not then just ignore it).
*/
case YSoundObjectKill:
printf("Sound object YID %ld has stopped playing.\n",
event->kill.yid
);
break;
/* A mixer channel value has changed. This is usually
* not very important and you can ignore it.
*/
case YMixerChannel:
printf("Mixer %i has changed value to %.4lf %.4lf\n",
event->mixer.code,
((YMixerValues >= 1) ? event->mixer.value[0] : 0),
((YMixerValues >= 2) ? event->mixer.value[1] : 0)
);
break;
/* The Y server has shut down, treat this event
* as if it were a YDisconnect type event.
*/
case YShutdown:
printf("Y server has shut down, reason code %i.\n",
event->shutdown.reason
);
/* Need to close connection afterwards. */
YCloseConnection(con, False);
con = NULL;
break;
}
}
Most of the events can be ignored for a simple Ylib program. There
are only two very important events that you must watch for,
they are
YDisconnect
and YShutdown
. When you
recieve either of those events you must call
YCloseConnection()
and then set the connection pointer to
NULL
. Those events indicate that the Y server has either
disconnected you or it has shut down.
Another important responsibility on your part is to make sure that
you call YGetNextEvent()
to allow it to manage itself
and flush the event queue. This does not need to be called on every
loop but atleast reasonably often enough to ensure that the Ylib side
of things are kept reasonably up to date.
This is the end of the current YLib tutorial, we hope that you've had a productive and educating experiance and wish you the best of luck with YLib!
If you feel that there is a topic that should be included, please write to the developers.