view editor/edmain.cpp @ 2576:812b16ee49db

I had been living under apparent false impression that "realfft.c" on which the FFT implementation in DMLIB was basically copied from was released in public domain at some point, but it could very well be that it never was. Correct license is (or seems to be) GNU GPL. Thus I removing the code from DMLIB, and profusely apologize to the author, Philip Van Baren. It was never my intention to distribute code based on his original work under a more liberal license than originally intended.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 11 Mar 2022 16:32:50 +0200
parents 69a5af2eb1ea
children
line wrap: on
line source

//
// Demo Editor -- Main program
// (C) Copyright 2012 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
//
#include <SDL.h>
#include "dmengine.h"
#include "edmain.h"
#include <QSettings>
#include <QGLWidget>


int main(int argc, char *argv[])
{
    dmVerbosity = 5;

    QApplication app(argc, argv);

    app.setOrganizationName("TNSP");
    app.setOrganizationDomain("tnsp.org");
    app.setApplicationName(PROGRAM_NAME);
    app.setApplicationVersion(PROGRAM_VERSION);

    DemoEditor mainWin;

    mainWin.show();
          
    return app.exec();
}


void engineAudioCallback(void *userdata, Uint8 * stream, int len)
{
    DMEngineData *engine = (DMEngineData *) userdata;

    if (engine->paused)
    {
        memset(stream, 0, len);
    }
    else
#ifdef DM_USE_JSS
    {
        if (engine->jssDev != NULL)
            jvmRenderAudio(engine->jssDev, stream,
                           len / jvmGetSampleSize(engine->jssDev));
    }
#endif
#ifdef DM_USE_TREMOR
    if (engine->audioPos + len >= engine->audioRes->resSize)
    {
        engine->exitFlag = true;
    }
    else
    {
        memcpy(stream, (Uint8 *) engine->audioRes->resData + engine->audioPos, len);
        engine->audioPos += len;
    }
#endif
}


int DemoEditor::reopenResources()
{
    int err;

    if ((err = dmResourcesInit(
        &engine.resources, engine.optPackFilename, engine.optDataPath,
        engine.optResFlags, engineClassifier)) != DMERR_OK)
    {
        dmErrorMsg("Could not initialize resource manager: %d, %s.\n",
            err, dmErrorStr(err));
    }
    return err;
}


int DemoEditor::loadResources()
{
    int err, loaded = 0, total = 0;
    BOOL first = TRUE;

    do
    {
/*
        // Show a nice progress bar while loading
        if ((err = engineShowProgress(loaded, total)) != DMERR_OK)
            return err;
*/
        err = dmResourcesPreload(engine.resources, first, &loaded, &total);
        first = FALSE;
    }
    while (err == DMERR_PROGRESS);
    return DMERR_OK;
}


DemoEditor::DemoEditor()
{
    int err;

    resize(1024, 768);
    setWindowTitle(QCoreApplication::applicationName());

    memset(&engine, 0, sizeof(engine));
    initSDL = FALSE;
    currTimeline = NULL;

    // Pre-initialization
    if ((err = demoPreInit(&engine)) != DMERR_OK)
        goto error_exit;

    // Initialize SDL components
    dmPrint(1, "Initializing libSDL.\n");
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0)
    {
        dmErrorMsg("Could not initialize SDL: %s\n", SDL_GetError());
        goto error_exit;
    }
    initSDL = true;

    // Initialize audio parts
    if (engine.optAfmt.freq == 0 && engine.optAfmt.channels == 0)
    {
        // Defaults, if none seem to be set
        engine.optAfmt.freq = 44100;
        engine.optAfmt.format = AUDIO_S16SYS;
        engine.optAfmt.channels = 2;
        engine.optAfmt.samples = engine.optAfmt.freq / 16;
    }

#ifdef DM_USE_JSS
    jssInit();

    switch (engine.optAfmt.format)
    {
        case AUDIO_S16SYS:
            engine.jssFormat = JSS_AUDIO_S16;
            break;
        case AUDIO_U16SYS:
            engine.jssFormat = JSS_AUDIO_U16;
            break;
        case AUDIO_S8:
            engine.jssFormat = JSS_AUDIO_S8;
            break;
        case AUDIO_U8:
            engine.jssFormat = JSS_AUDIO_U8;
            break;
    }

    dmPrint(1, "Initializing miniJSS mixer with fmt=%d, chn=%d, freq=%d\n",
            engine.jssFormat, engine.optAfmt.channels, engine.optAfmt.freq);

    if ((engine.jssDev =
         jvmInit(engine.jssFormat, engine.optAfmt.channels,
                 engine.optAfmt.freq, JMIX_AUTO)) == NULL)
    {
        dmErrorMsg("jvmInit() returned NULL, voi perkele.\n");
        goto error_exit;
    }

    if ((engine.jssPlr = jmpInit(engine.jssDev)) == NULL)
    {
        dmErrorMsg("jmpInit() returned NULL\n");
        goto error_exit;
    }
#endif

    // Initialize SDL audio
    dmPrint(1, "Trying to init SDL audio with: fmt=%d, chn=%d, freq=%d\n",
            engine.optAfmt.format, engine.optAfmt.channels,
            engine.optAfmt.freq);

    engine.optAfmt.callback = engineAudioCallback;

    if (SDL_OpenAudio(&engine.optAfmt, NULL) < 0)
    {
        dmErrorMsg("Couldn't open SDL audio: %s\n", SDL_GetError());
        goto error_exit;
    }

    // Initialize SDL video
    if (engine.demoInitPreVideo != NULL &&
        (err = engine.demoInitPreVideo(&engine)) != DMERR_OK)
    {
        dmErrorMsg("demoInitPreVideo() failed, %d: %s\n", err, dmErrorStr(err));
        goto error_exit;
    }

    dmPrint(1, "Initializing SDL video %d x %d x %dbpp, flags=0x%08x\n",
        engine.optVidWidth, engine.optVidHeight, engine.optVidDepth, engine.optVFlags);

    engine.screen = SDL_CreateRGBSurface(SDL_SWSURFACE, engine.optVidWidth, engine.optVidHeight, engine.optVidDepth, 0, 0, 0, 0);
    if (engine.screen == NULL)
    {
        dmErrorMsg("Could not allocate video backbuffer surface.\n");
        goto error_exit;
    }

    if (engine.demoInitPostVideo != NULL &&
        (err = engine.demoInitPostVideo(&engine)) != DMERR_OK)
    {
        dmErrorMsg("demoInitPostVideo() failed, %d: %s\n", err, dmErrorStr(err));
        goto error_exit;
    }


error_exit:

    // Setup GUI elements
    createMainGUI();
    createNewFile();
    timelineView->setTimeline(currTimeline);
    settingsRestore();
    initEffectsAndResources();
    statusMsg("Application started.");
}


DemoEditor::~DemoEditor()
{
    statusMsg("Shutting down.");

    settingsSave();
    delete demoView;
    historyReset();

    if (engine.screen)
        SDL_FreeSurface(engine.screen);

    SDL_LockAudio();
    SDL_PauseAudio(1);
#ifdef DM_USE_JSS
    jmpClose(engine.jssPlr);
    jvmClose(engine.jssDev);
    jssClose();
#endif
    SDL_UnlockAudio();

    shutdownEffectsAndResources();

    if (initSDL)
        SDL_Quit();
}


int DemoEditor::getTimelineDuration()
{
    return timelineAudioTrack->getDuration();
}


void DemoEditor::updateResourceView()
{
}


void DemoEditor::updateTimelineView()
{
    demoView->setEngineData(&engine);    

    if (engine.audioRes != NULL)
    {
        timelineAudioTrack->setWaveform(
            engine.audioRes->resData, engine.audioRes->resSize,
            engine.optAfmt.format, engine.optAfmt.channels,
            engine.optAfmt.freq);
    }

    timelineAudioTrack->setOffset(currViewOffset);
    timelineAudioTrack->setScale(currViewScale);
    

    timelineView->setTime(currFrameTime);
    timelineView->setOffset(currViewOffset);
    timelineView->setScale(currViewScale);

    timelineScrollBar->setRange(0, getTimelineDuration());
    timelineScrollBar->setValue(currViewOffset);
}


void DemoEditor::actionTimelineScrollChanged(int value)
{   
    currViewOffset = value;
    updateTimelineView();
}


void DemoEditor::actionOffsetChanged(float value)
{   
    currViewOffset = value;
    updateTimelineView();
}


void DemoEditor::actionTimeChanged(float value)
{   
    currFrameTime = value;
    updateTimelineView();
}


void DemoEditor::actionTimelineChanged()
{
    updateMenuStates();
    update();
}


int DemoEditor::initEffectsAndResources()
{
    int err;

    currViewOffset = 0;
    currFrameTime = 0;
    currViewScale = 1.0f;
    
    // Initialize resource subsystem
    statusMsg("Initializing resources subsystem.");
    if ((err = reopenResources()) != DMERR_OK)
        return err;

    // Load resources
    statusMsg("Loading resources, please wait...");
    if ((err = loadResources()) != DMERR_OK)
    {
        dmErrorMsg("Error loading resources, %d: %s.\n", err, dmErrorStr(err));
        return err;
    }


    // Final initializations
    statusMsg("Initializing custom demo data.");
    if ((err = engine.demoInit(&engine)) != DMERR_OK)
    {
        dmErrorMsg("Failure in demoInit(), %d: %s\n",
            err, dmErrorStr(err));
        return err;
    }

    // Initialize effects
    statusMsg("Initializing effects ...");
    if ((err = engineInitializeEffects(&engine)) != DMERR_OK)
    {
        dmErrorMsg("Effects initialization failed, %d: %s\n",
            err, dmErrorStr(err));
        return err;
    }

    // Etc.
    rehash();
    return DMERR_OK;
}


void DemoEditor::shutdownEffectsAndResources()
{
    delete currTimeline;
    dmFreePreparedTimelineData(engine.ptl);
    engineShutdownEffects(&engine);
    dmResourcesClose(engine.resources);

    if (engine.demoShutdown != NULL)
        engine.demoShutdown(&engine);
}


void DemoEditor::rehash()
{
    timelineView->setTimeline(currTimeline);
    updateResourceView();
    updateTimelineView();
    updateMenuStates();
    update();
}


void DemoEditor::createNewFile()
{
    delete currTimeline;
    currTimeline = new EDTimelineObject();

    DMTimelineTrack *tr;
    dmTimelineAddTrack(currTimeline->tl, &tr, "Penis");
    
    DMTimelineEvent *ev;
    dmTimelineTrackAddEvent(tr, 500, 100, &ev);
    
    dmTimelineEventSetEffectByIndex(ev, 0);

    historyReset();
    updateMenuStates();
    update();
}


void DemoEditor::readFromFile(QString filename)
{
    EDTimelineObject *tmp = new EDTimelineObject();
    int ret = tmp->load(filename);
    if (ret != DMERR_OK)
    {
        showFileErrorDialog("Loading demo blob file "+ filename, ret);
        delete tmp;
    }
    else
    {
        delete currTimeline;
        currTimeline = tmp;
        rehash();
    }
}


void DemoEditor::saveToFile(QString filename)
{
    int ret = currTimeline->save(filename);
    if (ret != DMERR_OK)
    {
        showFileErrorDialog("Saving demo blob file "+ filename, ret);
    }

    updateMenuStates();
}


void DemoEditor::settingsRestore()
{
    QSettings s;

    restoreGeometry(s.value("windowGeometry").toByteArray());
    restoreState(s.value("windowState").toByteArray());
}


void DemoEditor::settingsSave()
{
    QSettings s;

    s.setValue("windowGeometry", saveGeometry());
    s.setValue("windowState", saveState());
}


//
// Edit history functionality
//
void DemoEditor::historyReset()
{
    if (currTimeline != NULL)
        currTimeline->scrub();

    undoHistoryPos = -1;
    undoHistoryMax = DOC_UNDO_MAX;
    undoHistory.clear();
}


void DemoEditor::historyPush(QString description)
{
    if (currTimeline == NULL)
        return;

    if (!undoHistory.isEmpty() && undoHistory.last()->state() == "-")
    {
        delete undoHistory.takeLast();
    }
    
    while (undoHistory.size() >= undoHistoryMax)
    {
        delete undoHistory.takeFirst();
    }
    
    EDTimelineObject *copy = new EDTimelineObject(currTimeline);
    copy->setState(description);
    undoHistory.append(copy);
}


void DemoEditor::historyTop()
{
    if (currTimeline == NULL)
        return;

    EDTimelineObject *copy = new EDTimelineObject(currTimeline);
    copy->setState("-");
    undoHistory.append(copy);

    undoHistoryPos = undoHistory.size() - 1;
    currTimeline->touch();
    updateTimelineView();
    updateMenuStates();
    update();
}


void DemoEditor::historyPop()
{
    if (!undoHistory.isEmpty())
    {
        delete undoHistory.takeLast();
    }
}


void DemoEditor::performRedo()
{
    if (undoHistoryPos >= 0 && undoHistoryPos < undoHistory.size() - 1)
    {
        undoHistoryPos++;
        delete currTimeline;
        currTimeline = new EDTimelineObject(undoHistory.at(undoHistoryPos));
        currTimeline->touch();
        updateTimelineView();
        updateMenuStates();
        update();
    }
}


void DemoEditor::performUndo()
{
    if (undoHistoryPos > 0 && undoHistory.size() > 1)
    {
        undoHistoryPos--;
        delete currTimeline;
        currTimeline = new EDTimelineObject(undoHistory.at(undoHistoryPos));
        currTimeline->touch();
        updateTimelineView();
        updateMenuStates();
        update();
    }
}