/*
* Copyright (c) 2004 Shiman Associates Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
/* Plays a single 44100Hz .wav file (stereo, signed 16bit samples) using MAS. */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include "mas/mas.h"
#include "mas/mas_core.h"
/* this client will set up the following chain of devices:
* source_wav --> endian --> sbuf --> [ mix --> anx ]
* local host | ---- target host ----
*/
/* convenience function */
void
fail( int32 err, char *msg )
{
masc_logerror( err, msg );
exit( EXIT_FAILURE );
}
int main(int argc, char* argv[])
{
int32 err;
mas_device_t source;
mas_device_t endian;
mas_device_t sbuf;
mas_device_t mix;
mas_channel_t local;
struct mas_package nugget;
struct mas_data_characteristic *dc;
struct stat st;
int16 pos;
int i;
/* MAS has a logging facility; use it if you wish */
masc_log_verbosity( MAS_VERBLVL_DEBUG );
masc_log_message( MAS_VERBLVL_INFO, "MAS wav demo.");
/* a bit of sanity checking */
if( argc<2 )
fail( mas_error(MERR_INVALID), "Please give file name(s) on command line." );
stat( argv[1], &st );
if( !S_ISREG(st.st_mode) )
{
fprintf( stderr, "'%s' is not a regular file.\n", argv[1] );
exit( EXIT_FAILURE );
}
/* Initiate a connection with MAS. */
err = mas_init();
if (err < 0)
{
fprintf( stderr, "Connection with server failed.\n" );
exit( EXIT_FAILURE );
}
/* --[ INSTANTIATE DEVICES ]--------------------------------------*/
/* Create the source device LOCALLY.
By default, we communicate with whereever MAS_HOST points (or
DISPLAY, if MAS_HOST wasn't set). However, we want the source
device to always sit on the same host as this client.
Therefore, we obtain a control channel to the local host. */
err = mas_get_local_control_channel( &local );
if ( err < 0 )
fail( err, "Failed to get local control channel" );
err = mas_asm_instantiate_device_on_channel( "source_wav", 0, 0, &source, local );
if( err<0 )
fail( err, "Failed to instantiate source_wav device" );
/* The other devices will sit on the target host */
err = mas_asm_instantiate_device( "endian", 0, 0, &endian );
if ( err < 0 )
fail( err, "Failed to instantiate endian converter device" );
/* If we're running over the network it's a good idea to have a
short (100ms by default) buffer on the other side */
err = mas_asm_instantiate_device( "sbuf", 0, 0, &sbuf );
if( err<0 )
fail( err, "Failed to instantiate sbuf" );
/* The mix device is already instantiated in the server. If you're
* sure that there's only one device of the given name in the
* server, you can use this function to get a handle. */
err = mas_asm_get_device_by_name( "mix", &mix );
if( err< 0 )
fail( err, "Failed to get mix device" );
/* --[ CONFIGURE AND CONNECT DEVICES ]----------------------- */
/* Currently the wav source device can only handle 44.1 kHz
stereo, signed short int data. This should change.
But anyways, since the endian's source port is already
configured, we can do this: */
err = mas_asm_connect_devices( source, endian, "source", "sink" );
if( err<0 )
fail( err, "Failed to connect source_wav->endian" );
/* the endian device is supposed to convert to whatever the host
system is... by configuring the source port we tell it to do that */
dc = masc_make_audio_basic_dc( MAS_LINEAR_FMT, 44100, 16, 2, MAS_HOST_ENDIAN_FMT );
err = mas_asm_connect_devices_dc( endian, sbuf, "source", "sink", dc);
if( err<0 )
fail( err, "Failed to connect endian->sbuf" );
/* Don't need the dc any more. This frees its memory. */
masc_strike_dc( dc );
/* In a lot of devices it will be obvious what the source port's
format is once the sink has been configured (or vice
versa). These devices usually set the opposite port's dc for
you, so you can use this here function call.
nb: the "default_mix_sink" port of the mix device is special:
When a client connects to it, the mixer changes this ports
_name_ (but not the id) and makes a new one called
"default_mix_sink" so that other clients can connect to it. */
err = mas_asm_connect_devices( sbuf, mix, "source", "default_mix_sink" );
if( err<0 )
fail( err, "Failed to connect sbuf->mix" );
/* Yes, in general you'll have to read a device's source or
README to find out what things like "postout_time_ms" might mean. */
masc_setup_package( &nugget, NULL, 0, 0 );
masc_pushk_uint32( &nugget, "postout_time_ms", 90 );
masc_finalize_package( &nugget );
mas_set( sbuf, "postout_time_ms", &nugget );
masc_strike_package( &nugget );
/* The source device can actually deal with more than one song; it
has the concept of a playlist. Let's give it one. */
masc_setup_package( &nugget, NULL, 0, 0 );
masc_pushk_int16( &nugget, "pos", 0 );
for( i=1; i<argc; i++ )
masc_push_string( &nugget, argv[i] );
masc_finalize_package( &nugget );
mas_set( source, "playlist", &nugget );
masc_strike_package( &nugget );
/* --[ START PLAYING ]--------------------------------------- */
mas_source_play( source );
mas_source_play( sbuf );
masc_log_message( MAS_VERBLVL_DEBUG, "Playing..." );
/* Dumbly ask the source device every other second what the
current song number is. If it's zero, we're finished. In an
actual player, there would be an event loop here...
*/
pos = 1;
while( pos )
{
sleep( 2 );
mas_get( source, "ctrack", NULL, &nugget );
masc_pullk_int16( &nugget, "pos", &pos );
masc_strike_package( &nugget ); /* in case the package used
dynamic memory */
}
exit( EXIT_SUCCESS );
}