[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 |