Exemplo n.º 1
0
/**
 *  Writes to outputSource.
 *
 * @param outputSource The SampleSource to write to.
 * @param silenceSource The source from where to write skipHeadFrames frames.
 * @param buffer The SampleBuffer with the samples to be written.
 * @param skipHeadFrames Number of frames to ignore before writing to outputSource.
 */
void writeOutput(SampleSource outputSource, SampleSource silenceSource, SampleBuffer buffer, unsigned long skipHeadFrames) {
  unsigned long framesSkiped = silenceSource->numSamplesProcessed / buffer->numChannels;
  unsigned long framesProcessed = framesSkiped + outputSource->numSamplesProcessed / buffer->numChannels;
  unsigned long nextBlockStart = framesProcessed + buffer->blocksize;

  if(framesProcessed != getAudioClock()->currentFrame) {
    logInternalError("framesProcessed (%lu) != getAudioClock()->currentFrame (%lu)", framesProcessed, getAudioClock()->currentFrame);
  }
  //Cut the delay at the start
  if(        nextBlockStart <= skipHeadFrames ) {
    // Cutting away the whole block. nothing is written to the outputSource
    silenceSource->writeSampleBlock(silenceSource, buffer);
  } else if(framesProcessed <  skipHeadFrames
         &&                    skipHeadFrames < nextBlockStart) {
    SampleBuffer sourceBuffer = newSampleBuffer(buffer->numChannels, buffer->blocksize);//blocksize < skipHeadFrames
    unsigned long skippedFrames = skipHeadFrames - framesProcessed;
    unsigned long soundFrames = nextBlockStart - skipHeadFrames;

    // Cutting away start part of the block.
    sourceBuffer->blocksize = skippedFrames;
    sampleBufferCopyAndMapChannelsWithOffset(sourceBuffer, 0, buffer, 0, sourceBuffer->blocksize);
    silenceSource->writeSampleBlock(silenceSource, sourceBuffer);

    // Writing remaining end part of the block.
    sourceBuffer->blocksize = soundFrames;
    sampleBufferCopyAndMapChannelsWithOffset(sourceBuffer, 0, buffer, skippedFrames, sourceBuffer->blocksize);
    outputSource->writeSampleBlock(outputSource, sourceBuffer);

    freeSampleBuffer(sourceBuffer);
  } else { //                  skipHeadFrames <=  framesProcessed
    // Normal case: Nothing more to cut. The whole block shall be written.
    outputSource->writeSampleBlock(outputSource, buffer);
  }
}
Exemplo n.º 2
0
static void _logMessage(const LogLevel logLevel, const char *message, va_list arguments)
{
    long elapsedTimeInMs;
    EventLogger eventLogger = _getEventLoggerInstance();
#if WINDOWS
    ULONGLONG currentTime;
#else
    struct timeval currentTime;
#endif

    if (eventLogger != NULL && logLevel >= eventLogger->logLevel) {
        CharString formattedMessage = newCharStringWithCapacity(kCharStringLengthDefault * 2);
        vsnprintf(formattedMessage->data, formattedMessage->capacity, message, arguments);
#if WINDOWS
        currentTime = GetTickCount();
        elapsedTimeInMs = (unsigned long)(currentTime - eventLogger->startTimeInMs);
#else
        gettimeofday(&currentTime, NULL);
        elapsedTimeInMs = ((currentTime.tv_sec - (eventLogger->startTimeInSec + 1)) * 1000) +
                          (currentTime.tv_usec / 1000) + (1000 - eventLogger->startTimeInMs);
#endif
        _printMessage(logLevel, elapsedTimeInMs, getAudioClock()->currentFrame, formattedMessage->data, eventLogger);
        freeCharString(formattedMessage);
    }
}
Exemplo n.º 3
0
static int _testInitAudioClock(void)
{
    AudioClock audioClock = getAudioClock();
    assertUnsignedLongEquals(ZERO_UNSIGNED_LONG, audioClock->currentFrame);
    assertFalse(audioClock->isPlaying);
    assertFalse(audioClock->transportChanged);
    return 0;
}
Exemplo n.º 4
0
static int _testStopAudioClock(void)
{
    AudioClock audioClock = getAudioClock();
    advanceAudioClock(audioClock, kAudioClockTestBlocksize);
    audioClockStop(audioClock);
    assertFalse(audioClock->isPlaying);
    assert(audioClock->transportChanged)
    return 0;
}
Exemplo n.º 5
0
static int _testAdvanceAudioClock(void)
{
    AudioClock audioClock = getAudioClock();
    advanceAudioClock(audioClock, kAudioClockTestBlocksize);
    assertUnsignedLongEquals(kAudioClockTestBlocksize, audioClock->currentFrame);
    assert(audioClock->isPlaying);
    assert(audioClock->transportChanged);
    return 0;
}
Exemplo n.º 6
0
static int _testAdvanceClockMulitpleTimes(void)
{
    AudioClock audioClock = getAudioClock();
    int i;

    for (i = 0; i < 100; i++) {
        advanceAudioClock(audioClock, kAudioClockTestBlocksize);
    }

    assert(audioClock->isPlaying);
    assertFalse(audioClock->transportChanged);
    assertUnsignedLongEquals(kAudioClockTestBlocksize * 100, audioClock->currentFrame);
    return 0;
}
Exemplo n.º 7
0
int mrsWatsonMain(ErrorReporter errorReporter, int argc, char** argv) {
  ReturnCodes result;
  // Input/Output sources, plugin chain, and other required objects
  SampleSource inputSource = NULL;
  SampleSource outputSource = NULL;
  AudioClock audioClock;
  PluginChain pluginChain;
  CharString pluginSearchRoot = newCharString();
  boolByte shouldDisplayPluginInfo = false;
  MidiSequence midiSequence = NULL;
  MidiSource midiSource = NULL;
  unsigned long maxTimeInMs = 0;
  unsigned long maxTimeInFrames = 0;
  unsigned long tailTimeInMs = 0;
  unsigned long tailTimeInFrames = 0;
  unsigned long processingDelayInFrames;
  ProgramOptions programOptions;
  ProgramOption option;
  Plugin headPlugin;
  SampleBuffer inputSampleBuffer = NULL;
  SampleBuffer outputSampleBuffer = NULL;
  TaskTimer initTimer, totalTimer, inputTimer, outputTimer = NULL;
  LinkedList taskTimerList = NULL;
  CharString totalTimeString = NULL;
  boolByte finishedReading = false;
  SampleSource silentSampleInput;
  SampleSource silentSampleOutput;
  unsigned int i;

  initTimer = newTaskTimerWithCString(PROGRAM_NAME, "Initialization");
  totalTimer = newTaskTimerWithCString(PROGRAM_NAME, "Total Time");
  taskTimerStart(initTimer);
  taskTimerStart(totalTimer);

  initEventLogger();
  initAudioSettings();
  initAudioClock();
  audioClock = getAudioClock();
  initPluginChain();
  pluginChain = getPluginChain();
  programOptions = newMrsWatsonOptions();
  inputSource = sampleSourceFactory(NULL);

  if(!programOptionsParseArgs(programOptions, argc, argv)) {
    printf("Run with '--help' to see possible options\n");
    printf("Or run with '--help full' to see extended help for all options\n");
    return RETURN_CODE_INVALID_ARGUMENT;
  }

  // These options conflict with standard processing (more or less), so check to see if the user wanted one
  // of these and then exit right away.
  if(argc == 1) {
    printf("%s needs at least a plugin, input source, and output source to run.\n\n", PROGRAM_NAME);
    printMrsWatsonQuickstart(argv[0]);
    return RETURN_CODE_NOT_RUN;
  }
  else if(programOptions->options[OPTION_HELP]->enabled) {
    printMrsWatsonQuickstart(argv[0]);
    if(charStringIsEmpty(programOptionsGetString(programOptions, OPTION_HELP))) {
      printf("All options, where <argument> is required and [argument] is optional:\n");
      programOptionsPrintHelp(programOptions, false, DEFAULT_INDENT_SIZE);
    }
    else {
      if(charStringIsEqualToCString(programOptionsGetString(programOptions, OPTION_HELP), "full", true)) {
        programOptionsPrintHelp(programOptions, true, DEFAULT_INDENT_SIZE);
      }
      // Yeah this is a bit silly, but the performance obviously doesn't matter
      // here and I don't feel like cluttering up this already huge function
      // with more variables.
      else if(programOptionsFind(programOptions, programOptionsGetString(programOptions, OPTION_HELP))) {
        programOptionPrintHelp(programOptionsFind(programOptions, programOptionsGetString(programOptions, OPTION_HELP)),
          true, DEFAULT_INDENT_SIZE, 0);
      }
      else {
        printf("Invalid option '%s', try running --help full to see help for all options\n",
          programOptionsGetString(programOptions, OPTION_HELP)->data);
      }
    }
    return RETURN_CODE_NOT_RUN;
  }
  else if(programOptions->options[OPTION_VERSION]->enabled) {
    printVersion();
    return RETURN_CODE_NOT_RUN;
  }
  else if(programOptions->options[OPTION_COLOR_TEST]->enabled) {
    printTestPattern();
    return RETURN_CODE_NOT_RUN;
  }
  // See if we are to make an error report and make necessary changes to the
  // options for good diagnostics. Note that error reports cannot be generated
  // for any of the above options which return with RETURN_CODE_NOT_RUN.
  else if(programOptions->options[OPTION_ERROR_REPORT]->enabled) {
    errorReporterInitialize(errorReporter);
    programOptions->options[OPTION_VERBOSE]->enabled = true;
    programOptions->options[OPTION_LOG_FILE]->enabled = true;
    programOptions->options[OPTION_DISPLAY_INFO]->enabled = true;
    // Shell script with original command line arguments
    errorReporterCreateLauncher(errorReporter, argc, argv);
    // Rewrite some paths before any input or output sources have been opened.
    _remapFileToErrorReport(errorReporter, programOptions->options[OPTION_INPUT_SOURCE], true);
    _remapFileToErrorReport(errorReporter, programOptions->options[OPTION_OUTPUT_SOURCE], false);
    _remapFileToErrorReport(errorReporter, programOptions->options[OPTION_MIDI_SOURCE], true);
    _remapFileToErrorReport(errorReporter, programOptions->options[OPTION_LOG_FILE], false);
  }

  // Read in options from a configuration file, if given
  if(programOptions->options[OPTION_CONFIG_FILE]->enabled) {
    if(!programOptionsParseConfigFile(programOptions, programOptionsGetString(programOptions, OPTION_CONFIG_FILE))) {
      return RETURN_CODE_INVALID_ARGUMENT;
    }
  }

  // Parse these options first so that log messages displayed in the below
  // loop are properly displayed
  if(programOptions->options[OPTION_VERBOSE]->enabled) {
    setLogLevel(LOG_DEBUG);
  }
  else if(programOptions->options[OPTION_QUIET]->enabled) {
    setLogLevel(LOG_ERROR);
  }
  else if(programOptions->options[OPTION_LOG_LEVEL]->enabled) {
    setLogLevelFromString(programOptionsGetString(programOptions, OPTION_LOG_LEVEL));
  }
  if(programOptions->options[OPTION_COLOR_LOGGING]->enabled) {
    // If --color was given but with no string argument, then force color. Otherwise
    // colors will be provided automatically anyways.
    if(charStringIsEmpty(programOptionsGetString(programOptions, OPTION_COLOR_LOGGING))) {
      programOptionsSetCString(programOptions, OPTION_COLOR_LOGGING, "force");
    }
    setLoggingColorEnabledWithString(programOptionsGetString(programOptions, OPTION_COLOR_LOGGING));
  }
  if(programOptions->options[OPTION_LOG_FILE]->enabled) {
    setLogFile(programOptionsGetString(programOptions, OPTION_LOG_FILE));
  }

  // Parse other options and set up necessary objects
  for(i = 0; i < programOptions->numOptions; i++) {
    option = programOptions->options[i];
    if(option->enabled) {
      switch(option->index) {
        case OPTION_BLOCKSIZE:
          setBlocksize((const unsigned long)programOptionsGetNumber(programOptions, OPTION_BLOCKSIZE));
          break;
        case OPTION_CHANNELS:
          setNumChannels((const unsigned long)programOptionsGetNumber(programOptions, OPTION_CHANNELS));
          break;
        case OPTION_DISPLAY_INFO:
          shouldDisplayPluginInfo = true;
          break;
        case OPTION_INPUT_SOURCE:
          freeSampleSource(inputSource);
          inputSource = sampleSourceFactory(programOptionsGetString(programOptions, OPTION_INPUT_SOURCE));
          break;
        case OPTION_MAX_TIME:
          maxTimeInMs = (const unsigned long)programOptionsGetNumber(programOptions, OPTION_MAX_TIME);
          break;
        case OPTION_MIDI_SOURCE:
          midiSource = newMidiSource(guessMidiSourceType(programOptionsGetString(
            programOptions, OPTION_MIDI_SOURCE)),
            programOptionsGetString(programOptions, OPTION_MIDI_SOURCE));
          break;
        case OPTION_OUTPUT_SOURCE:
          outputSource = sampleSourceFactory(programOptionsGetString(programOptions, OPTION_OUTPUT_SOURCE));
          break;
        case OPTION_PLUGIN_ROOT:
          charStringCopy(pluginSearchRoot, programOptionsGetString(programOptions, OPTION_PLUGIN_ROOT));
          break;
        case OPTION_SAMPLE_RATE:
          setSampleRate(programOptionsGetNumber(programOptions, OPTION_SAMPLE_RATE));
          break;
        case OPTION_TAIL_TIME:
          tailTimeInMs = (long)programOptionsGetNumber(programOptions, OPTION_TAIL_TIME);
          break;
        case OPTION_TEMPO:
          setTempo(programOptionsGetNumber(programOptions, OPTION_TEMPO));
          break;
        case OPTION_TIME_SIGNATURE:
          if(!setTimeSignatureFromString(programOptionsGetString(programOptions, OPTION_TIME_SIGNATURE))) {
            return RETURN_CODE_INVALID_ARGUMENT;
          }
          break;
        case OPTION_ZEBRA_SIZE:
          setLoggingZebraSize((int)programOptionsGetNumber(programOptions, OPTION_ZEBRA_SIZE));
          break;
        default:
          // Ignore -- no special handling needs to be performed here
          break;
      }
    }
  }

  if(programOptions->options[OPTION_LIST_PLUGINS]->enabled) {
    listAvailablePlugins(pluginSearchRoot);
    return RETURN_CODE_NOT_RUN;
  }
  if(programOptions->options[OPTION_LIST_FILE_TYPES]->enabled) {
    sampleSourcePrintSupportedTypes();
    return RETURN_CODE_NOT_RUN;
  }

  printWelcomeMessage(argc, argv);
  if((result = setupInputSource(inputSource)) != RETURN_CODE_SUCCESS) {
    logError("Input source could not be opened, exiting");
    return result;
  }
  if((result = buildPluginChain(pluginChain, programOptionsGetString(programOptions, OPTION_PLUGIN),
    pluginSearchRoot)) != RETURN_CODE_SUCCESS) {
    logError("Plugin chain could not be constructed, exiting");
    return result;
  }
  if(midiSource != NULL) {
    result = setupMidiSource(midiSource, &midiSequence);
    if(result != RETURN_CODE_SUCCESS) {
      logError("MIDI source could not be opened, exiting");
      return result;
    }
  }

  // Copy plugins before they have been opened
  if(programOptions->options[OPTION_ERROR_REPORT]->enabled) {
    if(errorReporterShouldCopyPlugins()) {
      if(!errorReporterCopyPlugins(errorReporter, pluginChain)) {
        logWarn("Failed copying plugins to error report directory");
      }
    }
  }

  // Initialize the plugin chain after the global sample rate has been set
  result = pluginChainInitialize(pluginChain);
  if(result != RETURN_CODE_SUCCESS) {
    logError("Could not initialize plugin chain");
    return result;
  }

  // Display info for plugins in the chain before checking for valid input/output sources
  if(shouldDisplayPluginInfo) {
    pluginChainInspect(pluginChain);
  }

  // Execute any parameter changes
  if(programOptions->options[OPTION_PARAMETER]->enabled) {
    if(!pluginChainSetParameters(pluginChain, programOptionsGetList(programOptions, OPTION_PARAMETER))) {
      return RETURN_CODE_INVALID_ARGUMENT;
    }
  }

  // Setup output source here. Having an invalid output source should not cause the program
  // to exit if the user only wants to list plugins or query info about a chain.
  if((result = setupOutputSource(outputSource)) != RETURN_CODE_SUCCESS) {
    logError("Output source could not be opened, exiting");
    return result;
  }

  // Verify input/output sources. This must be done after the plugin chain is initialized
  // otherwise the head plugin type is not known, which influences whether we must abort
  // processing.
  if(programOptions->options[OPTION_ERROR_REPORT]->enabled) {
    if(charStringIsEqualToCString(inputSource->sourceName, "-", false) ||
       charStringIsEqualToCString(outputSource->sourceName, "-", false)) {
      printf("ERROR: Using stdin/stdout is incompatible with --error-report\n");
      return RETURN_CODE_NOT_RUN;
    }
    if(midiSource != NULL && charStringIsEqualToCString(midiSource->sourceName, "-", false)) {
      printf("ERROR: MIDI source from stdin is incompatible with --error-report\n");
      return RETURN_CODE_NOT_RUN;
    }
  }
  if(outputSource == NULL) {
    logInternalError("Default output sample source was null");
    return RETURN_CODE_INTERNAL_ERROR;
  }
  if(inputSource == NULL || inputSource->sampleSourceType == SAMPLE_SOURCE_TYPE_SILENCE) {
    // If the first plugin in the chain is an instrument, use the silent source as our input and
    // make sure that there is a corresponding MIDI file
    headPlugin = pluginChain->plugins[0];
    if(headPlugin->pluginType == PLUGIN_TYPE_INSTRUMENT) {
      if(midiSource == NULL) {
        // I guess some instruments (like white noise generators etc.) don't necessarily
        // need MIDI, actually this is most useful for our internal plugins and generators.
        // Anyways, this should only be a soft warning for those who know what they're doing.
        logWarn("Plugin chain contains an instrument, but no MIDI source was supplied");
        if(maxTimeInMs == 0) {
          // However, if --max-time wasn't given, then there is effectively no input source
          // and thus processing would continue forever. That won't work.
          logError("No valid input source or maximum time, don't know when to stop processing");
          return RETURN_CODE_MISSING_REQUIRED_OPTION;
        }
        else {
          // If maximum time was given and there is no other input source, then use silence
          inputSource = newSampleSourceSilence();
        }
      }
    }
    else {
      logError("Plugin chain contains only effects, but no input source was supplied");
      return RETURN_CODE_MISSING_REQUIRED_OPTION;
    }
  }

  inputSampleBuffer = newSampleBuffer(getNumChannels(), getBlocksize());
  inputTimer = newTaskTimerWithCString(PROGRAM_NAME, "Input Source");
  outputSampleBuffer = newSampleBuffer(getNumChannels(), getBlocksize());
  outputTimer = newTaskTimerWithCString(PROGRAM_NAME, "Output Source");

  // Initialization is finished, we should be able to free this memory now
  freeProgramOptions(programOptions);

  // If a maximum time was given, figure it out here
  if(maxTimeInMs > 0) {
    maxTimeInFrames = (unsigned long)(maxTimeInMs * getSampleRate()) / 1000l;
  }

  processingDelayInFrames = pluginChainGetProcessingDelay(pluginChain);
  // Get largest tail time requested by any plugin in the chain
  tailTimeInMs += pluginChainGetMaximumTailTimeInMs(pluginChain);
  tailTimeInFrames = (unsigned long)(tailTimeInMs * getSampleRate()) / 1000l + processingDelayInFrames;
  pluginChainPrepareForProcessing(pluginChain);

  // Update sample rate on the event logger
  setLoggingZebraSize((long)getSampleRate());
  logInfo("Starting processing input source");
  logDebug("Sample rate: %.0f", getSampleRate());
  logDebug("Blocksize: %d", getBlocksize());
  logDebug("Channels: %d", getNumChannels());
  logDebug("Tempo: %.2f", getTempo());
  logDebug("Processing delay frames: %lu", processingDelayInFrames);
  logDebug("Time signature: %d/%d", getTimeSignatureBeatsPerMeasure(), getTimeSignatureNoteValue());
  taskTimerStop(initTimer);

  silentSampleInput = sampleSourceFactory(NULL);
  silentSampleOutput = sampleSourceFactory(NULL);
  // Main processing loop
  while(!finishedReading) {
    taskTimerStart(inputTimer);
    finishedReading = !readInput(inputSource, silentSampleInput, inputSampleBuffer, tailTimeInFrames);

    // TODO: For streaming MIDI, we would need to read in events from source here
    if(midiSequence != NULL) {
      LinkedList midiEventsForBlock = newLinkedList();
      // MIDI source overrides the value set to finishedReading by the input source
      finishedReading = !fillMidiEventsFromRange(midiSequence, audioClock->currentFrame, getBlocksize(), midiEventsForBlock);
      linkedListForeach(midiEventsForBlock, _processMidiMetaEvent, &finishedReading);
      pluginChainProcessMidi(pluginChain, midiEventsForBlock);
      freeLinkedList(midiEventsForBlock);
    }
    taskTimerStop(inputTimer);

    if(maxTimeInFrames > 0 && audioClock->currentFrame >= maxTimeInFrames) {
      logInfo("Maximum time reached, stopping processing after this block");
      finishedReading = true;
    }

    pluginChainProcessAudio(pluginChain, inputSampleBuffer, outputSampleBuffer);

    taskTimerStart(outputTimer);
    if(finishedReading) {
      outputSampleBuffer->blocksize = inputSampleBuffer->blocksize;//The input buffer size has been adjusted.
      logDebug("Using buffer size of %d for final block", outputSampleBuffer->blocksize);
    }
    writeOutput(outputSource, silentSampleOutput, outputSampleBuffer, processingDelayInFrames);
    taskTimerStop(outputTimer);
    advanceAudioClock(audioClock, outputSampleBuffer->blocksize);
  }

  // Close file handles for input/output sources
  silentSampleInput->closeSampleSource(silentSampleInput);
  silentSampleOutput->closeSampleSource(silentSampleOutput);
  inputSource->closeSampleSource(inputSource);
  outputSource->closeSampleSource(outputSource);

  // Print out statistics about each plugin's time usage
  // TODO: On windows, the total processing time is stored in clocks and not milliseconds
  // These values must be converted using the QueryPerformanceFrequency() function
  audioClockStop(audioClock);
  taskTimerStop(totalTimer);

  if(totalTimer->totalTaskTime > 0) {
    taskTimerList = newLinkedList();
    linkedListAppend(taskTimerList, initTimer);
    linkedListAppend(taskTimerList, inputTimer);
    linkedListAppend(taskTimerList, outputTimer);
    for(i = 0; i < pluginChain->numPlugins; i++) {
      linkedListAppend(taskTimerList, pluginChain->audioTimers[i]);
      linkedListAppend(taskTimerList, pluginChain->midiTimers[i]);
    }

    totalTimeString = taskTimerHumanReadbleString(totalTimer);
    logInfo("Total processing time %s, approximate breakdown:", totalTimeString->data);
    linkedListForeach(taskTimerList, _printTaskTime, totalTimer);
  }
  else {
    // Woo-hoo!
    logInfo("Total processing time <1ms. Either something went wrong, or your computer is smokin' fast!");
  }
  freeTaskTimer(initTimer);
  freeTaskTimer(inputTimer);
  freeTaskTimer(outputTimer);
  freeTaskTimer(totalTimer);
  freeLinkedList(taskTimerList);
  freeCharString(totalTimeString);

  if(midiSequence != NULL) {
    logInfo("Read %ld MIDI events from %s",
      midiSequence->numMidiEventsProcessed,
      midiSource->sourceName->data);
  }
  else {
    logInfo("Read %ld frames from %s",
      inputSource->numSamplesProcessed / getNumChannels(),
      inputSource->sourceName->data);
  }
  logInfo("Wrote %ld frames to %s",
    outputSource->numSamplesProcessed / getNumChannels(),
    outputSource->sourceName->data);

  // Shut down and free data (will also close open files, plugins, etc)
  logInfo("Shutting down");
  freeSampleSource(inputSource);
  freeSampleSource(outputSource);
  freeSampleBuffer(inputSampleBuffer);
  freeSampleBuffer(outputSampleBuffer);
  pluginChainShutdown(pluginChain);
  freePluginChain(pluginChain);

  if(midiSource != NULL) {
    freeMidiSource(midiSource);
  }
  if(midiSequence != NULL) {
    freeMidiSequence(midiSequence);
  }

  freeAudioSettings();
  logInfo("Goodbye!");
  freeEventLogger();
  freeAudioClock(getAudioClock());

  if(errorReporter->started) {
    errorReporterClose(errorReporter);
  }
  freeErrorReporter(errorReporter);

  return RETURN_CODE_SUCCESS;
}
Exemplo n.º 8
0
VstIntPtr VSTCALLBACK pluginVst2xHostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *dataPtr, float opt) {
  // This string is used in a bunch of logging calls below
  PluginVst2xId pluginId;
  if(effect != NULL) {
    pluginId = newPluginVst2xIdWithId(effect->uniqueID);
  }
  else {
    // During plugin initialization, the dispatcher can be called without a
    // valid plugin instance, as the AEffect* struct is still not fully constructed
    // at that point.
    pluginId = newPluginVst2xId();
  }
  const char* pluginIdString = pluginId->idString->data;
  VstIntPtr result = 0;

  logDebug("Plugin '%s' called host dispatcher with %d, %d, %d", pluginIdString, opcode, index, value);
  switch(opcode) {
    case audioMasterAutomate:
      // The plugin will call this if a parameter has changed via MIDI or the GUI, so the host can update
      // itself accordingly. We don't care about this (for the time being), and as we don't support either
      // GUI's or live MIDI, this opcode can be ignored.
      break;
    case audioMasterVersion:
      // We are a VST 2.4 compatible host
      result = 2400;
      break;
    case audioMasterCurrentId:
      // Use the current plugin ID, needed by VST shell plugins to determine which sub-plugin to load
      result = currentPluginUniqueId;
      break;
    case audioMasterIdle:
      // Ignore
      result = 1;
      break;
    case audioMasterPinConnected:
      logDeprecated("audioMasterPinConnected", pluginIdString);
      break;
    case audioMasterWantMidi:
      // This (deprecated) call is sometimes made by VST2.3 instruments to tell
      // the host that it is an instrument. We can safely ignore it.
      result = 1;
      break;
    case audioMasterGetTime: {
      AudioClock audioClock = getAudioClock();

      // These values are always valid
      vstTimeInfo.samplePos = audioClock->currentFrame;
      vstTimeInfo.sampleRate = getSampleRate();

      // Set flags for transport state
      vstTimeInfo.flags = 0;
      vstTimeInfo.flags |= audioClock->transportChanged ? kVstTransportChanged : 0;
      vstTimeInfo.flags |= audioClock->isPlaying ? kVstTransportPlaying : 0;

      // Fill values based on other flags which may have been requested
      if(value & kVstNanosValid) {
        // It doesn't make sense to return this value, as the plugin may try to calculate
        // something based on the current system time. As we are running offline, anything
        // the plugin calculates here will probably be wrong given the way we are running.
        // However, for realtime mode, this flag should be implemented in that case.
        logWarn("Plugin '%s' asked for time in nanoseconds (unsupported)", pluginIdString);
      }
      if(value & kVstPpqPosValid) {
        // TODO: Move calculations to AudioClock
        double samplesPerBeat = (60.0 / getTempo()) * getSampleRate();
        // Musical time starts with 1, not 0
        vstTimeInfo.ppqPos = (vstTimeInfo.samplePos / samplesPerBeat) + 1.0;
        logDebug("Current PPQ position is %g", vstTimeInfo.ppqPos);
        vstTimeInfo.flags |= kVstPpqPosValid;
      }
      if(value & kVstTempoValid) {
        vstTimeInfo.tempo = getTempo();
        vstTimeInfo.flags |= kVstTempoValid;
      }
      if(value & kVstBarsValid) {
        if(!(value & kVstPpqPosValid)) {
          logError("Plugin requested position in bars, but not PPQ");
        }
        // TODO: Move calculations to AudioClock
        double currentBarPos = floor(vstTimeInfo.ppqPos / (double)getTimeSignatureBeatsPerMeasure());
        vstTimeInfo.barStartPos = currentBarPos * (double)getTimeSignatureBeatsPerMeasure() + 1.0;
        logDebug("Current bar is %g", vstTimeInfo.barStartPos);
        vstTimeInfo.flags |= kVstBarsValid;
      }
      if(value & kVstCyclePosValid) {
        // We don't support cycling, so this is always 0
      }
      if(value & kVstTimeSigValid) {
        vstTimeInfo.timeSigNumerator = getTimeSignatureBeatsPerMeasure();
        vstTimeInfo.timeSigDenominator = getTimeSignatureNoteValue();
        vstTimeInfo.flags |= kVstTimeSigValid;
      }
      if(value & kVstSmpteValid) {
        logUnsupportedFeature("Current time in SMPTE format");
      }
      if(value & kVstClockValid) {
        logUnsupportedFeature("Sample frames until next clock");
      }

      result = (VstIntPtr)&vstTimeInfo;
      break;
    }
    case audioMasterProcessEvents:
      logUnsupportedFeature("VST master opcode audioMasterProcessEvents");
      break;
    case audioMasterSetTime:
      logDeprecated("audioMasterSetTime", pluginIdString);
      break;
    case audioMasterTempoAt:
      logDeprecated("audioMasterTempoAt", pluginIdString);
      break;
    case audioMasterGetNumAutomatableParameters:
      logDeprecated("audioMasterGetNumAutomatableParameters", pluginIdString);
      break;
    case audioMasterGetParameterQuantization:
      logDeprecated("audioMasterGetParameterQuantization", pluginIdString);
      break;
    case audioMasterIOChanged: {
      PluginChain pluginChain = getPluginChain();
      logDebug("Number of inputs: %d", effect->numInputs);
      logDebug("Number of outputs: %d", effect->numOutputs);
      logDebug("Number of parameters: %d", effect->numParams);
      logDebug("Initial Delay: %d", effect->initialDelay);
      result = -1;
      for(unsigned int i = 0; i < pluginChain->numPlugins; ++i){
        if((unsigned long)effect->uniqueID == pluginVst2xGetUniqueId(pluginChain->plugins[i])){
          logDebug("Updating plugin");
          pluginVst2xAudioMasterIOChanged(pluginChain->plugins[i], effect);
          result = 0;
          break;//Only one plugin will match anyway.
        }
      }
      break;
    }
    case audioMasterNeedIdle:
      logDeprecated("audioMasterNeedIdle", pluginIdString);
      break;
    case audioMasterSizeWindow:
      logWarn("Plugin '%s' asked us to resize window (unsupported)", pluginIdString);
      break;
    case audioMasterGetSampleRate:
      result = (int)getSampleRate();
      break;
    case audioMasterGetBlockSize:
      result = getBlocksize();
      break;
    case audioMasterGetInputLatency:
      // Input latency is not used, and is always 0
      result = 0;
      break;
    case audioMasterGetOutputLatency:
      // Output latency is not used, and is always 0
      result = 0;
      break;
    case audioMasterGetPreviousPlug:
      logDeprecated("audioMasterGetPreviousPlug", pluginIdString);
      break;
    case audioMasterGetNextPlug:
      logDeprecated("audioMasterGetNextPlug", pluginIdString);
      break;
    case audioMasterWillReplaceOrAccumulate:
      logDeprecated("audioMasterWillReplaceOrAccumulate", pluginIdString);
      break;
    case audioMasterGetCurrentProcessLevel:
      // We are not a multithreaded app and have no GUI, so this is unsupported.
      result = kVstProcessLevelUnknown;
      break;
    case audioMasterGetAutomationState:
      // Automation is also not supported (for now)
      result = kVstAutomationUnsupported;
      break;
    case audioMasterOfflineStart:
      logWarn("Plugin '%s' asked us to start offline processing (unsupported)", pluginIdString);
      break;
    case audioMasterOfflineRead:
      logWarn("Plugin '%s' asked to read offline data (unsupported)", pluginIdString);
      break;
    case audioMasterOfflineWrite:
      logWarn("Plugin '%s' asked to write offline data (unsupported)", pluginIdString);
      break;
    case audioMasterOfflineGetCurrentPass:
      logWarn("Plugin '%s' asked for current offline pass (unsupported)", pluginIdString);
      break;
    case audioMasterOfflineGetCurrentMetaPass:
      logWarn("Plugin '%s' asked for current offline meta pass (unsupported)", pluginIdString);
      break;
    case audioMasterSetOutputSampleRate:
      logDeprecated("audioMasterSetOutputSampleRate", pluginIdString);
      break;
    case audioMasterGetOutputSpeakerArrangement:
      logDeprecated("audioMasterGetOutputSpeakerArrangement", pluginIdString);
      break;
    case audioMasterGetVendorString:
      strncpy((char*)dataPtr, VENDOR_NAME, kVstMaxVendorStrLen);
      result = 1;
      break;
    case audioMasterGetProductString:
      strncpy((char*)dataPtr, PROGRAM_NAME, kVstMaxProductStrLen);
      result = 1;
      break;
    case audioMasterGetVendorVersion:
      // Return our version as a single string, in the form ABCC, which corresponds to version A.B.C
      // Often times the patch can reach double-digits, so it gets two decimal places.
      result = VERSION_MAJOR * 1000 + VERSION_MINOR * 100 + VERSION_PATCH;
      break;
    case audioMasterVendorSpecific:
      logWarn("Plugin '%s' made a vendor specific call (unsupported). Arguments: %d, %d, %f", pluginIdString, index, value, opt);
      break;
    case audioMasterCanDo:
      result = _canHostDo(pluginIdString, (char*)dataPtr);
      break;
    case audioMasterSetIcon:
      logDeprecated("audioMasterSetIcon", pluginIdString);
      break;
    case audioMasterGetLanguage:
      result = kVstLangEnglish;
      break;
    case audioMasterOpenWindow:
      logDeprecated("audioMasterOpenWindow", pluginIdString);
      break;
    case audioMasterCloseWindow:
      logDeprecated("audioMasterCloseWindow", pluginIdString);
      break;
    case audioMasterGetDirectory:
      logWarn("Plugin '%s' asked for directory pointer (unsupported)", pluginIdString);
      break;
    case audioMasterUpdateDisplay:
      // Ignore
      break;
    case audioMasterBeginEdit:
      logWarn("Plugin '%s' asked to begin parameter automation (unsupported)", pluginIdString);
      break;
    case audioMasterEndEdit:
      logWarn("Plugin '%s' asked to end parameter automation (unsupported)", pluginIdString);
      break;
    case audioMasterOpenFileSelector:
      logWarn("Plugin '%s' asked us to open file selector (unsupported)", pluginIdString);
      break;
    case audioMasterCloseFileSelector:
      logWarn("Plugin '%s' asked us to close file selector (unsupported)", pluginIdString);
      break;
    case audioMasterEditFile:
      logDeprecated("audioMasterEditFile", pluginIdString);
      break;
    case audioMasterGetChunkFile:
      logDeprecated("audioMasterGetChunkFile", pluginIdString);
      break;
    case audioMasterGetInputSpeakerArrangement:
      logDeprecated("audioMasterGetInputSpeakerArrangement", pluginIdString);
      break;
    default:
      logWarn("Plugin '%s' asked if host can do unknown opcode %d", pluginIdString, opcode);
      break;
  }

  freePluginVst2xId(pluginId);
  return result;
}
Exemplo n.º 9
0
static void _audioClockTestTeardown(void)
{
    freeAudioClock(getAudioClock());
}