// ---------------------------------------------------------------------------- // void BeatDetector::attach( AudioInputStream* audio, unsigned frequency_bins ) { detach(); m_audio_stream = audio; m_frequency_bins = frequency_bins; if ( audio ) { m_sample_size = m_audio_stream->getSampleSize(); m_samples_per_second = m_audio_stream->getFormat().Format.nSamplesPerSec; STUDIO_ASSERT( m_frequency_bins <= m_sample_size/2, "Too many frequency bins" ); // THROW ERROR STUDIO_ASSERT( m_frequency_bins > 0, "Too few frequency bins" ); STUDIO_ASSERT( m_frequency_bins <= BEAT_DETECTOR_MASK_SIZE*32, "Too many beat detector frequency bins - %d requested", m_frequency_bins ); for ( unsigned i=0; i < m_frequency_bins; i++ ) m_bins.push_back( FreqBin( m_samples_per_second / m_sample_size, m_sensitivity_ms ) ); m_B = new double[ m_sample_size ]; m_Es = new double[ m_sample_size ]; // Reset all beat frequency bins for ( FreqBinArray::iterator it=m_bins.begin(); it != m_bins.end(); ++it ) (*it).reset(); m_audio_stream->addAudioProcessor( this, ProcessorFormat( 2, m_frequency_bins > 1 ) ); } }
// ---------------------------------------------------------------------------- // NOTE: Dimmer channel should contain 0 for full off and 255 for full on and // no other values // void SceneMovementAnimatorTask::populateChannelAnimations( ParticipantArray& participants, size_t& particpant_index, AngleList& tilt, AngleList& pan, ChannelValueArray& dimmer, ChannelValueArray& speed, size_t group_size, bool run_once ) { size_t end = particpant_index + group_size; // If run once, add last entry to shut down the lights if ( run_once && dimmer.size() > 0 ) { dimmer.push_back( 0 ); } ChannelAnimationStyle style( run_once ? CAM_LIST_ONCE : CAM_LIST ); for ( ; particpant_index < end && particpant_index < participants.size(); particpant_index++ ) { channel_address pan_channel = participants[ particpant_index ].m_head.m_pan; channel_address tilt_channel = participants[ particpant_index ].m_head.m_tilt; channel_address dimmer_channel = participants[ particpant_index ].m_head.m_dimmer; channel_address speed_channel = participants[ particpant_index ].m_head.m_speed; UID actor_uid = participants[ particpant_index ].m_actor_uid; Fixture* pf = getActorRepresentative( actor_uid ); if ( !pf ) continue; if ( tilt_channel != INVALID_CHANNEL && tilt.size() ) { Channel* cp = pf->getChannel( tilt_channel ); add( actor_uid, tilt_channel, style, anglesToValues( cp, tilt, cp->getMinAngle(), cp->getMaxAngle() ) ); } if ( pan_channel != INVALID_CHANNEL && pan.size() ) { Channel* cp = pf->getChannel( pan_channel ); add( actor_uid, pan_channel, style, anglesToValues( cp, pan, cp->getMinAngle(), cp->getMaxAngle() ) ); } if ( speed_channel != INVALID_CHANNEL && speed.size() ) { add( actor_uid, speed_channel, style, speed ); } if ( dimmer_channel != INVALID_CHANNEL && dimmer.size() ) { Channel* ch = pf->getChannel( dimmer_channel ); STUDIO_ASSERT( ch, "Can't access dimmer channel %d on fixture %s", dimmer_channel, pf->getFullName() ); BYTE low = ch->getDimmerLowestIntensity(); BYTE high = ch->getDimmerHighestIntensity(); // Replace all 255 dimmer high value with the fixture's dimmer value iff the actors value != 0 SceneActor* actor = getActor( actor_uid ); if ( actor && actor->getFinalChannelValue( pf->getUID(), dimmer_channel ) != 0 ) high = actor->getFinalChannelValue( pf->getUID(), dimmer_channel ); if ( low != 0 || high != 255 ) { // Special case odd dimmer values for ( size_t i=0; i < dimmer.size(); i++ ) dimmer[i] = ( dimmer[i] == 255 ) ? high : low; } add( actor_uid, dimmer_channel, style, dimmer ); } } }
// ---------------------------------------------------------------------------- // Get master volume // void AudioVolumeController::setMasterVolume( UINT volume ) { STUDIO_ASSERT( volume >= 0 && volume <= 100, "Master volume value outside allowed range of 0-100" ); HRESULT hr; hr = m_endpointVolume->SetMasterVolumeLevelScalar( static_cast<float>(volume) / 100.0F, NULL ); AUDIO_VOLUME_ASSERT( hr, "Cannot set current volume" ); }
// ---------------------------------------------------------------------------- // void MusicPlayer::initialize( ) { m_library = LoadLibrary( m_dll_path ); STUDIO_ASSERT( m_library, "Unable to load music controller DLL '%s'", m_dll_path ); GetPlayerApiVersion pGetPlayerApiVersion = getAddress<GetPlayerApiVersion>( "GetPlayerApiVersion" ); STUDIO_ASSERT( (*pGetPlayerApiVersion)() == 2, "Music controller version is not compatible" ); m_GetPlayerInfo = getAddress<GetPlayerInfo>( "GetPlayerInfo" ); m_Connect = getAddress<Connect>( "Connect" ); m_Disconnect = getAddress<Disconnect>( "Disconnect" ); m_RegisterEventListener = getAddress<RegisterEventListener>( "RegisterEventListener" ); m_UnregisterEventListener = getAddress<UnregisterEventListener>( "UnregisterEventListener" ); m_Signon = getAddress<Signon>( "Signon" ); m_AcceptAuthorization = getAddress<AcceptAuthorization>( "AcceptAuthorization" ); m_IsLoggedIn = getAddress<IsLoggedIn>( "IsLoggedIn" ); m_GetPlaylists = getAddress<GetPlaylists>( "GetPlaylists" ); m_GetPlaylistInfo = getAddress<GetPlaylistInfo>( "GetPlaylistInfo" ); m_GetTracks = getAddress<GetTracks>( "GetTracks" ); m_SearchTracks = getAddress<SearchTracks>( "SearchTracks" ); m_QueueTrack = getAddress<QueueTrack>( "QueueTrack" ); m_PlayTrack = getAddress<PlayTrack>( "PlayTrack" ); m_PlayAllTracks = getAddress<PlayAllTracks>( "PlayAllTracks" ); m_ForwardTrack = getAddress<ForwardTrack>( "ForwardTrack" ); m_BackTrack = getAddress<BackTrack>( "BackTrack" ); m_StopTrack = getAddress<StopTrack>( "StopTrack" ); m_PauseTrack = getAddress<PauseTrack>( "PauseTrack" ); m_GetPlayingTrack = getAddress<GetPlayingTrack>( "GetPlayingTrack" ); m_IsTrackPaused = getAddress<IsTrackPaused>( "IsTrackPaused" ); m_GetTrackInfo = getAddress<GetTrackInfo>( "GetTrackInfo" ); m_GetTrackAudioInfo = getAddress<GetTrackAudioInfo>( "GetTrackAudioInfo" ); m_GetQueuedTracks = getAddress<GetQueuedTracks>( "GetQueuedTracks" ); m_GetPlayedTracks = getAddress<GetPlayedTracks>( "GetPlayedTracks" ); m_GetLastPlayerError = getAddress<GetLastPlayerError>( "GetLastPlayerError" ); m_WaitOnTrackEvent = getAddress<WaitOnTrackEvent>( "WaitOnTrackEvent" ); m_GetTrackAnalysis = getAddress<GetTrackAnalysis>( "GetTrackAnalysis" ); getPlayerInfo( &m_player_info ); }
// ---------------------------------------------------------------------------- // ChannelValueArray SceneMovementAnimatorTask::anglesToValues( Channel* channel, AngleList& angles, UINT min_angle, UINT max_angle ) { ChannelValueArray values; for ( UINT angle : angles ) { if ( angle < min_angle ) angle = min_angle; else if ( angle > max_angle ) angle = max_angle; values.push_back( channel->convertAngleToValue( angle ) ); } STUDIO_ASSERT( values.size() > 0, "Invalid angles array size" ); return values; }
// ---------------------------------------------------------------------------- // Scene* VenueReader::read( TiXmlElement* self, Scene* scene ) { scene = new Scene(); readDObject( self, scene, "scene_number" ); scene->m_bpm_rating = (BPMRating)read_int_attribute( self, "bpm_rating", BPMRating::BPM_NO_RATING); std::vector<SceneActor *> actors = read_xml_list<SceneActor>( self->FirstChildElement( "actors" ), "actor" ); for ( std::vector<SceneActor *>::iterator it=actors.begin(); it != actors.end(); ++it ) { scene->addActor( *(*it) ); delete (*it); } TiXmlElement* acts = self->FirstChildElement( "acts" ); if ( acts ) { TiXmlElement* element = acts->FirstChildElement( "act" ); while ( element ) { scene->m_acts.insert( read_unsigned_attribute( element, "number" ) ) ; element = element->NextSiblingElement(); } } TiXmlElement* container = self->FirstChildElement( "animations" ); if ( container ) { TiXmlElement* element = container->FirstChildElement( "animation" ); while ( element ) { const char* class_name = read_text_attribute( element, "class" ); AbstractAnimation* animation = NULL; if ( !strcmp( class_name, SceneSequence::className ) ) animation = read( element, (SceneSequence*)NULL ); else if ( !strcmp( class_name, SceneSoundLevel::className ) ) animation = read( element, (SceneSoundLevel*)NULL ); else if ( !strcmp( class_name, SceneChannelAnimator::className ) ) animation = read( element, (SceneChannelAnimator*)NULL ); else if ( !strcmp( class_name, ScenePatternDimmer::className ) ) animation = read( element, (ScenePatternDimmer*)NULL ); else if ( !strcmp( class_name, SceneColorFader::className ) ) animation = read( element, (SceneColorFader*)NULL ); else if ( !strcmp( class_name, SceneMovementAnimator::className ) ) animation = read( element, (SceneMovementAnimator*)NULL ); else if ( !strcmp( class_name, SceneStrobeAnimator::className ) ) animation = read( element, (SceneStrobeAnimator*)NULL ); else if ( !strcmp( class_name, ScenePixelAnimator::className ) ) animation = read( element, (ScenePixelAnimator*)NULL ); else if ( !strcmp( class_name, SceneChannelFilter::className ) ) animation = read( element, (SceneChannelFilter*)NULL ); else STUDIO_ASSERT( false, "Unknown animation class '%s'", class_name ); scene->addAnimation( animation ); element = element->NextSiblingElement(); } } return scene; }
// ---------------------------------------------------------------------------- // Channel* DefinitionReader::read( TiXmlElement* self, Channel* channel ) { channel = new Channel(); try { channel->m_channel_offset = (channel_t)read_dword_attribute( self, "index" ); channel->m_type = Channel::convertTextToChannelType( read_text_attribute( self, "type" ) ); channel->m_name = read_text_element( self, "name" ); channel->m_is_color = read_bool_attribute( self, "color" ); channel->m_can_blackout = read_bool_attribute( self, "blackout" ); channel->m_can_whiteout = read_bool_attribute( self, "whiteout", true ); channel->m_default_value = (BYTE)read_int_attribute( self, "value" ); channel->m_home_value = (BYTE)read_int_attribute( self, "home_value" ); channel->m_pixel_index = (BYTE)read_int_attribute( self, "pixel" ); channel->m_head_number = (BYTE)read_int_attribute( self, "head" ); // If head number is not set on tilt or pan, default to 1 if ( channel->m_head_number == 0 && (channel->m_type == CHNLT_TILT || channel->m_type == CHNLT_PAN || channel->m_type == CHNLT_PAN_FINE || channel->m_type == CHNLT_TILT_FINE) ) channel->m_head_number = 1; STUDIO_ASSERT( channel->m_channel_offset > 0, "Channel '%s' index < 1", channel->m_name ); channel->m_channel_offset--; // Adjust offset for internal zero based TiXmlElement *dimmer = self->FirstChildElement( "dimmer" ); if ( dimmer ) { channel->m_is_dimmer = true; channel->m_lowest_intensity = (BYTE)read_int_attribute( dimmer, "lowest_intensity", 0 ); channel->m_highest_intensity = (BYTE)read_int_attribute( dimmer, "highest_intensity", 255 ); } else { channel->m_is_dimmer = ( channel->m_type == CHNLT_DIMMER ); // Implies this is the default 0-255 dimmer channel type channel->m_lowest_intensity = 0; channel->m_highest_intensity = 255; } // Add channel ranges std::vector<ChannelValueRange *> ranges = read_xml_list<ChannelValueRange>( self->FirstChildElement( "ranges" ), "range" ); for ( std::vector<ChannelValueRange *>::iterator it=ranges.begin(); it != ranges.end(); ++it ) { STUDIO_ASSERT( (*it)->getEnd() >= (*it)->getStart(), "Channel '%s' range %s invalid", channel->m_name, (*it)->getName() ); STUDIO_ASSERT( channel->getRange( (*it)->getEnd() ) == NULL, "Channel '%s' range %s overlaps", channel->m_name, (*it)->getName() ); STUDIO_ASSERT( channel->getRange( (*it)->getStart() ) == NULL, "Channel '%s' range %s overlaps", channel->m_name, (*it)->getName() ); channel->m_ranges.push_back( *(*it) ); delete (*it); } // Add angles std::vector<ChannelAngle *> angles = read_xml_list<ChannelAngle>( self->FirstChildElement( "angles" ), "angle" ); for ( std::vector<ChannelAngle *>::iterator it=angles.begin(); it != angles.end(); ++it ) { channel->m_angles[ (*it)->getAngle() ] = *(*it); delete (*it); } channel->generateAngleTable(); } catch( ... ) { delete channel; throw; } return channel; }
// ---------------------------------------------------------------------------- // void SceneMovementAnimatorTask::generateProgram( AnimationDefinition* definition ) { SceneMovementAnimator* config = dynamic_cast< SceneMovementAnimator *>( definition ); MovementAnimation& movement = config->movement(); ParticipantArray participants; Head head; // Determine which actors will be participating for ( SceneActor& actor : getActors() ) { Fixture* pf = getActorRepresentative( actor.getActorUID() ); if ( !pf || !pf->canMove() ) continue; if ( movement.m_head_number == 0 ) { // All heads on fixture(s) for ( UINT head_number=1; head_number <= pf->getNumHeads(); head_number++ ) { if ( pf->getHead( head_number, head ) ) participants.emplace_back( actor.getActorUID(), head ); } } else { // Single head on fixture(s) if ( pf->getHead( movement.m_head_number, head ) ) participants.emplace_back( actor.getActorUID(), head ); } } STUDIO_ASSERT( movement.m_group_size >= 1, "Movement group size must be >= 1" ); STUDIO_ASSERT( movement.m_home_wait_periods > 0 && movement.m_home_wait_periods < MAX_WAIT_PERIODS, "Home wait periods must be between 1 and %d", MAX_WAIT_PERIODS ); STUDIO_ASSERT( movement.m_dest_wait_periods > 0 && movement.m_dest_wait_periods < MAX_WAIT_PERIODS, "Destination wait periods must be between 1 and %d", MAX_WAIT_PERIODS ); // TODO: At some point, pre-compute the channel arrays once rather on each scene load as // that will be a problem for many fixtures/animations in a single scene. switch ( movement.m_movement_type ) { case MOVEMENT_RANDOM: // Move to random locations genRandomMovement( movement, participants ); break; case MOVEMENT_FAN: // Fan beams genFanMovement( movement, participants ); break; case MOVEMENT_ROTATE: // Simple rotate at tilt angle genRotateMovement( movement, participants ); break; case MOVEMENT_NOD: // Simple up and down genNodMovement( movement, participants ); break; case MOVEMENT_XCROSS: // Cross fixture beams genXcrossMovement( movement, participants ); break; case MOVEMENT_MOONFLOWER: // Moonflower effect genMoonflowerMovement( movement, participants ); break; case MOVEMENT_COORDINATES: // Absolute coordinates effect genCoordinatesMovement( movement, participants ); break; case MOVEMENT_SINEWAVE: // Sinewave movement genSineMovement( movement, participants ); break; } }
// ---------------------------------------------------------------------------- // void SceneMovementAnimatorTask::genMoonflowerMovement( MovementAnimation& movement, ParticipantArray& participants ) { STUDIO_ASSERT( movement.m_height > 0, "Height must be a positive integer > 0" ); if ( participants.size() == 0 ) return; double fixture_angle_increment = 360 / participants.size(); double start_angle = 0; for ( size_t particpant_index=0; particpant_index < participants.size(); ) { AngleList pan; AngleList tilt; ChannelValueArray dimmer; ChannelValueArray speed; UINT pan_start; UINT tilt_start; compute_pan_tilt( particpant_index, movement.m_height, movement.m_fixture_spacing, movement.m_home_x, movement.m_home_y, pan_start, tilt_start ); double angle = start_angle; for ( size_t i=0; i < movement.m_positions; i++ ) { // Go home tilt.push_back( tilt_start ); pan.push_back( pan_start ); double target_x = movement.m_home_x + movement.m_radius * cos( angle * 180 / M_PI ); double target_y = movement.m_home_y + movement.m_radius * sin( angle * 180 / M_PI ); //printf( "fixture %d: %f,%f to x,y=%f,%f angle=%f\n", particpant_index, home_x, home_y, target_x, target_y, angle ); UINT pan_target; UINT tilt_target; compute_pan_tilt( particpant_index, movement.m_height, movement.m_fixture_spacing, target_x, target_y, pan_target, tilt_target ); //printf( "%d: pan_start=%d tilt_start=%d pan_end=%d tilt_end=%d\n", particpant_index, pan_start, tilt_start, pan_target, tilt_target); compute_fastest_path( pan_start, tilt_start, pan_target, tilt_target ); //printf( "%d: pan_start=%d tilt_start=%d pan_end=%d tilt_end=%d\n", particpant_index, pan_start, tilt_start, pan_target, tilt_target); // Go to destination for ( UINT i=0; i < movement.m_dest_wait_periods; i++ ) { tilt.push_back( tilt_target ); pan.push_back( pan_target ); } // Go back home for ( UINT i=0; i < movement.m_home_wait_periods-1; i++ ) { tilt.push_back( tilt_start ); pan.push_back( pan_start ); } angle += movement.m_pan_increment; if ( angle > 360 ) angle -= 360; } populateChannelAnimations( participants, particpant_index, tilt, pan, dimmer, speed, 1, movement.m_run_once ); start_angle += fixture_angle_increment; } }
void SceneMovementAnimatorTask::genRandomMovement( MovementAnimation& movement, ParticipantArray& participants ) { STUDIO_ASSERT( movement.m_positions > 0 && movement.m_positions < MAX_RANDOM_POSITIONS, "Random postions should be between 1 and %d", MAX_RANDOM_POSITIONS ); for ( size_t particpant_index=0; particpant_index < participants.size(); ) { Fixture* pf = getActorRepresentative( participants[ particpant_index ].m_actor_uid ); if ( !pf ) continue; UINT tilt_start = movement.m_tilt_start, tilt_end = movement.m_tilt_end; UINT pan_start = movement.m_pan_start, pan_end = movement.m_pan_end; AngleList pan; AngleList tilt; ChannelValueArray dimmer; ChannelValueArray speed; channel_address tilt_channel = participants[ particpant_index ].m_head.m_tilt; if ( tilt_channel != INVALID_CHANNEL ) { Channel* cp = pf->getChannel( tilt_channel ); tilt_start = std::max<UINT>( tilt_start, cp->getMinAngle() ); tilt_end = std::min<UINT>( tilt_end, cp->getMaxAngle() ); } channel_address pan_channel = participants[ particpant_index ].m_head.m_pan; if ( pan_channel != INVALID_CHANNEL ) { Channel* cp = pf->getChannel( pan_channel ); pan_start = std::max<UINT>( pan_start, cp->getMinAngle() ); pan_end = std::min<UINT>( pan_end, cp->getMaxAngle() ); } // Generate random locations within the tilt and pan bounds for ( UINT generate=0; generate < movement.m_positions; generate++ ) { UINT tilt_angle = random_angle( tilt_start, tilt_end ); UINT pan_angle = random_angle( pan_start, pan_end ); UINT wait_periods = movement.m_dest_wait_periods; // No light during movement if ( movement.m_backout_home_return && wait_periods > 1 ) { pan.push_back( pan_angle ); tilt.push_back( tilt_angle ); speed.push_back( 0 ); dimmer.push_back( 0 ); wait_periods--; } for ( UINT wait=0; wait < wait_periods; wait++ ) { pan.push_back( pan_angle ); tilt.push_back( tilt_angle ); speed.push_back( movement.m_speed ); dimmer.push_back( 255 ); } } if ( !movement.m_backout_home_return ) // TEMP fix for dimmer channel contention dimmer.clear(); // Populate the channel arrays for the next group of fixtures populateChannelAnimations( participants, particpant_index, tilt, pan, dimmer, speed, movement.m_group_size, movement.m_run_once ); } }