# Using asynchronous streams in aRts

## Sending data

Suppose you decided that you want to write a module to produce sound asynchronously. Suppose its interface looks like that:

    interface SomeModule : SynthModule
    {
        async out byte stream outdata;
    };

Fine. So how exactly do you send something now?

#### push delivery

With asynchronous streams you send the data as packets. That means, you send individual packets with bytes in the above example. The actual process is: allocate a packet, fill it, send it.

So step by step:

- allocate a packet

          DataPacket<mcopbyte> *packet = outdata.allocPacket(100);

- fill it

          // cast so that fgets is happy that it has a (char *) pointer
          char *data = (char *)packet->contents;

          // as you can see, you can shrink the packet size after allocation
          // if you like
          if(fgets(data,100,stdin))
              packet->size = strlen(data);
          else
              packet->size = 0;

- send it

          packet->send();

Pretty simple. But of course, if you want to send packets exactly as fast as the receiver can process them, there needs to be another way.

#### pull delivery

That other way is pull delivery. You say: please, I want to send packets as fast as the receiver is ready to process them. You start with a certain amount of packets you send. And as the receiver processes one packet, and another, you start refilling them with fresh data, and send them again.

You start that by calling setPull. So for instance

        outdata.setPull(8, 1024);

This means that you want to send packets over outdata. You want to start sending 8 packets at once, and as the receiver processes some of them, you want to refill these.

Then, you need to implement a method which fills the packets, which could look like that:

        void request_outdata(DataPacket<mcopbyte> *packet)
        {
            packet->size = 1024;  // shouldn't be more than 1024
            for(int i = 0;i < 1024; i++)
                packet->contents[i] = (mcopbyte)'A';
            packet->send();
        }

Thats it. When you don't have any data any more, you can start sending packets with zero size, which will stop the pulling.

Its essential to call that method exactly request_<streamname>.

## Receiving data

Thats much simpler. Suppose you have a simple ToLower filter, which simply converts all letters in lowercase:

    interface ToLower {
        async in byte stream indata;
        async out byte stream outdata;
    };

Thats really simply to implement. Here is the whole implementation.

    class ToLower_impl : public ToLower_skel {
    public:
        void process_indata(DataPacket<mcopbyte> *inpacket)
        {
            DataPacket<mcopbyte> *outpacket = outdata.allocPacket(inpacket->size);

            // convert to lowercase letters
            char *instring = (char *)inpacket->contents;
            char *outstring = (char *)outpacket->contents;

            for(int i=0;i<inpacket->size;i++)
                outstring[i] = tolower(instring[i]);

            inpacket->processed();
            outpacket->send();
        }
    };

    REGISTER_IMPLEMENTATION(ToLower_impl);

Again: its essential to call the method exactly process_<streamname>.

As you see, for each arriving packet you get a call for a function (the process_indata call in our case). You need to call the processed() method of a packet to indicate you have processed it.

Hint: if processing takes longer (i.e. if you need to wait for soundcard output or something like that), don't call processed immediately, but store the whole data packet and call processed only as soon as you really processed that packet. That way, senders have a chance to know how long it really takes to do your work.

As synchronization isn't so nice with asynchronous streams, you should use synchronous streams whereever possible, and asynchronous streams only when necessary.

back to index