Csound Csound-dev Csound-tekno Search About

[Cs-dev] API examples for looking over

Date2005-10-04 23:54
FromIain Duncan
Subject[Cs-dev] API examples for looking over
Attachmentscs_simple_1.c  cs_simple_2.c  cs_simple.csd  cs_simple_2.csd  
Hi everyone, I would like my heavilly commented simple examples to 
eventually be usable as tutorials. Hoping someone can take a moment to 
make sure that I have done nothing wrong or comment or poor style.

One question for example 2, what should the thread routine return?

Thanks
Iain

Date2005-10-05 11:54
FromIstvan Varga
SubjectRe: [Cs-dev] API examples for looking over
Iain Duncan wrote:

> // cs_simple_2.c
> // October 2005, by Iain Duncan with much assistance from the csound dev list. 
> // If this has been helpful to you, please visit www.xornot.com
> 
> // This example demonstrates spawning a csound thread in which to perform csound
> // and making an api call in the csound thread to get a table value back to the host.
> // The host can then continue doing it's own thing in the main thread.

Note that C (at least pre-C99) only supports /* */ style comments.
This should not be a problem, though, as long as you do not try to
compile the example with some older compilers.

> // the routine for the csound thread
> // csoundCreateThread requires the thread routine to return a unintptr_t pointer, and take a void pointer as an arg
> uintptr_t *csound_thread_routine( void *user_data );

This should actually be uintptr_t csound_thread_routine( void *user_data ),
that is, the function is expected to return an unsigned integer of type
uintptr_t, rather than a pointer to such integer value.

> // an empty user_data pointer ( would be the data for the csound thread routine )
> void *user_data = NULL;
> 
> // pointer to an instance of csound, this should always be of type CSOUND
> CSOUND *csound;

Instead of these global variables, you can also define a structure type
that holds the parameters to be passed to the Csound thread, for example:

typedef struct {
     CSOUND *csound;
     int    argc;
     char   **argv;
} csThreadParams;

(change name as needed)

> int main( void )
> {

Now, instead of hardcoding the command line flags, another option
is to take them from the actual command line:

int main(int argc, char **argv)
{
     csThreadParams p;

     p.argc = argc;
     p.argv = argv;
     p.csound = csoundCreate(NULL);

> 	// create an instance of csound with no user data
> 	csound = csoundCreate( NULL );

While not critical, you may want to check if csoundCreate() failed
and returned NULL. This rarely happens, but when it does, the Csound
thread will crash.

> 	printf( "\n\n HOST: SPAWNING CSOUND THREAD.\n\n" );
> 	
> 	// create a csound thread
> 	// returns a thread handle pointer, takes pointers to the thread routine and to user data
> 	void *cs_thread = csoundCreateThread(csound_thread_routine, user_data );

Using the above structure to pass data to the newly created thread:

void *cs_thread = csoundCreateThread(csound_thread_routine, (void*) &p);

By the way, another portability note: the C language did not allow
mixing declarations and code before the C99 standard.
So, if compatibility with older non-gcc compilers is important,
you may want to move the declarations of variables to the beginning
of a { } block.

> 	printf( "\n\n HOST: CSOUND THREAD SUCCESSFULLY SPAWNED.\n\n" );
> 	
> 	// wait on the csound thread until it is done, this keeps main open until csound is finished
> 	uintptr_t cs_result = csoundJoinThread( cs_thread );
> 
> 	printf( "\n\n HOST: CSOUND THREAD HAS COMPLETED AND BEEN JOINED. EXITING. \n\n" );
> 	csoundDestroy(csound);
> 	
> 	exit(0);

You may also return the value from the thread routine as error code:

         return ((int) cs_result >= 0 ? 0 : (int) cs_result);

> // This is the thread function in which csound will run.
> // all csound api calls should be in here
> uintptr_t *csound_thread_routine(void *user_data)

Again, the type returned is uintptr_t, and not uintptr_t*.

> {
> 	
> 	// compile csound, to add other options just put them in the csd file
> 	const char *argv[] = {"csound", "cs_simple_2.csd" };

This is where the "user data" pointer can be made useful:

         CSOUND *csound = ((csThreadParams*) user_data)->csound;
         int argc = ((csThreadParams*) user_data)->argc;
         char **argv = ((csThreadParams*) user_data)->argv;

> 	csoundCompile(csound, 2, (char **)argv);

Now, in the case of csoundCompile(), failure is much more likely to
occur, for example as a result of not finding the CSD file, or syntax
errors in the orchestra. So, it is definitely worth checking the
return value:

         int err = csoundCompile(csound, argc, argv);
         if (err != 0)
                 return (uintptr_t) err;

> 	// keep processing csound kpasses as long as the flag holds true ( starts that way )
> 	while( cs_performing ) 
> 	{
> 	    // Csound will call cb_thread_yield.
> 	    cs_performing = !csoundPerformKsmps(csound);

This has the potential problem of trying to access the table when a
fatal error may already have occured, so it is best to terminate the
loop as soon as csoundPerformKsmps() returns a non-zero value.

         while (1) {
                 err = csoundPerformKsmps(csound));
                 cs_performing = !err;
                 if (!cs_performing)
                         break;

Note that cs_performing is not actually needed. However, the return
value is preserved for later use.

> 	// csound has finished performing, so clean up
> 	csoundCleanup(csound);

You may also call csoundReset() (implies csoundCleanup()) to do a
"full" cleanup, freeing all memory allocated by the compile and
perform functions, closing files, etc.

> 	// hmm, what should this return now?
> 	return 1;

It can return anything, and the same value will be returned by
csoundJoinThread(). In this example, it may return the error code
from csoundPerformKsmps():

         return (uintptr_t) err;


-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
_______________________________________________
Csound-devel mailing list
Csound-devel@lists.sourceforge.net

Date2005-10-05 21:16
FromIain Duncan
SubjectRe: [Cs-dev] API examples for looking over
Thanks for all the feedback. One issue is that I want to keep the 
examples as simple as possible, so I may not do the structure with 
command line args until a later example. I know when I read tutorial 
code, I find it easy to learn if only one area is changed per tutorial. 
But I will certainly include that style later. I think that could be the 
basis for the 3rd example.

A couple of questions:

What is the reason for using unintptr_t now instead of int? I would like 
to add a comment explaining that.

Do you think anyone is using compilers old enough to require making it 
pre C99 compatible? I can certainly change it all, but I find C99 style 
much more readable for new programmers, a concern for tutorials.

What should the csound thread routine return on success? 1?

Thanks for the comments on the while loop, that makes sense to me. I 
guess we can't make any api calls before the first pass of 
csoundPerformKsmps? ( Which would allow me to just make it a do-while loop )

Will add error handling to the others then too and post back for further 
check ups.

Thanks
Iain

Istvan Varga wrote:
> Iain Duncan wrote:
> 
>> // cs_simple_2.c
>> // October 2005, by Iain Duncan with much assistance from the csound 
>> dev list. // If this has been helpful to you, please visit www.xornot.com
>>
>> // This example demonstrates spawning a csound thread in which to 
>> perform csound
>> // and making an api call in the csound thread to get a table value 
>> back to the host.
>> // The host can then continue doing it's own thing in the main thread.
> 
> 
> Note that C (at least pre-C99) only supports /* */ style comments.
> This should not be a problem, though, as long as you do not try to
> compile the example with some older compilers.
> 
>> // the routine for the csound thread
>> // csoundCreateThread requires the thread routine to return a 
>> unintptr_t pointer, and take a void pointer as an arg
>> uintptr_t *csound_thread_routine( void *user_data );
> 
> 
> This should actually be uintptr_t csound_thread_routine( void *user_data ),
> that is, the function is expected to return an unsigned integer of type
> uintptr_t, rather than a pointer to such integer value.
> 
>> // an empty user_data pointer ( would be the data for the csound 
>> thread routine )
>> void *user_data = NULL;
>>
>> // pointer to an instance of csound, this should always be of type CSOUND
>> CSOUND *csound;
> 
> 
> Instead of these global variables, you can also define a structure type
> that holds the parameters to be passed to the Csound thread, for example:
> 
> typedef struct {
>     CSOUND *csound;
>     int    argc;
>     char   **argv;
> } csThreadParams;
> 
> (change name as needed)
> 
>> int main( void )
>> {
> 
> 
> Now, instead of hardcoding the command line flags, another option
> is to take them from the actual command line:
> 
> int main(int argc, char **argv)
> {
>     csThreadParams p;
> 
>     p.argc = argc;
>     p.argv = argv;
>     p.csound = csoundCreate(NULL);
> 
>>     // create an instance of csound with no user data
>>     csound = csoundCreate( NULL );
> 
> 
> While not critical, you may want to check if csoundCreate() failed
> and returned NULL. This rarely happens, but when it does, the Csound
> thread will crash.
> 
>>     printf( "\n\n HOST: SPAWNING CSOUND THREAD.\n\n" );
>>     
>>     // create a csound thread
>>     // returns a thread handle pointer, takes pointers to the thread 
>> routine and to user data
>>     void *cs_thread = csoundCreateThread(csound_thread_routine, 
>> user_data );
> 
> 
> Using the above structure to pass data to the newly created thread:
> 
> void *cs_thread = csoundCreateThread(csound_thread_routine, (void*) &p);
> 
> By the way, another portability note: the C language did not allow
> mixing declarations and code before the C99 standard.
> So, if compatibility with older non-gcc compilers is important,
> you may want to move the declarations of variables to the beginning
> of a { } block.
> 
>>     printf( "\n\n HOST: CSOUND THREAD SUCCESSFULLY SPAWNED.\n\n" );
>>     
>>     // wait on the csound thread until it is done, this keeps main 
>> open until csound is finished
>>     uintptr_t cs_result = csoundJoinThread( cs_thread );
>>
>>     printf( "\n\n HOST: CSOUND THREAD HAS COMPLETED AND BEEN JOINED. 
>> EXITING. \n\n" );
>>     csoundDestroy(csound);
>>     
>>     exit(0);
> 
> 
> You may also return the value from the thread routine as error code:
> 
>         return ((int) cs_result >= 0 ? 0 : (int) cs_result);
> 
>> // This is the thread function in which csound will run.
>> // all csound api calls should be in here
>> uintptr_t *csound_thread_routine(void *user_data)
> 
> 
> Again, the type returned is uintptr_t, and not uintptr_t*.
> 
>> {
>>     
>>     // compile csound, to add other options just put them in the csd file
>>     const char *argv[] = {"csound", "cs_simple_2.csd" };
> 
> 
> This is where the "user data" pointer can be made useful:
> 
>         CSOUND *csound = ((csThreadParams*) user_data)->csound;
>         int argc = ((csThreadParams*) user_data)->argc;
>         char **argv = ((csThreadParams*) user_data)->argv;
> 
>>     csoundCompile(csound, 2, (char **)argv);
> 
> 
> Now, in the case of csoundCompile(), failure is much more likely to
> occur, for example as a result of not finding the CSD file, or syntax
> errors in the orchestra. So, it is definitely worth checking the
> return value:
> 
>         int err = csoundCompile(csound, argc, argv);
>         if (err != 0)
>                 return (uintptr_t) err;
> 
>>     // keep processing csound kpasses as long as the flag holds true ( 
>> starts that way )
>>     while( cs_performing )     {
>>         // Csound will call cb_thread_yield.
>>         cs_performing = !csoundPerformKsmps(csound);
> 
> 
> This has the potential problem of trying to access the table when a
> fatal error may already have occured, so it is best to terminate the
> loop as soon as csoundPerformKsmps() returns a non-zero value.
> 
>         while (1) {
>                 err = csoundPerformKsmps(csound));
>                 cs_performing = !err;
>                 if (!cs_performing)
>                         break;
> 
> Note that cs_performing is not actually needed. However, the return
> value is preserved for later use.
> 
>>     // csound has finished performing, so clean up
>>     csoundCleanup(csound);
> 
> 
> You may also call csoundReset() (implies csoundCleanup()) to do a
> "full" cleanup, freeing all memory allocated by the compile and
> perform functions, closing files, etc.
> 
>>     // hmm, what should this return now?
>>     return 1;
> 
> 
> It can return anything, and the same value will be returned by
> csoundJoinThread(). In this example, it may return the error code
> from csoundPerformKsmps():
> 
>         return (uintptr_t) err;
> 
> 
> -------------------------------------------------------
> This SF.Net email is sponsored by:
> Power Architecture Resource Center: Free content, downloads, discussions,
> and more. http://solutions.newsforge.com/ibmarch.tmpl
> _______________________________________________
> Csound-devel mailing list
> Csound-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/csound-devel
> 


-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
_______________________________________________
Csound-devel mailing list
Csound-devel@lists.sourceforge.net

Date2005-10-06 09:06
Fromjpff@codemist.co.uk
SubjectRe: [Cs-dev] API examples for looking over
Do you think anyone is using compilers old enough to require making it 
pre C99 compatible?
    Yes.  I come across C89 compilers regularly, and indeed it was a
    much better language that C99.

==John ffitch


-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
_______________________________________________
Csound-devel mailing list
Csound-devel@lists.sourceforge.net

Date2005-10-06 13:24
FromIstvan Varga
SubjectRe: [Cs-dev] API examples for looking over
Iain Duncan wrote:

> Thanks for all the feedback. One issue is that I want to keep the 
> examples as simple as possible, so I may not do the structure with 
> command line args until a later example. I know when I read tutorial 
> code, I find it easy to learn if only one area is changed per tutorial. 

Well, bad practices like ignoring the return value of a function
that can easily fail are best avoided, even in tutorials.

> What is the reason for using unintptr_t now instead of int? I would like 
> to add a comment explaining that.

It is closer to the types used by the native thread interfaces,
i.e. unsigned int on Win32, and void* on POSIX systems.
As int is expected to have smaller or equal size, you can just
cast the 'int' value when returning from the thread routine, and
then cast back to int when calling csoundJoinThread(), without
losing bits. Although, one reason for reverting back to int may
be that Win64 possibly truncates the return value, but I am not
sure about this.

> Do you think anyone is using compilers old enough to require making it 
> pre C99 compatible? I can certainly change it all, but I find C99 style 
> much more readable for new programmers, a concern for tutorials.

At least some people use the Microsoft compiler, and that does not
support C99 (and I think it never will, but hope that I am wrong).
To keep // comments, and mixed declarations and code, you can also
rename the file to have .cpp extension; C++ does support the above
features for a long time.
I am not sure how // style comments and mixing declarations with
statements improve the readability of code, though.

> What should the csound thread routine return on success? 1?

It returns whatever you want, and the same value is then returned by
csoundJoinThread() as well. It is up to you to decide how to use this
feature (including the possibility of not using it at all, by returning
0 in the thread routine, and ignoring the return value of
csoundJoinThread()). For error codes, 0 is a standard value for success.

> Thanks for the comments on the while loop, that makes sense to me. I 
> guess we can't make any api calls before the first pass of 
> csoundPerformKsmps? ( Which would allow me to just make it a do-while 
> loop )

No, the (in this case probably only theoretical) problem was
calling API functions other than cleanup, reset, or destroy
when compiling or performing an instance of Csound may already
have failed in a fatal way, and thus continuing to use it may
have undefined results.
It varies (and so should eventually be documented) when a particular
API function is safe to use, but the first call of csoundPerformKsmps()
is not special in any way, that is, as long as it returns 0.


-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
_______________________________________________
Csound-devel mailing list
Csound-devel@lists.sourceforge.net

Date2005-10-06 20:27
FromIain Duncan
SubjectRe: [Cs-dev] API examples for looking over
> Do you think anyone is using compilers old enough to require making it 
> pre C99 compatible?
>     Yes.  I come across C89 compilers regularly, and indeed it was a
>     much better language that C99.

Can you tell me which things in my examples need to be changed to make 
it C89 compatible?

Thanks
Iain


-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
_______________________________________________
Csound-devel mailing list
Csound-devel@lists.sourceforge.net

Date2005-10-06 20:31
FromIain Duncan
SubjectRe: [Cs-dev] API examples for looking over
>> Thanks for all the feedback. One issue is that I want to keep the 
>> examples as simple as possible, so I may not do the structure with 
>> command line args until a later example. I know when I read tutorial 
>> code, I find it easy to learn if only one area is changed per tutorial. 
> 
> 
> Well, bad practices like ignoring the return value of a function
> that can easily fail are best avoided, even in tutorials.

Thanks, it was not immediately obvious to me from reading csound.h what 
the return values should be and what should be done with them. If you 
have the time, I would like to see how you would correct that issue.

>> What is the reason for using unintptr_t now instead of int? I would 
>> like to add a comment explaining that.
> 
> 
> It is closer to the types used by the native thread interfaces,
> i.e. unsigned int on Win32, and void* on POSIX systems.
> As int is expected to have smaller or equal size, you can just
> cast the 'int' value when returning from the thread routine, and
> then cast back to int when calling csoundJoinThread(), without
> losing bits. Although, one reason for reverting back to int may
> be that Win64 possibly truncates the return value, but I am not
> sure about this.
> 
>> Do you think anyone is using compilers old enough to require making it 
>> pre C99 compatible? I can certainly change it all, but I find C99 
>> style much more readable for new programmers, a concern for tutorials.
> 
> 
> At least some people use the Microsoft compiler, and that does not
> support C99 (and I think it never will, but hope that I am wrong).
> To keep // comments, and mixed declarations and code, you can also
> rename the file to have .cpp extension; C++ does support the above
> features for a long time.
> I am not sure how // style comments and mixing declarations with
> statements improve the readability of code, though.
> 
>> What should the csound thread routine return on success? 1?
> 
> 
> It returns whatever you want, and the same value is then returned by
> csoundJoinThread() as well. It is up to you to decide how to use this
> feature (including the possibility of not using it at all, by returning
> 0 in the thread routine, and ignoring the return value of
> csoundJoinThread()). For error codes, 0 is a standard value for success.
> 
>> Thanks for the comments on the while loop, that makes sense to me. I 
>> guess we can't make any api calls before the first pass of 
>> csoundPerformKsmps? ( Which would allow me to just make it a do-while 
>> loop )
> 
> 
> No, the (in this case probably only theoretical) problem was
> calling API functions other than cleanup, reset, or destroy
> when compiling or performing an instance of Csound may already
> have failed in a fatal way, and thus continuing to use it may
> have undefined results.
> It varies (and so should eventually be documented) when a particular
> API function is safe to use, but the first call of csoundPerformKsmps()
> is not special in any way, that is, as long as it returns 0.

I'm lost, so it would be ok to call my table read api functions before 
the first csoundPerformKsmps() or not?

Thanks again
Iain


-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
_______________________________________________
Csound-devel mailing list
Csound-devel@lists.sourceforge.net

Date2005-10-06 22:20
FromIstvan Varga
SubjectRe: [Cs-dev] API examples for looking over
Iain Duncan wrote:

> Thanks, it was not immediately obvious to me from reading csound.h what 
> the return values should be and what should be done with them. If you 
> have the time, I would like to see how you would correct that issue.

csoundCompile() returns one of the following values:

0:
     the orchestra was successfully loaded, and is ready to
     perform
CSOUND_EXITJMP_SUCCESS (256):
     compilation was aborted, but not as a result of an error,
     but rather because something other than compiling an orchestra
     was done, such as printing usage with --help, or successfully
     running an utility with -U
CSOUND_SIGNAL (-5):
     SIGINT (Control-C) or SIGTERM was caught. The host should
     clean up and exit in this case.
anything else:
     a fatal error occured in some way, for example an input file
     was not found, syntax error in the orchestra, invalid command
     line option, failed runnig utility with -U, memory allocation
     error, and so on. A particular type of failure may be indicated
     by a specific negative error code.

in general, if anything other than zero is returned, you should
call csoundReset() to clean up after the error, instead of trying
to perform.

csoundPerformKsmps() and related functions have similar return
values, the only difference is that small positive values in the
range 1 to 255 mean that the end of the score or MIDI file has
been reached and performance is complete.
Again, if anything non-zero is returned, you should call
csoundReset() (implies csoundCleanup) or csoundDestroy() (implies
csoundReset).

> I'm lost, so it would be ok to call my table read api functions before 
> the first csoundPerformKsmps() or not?

It should be OK after a call to csoundCompile() that returned zero,
and also after any later csoundPerformKsmps() calls that return zero.
Of course, in the first case the table may not exist yet if it is
defined somewhere else than the orchestra header.


-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
_______________________________________________
Csound-devel mailing list
Csound-devel@lists.sourceforge.net