[Cs-dev] API examples for looking over
| Date | 2005-10-04 23:54 |
| From | Iain Duncan |
| Subject | [Cs-dev] API examples for looking over |
| Attachments | cs_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 |
| Date | 2005-10-05 11:54 |
| From | Istvan Varga |
| Subject | Re: [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 |
| Date | 2005-10-05 21:16 |
| From | Iain Duncan |
| Subject | Re: [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 |
| Date | 2005-10-06 09:06 |
| From | jpff@codemist.co.uk |
| Subject | Re: [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 |
| Date | 2005-10-06 13:24 |
| From | Istvan Varga |
| Subject | Re: [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 |
| Date | 2005-10-06 20:27 |
| From | Iain Duncan |
| Subject | Re: [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 |
| Date | 2005-10-06 20:31 |
| From | Iain Duncan |
| Subject | Re: [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 |
| Date | 2005-10-06 22:20 |
| From | Istvan Varga |
| Subject | Re: [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 |