Re: Streaming Audio to CAPI



Hi,

Am 19.04.2006, 18:39 Uhr, schrieb Kinfe Tadesse <tadesse@xxxxxxxxxxxxxxxxxxxxxxxx>:

Look at this code. (Most probably doesn't even compile and is full of
mistakes...)


/* -------------------------------------------------------------- */
struct reader {
unsigned char *blk;
unsigned long pos;
unsigned long size;
};

Don't we need something like "FILE*" filehandle in the reader struct above?

No, we don't. That reader struct was meant for reading (sequentially)
from CAPI messages you retrieve via CAPI_GET_MESSAGE, not from a file.

It was intended for use similar to:

void capi_message_loop( unsigned long appl_id ) {
void *msg;
struct reader rdr;
unsigned short len;
unsigned short appl_id;
unsigned short command;
/* And so on. */

while(1) {
/* TODO: handle errors */
CAPI_GET_MESSAGE( appl_id, &msg );

/* application-level CAPI on Windows doesn't return the
* message length separately but in the first two bytes
* of the message, so we need to get it out first.
*/
reader_init( &rdr, msg, 2 );
len = reader_readw( &rdr, 0 );

/* Now we know the length, so initialize the reader
* again, this time with the proper value.
*/
reader_init( &rdr, msg, len );

/* Since we've just re-initialized the reader we're back
* at the beginning and need to consume the length value
* again.
*/
reader_skip( &rdr, 2 );

/* Start decoding the message header. */
appl_id = reader_readw( &rdr, 0 );
command = reader_readb( &rdr, 0 );

/* And so on... */
}

/* No need to clean up the reader, as it has automatic scope. No
* need to free the msg, as it's memory owned by CAPI.
*/
}




void reader_init( struct reader *rdr,
unsigned char *blk,
unsigned char *size ) {
rdr->blk = blk;
rdr->pos = 0;
rdr->size = size;
}

I think this is meant to initialize the reader! Do we really need to? The
last parameter of the above method should be unsigned long size. You wrote
unsigned char *size, was it deliberate or a mistake?

It was a mistake. You're right that unsigned long is needed here. I'm
not sure whether a function for initializing is needed, as it's most
probably only done once in your program -- right after CAPI_GET_MESSAGE.
I consider it good style, however.

/* Retrieves number of bytes that are remaining in the reader. */
void reader_rem( struct reader *rdr ) {
return rdr->size - rdr->pos;
}

/* Skips a number of bytes. */
void reader_skip( struct reader *rdr, unsigned long skip ) {
if ( rdr->pos + skip > rdr->size )
rdr->pos = rdr->size;
else
rdr->pos += skip;
}

Would you please describe the logic behind the above two methods?

To nitpick: those two aren't methods, they are functions.

reader_rem() determines how many bytes are still available (not consumed)
in the reader. We initialize the reader with a buffer and a size
argument, the readers position (cursor) is set to 0. So initially we can
read up to 'size' bytes from the reader. Since each of the read
functions increments the readers position by the number of bytes read we
consume more and of the data available.
So determine if there's any more data to read, we have this function.
(Note that it's not used in the remainder of the code I posted. All the
reader_read*-functions take a default value to return if we already
consumed the whole buffer. Still might be useful. )

reader_skip() in turn consumes some bytes from the reader without
actually returning it. I've used it in the little chunk of code I posted
above -- as we've already read the length of the message, we don't need
to do it a second time. Still, we could also have used reader_readw()
there again. See reader_readstruct() below for where reader_skip() is
useful.
The if( rdr->pos ... statement in the beginning of the function just
ensures that we won't skip too much -- that is, move our cursor past the
bounds of 'size' parameter that was used to initialize the reader.



/* Reads a byte. */
unsigned char reader_readb( struct reader *rdr, unsigned char dflt ) {
if( rdr->pos >= rdr->size )
return dflt;

return rdr->blk[rdr->pos++];
}

/* Reads a word. */
unsigned short reader_readw( struct reader *rdr, unsigned short dflt ) {
if( rdr->pos + 1 >= rdr->size ) {
rdr->pos = rdr->size;
return dflt;
}

return reader_readb( rdr, 0 )
| reader_readb( rdr, 0 ) << 8;
}

/* Reads a dword. */
unsigned long reader_readdw( struct reader *rdr, unsigned long dflt ) {
if( rdr->pos + 3 >= rdr->size ) {
rdr->pos = rdr->size;
return dflt;
}

return reader_readb( rdr, 0 )
| reader_readb( rdr, 0 ) << 8;
| reader_readb( rdr, 0 ) << 16;
| reader_readb( rdr, 0 ) << 24;
}

Why do we need to write different methods for reading a byte, a word, etc.

Because you're reading different things -- bytes, words, etc.

Is it not possible to implement one generic reader function which can read a
byte, a word, a dword, etc?

That surely is possible, but I can't see a good reason for it -- after
all it's different things you're going to read so different functions
seem appropriate. I find it much more readable to see a

/* 0 is given as the default if there's no more data available
* from the reader.
*/
byte_val = reader_readb( rdr, 0 );
word_val = reader_readw( rdr, 0 );

instead of a

/* second parameter is number of bytes to read, third parameter
* is the default value again.
*/
byte_val = reader_read( rdr, 1, 0 );
word_val = reader_read( rdr, 2, 0 );

but that's a matter of taste, I think.

You know the reason for those functions, don't you? If you do feel free
to ignore this, but if you really don't, read on:

The reason for us having such functions in the first place is that CAPI
specifies a given byte order, namely 'little endian'. Let's assume we
have a value of (decimal) 2864434397. Converted to hexadecimal notation
this conveniently shows as 0xaabbccdd. Now in memory, which consists of
bytes, one could conceive two different ways of storing this.

1) big endian
This would be the natural way of storing things. You store the most
significant byte first, the least significant byte last. So in memory
your value will look like this

|....|....|....|....|
| AA | BB | CC | DD |
|....|....|....|....|

Pretty logical. That's what's generally used on PowerPC based
systems. (And more.)

2) little endian
For some arbitrary reason someone though of reversing the logical
order, storing the least significant byte first.

|....|....|....|....|
| DD | CC | BB | AA |
|....|....|....|....|

This is used on nearly all modern processors -- including the Pentium
and compatibles.

CAPI uses the second alternative -- little endian -- in it's encoding. That
is, if you encode a DWORD with value 1, the encoding doesn't look like the
expected:

|....|....|....|....|
| 00 | 00 | 00 | 01 |
|....|....|....|....|

But it will look like:

|....|....|....|....|
| 01 | 00 | 00 | 00 |
|....|....|....|....|


Since your software most probably will ever only run on little endian
systems, both CAPI representation and in-memory representation are the same,
so you could take the easy route by doing a

void reader_readcrap( struct reader *rdr, void *dest, unsigned len ) {
/* TODO: check reader if length still fits */

memcpy( dest, rdr->blk[ rdr->pos ], len );
rdr->pos += len;
}

and everything would work. At least in reality it would. Still this code
isn't standards compliant C for some pretty weird reasons. (The reason being
padding bits, if you care.)

But I'd recommend staying independent of such profane issues as endiannes.
With pretty little effort a solution independent of endianness can be made
(our readers and writers). The only solution guaranteed to work is reading
byte by byte and shifting-and-or'ing them together. (See below.)


Do I have to implement these method as they are
here or do I need to modify them?

Nope, I think (but beware of mistakes I might have made...) that should work
as posted.

Moreover, even though it might be too
naive a question for you, I couldn't understand the return statements above

No question is too naive I think. I'm sorry if you took it to heart that I
regarded the struct-trickery you posted as 'naive'. It wasn't intended as
cruel finger-poking but more as constructive critique.

What I find naive (not of you, mind you), however, is that Eicon dares to
post such crap on the net, in a guide meant for beginners.


return reader_readb( rdr, 0 )
| reader_readb( rdr, 0 ) << 8;
| reader_readb( rdr, 0 ) << 16;
| reader_readb( rdr, 0 ) << 24;
I could understand neither the syntax nor the meaning. Would you please
extend your kindness and explain?

Sure. (Ummm, on a side note -- perhaps it's better to remove the semicolons
after 8 and 16. Those are copy&paste artefacts which pretty much make that
code uncompileable. Mind you, this code was written in a hurry and without
checking it. You might be in for more such surprises.)

First, let's sort out the evaluation order and precedence issues with all
those operators involved. The above code (sans the two semicolons) is
equivalent to this:

return reader_readb( rdr, 0 )
| ( reader_readb( rdr, 0 ) << 8 )
| ( reader_readb( rdr, 0 ) << 16 )
| ( reader_readb( rdr, 0 ) << 24 );

Perhaps the parenthesis make the precedence clearer.

So basically the return statement is a large "binary or" of four values.
Since 'or' is specified as being evaluated from left to right, the
reader_readb() functions are called in the same order as written.

Assume we have a reader set up, with the four bytes after the cursor
position being

|....|....|....|....|
| DD | CC | BB | AA |
|....|....|....|....|
^^

(^^ indicates the current cursor position)

So the topmost reader_readb() would read the first byte (0xdd) from the
reader. Leaving us with

|....|....|....|....|
| DD | CC | BB | AA |
|....|....|....|....|
^^

That will have the next reader_readb() return 0xcc and so on. If we insert
the values just read into our return statements in place of the operations
which yielded them we have:

return 0xdd
| ( 0xcc << 8 )
| ( 0xbb << 16 )
| ( 0xaa << 24 );

The next thing that's executed is the shift statements which shift their
operands to the left by one, two, or three bytes. To replace them
again:

return 0xdd
| 0xcc00
| 0xbb0000
| 0xaa000000;

Or pretty-printed with leading zeros inserted:

return 0x000000dd
| 0x0000cc00
| 0x00bb0000
| 0xaa000000;

Finally, the binary-or operations combine them:

return 0xaabbccdd;

Which -- converted back to decimal -- is 2864434397. Yay! (Mind you, on your
Intel machine that number is stored in memory again in a mangled fashion,
but that's not something you need to care about.)


/* Reads a CAPI length. */
unsigned short reader_readlen( struct reader *rdr ) {
unsigned char l;
l = reader_readb( rdr, 0 );
if( l != 0xff )
return l;
return reader_readw( rdr, 0 );
}

if( l != 0xff ). What does it mean?

CAPI structs start with a length determinant. This length determinant gives
the number of bytes succeeding it, which are regarded as being part of the
structs contents.

Now, with a byte we could only encode structs with a maximum size of 255
bytes, which really isn't much. Unfortunately, this wasn't thought about
early, as earlier no CAPI structs were defined that could grow so large.

(This partly has to do with the inner workings of Q.931 -- the ISDN
D-channel signalling protocol. This protocol supports transmitting so called
"information elements" which can't grow larger that this as well.)

If someone had thought about this early on, we'd probably have length values
encoded as CAPI-words or so -- but this isn't the case.

As later on the need arose to encode bigger length values, the CAPI makers
invented a way to escape to a different encoding scheme. So if the length
value contains the special value 255 (0xff in hexadecimal) it's disregarded
and another CAPI-word is read, which then gives the length of the following
struct.

This basically is what the reader_readlen() function does -- it reads a byte
and returns this as the length, unless the byte contains the special escape
code 255. In this case another word is read, which then is returned.

/* Initializes a reader such that the contents of a CAPI struct can be
* read from it. The source-readers cursor position is moved behind
* the struct.
*/
void reader_readstruct( struct reader *dest, struct reader *src ) {
unsigned short l;

l = reader_readlen( rdr );
dest->blk = src->blk;
dest->pos = src->pos;
dest->size = src->size;
if( dest->pos + l < dest->size )
dest->size = dest->pos + l;
reader_skip( src, l );
}

You had no questions regarding this function -- I think, however, that an
explaination might be in order. You probably know this already, but the
intended way of using it is:

void handle_connect_ind( struct reader *rdr ) {
unsigned long plci;
unsigned short cip_value;
struct reader called_party;
struct reader calling_party;
struct reader called_sub;

/* Presumably the message header already has been read at this
* point.
*/

plci = reader_readdw( rdr, 0 );
cip_value = reader_readw( rdr, 0 );

/* Now here comes a struct "Called party number". */
reader_readstruct( &called_party, rdr );

/* Here we could read from the &called_party reader. But to
* illustrate the point, we won't.
*/

/* Next is the calling party number. */
reader_readstruct( &calling_party, rdr );

/* Next is the called party subaddress. */
reader_readstruct( &called_sub, rdr );

/* You now have three readers containing only the contents of their
* respective structs. You now can read from them, even after you
* have read additional bytes from their surrounding reader.
*/

/* For example: */
handle_called_party_num( &called_party );
}

I probably didn't need to explain that, but it can't do no harm.


appl_id = reader_readw( &rdr, 0 );
command = reader_readb( &rdr, 0 );
subcommand = reader_readb( &rdr, 0 );
msgnum = reader_readw( &rdr, 0 );

/* Here you could do your dispatch, based on command and
* subcommand.
*/
}

Would you please be a bit more specific about the dispatch?

With dispatch I just mean acting differently on different messages. Such as

#define CAPI_REQ 0x80
#define CAPI_CONF 0x81
#define CAPI_IND 0x82
#define CAPI_RESP 0x83

#define CAPI_CONNECT 0x02
#define CAPI_CONNECT_ACTIVE 0x03
#define CAPI_DISCONNECT 0x04

switch( command ) {
case CAPI_CONNECT:
if( CAPI_IND == subcommand )
handle_incoming_call();
else if( CAPI_CONF == subcommand )
; /* Do stuff. */
break;

case CAPI_DISCONNECT:
if( CAPI_IND == subcommand )
handle_hangup();
break;
}

And so on and on...


You can probably implement the counterpart (a 'writer', that is)
easily. For the remainder of this post I'm assuming you've done so :-)

I hope so, once I understand the reader!

If you don't mind, feel free to post your code, so we can have a look! I'd
be interested in how you implement the struct-writing thing...



If you try this out you should notice one more thing: your audio will
sound absolutely horrible. All audio-data needs to be sent down to
CAPI bit-flipped -- that is, every byte you send down has to be
mirrored. That is, 0x01 becomes 0x80 and so on. Basically
[abcd|efgh] --> [hgfe|dcba].

Something like this could help:

void flip( unsigned char data, unsigned long len ) {
unsigned long i;
static const unsigned char flipt[256] = {
0x00, 0x80,
/* Lots of elements which you can fill out. */
..., 0xFF
};
for( i=0; i<len; i++ )
data[i] = flipt[data[i]];
}


I have already filled the table. But I didn't understand how the loop
reverses the bits. I simply filled the table but I didn't know what those
hexadecimal numbers stand for.

Hmmm, if you didn't know what those numbers stand for, how did you then fill
the table?

The idea of this function is as follows: (As soon as you make the first
parameter an unsigned char * instead of a plain unsigned char. Typo again.)

You have an array of bytes, each of which can take a value from 0 to 255.
(At least on systems where a CHAR_BIT == 8. But I don't think CAPI is
defined on systems where there are more than eight bits to a byte or even
that such systems are still in widespread use.)

What we want in the end is the same array, but with each byte having the
bit-order reversed. So we take each bytes value (0-255 again) and use it as
an index into our flip-table. This table contains, at the proper positions,
the byte with bit-order reversed. This reversed value then is written back
into the byte-array we started with in the first place.

There's not much more to be said about that...



Last question, is there anything else I need to implement apart from the
ones you have explained so far?

No, I don't think there's much more left to do then. Even if it might sound
daunting at first, a basic CAPI application which can accept calls and
stream audio in both directions can be written in a few hundred lines of
code.

Best regards,

Danilo
.



Relevant Pages

  • Re: Streaming Audio to CAPI
    ... To do that we need to stream audio to CAPI ... messages and the reader functions for CONFIRMATION and INDICATION messages. ... struct reader rdr; ... reader_init(&rdr, msg, 2); ...
    (comp.dcom.isdn.capi)
  • Win2000 Smartcard logon problem
    ... I develop a SCard Reader compatible with CCID USB spec v.1.00. ... The Reader works fine, I have installed a certificate on a smartcard using the reader and all the tests where OK. ... The RDR answers these commands as it suppose to ...
    (microsoft.public.platformsdk.security)
  • Re: Change-Ereignis / Magnetkartenleser
    ... Dim Rdr As SU ... 'Zieh an dieser Stelle mal eine Karte durch den Reader. ... Gruss - Ingrid ...
    (microsoft.public.de.access)