//============================================================================== void AudioProcessorPlayer::setProcessor (AudioProcessor* const processorToPlay) { if (processor != processorToPlay) { if (processorToPlay != nullptr && sampleRate > 0 && blockSize > 0) { processorToPlay->setPlayConfigDetails (numInputChans, numOutputChans, sampleRate, blockSize); processorToPlay->prepareToPlay (sampleRate, blockSize); } AudioProcessor* oldOne; { const ScopedLock sl (lock); oldOne = isPrepared ? processor : nullptr; processor = processorToPlay; isPrepared = true; } if (oldOne != nullptr) oldOne->releaseResources(); } }
//============================================================================== void AudioProcessorPlayer::setProcessor (AudioProcessor* const processorToPlay) { if (processor != processorToPlay) { if (processorToPlay != nullptr && sampleRate > 0 && blockSize > 0) { processorToPlay->setPlayConfigDetails (numInputChans, numOutputChans, sampleRate, blockSize); const bool supportsDouble = processorToPlay->supportsDoublePrecisionProcessing() && isDoublePrecision; AudioProcessor::ProcessingPrecision precision = supportsDouble ? AudioProcessor::doublePrecision : AudioProcessor::singlePrecision; processorToPlay->setProcessingPrecision (precision); processorToPlay->prepareToPlay (sampleRate, blockSize); } AudioProcessor* oldOne; { const ScopedLock sl (lock); oldOne = isPrepared ? processor : nullptr; processor = processorToPlay; isPrepared = true; } if (oldOne != nullptr) oldOne->releaseResources(); } }
void PMixInterpolationSpaceLayout::mouseDoubleClick (const MouseEvent& e) { if(graphEditor.getLassoSelection().getNumSelected() == 1) { NodeComponent* selectedItem = dynamic_cast<NodeComponent*>(graphEditor.getLassoSelection().getSelectedItem(0)); if (selectedItem) { AudioProcessor* proc = audioEngine.getDoc().getNodeForId(selectedItem->nodeID)->getProcessor(); bool hasParams = (proc->getNumParameters() > 0); if (hasParams) { if (!InternalPluginFormat::isInternalFormat(proc->getName())) { double x = (double) e.getMouseDownX()/getWidth(); double y = (double) e.getMouseDownY()/getHeight(); audioEngine.getDoc().addPreset(selectedItem->nodeID, x, y); } } } } }
FilterIOConfigurationWindow::FilterIOConfigurationWindow (AudioProcessor& p) : AudioProcessorEditor (&p), title ("title", p.getName()) { setOpaque (true); title.setFont (title.getFont().withStyle (Font::bold)); addAndMakeVisible (title); { ScopedLock renderLock (p.getCallbackLock()); p.suspendProcessing (true); p.releaseResources(); } if (p.getBusCount (true) > 0 || p.canAddBus (true)) { inConfig.reset (new InputOutputConfig (*this, true)); addAndMakeVisible (inConfig.get()); } if (p.getBusCount (false) > 0 || p.canAddBus (false)) { outConfig.reset (new InputOutputConfig (*this, false)); addAndMakeVisible (outConfig.get()); } currentLayout = p.getBusesLayout(); setSize (400, (inConfig != nullptr && outConfig != nullptr ? 160 : 0) + 200); }
//------------------------------------------------------------------------------ void Mapping::updateParameter(float val) { AudioProcessor *filter = filterGraph->getNodeForId(plugin)->getProcessor(); if(parameter == -1) { BypassableInstance *bypassable = dynamic_cast<BypassableInstance *>(filter); if(bypassable) bypassable->setBypass(val > 0.5f); } else filter->setParameter(parameter, val); }
bool SndFileAudioFileReader::run(AudioProcessor& processor) { if (input_file_ == nullptr) { return false; } const int BUFFER_SIZE = 16384; short input_buffer[BUFFER_SIZE]; sf_count_t frames_to_read = BUFFER_SIZE / info_.channels; sf_count_t frames_read = frames_to_read; sf_count_t total_frames_read = 0; bool success = true; success = processor.init(info_.samplerate, info_.channels, BUFFER_SIZE); if (success) { showProgress(0, info_.frames); while (success && frames_read == frames_to_read) { frames_read = sf_readf_short( input_file_, input_buffer, frames_to_read ); success = processor.process( input_buffer, static_cast<int>(frames_read) ); total_frames_read += frames_read; showProgress(total_frames_read, info_.frames); } output_stream << "\nRead " << total_frames_read << " frames\n"; processor.done(); } close(); return success; }
static int callback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData ) { AudioProcessor* audioDevice = (AudioProcessor*)userData; float** output = (float**)outputBuffer; float** input = (float**)inputBuffer; // if(audioDevice->isPrepared()) audioDevice->render(input, audioDevice->getNumInputChannels(), output, audioDevice->getNumOutputChannels(), framesPerBuffer); return 0; }
void update (AudioProcessor& audioProcessor, bool forceLegacyParamIDs) { clear(); legacyParamIDs = forceLegacyParamIDs; auto numParameters = audioProcessor.getNumParameters(); usingManagedParameters = (audioProcessor.getParameters().size() == numParameters) && (! legacyParamIDs); for (int i = 0; i < numParameters; ++i) { AudioProcessorParameter* param = usingManagedParameters ? audioProcessor.getParameters()[i] : (legacy.add (new LegacyAudioParameter (audioProcessor, i))); params.add (param); } }
ProcessorParameterPropertyComp (const String& name, AudioProcessor& owner_, const int index_) : PropertyComponent (name), owner (owner_), index (index_), paramHasChanged (false), slider (owner_, index_) { startTimer (100); addAndMakeVisible (&slider); owner_.addListener (this); }
static int getParamIndex (AudioProcessor& processor, AudioProcessorParameter* param) noexcept { if (auto* legacy = dynamic_cast<LegacyAudioParameter*> (param)) { return legacy->parameterIndex; } else { auto n = processor.getNumParameters(); jassert (n == processor.getParameters().size()); for (int i = 0; i < n; ++i) { if (processor.getParameters()[i] == param) return i; } } return -1; }
void PMixInterpolationSpaceLayout::mouseDown (const MouseEvent& e) { selectedItems.deselectAll(); if (e.mods.isPopupMenu()) { if(graphEditor.getLassoSelection().getNumSelected() == 1) { NodeComponent* selectedItem = dynamic_cast<NodeComponent*>(graphEditor.getLassoSelection().getSelectedItem(0)); if (selectedItem) { AudioProcessor* proc = audioEngine.getDoc().getNodeForId(selectedItem->nodeID)->getProcessor(); PopupMenu m; bool hasParams = (proc->getNumParameters() > 0); m.addItem (1, TRANS("Add preset for node"), hasParams); const int r = m.show(); if (r == 1) { if (!InternalPluginFormat::isInternalFormat(proc->getName())) { double x = (double) e.getMouseDownX()/getWidth(); double y = (double) e.getMouseDownY()/getHeight(); audioEngine.getDoc().addPreset(selectedItem->nodeID, x, y); } } } } } else { addChildComponent (lassoComp); lassoComp.beginLasso (e, this); } }
PluginWindow* PluginWindow::getWindowFor (AudioProcessorGraph::Node* const node, WindowFormatType type) { jassert (node != nullptr); for (int i = activePluginWindows.size(); --i >= 0;) if (activePluginWindows.getUnchecked(i)->owner == node && activePluginWindows.getUnchecked(i)->type == type) return activePluginWindows.getUnchecked(i); AudioProcessor* processor = node->getProcessor(); AudioProcessorEditor* ui = nullptr; if (type == Normal) { ui = processor->createEditorIfNeeded(); if (ui == nullptr) type = Generic; } if (ui == nullptr) { //if (type == Generic || type == Parameters) //ui = new PMixGenericAudioProcessorEditor (processor); // else if (type == Programs) // ui = new ProgramAudioProcessorEditor (processor); } if (ui != nullptr) { if (AudioPluginInstance* const plugin = dynamic_cast<AudioPluginInstance*> (processor)) ui->setName (plugin->getName()); return new PluginWindow (ui, node, type); } return nullptr; }
ParamSlider (AudioProcessor& p, int paramIndex) : owner (p), index (paramIndex) { const int steps = owner.getParameterNumSteps (index); const AudioProcessorParameter::Category category = p.getParameterCategory (index); const bool isLevelMeter = (((category & 0xffff0000) >> 16) == 2); if (steps > 1 && steps < 0x7fffffff) setRange (0.0, 1.0, 1.0 / (steps - 1.0)); else setRange (0.0, 1.0); setEnabled (! isLevelMeter); setSliderStyle (Slider::LinearBar); setTextBoxIsEditable (false); setScrollWheelEnabled (true); }
bool SndFileAudioFileReader::run(AudioProcessor& processor) { if (input_file_ == nullptr) { return false; } const int BUFFER_SIZE = 16384; float float_buffer[BUFFER_SIZE]; short input_buffer[BUFFER_SIZE]; const int sub_type = info_.format & SF_FORMAT_SUBMASK; const bool is_floating_point = sub_type == SF_FORMAT_FLOAT || sub_type == SF_FORMAT_DOUBLE; sf_count_t frames_to_read = BUFFER_SIZE / info_.channels; sf_count_t frames_read = frames_to_read; sf_count_t total_frames_read = 0; bool success = true; success = processor.init(info_.samplerate, info_.channels, BUFFER_SIZE); if (success) { showProgress(0, info_.frames); while (success && frames_read == frames_to_read) { if (is_floating_point) { frames_read = sf_readf_float( input_file_, float_buffer, frames_to_read ); // Scale floating-point samples from [-1.0, 1.0] to 16-bit // integer range. Note: we don't use SFC_SET_SCALE_FLOAT_INT_READ // as this scales using the overall measured waveform peak // amplitude, resulting in an unwanted amplitude change. for (int i = 0; i < frames_read * info_.channels; ++i) { input_buffer[i] = static_cast<short>( float_buffer[i] * std::numeric_limits<short>::max() ); } } else { frames_read = sf_readf_short( input_file_, input_buffer, frames_to_read ); } success = processor.process( input_buffer, static_cast<int>(frames_read) ); total_frames_read += frames_read; showProgress(total_frames_read, info_.frames); } output_stream << "\nRead " << total_frames_read << " frames\n"; processor.done(); } close(); return success; }
bool Mp3AudioFileReader::run(AudioProcessor& processor) { if (file_ == nullptr) { return false; } enum { STATUS_OK, STATUS_INIT_ERROR, STATUS_READ_ERROR, STATUS_PROCESS_ERROR } status = STATUS_OK; unsigned char input_buffer[INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD]; unsigned char* guard_ptr = nullptr; unsigned long frame_count = 0; short output_buffer[OUTPUT_BUFFER_SIZE]; short* output_ptr = output_buffer; const short* const output_buffer_end = output_buffer + OUTPUT_BUFFER_SIZE; int channels = 0; // Decoding options can here be set in the options field of the stream // structure. // {1} When decoding from a file we need to know when the end of the file is // reached at the same time as the last bytes are read (see also the comment // marked {3} below). Neither the standard C fread() function nor the POSIX // read() system call provides this feature. We thus need to perform our // reads through an interface having this feature, this is implemented here // by the bstdfile.c module. BStdFile bstd_file(file_); // Initialize the structures used by libmad. MadStream stream; MadFrame frame; MadSynth synth; mad_timer_t timer; mad_timer_reset(&timer); // This is the decoding loop. for (;;) { // The input bucket must be filled if it becomes empty or if it's the // first execution of the loop. if (stream.buffer == nullptr || stream.error == MAD_ERROR_BUFLEN) { size_t read_size; size_t remaining; unsigned char* read_start; // {2} libmad may not consume all bytes of the input buffer. If the // last frame in the buffer is not wholly contained by it, then that // frame's start is pointed by the next_frame member of the stream // structure. This common situation occurs when mad_frame_decode() // fails, sets the stream error code to MAD_ERROR_BUFLEN, and sets // the next_frame pointer to a non-NULL value. (See also the comment // marked {4} below.) // // When this occurs, the remaining unused bytes must be put back at // the beginning of the buffer and taken in account before refilling // the buffer. This means that the input buffer must be large enough // to hold a whole frame at the highest observable bit-rate // (currently 448 kb/s). XXX=XXX Is 2016 bytes the size of the // largest frame? (448000*(1152/32000))/8 if (stream.next_frame != nullptr) { remaining = stream.bufend - stream.next_frame; memmove(input_buffer, stream.next_frame, remaining); read_start = input_buffer + remaining; read_size = INPUT_BUFFER_SIZE - remaining; } else { read_size = INPUT_BUFFER_SIZE; read_start = input_buffer; remaining = 0; } // Fill-in the buffer. If an error occurs print a message and leave // the decoding loop. If the end of stream is reached we also leave // the loop but the return status is left untouched. read_size = bstd_file.read(read_start, 1, read_size); if (read_size <= 0) { if (ferror(file_)) { error_stream << "\nRead error on bit-stream: " << strerror(errno) << '\n'; status = STATUS_READ_ERROR; } break; } // {3} When decoding the last frame of a file, it must be followed // by MAD_BUFFER_GUARD zero bytes if one wants to decode that last // frame. When the end of file is detected we append that quantity // of bytes at the end of the available data. Note that the buffer // can't overflow as the guard size was allocated but not used the // the buffer management code. (See also the comment marked {1}.) // // In a message to the mad-dev mailing list on May 29th, 2001, Rob // Leslie explains the guard zone as follows: // // "The reason for MAD_BUFFER_GUARD has to do with the way // decoding is performed. In Layer III, Huffman decoding may // inadvertently read a few bytes beyond the end of the buffer in // the case of certain invalid input. This is not detected until // after the fact. To prevent this from causing problems, and // also to ensure the next frame's main_data_begin pointer is // always accessible, MAD requires MAD_BUFFER_GUARD (currently 8) // bytes to be present in the buffer past the end of the current // frame in order to decode the frame." if (bstd_file.eof()) { guard_ptr = read_start + read_size; memset(guard_ptr, 0, MAD_BUFFER_GUARD); read_size += MAD_BUFFER_GUARD; } // Pipe the new buffer content to libmad's stream decoder facility. mad_stream_buffer(&stream, input_buffer, read_size + remaining); stream.error = MAD_ERROR_NONE; } // Decode the next MPEG frame. The streams is read from the buffer, its // constituents are break down and stored the the frame structure, ready // for examination/alteration or PCM synthesis. Decoding options are // carried in the frame structure from the stream structure. // // Error handling: mad_frame_decode() returns a non zero value when an // error occurs. The error condition can be checked in the error member // of the stream structure. A mad error is recoverable or fatal, the // error status is checked with the MAD_RECOVERABLE macro. // // {4} When a fatal error is encountered all decoding activities shall // be stopped, except when a MAD_ERROR_BUFLEN is signaled. This // condition means that the mad_frame_decode() function needs more input // to complete its work. One shall refill the buffer and repeat the // mad_frame_decode() call. Some bytes may be left unused at the end of // the buffer if those bytes forms an incomplete frame. Before // refilling, the remaining bytes must be moved to the beginning of the // buffer and used for input for the next mad_frame_decode() invocation. // (See the comments marked {2} earlier for more details.) // // Recoverable errors are caused by malformed bit-streams, in this case // one can call again mad_frame_decode() in order to skip the faulty // part and re-sync to the next frame. if (mad_frame_decode(&frame, &stream)) { if (MAD_RECOVERABLE(stream.error)) { // Do not print a message if the error is a loss of // synchronization and this loss is due to the end of stream // guard bytes. (See the comment marked {3} above for more // information about guard bytes.) if (stream.error != MAD_ERROR_LOSTSYNC || stream.this_frame != guard_ptr) { // For any MP3 file we typically see two errors in the // first frame processed: // - lost synchronization // - reserved header layer value // This seems to be OK, so don't print these if (frame_count != 0) { error_stream << "\nRecoverable frame level error: " << mad_stream_errorstr(&stream) << '\n'; } } continue; } else { if (stream.error == MAD_ERROR_BUFLEN) { continue; } else { error_stream << "\nUnrecoverable frame level error: " << mad_stream_errorstr(&stream) << '\n'; status = STATUS_READ_ERROR; break; } } } // Display the characteristics of the stream's first frame. The first // frame is representative of the entire stream. if (frame_count == 0) { const int sample_rate = frame.header.samplerate; channels = MAD_NCHANNELS(&frame.header); dumpInfo(output_stream, frame.header); if (!processor.init(sample_rate, channels, OUTPUT_BUFFER_SIZE)) { status = STATUS_PROCESS_ERROR; break; } showProgress(0, file_size_); } // Accounting. The computed frame duration is in the frame header // structure. It is expressed as a fixed point number whole data type is // mad_timer_t. It is different from the samples fixed point format and // unlike it, it can't directly be added or subtracted. The timer module // provides several functions to operate on such numbers. Be careful // there, as some functions of libmad's timer module receive some of // their mad_timer_t arguments by value! frame_count++; mad_timer_add(&timer, frame.header.duration); // Once decoded the frame is synthesized to PCM samples. No errors are // reported by mad_synth_frame(); mad_synth_frame(&synth, &frame); // Synthesized samples must be converted from libmad's fixed point // number to the consumer format. Here we use signed 16 bit integers on // two channels. Integer samples are temporarily stored in a buffer that // is flushed when full. for (int i = 0; i < synth.pcm.length; i++) { // Left channel short sample = MadFixedToSshort(synth.pcm.samples[0][i]); *output_ptr++ = sample; // Right channel. If the decoded stream is monophonic then the right // output channel is the same as the left one. if (MAD_NCHANNELS(&frame.header) == 2) { sample = MadFixedToSshort(synth.pcm.samples[1][i]); *output_ptr++ = sample; } // Flush the output buffer if it is full if (output_ptr == output_buffer_end) { long pos = ftell(file_); showProgress(pos, file_size_); bool success = processor.process( output_buffer, OUTPUT_BUFFER_SIZE / channels ); if (!success) { status = STATUS_PROCESS_ERROR; break; } output_ptr = output_buffer; } } } // If the output buffer is not empty and no error occurred during the last // write, then flush it. if (output_ptr != output_buffer && status != STATUS_PROCESS_ERROR) { int buffer_size = static_cast<int>(output_ptr - output_buffer); bool success = processor.process(output_buffer, buffer_size / channels); if (!success) { status = STATUS_PROCESS_ERROR; } } // Accounting report if no error occurred. if (status == STATUS_OK) { // Report 100% done. showProgress(file_size_, file_size_); char buffer[80]; // The duration timer is converted to a human readable string with the // versatile, but still constrained mad_timer_string() function, in a // fashion not unlike strftime(). The main difference is that the timer // is broken into several values according some of its arguments. The // units and fracunits arguments specify the intended conversion to be // executed. // // The conversion unit (MAD_UNIT_MINUTES in our example) also specify // the order and kind of conversion specifications that can be used in // the format string. // // It is best to examine libmad's timer.c source-code for details of the // available units, fraction of units, their meanings, the format // arguments, etc. mad_timer_string(timer, buffer, "%lu:%02lu.%03u", MAD_UNITS_MINUTES, MAD_UNITS_MILLISECONDS, 0); output_stream << "\nFrames decoded: " << frame_count << " (" << buffer << ")\n"; } processor.done(); close(); return status == STATUS_OK; }
String getParamID (AudioProcessor& processor, int idx) const noexcept { return usingManagedParameters ? processor.getParameterID (idx) : String (idx); }