ofxPiMapper fixed for C++17 & oF 12.0
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

531 lines
13 KiB

//
// magSlideShowSource.cpp
// Copyright (c) 2017 Cristobal Mendoza
// http://cuppetellimendoza.com
//
#include "magSlideShowSource.h"
#include "magSlideTransition.h"
#include "SettingsLoader.h"
#include "magSlideTransitionFactory.h"
const std::string magSlideShowSource::SettingsFileName = "magslideshow_settings.xml";
magSlideShowSource::magSlideShowSource() {
name = "Slide Show Source";
currentSlideIndex = 0;
isPlaying = false;
directoryWatcher = 0;
doInit = false;
if (!loadFromXml(SettingsFileName))
{
ofLogError("magSlideShowSource") << "Could not find slide show settings file " << SettingsFileName;
Settings sets;
initialize(sets);
}
}
magSlideShowSource::~magSlideShowSource(){
if(directoryWatcher != 0){
delete directoryWatcher;
}
}
bool magSlideShowSource::initialize(magSlideShowSource::Settings settings) {
this->settings = settings;
bool success = true;
if (settings.width <= 0 || settings.height <= 0)
{
ofLogError("magSlideShowSource::initialize") << "Invalid value for width or height. Width and height "
"must be assigned in your Settings struct!";
return false;
}
// Allocate the FBO:
allocate(settings.width, settings.height);
// If there is a path in slidesFolderPath, attempt
// to load the folder and any files in it:
if (!settings.slidesFolderPath.empty())
{
// ofDirectory dir = ofDirectory(settings.slidesFolderPath);
success = createFromFolderContents(settings.slidesFolderPath);
if (!success)
{
ofLogError("magSlideShowSource::initialize") << "Failed to create slide show from folder "
<< settings.slidesFolderPath;
return success;
}
{
if (directoryWatcher == 0)
{
using namespace ofx::piMapper;
directoryWatcher = new DirectoryWatcher(settings.slidesFolderPath,
SourceTypeHelper::GetSourceTypeHelperEnum(
SOURCE_TYPE_NAME_IMAGE));
ofAddListener(directoryWatcher->directoryFileAddedEvent, this, &magSlideShowSource::fileAddedListener);
ofAddListener(directoryWatcher->directoryFileRemovedEvent, this,
&magSlideShowSource::fileRemovedListener);
directoryWatcher->beginWatch();
}
}
}
else if (!settings.slideshowFilePath.empty())
{
success = false;
}
return success;
}
void magSlideShowSource::setup() {
ofx::piMapper::FboSource::setup();
}
void magSlideShowSource::update() {
// Perform re-initialization if the DirectoryWatcher
// detects file changes:
if (doInit)
{
initialize(settings);
doInit = false;
}
if (!isPlaying) return;
auto nowTime = ofGetElapsedTimeMillis();
deltaTime = nowTime-lastTime;
runningTime += deltaTime;
lastTime = nowTime;
for (auto &slide : activeSlides)
{
slide->update(deltaTime);
}
// Queue the next slide if it is time
if (doPlayNextSlide)
{
playNextSlide();
if (activeSlides.size() > 1)
{
activeSlides[1]->transition->start(activeSlides[0]);
}
doPlayNextSlide = false;
}
// Erase any complete slides:
auto iter = activeSlides.begin();
for (; iter < activeSlides.end(); iter++)
{
if ((*iter)->isSlideComplete())
{
// ofLogVerbose() << "Removing from active slides id: " << (*iter)->getId();
activeSlides.erase(iter);
--iter;
}
}
if (activeSlides.size() == 0 && isPlaying)
{
ofEventArgs args;
isPlaying = false;
ofNotifyEvent(slideshowCompleteEvent, args, this);
}
}
void magSlideShowSource::draw() {
ofBackground(0, 0);
ofPushMatrix();
ofPushStyle();
ofTranslate(getWidth()/2.0f, getHeight()/2.0f);
//ofEnableAlphaBlending();
ofSetRectMode(OF_RECTMODE_CENTER);
ofFill();
ofSetColor(255, 255);
for (auto &slide : activeSlides)
{
slide->draw();
}
ofPopStyle();
ofPopMatrix();
//ofDisableAlphaBlending();
}
bool magSlideShowSource::createFromFolderContents(std::string path) {
ofDirectory dir = ofDirectory(path);
slides.clear();
if (!dir.isDirectory())
{
ofLogError("magSlideShowSource::createFromFolderContents") << "Folder path " << dir.getAbsolutePath()
<< " is not a directory";
return false;
}
ofDirectory sortedDir = dir.getSorted();
std::vector<ofFile> files = sortedDir.getFiles();
if (files.size() < 1){
ofLogError("magSlideShowSource::createFromFolderContents") << "Folder " << dir.getAbsolutePath() << " is empty";
return false;
}
ofImage tempImage;
for(ofFile &file : files){
if (tempImage.load(file.getFileName())){
// make a new image slide
auto slide = std::make_shared<magImageSlide>();
slide->setup(tempImage);
slide->setDuration(static_cast<u_int64_t>(settings.slideDuration*1000));
slide->setTransitionDuration(static_cast<u_int64_t>(settings.transitionDuration*1000));
// if (settings.transitionName == "")
addSlide(slide);
}else{
auto ext = ofToLower(file.getExtension());
static std::vector<std::string> movieExtensions = {
"mov", "qt", // Mac
"mp4", "m4p", "m4v", // MPEG
"mpg", "mp2", "mpeg", "mpe", "mpv", "m2v", // MPEG
"3gp", // Phones
"avi", "wmv", "asf", // Windows
"webm", "mkv", "flv", "vob", // Other containers
"ogv", "ogg",
"drc", "mxf"
};
// Check if the extension matches known movie formats:
if (ofContains(movieExtensions, ext))
{
// Make a new video slide
auto slide = std::make_shared<magVideoSlide>();
if (slide->setup(file))
{
slide->setDuration(settings.slideDuration*1000.0);
slide->setTransitionDuration(settings.transitionDuration*1000.0);
addSlide(slide);
}
else
{
ofLogError("magSlideShowSource") << "Failed loading video: " << file.getAbsolutePath();
}
}
}
}
if (slides.size() > 0)
{
return true;
}
else
{
return false;
}
}
bool magSlideShowSource::loadFromXml(std::string path) {
auto xml = ofxXmlSettings();
Settings settings;
if (!xml.load(path))
{
ofLogError("magSlideShowSource") << "Could not load settings file " << path;
return false;
}
// xml.pushTag("surfaces");
if (!xml.pushTag("magSlideShow"))
{
ofLogError("magSlideShowSource") << "Slide show settings not found in " << path;
return false;
}
settings.width = xml.getValue("Width", settings.width);
settings.height = xml.getValue("Height", settings.height);
// Default slide duration:
settings.slideDuration = xml.getValue("SlideDuration", settings.slideDuration);
// Default loop:
if (xml.pushTag("Loop"))
{
auto type = xml.getValue("Type", "");
if (type == "NONE")
{
settings.loopType = LoopType::NONE;
}
else if (type == "NORMAL")
{
settings.loopType = LoopType::NORMAL;
}
else if (type == "PING-PONG")
{
settings.loopType = LoopType::PING_PONG;
}
settings.numLoops = xml.getValue("Count", settings.numLoops);
xml.popTag();
}
if (xml.pushTag("Transition"))
{
settings.transitionName = xml.getValue("Type", settings.transitionName);
settings.transitionDuration = xml.getValue("Duration", settings.transitionDuration);
xml.popTag();
}
// Default resize options:
auto ropts = xml.getValue("ResizeOption", "");
if (ropts == "NoResize")
{
settings.resizeOption = magSlide::NoResize;
}
else if (ropts == "Native")
{
settings.resizeOption = magSlide::Native;
}
else if (ropts == "Fit")
{
settings.resizeOption = magSlide::Fit;
}
else if (ropts == "FitProportionally")
{
settings.resizeOption = magSlide::FitProportionally;
}
else if (ropts == "FillProportionally")
{
settings.resizeOption = magSlide::FillProportionally;
}
initialize(settings);
return true;
}
void magSlideShowSource::addSlide(std::shared_ptr<magSlide> slide) {
// ofLogVerbose("addSlide") << slide->getId();
slides.insert(slides.begin(), slide);
auto rOption = slide->getResizeOption();
// If the slide does not have a resize option assign
// the slide show's option
if (rOption == magSlide::ResizeOptions::NoResize)
{
rOption = settings.resizeOption;
}
// Resize the slide according to the resize option:
switch (rOption){
float sw, sh, ratio;
case magSlide::ResizeOptions::FitProportionally:
sw = slide->getWidth();
sh = slide->getHeight();
if (sw > sh)
{
ratio = (float) getWidth()/sw;
}
else
{
ratio = (float) getHeight()/sh;
}
slide->setSize(sw*ratio, sh*ratio);
break;
case magSlide::ResizeOptions::FillProportionally:
sw = slide->getWidth();
sh = slide->getHeight();
if (sw > sh)
{
ratio = (float) getHeight()/sh;
}
else
{
ratio = (float) getWidth()/sw;
}
slide->setSize(sw*ratio, sh*ratio);
break;
case magSlide::Fit:
slide->setSize(getWidth(), getHeight());
break;
default:
break;
}
// Add transitions:
static ofParameterGroup bogusParamGroup; // This is temporary so that things compile
auto tf = magSlideTransitionFactory::instance();
// slide->buildIn = tf->createTransition(settings.transitionName,
// slide,
// bogusParamGroup,
// slide->buildInDuration);
slide->transition = tf->createTransition(settings.transitionName,
slide,
bogusParamGroup,
slide->buildOutDuration);
//// void method(const void * sender, ArgumentsType &args)
ofAddListener(slide->slideStateChangedEvent, this, &magSlideShowSource::slideStateChanged);
ofAddListener(slide->slideCompleteEvent, this, &magSlideShowSource::slideComplete);
}
void magSlideShowSource::play() {
if (!isPlaying && slides.size()){
runningTime = 0;
lastTime = ofGetElapsedTimeMillis();
isPlaying = true;
auto currentSlide = slides[currentSlideIndex];
enqueueSlide(currentSlide, ofGetElapsedTimeMillis());
}
}
void magSlideShowSource::pause() {
isPlaying = false;
}
void magSlideShowSource::playNextSlide() {
//TODO
// I should check here to see if there are less than two slides.
// If so, we should probably return
currentSlideIndex += direction;
ofEventArgs args;
// This makes sure that we are doing a signed integer comparison,
// otherwise things get weird
int num = slides.size();
switch (settings.loopType)
{
case LoopType::NONE:
if (currentSlideIndex >= slides.size() || currentSlideIndex < 0)
{
// If we are not looping and we are out of bounds, return
// without enqueueing a slide. This will cause the slide show
// to end once the last slide builds out.
return;
}
break;
case LoopType::NORMAL:
if (currentSlideIndex >= num)
{
loopCount++;
if (loopCount == settings.numLoops)
{
// Return without enqueueing a new slide if we have
// reached the max number of loops.
return;
}
currentSlideIndex = 0;
ofNotifyEvent(slideshowWillLoopEvent, args, this);
}
else if (currentSlideIndex < 0)
{
loopCount++;
if (loopCount == settings.numLoops)
{
// Return without enqueueing a new slide if we have
// reached the max number of loops.
return;
}
currentSlideIndex = slides.size()-1;
ofNotifyEvent(slideshowWillLoopEvent, args, this);
}
break;
case LoopType::PING_PONG:
int num = slides.size();
if (currentSlideIndex >= num)
{
loopCount++;
if (loopCount == settings.numLoops)
{
// Return without enqueueing a new slide if we have
// reached the max number of loops.
return;
}
direction = -1;
currentSlideIndex = slides.size()-2;
ofNotifyEvent(slideshowWillLoopEvent, args, this);
}
else if (currentSlideIndex < 0)
{
loopCount++;
if (loopCount == settings.numLoops)
{
// Return without enqueueing a new slide if we have
// reached the max number of loops.
return;
}
direction = 1;
currentSlideIndex = 1;
ofNotifyEvent(slideshowWillLoopEvent, args, this);
}
break;
}
enqueueSlide(slides[currentSlideIndex], ofGetElapsedTimeMillis());
}
void magSlideShowSource::playPrevSlide() {
currentSlideIndex -= (direction*2);
playNextSlide();
}
void magSlideShowSource::playSlide(int slideIndex) {
currentSlideIndex = slideIndex-direction;
playNextSlide();
}
void magSlideShowSource::enqueueSlide(std::shared_ptr<magSlide> slide, u_int64_t startTime) {
// ofLogVerbose() << "Enqueuing slide " << currentSlideIndex << " slide id: " << slide->getId();
slide->start(startTime);
activeSlides.insert(activeSlides.begin(), slide);
}
void magSlideShowSource::slideStateChanged(const void *sender, ofEventArgs &args) {
magSlide *slide = (magSlide *) sender;
// ofLogVerbose("slideStateChanged") << "Slide id: " << slide->getId() << " Slide state: "
// << slide->getSlideStateName();
if (slide->getSlideState() == magSlide::SlideState::BuildOut)
{
// Flag that we need to load the next slide:
doPlayNextSlide = true;
}
}
void magSlideShowSource::slideComplete(const void *sender, ofEventArgs &args) {
magSlide *slide = (magSlide *) sender;
// ofLogVerbose() << "Slide Complete. id: " << slide->getId();
slide->isComplete = true;
}
void magSlideShowSource::fileAddedListener(const void *sender) {
doInit = true;
}
void magSlideShowSource::fileRemovedListener(const void *sender) {
doInit = true;
}