Here's an example of how to write your own
sound player application:
// playsound_noph.c - sound player application
//
// Compile and link as follows:
//
// qcc -l media playsound_noph.c
//
//
// Usage:
//
// playsound_noph [-i -v] url
//
// Options:
// -i interactive mode
// -v be verbose
//
// url
// "file://..." or "http://... (... = full path to a soundfile )
//
// Commands available in interactive mode:
// p play/pause selection
// s stop selection
// q exit the application
//
// Note:
// you must press the "Enter" key after each interactive command
//
// Usage Examples:
//
// playsound_noph -i file:///media/waves/revenge.wav
// will play revenge.wav and wait for user input when done
//
// playsound_noph http://slonet.org/~rloomis/mail6a.wav
// will play mail6a.wav and quit when done playing
//
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <curses.h>
#include <pthread.h>
#include <dlfcn.h>
#include <photon/Mv.h>
#include <photon/MvReg.h>
#include <sys/asoundlib.h>
#include <sys/ioctl.h>
#include <errno.h>
typedef struct tag
{
pthread_mutex_t mutex;
pthread_cond_t cond;
int wakeUp;
pthread_t userInputThread_id;
MvPluginState_t lastState;
int endOfStream;
char *userInput;
} player_t;
player_t player;
int bInteractive = FALSE; // quit when done playing if TRUE
int bVerbose = FALSE; // be verbose
// This function is called if we have been launched from the background.
// This will terminate the user input thread since we don't have stdin.
void SIGTTIN_Handler( int sig_number )
{
signal( SIGTTIN, SIG_IGN );
//attempted background tty read
bInteractive = FALSE;
bVerbose = FALSE;
}
int LoadDll( const char *name, MvPluginCtrl_t *pctrl )
{
char *path;
if( ( path = strdup(name) ) != NULL )
{
void *dll;
if( ( dll = dlopen( path, RTLD_LOCAL ) ) != NULL )
{
MvInitF_t *init;
if( bVerbose )
{
fprintf( stderr,"Loaded DLL %p '%s'\n", dll, name );
}
pctrl->APIversion = MV_API_VERSION;
pctrl->name = path;
if( ( init = dlsym( dll, "MvInit" ) ) != NULL )
{
if(!MvGetMpSetting( &pctrl->setup) )
{
MvGetMpDefaultSetting( &pctrl->setup);
}
if( init( pctrl ) == 0 ) // calling plugin init function
{
if( bVerbose )
{
fprintf(stderr, "MvInit() succeeded\n" );
}
pctrl->dll_handle = dll;
return 0;
}
else if( bVerbose )
{
fprintf( stderr, "LoadDll(): MvInit() in \"%s\" failed\n", name );
}
}
else
{
fprintf( stderr, "LoadDll(): MvInit not found in \"%s\" (%s)\n", name, dlerror() );
}
pctrl->name = NULL;
dlclose( dll );
}
else
{
fprintf( stderr, " LoadDll(): dlopen(\"%s\") failed (%s)\n", name, dlerror() );
}
free( path );
}
else
{
fprintf( stderr, "LoadDll(): no memory\n" );
}
return -1;
}
// This thread gets user input from the keyboard
// and wakes up main().
static void* UserInputThread( void *arg )
{
char buffer[4];
pthread_detach( pthread_self() );
//while( (c = getchar()) != EOF )
while( bInteractive && fgets( buffer, 4, stdin ) != NULL )
{
if( bVerbose )
{
fprintf(stderr,"In %s UserInputThread() You pressed %s\n",__FILE__,buffer);
}
pthread_mutex_lock( &player.mutex );
player.userInput = buffer;
player.wakeUp = TRUE;
//wake up main thread
pthread_cond_signal( &player.cond );
pthread_mutex_unlock( &player.mutex );
}
//fprintf(stderr,"In %s UserInputThread() returning\n",__FILE__);
return arg;
}
// This function is called when the plugin has info
// to send back to the application.
// This function is executing from within the dll thread.
static void mpcallback( MvPluginCtrl_t *pctrl, MvEventFlags_t change, MvPluginStatus_t const *status )
{
static int hasPlayed = FALSE;
pthread_mutex_lock( &player.mutex );
if( change & MVS_PLUGIN_STATE )
{
player.lastState = status->state;
switch( status->state )
{
case MV_DEAD:
if( bVerbose )
{
fprintf(stderr,"mpcallback() STATE = MV_DEAD\n");
}
player.endOfStream = TRUE;
break;
case MV_CLOSED:
if( bVerbose )
{
fprintf(stderr,"mpcallback() STATE = MV_CLOSED\n");
}
player.endOfStream = TRUE;
break;
case MV_OPENING:
if( bVerbose )
{
fprintf(stderr,"mpcallback() STATE = MV_OPENING\n");
}
break;
case MV_STOPPED:
if( bVerbose )
{
fprintf(stderr,"mpcallback() STATE = MV_STOPPED\n");
}
if( !bInteractive && hasPlayed )
{
pctrl->calls->terminate( pctrl );
break;
}
else if( hasPlayed )
{
// rewind to the beginning of the file
MvCommandData_t cmdData = {0};
MvPlaybackParams_t playback_parms = {0};
cmdData.pluginCtrl = pctrl;
cmdData.cmdType = CMD_PLUGIN_SEEK_TO;
cmdData.which = MVP_DELTA | MVP_POSITION;
cmdData.param = &playback_parms;
playback_parms.delta = 0;
playback_parms.position = 0;
pctrl->calls->command( &cmdData );
}
hasPlayed = FALSE;
break;
case MV_PAUSED:
if( bVerbose )
{
fprintf(stderr,"mpcallback() STATE = MV_PAUSED\n");
}
break;
case MV_PREFETCHING:
if( bVerbose )
{
fprintf(stderr,"mpcallback() STATE = MV_PREFETCHING\n");
}
break;
case MV_PLAYING:
if( bVerbose )
{
fprintf(stderr," mpcallback() STATE = MV_PLAYING\n");
}
hasPlayed = TRUE;
break;
default:
break;
} // end switch
} // end if
if( change & MVS_ERRORMSG )
{
fprintf(stderr,"%s\n", status->errormsg);
player.endOfStream = TRUE;
}
if( player.endOfStream )
{
//wake up main thread
player.wakeUp = TRUE;
pthread_cond_signal( &player.cond );
}
pthread_mutex_unlock( &player.mutex );
} //end mpcallback
MvPluginCtrl_t* load_plugin( const char *name )
{
MvPluginCtrl_t *pc;
if( ( pc = malloc(sizeof(MvPluginCtrl_t)) ) != NULL )
{
memset( pc, 0, sizeof(MvPluginCtrl_t) );
pc->prio = getprio( 0 );
pc->cb = mpcallback;
if( LoadDll( name, pc ) == 0 )
{
//InitAudio( &audio);
}
else
{
free( pc );
pc = NULL;
}
}
return pc;
}
// This function is called from main() and
// sends the user input translated command to the plugin dll.
void SendCommand( MvPluginCtrl_t *pctrl, char *input )
{
MvCommandData_t cmdData = {0};
cmdData.pluginCtrl = pctrl;
if( !input || !pctrl )
{
return;
}
switch( input[0] )
{
case 'p':
case 'P':
// pause/play command
if( player.lastState == MV_PAUSED || player.lastState == MV_STOPPED )
{
cmdData.cmdType = CMD_PLUGIN_START;
cmdData.which = MVP_NONE;
pctrl->calls->command( &cmdData );
}
else
{
cmdData.cmdType = CMD_PLUGIN_PAUSE;
cmdData.which = MVP_NONE;
pctrl->calls->command( &cmdData );
}
break;
case 'q':
case 'Q':
// quit command
pctrl->calls->terminate( pctrl );
break;
case 's':
case 'S':
// stop command
cmdData.cmdType = CMD_PLUGIN_STOP;
cmdData.which = MVP_NONE;
pctrl->calls->command( &cmdData );
break;
default:
break;
}
}
int main( int argc, char *argv[] )
{
MvPluginCtrl_t *pctrl;
char* file;
int c = 0;
signal( SIGTTIN, SIGTTIN_Handler ); // set signal handler
while( ( c = getopt( argc, argv, "iv" ) ) != -1 )
{
switch( c )
{
case 'i':
// printf( "in getopt seeing q\n" );
bInteractive = TRUE;
break;
case 'v':
// printf( "in getopt seeing v\n" );
bVerbose = TRUE;
break;
default:
break;
}
}
if( optind >= argc )
{
fprintf(stderr,"\nInvalid argument: type 'use playsound_noph' for usage options\n");
exit(1);
}
else
{
file = argv[optind];
if( bVerbose )
{
fprintf(stderr,"file = %s\n",file);
}
if( !strstr( file, "file://" ) && !strstr( file, "http://" ))
{
//append "file://" to string
file = alloca( strlen( file) + 8 );
sprintf( file,"%s%s","file://",argv[argc -1] );
}
}
pthread_cond_init( &player.cond, NULL);
pthread_mutex_init( &player.mutex, NULL );
pctrl = load_plugin( "soundfile_noph.so");
if( pctrl )
{
// success
// send open_url command to plugin
MvCommandData_t cmdData = {0};
MvPlaybackParams_t playback_parms = {0};
cmdData.pluginCtrl = pctrl;
cmdData.cmdType = CMD_PLUGIN_OPEN_URLS;
cmdData.which = MVP_DELTA;
cmdData.nodeString = getenv("HOSTNAME");
cmdData.displayString = getenv("PHOTON");
cmdData.urls = (char**) &file;
cmdData.param = &playback_parms;
playback_parms.delta = 1;
pctrl->calls->command( &cmdData );
sleep(1);
// send start command to plugin
cmdData.cmdType = CMD_PLUGIN_START;
cmdData.which = MVP_NONE;
pctrl->calls->command( &cmdData );
}
// create the user input thread
if( bInteractive )
{
pthread_create( &player.userInputThread_id, NULL, UserInputThread, NULL ) ;
}
// main loop
while( !player.wakeUp )
{
pthread_mutex_lock( &player.mutex );
pthread_cond_wait( &player.cond, &player.mutex );
if( player.endOfStream )
{
delay(100); // give time to clean up
break;
}
if( player.userInput )
{
// fprintf(stderr,"In %s main You pressed %c (%d)\n",__FILE__,player.userInput,player.userInput);
player.wakeUp = 0;
pthread_mutex_unlock( &player.mutex );
SendCommand( pctrl, player.userInput );
pthread_mutex_lock( &player.mutex );
player.userInput = 0;
}
pthread_mutex_unlock( &player.mutex );
}
dlclose( pctrl->dll_handle );
pthread_mutex_destroy( &player.mutex );
pthread_cond_destroy( &player.cond );
return 0;
}