Refactor `SampleBuffer` (#6610)
* Add refactored SampleBuffer * Add Sample * Add SampleLoader * Integrate changes into AudioSampleRecorder * Integrate changes into Oscillator * Integrate changes into SampleClip/SamplePlayHandle * Integrate changes into Graph * Remove SampleBuffer include from SampleClipView * Integrate changes into Patman * Reduce indirection to sample buffer from Sample * Integrate changes into AudioFileProcessor * Remove old SampleBuffer * Include memory header in TripleOscillator * Include memory header in Oscillator * Use atomic_load within SampleClip::sample * Include memory header in EnvelopeAndLfoParameters * Use std::atomic_load for most calls to Oscillator::userWaveSample * Revert accidental change on SamplePlayHandle L.111 * Check if audio file is empty before loading * Add asserts to Sample * Add cassert include within Sample * Adjust assert expressions in Sample * Remove use of shared ownership for Sample Sample does not need to be wrapped around a std::shared_ptr. This was to work with the audio thread, but the audio thread can instead have their own Sample separate from the UI's Sample, so changes to the UI's Sample would not leave the audio worker thread using freed data if it had pointed to it. * Use ArrayVector in Sample * Enforce std::atomic_load for users of std::shared_ptr<const SampleBuffer> * Use requestChangesGuard in ClipView::remove Fixes data race when deleting SampleClip * Revert only formatting changes * Update ClipView::remove comment * Revert "Remove use of shared ownership for Sample" This reverts commit1d452331d1. In some cases, you can infact do away with shared ownership on Sample if there are no writes being made to either of them, but to make sure changes are reflected to the object in cases where writes do happen, they should work with the same one. * Fix heap-use-after-free in Track::loadSettings * Remove m_buffer asserts * Refactor play functionality (again) The responsibility of resampling the buffer and moving the frame index is now in Sample::play, allowing the removal of both playSampleRangeLoop and playSampleRangePingPong. * Change copyright * Cast processingSampleRate to float Fixes division by zero error * Update include/SampleLoader.h Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Update include/SampleLoader.h Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Format SampleLoader.h * Remove SampleBuffer.h include in SampleRecordHandle.h * Update src/core/Oscillator.cpp Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Use typeInfo<float> for float equality comparison Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Use std::min in Sample::visualize Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Move in result to m_data * Use if block in playSampleRange * Pass in unique_ptr to SampleClip::setSampleBuffer * Return const QString& from SampleBuffer::audioFile * Do not pass in unique_ptr by r-value reference * Use isEmpty() within SampleClipView::updateSample * Remove use of atomic_store and atomic_load * Remove ArrayVector comment * Use array specialization for unique_ptr when managing DrumSynth data Also made it so that we don't create result before checking if we failed to decode the file, potentially saving us an allocation. * Don't manually delete Clip if it has a Track * Clean up generateAntiAliasUserWaveTable function Also, make it so that we actually call this function when necessary in TripleOscillator. * Set user wave, even when value is empty If the value or file is empty, I think showing a error popup here is ideal. * Remove whitespace in EnvelopeAndLfoParameters.cpp L#121 * Fix error inc5f7ccba49We still have to delete the Clip's, or else we would just be eating up memory. But we should first make sure that the Track's no longer see this Clip in their m_clips vector. This has to happen as it's own operation because we have to wait for the audio thread(s) first. This would ensure that Track's do not create PlayHandle's that would refer to a Clip that is currently being destroyed. After that, then we call deleteLater on the Clip. * Convert std::shared_ptr<Sample> to Sample This conversion does not apply to Patman as there seems to be issues with it causing heap-use-after-free issues, such as with PatmanInstrument::unloadCurrentPatch * Fix segfault when closing LMMS Song should be deleted before AudioEngine. * Construct buffer through SampleLoader in FileBrowser's previewFileItem function + Remove const qualification in SamplePlayHandle(const QString&) constructor for m_sample * Move guard out of removeClip and deleteClips + Revert commit1769ed517dsince this would fix it anyway (we don't try to lock the engine to delete the global automation track when closing LMMS now) * Simplify the switch in play function for loopMode * Add SampleDecoder * Add LMMS_HAVE_OGGVORBIS comment * Fix unused variable error * Include unordered_map * Simplify SampleDecoder Instead of using the extension (which could be wrong) for the file, we simply loop through all the decoders available. First sndfile because it covers a lot of formats, then the ogg decoder for the few cases where sndfile would not work for certain audio codecs, and then the DrumSynth decoder. * Attempt to fix Mac builds * Attempt to fix Mac builds take 2 * Add vector include to SampleDecoder * Add TODO comment about shared ownership with clips Calls to ClipView::remove may occur at any point, which can cause a problem when the Track is using the clip about to be removed. A suitable solution would be to use shared ownership between the Track and ClipView for the clip. Track's can then simply remove the shared pointer in their m_clips vector, and ClipView can call reset on the shared pointer on calls to ClipView::remove. * Adjust TODO comment Disregard the shared ownership idea. Since we would be modifying the collection of Clip's in Track when removing the Clip, the Track could be iterating said collection while this happens, causing a bug. In this case, we do actually want a synchronization mechanism. However, I didn't mention another separate issue in the TODO comment that should've been addressed: ~Clip should not be responsible for actually removing the itself from it's Track. With calls to removeClip, one would expect that to already occur. * Remove Sample::playbackSize Inside SampleClip::sampleLength, we should be using Sample::sampleSize instead. * Fix issues involving length of Sample's SampleClip::sampleLength should be passing the Sample's sample rate to Engine::framesPerTick. I also changed sampleDuration to return a std::chrono::milliseconds instead of an int so that the callers know what time interval is being used. * Simplify if condition in src/gui/FileBrowser.cpp Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Simplify if condition in src/core/SampleBuffer.cpp Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Update style in include/Oscillator.h Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Format src/core/SampleDecoder.cpp Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Set the sample rate to be that of the AudioEngine by default I also removed some checks involving the state of the SampleBuffer. These functions should expect a valid SampleBuffer each time. This helps to simplify things since we don't have to validate it in each function. * Set single-argument constructors in Sample and SampleBuffer to be explicit * Do not make a copy when reading result from the decoder * Add constructor to pass in vector of sampleFrame's directly * Do a pass by value and move in SampleBuffer.cpp Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Pass vector by value in SampleBuffer.h Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Make Sample(std::shared_ptr) constructor explicit * Properly draw sample waveform when reversed * Collect sample not found errors when loading project Also return empty buffers when trying to load either an empty file or empty Base64 string * Use std::make_unique<SampleBuffer> in SampleLoader * Fix loop modes * Limit sample duration to [start, end] and not the entire buffer * Use structured binding to access buffer * Check if GUI exists before displaying error * Make Base64 constructor pass in the string instead * Remove use of QByteArray::fromBase64Encoding * Inline simple functions in SampleBuffer * Dynamically include supported audio file types * Remove redundant inline specifier Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Translate file types * Cache calls to SampleDecoder::supportedAudioTypes * Fix translations in SampleLoader (again) Also ensure that all the file types are listed first. Also simplified the generation of the list a bit. * Store static local variable for supported audio types instead of in the header Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Clamp frame index depending on loop mode * Inline member functions of PlaybackState * Do not collect errors in SampleLoader when loading projects Also fix conflicts with surrounding codebase * Default construct shared pointers to SampleBuffer * Simplify and optimize Sample::visulaize() * Remove redundant gui:: prefix * Rearrange Sample::visualize after optimizations by DanielKauss * Apply amplification when visualizing sample waveforms * Set default min and max values to 1 and -1 * Treat waveform as mono signal when visualizing * Ensure visualization works when framesPerPixel < 1 * Simplify Sample::visualize a bit more * Fix CPU lag in Sample by using atomics (with relaxed ordering) Changing any of the frame markers originally took a writer lock on a mutex. The problem is that Sample::play took a reader lock first before executing. Because Sample::play has to wait on the writer, this created a lot of lag and raised the CPU meter. The solution would to be to use atomics instead. * Fix errors from merge * Fix broken LFO controller functionality The shared_ptr should have been taken by reference. * Remove TODO * Update EnvelopeAndLfoView.cpp Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com> * Update src/gui/clips/SampleClipView.cpp Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com> * Update plugins/SlicerT/SlicerT.cpp Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com> * Update plugins/SlicerT/SlicerT.cpp Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com> * Store shortest relative path in SampleBuffer * Tie up a few loose ends * Use sample_rate_t when storing sample rate in SampleBuffer * Add missing named requirement functions and aliases * Use sampledata attribute when loading from Base64 in AFP * Remove initializer for m_userWave in the constructor * Do not use trailing return syntax when return is void * Move decoder functionality into unnamed namespace * Remove redundant gui:: prefix * Use PathUtil::toAbsolute to simplify code in SampleLoader::openAudioFile * Fix translations in SampleLoader::openAudioFile Co-authored-by: DomClark <mrdomclark@gmail.com> * Fix formatting for ternary operator * Remove redundant inlines * Resolve UB when decoding from Base64 data in SampleBuffer * Fix up SampleClip constructors * Add AudioResampler, a wrapper class around libsamplerate The wrapper has only been applied to Sample::PlaybackState for now. AudioResampler should be used by other classes in the future that do resampling with libsamplerate. * Move buffer when moving and simplify assignment functions in Sample * Move Sample::visualize out of Sample and into the GUI namespace * Initialize supportedAudioTypes in static lambda * Return shared pointer from SampleLoader * Create and use static empty SampleBuffer by default * Fix header guard in SampleWaveform.h * Remove use of src_clone CI seems to have an old version of libsamplerate and does not have this method. * Include memory header in SampleBuffer.h * Remove mutex and shared_mutex includes in Sample.h * Attempt to fix string operand error within AudioResampler * Include string header in AudioResampler.cpp * Add LMMS_EXPORT for SampleWaveform class declaration * Add LMMS_EXPORT for AudioResampler class declaration * Enforce returning std::shared_ptr<const SampleBuffer> * Restrict the size of the memcpy to the destination size, not the source size * Do not make resample const AudioResampler::resample, while seemingly not changing the data of the resampler, still alters its internal state and therefore should not be const. This is because libsamplerate manages state when resampling. * Initialize data.end_of_input * Add trailing new lines * Simplify AudioResampler interface * Fix header guard prefix to LMMS_GUI instead of LMMS * Remove Sample::resampleSampleRange --------- Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> Co-authored-by: Daniel Kauss <daniel.kauss.serna@gmail.com> Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com> Co-authored-by: DomClark <mrdomclark@gmail.com>
This commit is contained in:
64
include/AudioResampler.h
Normal file
64
include/AudioResampler.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* AudioResampler.h - wrapper around libsamplerate
|
||||
*
|
||||
* Copyright (c) 2023 saker <sakertooth@gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LMMS_AUDIO_RESAMPLER_H
|
||||
#define LMMS_AUDIO_RESAMPLER_H
|
||||
|
||||
#include <samplerate.h>
|
||||
|
||||
#include "lmms_export.h"
|
||||
|
||||
namespace lmms {
|
||||
|
||||
class LMMS_EXPORT AudioResampler
|
||||
{
|
||||
public:
|
||||
struct ProcessResult
|
||||
{
|
||||
int error;
|
||||
long inputFramesUsed;
|
||||
long outputFramesGenerated;
|
||||
};
|
||||
|
||||
AudioResampler(int interpolationMode, int channels);
|
||||
AudioResampler(const AudioResampler&) = delete;
|
||||
AudioResampler(AudioResampler&&) = delete;
|
||||
~AudioResampler();
|
||||
|
||||
AudioResampler& operator=(const AudioResampler&) = delete;
|
||||
AudioResampler& operator=(AudioResampler&&) = delete;
|
||||
|
||||
auto resample(const float* in, long inputFrames, float* out, long outputFrames, double ratio) -> ProcessResult;
|
||||
auto interpolationMode() const -> int { return m_interpolationMode; }
|
||||
auto channels() const -> int { return m_channels; }
|
||||
|
||||
private:
|
||||
int m_interpolationMode = -1;
|
||||
int m_channels = 0;
|
||||
int m_error = 0;
|
||||
SRC_STATE* m_state = nullptr;
|
||||
};
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_AUDIO_RESAMPLER_H
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <memory>
|
||||
|
||||
#include "AudioDevice.h"
|
||||
|
||||
@@ -44,8 +45,7 @@ public:
|
||||
~AudioSampleRecorder() override;
|
||||
|
||||
f_cnt_t framesRecorded() const;
|
||||
void createSampleBuffer( SampleBuffer** sampleBuffer );
|
||||
|
||||
std::shared_ptr<const SampleBuffer> createSampleBuffer();
|
||||
|
||||
private:
|
||||
void writeBuffer( const surroundSampleFrame * _ab,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#ifndef LMMS_ENVELOPE_AND_LFO_PARAMETERS_H
|
||||
#define LMMS_ENVELOPE_AND_LFO_PARAMETERS_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "JournallingObject.h"
|
||||
@@ -167,7 +168,7 @@ private:
|
||||
sample_t * m_lfoShapeData;
|
||||
sample_t m_random;
|
||||
bool m_bad_lfoShapeData;
|
||||
SampleBuffer m_userWave;
|
||||
std::shared_ptr<const SampleBuffer> m_userWave = SampleBuffer::emptyBuffer();
|
||||
|
||||
enum class LfoShape
|
||||
{
|
||||
|
||||
@@ -87,7 +87,7 @@ protected:
|
||||
|
||||
private:
|
||||
float m_heldSample;
|
||||
SampleBuffer * m_userDefSampleBuffer;
|
||||
std::shared_ptr<const SampleBuffer> m_userDefSampleBuffer = SampleBuffer::emptyBuffer();
|
||||
|
||||
protected slots:
|
||||
void updatePhase();
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <fftw3.h>
|
||||
#include <memory>
|
||||
#include <cstdlib>
|
||||
#include "interpolation.h"
|
||||
|
||||
#include "Engine.h"
|
||||
#include "lmms_constants.h"
|
||||
@@ -46,7 +48,6 @@ class IntModel;
|
||||
|
||||
class LMMS_EXPORT Oscillator
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
enum class WaveShape
|
||||
{
|
||||
@@ -91,18 +92,23 @@ public:
|
||||
|
||||
static void waveTableInit();
|
||||
static void destroyFFTPlans();
|
||||
static void generateAntiAliasUserWaveTable(SampleBuffer* sampleBuffer);
|
||||
static std::unique_ptr<OscillatorConstants::waveform_t> generateAntiAliasUserWaveTable(const SampleBuffer* sampleBuffer);
|
||||
|
||||
inline void setUseWaveTable(bool n)
|
||||
{
|
||||
m_useWaveTable = n;
|
||||
}
|
||||
|
||||
inline void setUserWave( const SampleBuffer * _wave )
|
||||
void setUserWave(std::shared_ptr<const SampleBuffer> _wave)
|
||||
{
|
||||
m_userWave = _wave;
|
||||
}
|
||||
|
||||
void setUserAntiAliasWaveTable(std::shared_ptr<const OscillatorConstants::waveform_t> waveform)
|
||||
{
|
||||
m_userAntiAliasWaveTable = waveform;
|
||||
}
|
||||
|
||||
void update(sampleFrame* ab, const fpp_t frames, const ch_cnt_t chnl, bool modulator = false);
|
||||
|
||||
// now follow the wave-shape-routines...
|
||||
@@ -164,9 +170,18 @@ public:
|
||||
return 1.0f - fast_rand() * 2.0f / FAST_RAND_MAX;
|
||||
}
|
||||
|
||||
inline sample_t userWaveSample( const float _sample ) const
|
||||
static sample_t userWaveSample(const SampleBuffer* buffer, const float sample)
|
||||
{
|
||||
return m_userWave->userWaveSample( _sample );
|
||||
if (buffer == nullptr || buffer->size() == 0) { return 0; }
|
||||
const auto frames = buffer->size();
|
||||
const auto frame = sample * frames;
|
||||
auto f1 = static_cast<f_cnt_t>(frame) % frames;
|
||||
if (f1 < 0)
|
||||
{
|
||||
f1 += frames;
|
||||
}
|
||||
|
||||
return linearInterpolate(buffer->data()[f1][0], buffer->data()[(f1 + 1) % frames][0], fraction(frame));
|
||||
}
|
||||
|
||||
struct wtSampleControl {
|
||||
@@ -203,7 +218,7 @@ public:
|
||||
table[control.band][control.f2], fraction(control.frame));
|
||||
}
|
||||
|
||||
inline sample_t wtSample(const std::unique_ptr<OscillatorConstants::waveform_t>& table, const float sample) const
|
||||
sample_t wtSample(const OscillatorConstants::waveform_t* table, const float sample) const
|
||||
{
|
||||
assert(table != nullptr);
|
||||
wtSampleControl control = getWtSampleControl(sample);
|
||||
@@ -247,7 +262,8 @@ private:
|
||||
Oscillator * m_subOsc;
|
||||
float m_phaseOffset;
|
||||
float m_phase;
|
||||
const SampleBuffer * m_userWave;
|
||||
std::shared_ptr<const SampleBuffer> m_userWave = SampleBuffer::emptyBuffer();
|
||||
std::shared_ptr<const OscillatorConstants::waveform_t> m_userAntiAliasWaveTable;
|
||||
bool m_useWaveTable;
|
||||
// There are many update*() variants; the modulator flag is stored as a member variable to avoid
|
||||
// adding more explicit parameters to all of them. Can be converted to a parameter if needed.
|
||||
|
||||
139
include/Sample.h
Normal file
139
include/Sample.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Sample.h - State for container-class SampleBuffer
|
||||
*
|
||||
* Copyright (c) 2023 saker <sakertooth@gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LMMS_SAMPLE_H
|
||||
#define LMMS_SAMPLE_H
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
#include "AudioResampler.h"
|
||||
#include "Note.h"
|
||||
#include "SampleBuffer.h"
|
||||
#include "lmms_export.h"
|
||||
|
||||
class QPainter;
|
||||
class QRect;
|
||||
|
||||
namespace lmms {
|
||||
class LMMS_EXPORT Sample
|
||||
{
|
||||
public:
|
||||
// values for buffer margins, used for various libsamplerate interpolation modes
|
||||
// the array positions correspond to the converter_type parameter values in libsamplerate
|
||||
// if there appears problems with playback on some interpolation mode, then the value for that mode
|
||||
// may need to be higher - conversely, to optimize, some may work with lower values
|
||||
static constexpr auto s_interpolationMargins = std::array<int, 5>{64, 64, 64, 4, 4};
|
||||
|
||||
enum class Loop
|
||||
{
|
||||
Off,
|
||||
On,
|
||||
PingPong
|
||||
};
|
||||
|
||||
class LMMS_EXPORT PlaybackState
|
||||
{
|
||||
public:
|
||||
PlaybackState(bool varyingPitch = false, int interpolationMode = SRC_LINEAR)
|
||||
: m_resampler(interpolationMode, DEFAULT_CHANNELS)
|
||||
, m_varyingPitch(varyingPitch)
|
||||
{
|
||||
}
|
||||
|
||||
auto resampler() -> AudioResampler& { return m_resampler; }
|
||||
auto frameIndex() const -> f_cnt_t { return m_frameIndex; }
|
||||
auto varyingPitch() const -> bool { return m_varyingPitch; }
|
||||
auto backwards() const -> bool { return m_backwards; }
|
||||
|
||||
void setFrameIndex(f_cnt_t frameIndex) { m_frameIndex = frameIndex; }
|
||||
void setVaryingPitch(bool varyingPitch) { m_varyingPitch = varyingPitch; }
|
||||
void setBackwards(bool backwards) { m_backwards = backwards; }
|
||||
|
||||
private:
|
||||
AudioResampler m_resampler;
|
||||
f_cnt_t m_frameIndex = 0;
|
||||
bool m_varyingPitch = false;
|
||||
bool m_backwards = false;
|
||||
friend class Sample;
|
||||
};
|
||||
|
||||
Sample() = default;
|
||||
Sample(const QByteArray& base64, int sampleRate = Engine::audioEngine()->processingSampleRate());
|
||||
Sample(const sampleFrame* data, int numFrames, int sampleRate = Engine::audioEngine()->processingSampleRate());
|
||||
Sample(const Sample& other);
|
||||
Sample(Sample&& other);
|
||||
explicit Sample(const QString& audioFile);
|
||||
explicit Sample(std::shared_ptr<const SampleBuffer> buffer);
|
||||
|
||||
auto operator=(const Sample&) -> Sample&;
|
||||
auto operator=(Sample&&) -> Sample&;
|
||||
|
||||
auto play(sampleFrame* dst, PlaybackState* state, int numFrames, float desiredFrequency = DefaultBaseFreq,
|
||||
Loop loopMode = Loop::Off) -> bool;
|
||||
|
||||
auto sampleDuration() const -> std::chrono::milliseconds;
|
||||
auto sampleFile() const -> const QString& { return m_buffer->audioFile(); }
|
||||
auto sampleRate() const -> int { return m_buffer->sampleRate(); }
|
||||
auto sampleSize() const -> int { return m_buffer->size(); }
|
||||
|
||||
auto toBase64() const -> QString { return m_buffer->toBase64(); }
|
||||
|
||||
auto data() const -> const sampleFrame* { return m_buffer->data(); }
|
||||
auto buffer() const -> std::shared_ptr<const SampleBuffer> { return m_buffer; }
|
||||
auto startFrame() const -> int { return m_startFrame.load(std::memory_order_relaxed); }
|
||||
auto endFrame() const -> int { return m_endFrame.load(std::memory_order_relaxed); }
|
||||
auto loopStartFrame() const -> int { return m_loopStartFrame.load(std::memory_order_relaxed); }
|
||||
auto loopEndFrame() const -> int { return m_loopEndFrame.load(std::memory_order_relaxed); }
|
||||
auto amplification() const -> float { return m_amplification.load(std::memory_order_relaxed); }
|
||||
auto frequency() const -> float { return m_frequency.load(std::memory_order_relaxed); }
|
||||
auto reversed() const -> bool { return m_reversed.load(std::memory_order_relaxed); }
|
||||
|
||||
void setStartFrame(int startFrame) { m_startFrame.store(startFrame, std::memory_order_relaxed); }
|
||||
void setEndFrame(int endFrame) { m_endFrame.store(endFrame, std::memory_order_relaxed); }
|
||||
void setLoopStartFrame(int loopStartFrame) { m_loopStartFrame.store(loopStartFrame, std::memory_order_relaxed); }
|
||||
void setLoopEndFrame(int loopEndFrame) { m_loopEndFrame.store(loopEndFrame, std::memory_order_relaxed); }
|
||||
void setAllPointFrames(int startFrame, int endFrame, int loopStartFrame, int loopEndFrame);
|
||||
void setAmplification(float amplification) { m_amplification.store(amplification, std::memory_order_relaxed); }
|
||||
void setFrequency(float frequency) { m_frequency.store(frequency, std::memory_order_relaxed); }
|
||||
void setReversed(bool reversed) { m_reversed.store(reversed, std::memory_order_relaxed); }
|
||||
|
||||
private:
|
||||
void playSampleRange(PlaybackState* state, sampleFrame* dst, size_t numFrames) const;
|
||||
void amplifySampleRange(sampleFrame* src, int numFrames) const;
|
||||
void copyBufferForward(sampleFrame* dst, int initialPosition, int advanceAmount) const;
|
||||
void copyBufferBackward(sampleFrame* dst, int initialPosition, int advanceAmount) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<const SampleBuffer> m_buffer = SampleBuffer::emptyBuffer();
|
||||
std::atomic<int> m_startFrame = 0;
|
||||
std::atomic<int> m_endFrame = 0;
|
||||
std::atomic<int> m_loopStartFrame = 0;
|
||||
std::atomic<int> m_loopEndFrame = 0;
|
||||
std::atomic<float> m_amplification = 1.0f;
|
||||
std::atomic<float> m_frequency = DefaultBaseFreq;
|
||||
std::atomic<bool> m_reversed = false;
|
||||
};
|
||||
} // namespace lmms
|
||||
#endif
|
||||
@@ -25,333 +25,74 @@
|
||||
#ifndef LMMS_SAMPLE_BUFFER_H
|
||||
#define LMMS_SAMPLE_BUFFER_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <memory>
|
||||
#include <QReadWriteLock>
|
||||
#include <QObject>
|
||||
|
||||
#include <optional>
|
||||
#include <samplerate.h>
|
||||
#include <vector>
|
||||
|
||||
#include "lmms_export.h"
|
||||
#include "interpolation.h"
|
||||
#include "AudioEngine.h"
|
||||
#include "Engine.h"
|
||||
#include "lmms_basics.h"
|
||||
#include "lmms_math.h"
|
||||
#include "shared_object.h"
|
||||
#include "OscillatorConstants.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "lmms_export.h"
|
||||
|
||||
|
||||
class QPainter;
|
||||
class QRect;
|
||||
|
||||
namespace lmms
|
||||
namespace lmms {
|
||||
class LMMS_EXPORT SampleBuffer
|
||||
{
|
||||
|
||||
// values for buffer margins, used for various libsamplerate interpolation modes
|
||||
// the array positions correspond to the converter_type parameter values in libsamplerate
|
||||
// if there appears problems with playback on some interpolation mode, then the value for that mode
|
||||
// may need to be higher - conversely, to optimize, some may work with lower values
|
||||
const f_cnt_t MARGIN[] = { 64, 64, 64, 4, 4 };
|
||||
|
||||
class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject
|
||||
{
|
||||
Q_OBJECT
|
||||
MM_OPERATORS
|
||||
public:
|
||||
enum class LoopMode {
|
||||
Off = 0,
|
||||
On,
|
||||
PingPong
|
||||
};
|
||||
class LMMS_EXPORT handleState
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
handleState(bool varyingPitch = false, int interpolationMode = SRC_LINEAR);
|
||||
virtual ~handleState();
|
||||
using value_type = sampleFrame;
|
||||
using reference = sampleFrame&;
|
||||
using const_reference = const sampleFrame&;
|
||||
using iterator = std::vector<sampleFrame>::iterator;
|
||||
using const_iterator = std::vector<sampleFrame>::const_iterator;
|
||||
using difference_type = std::vector<sampleFrame>::difference_type;
|
||||
using size_type = std::vector<sampleFrame>::size_type;
|
||||
using reverse_iterator = std::vector<sampleFrame>::reverse_iterator;
|
||||
using const_reverse_iterator = std::vector<sampleFrame>::const_reverse_iterator;
|
||||
|
||||
const f_cnt_t frameIndex() const
|
||||
{
|
||||
return m_frameIndex;
|
||||
}
|
||||
SampleBuffer() = default;
|
||||
explicit SampleBuffer(const QString& audioFile);
|
||||
SampleBuffer(const QString& base64, int sampleRate);
|
||||
SampleBuffer(std::vector<sampleFrame> data, int sampleRate);
|
||||
SampleBuffer(
|
||||
const sampleFrame* data, int numFrames, int sampleRate = Engine::audioEngine()->processingSampleRate());
|
||||
|
||||
void setFrameIndex(f_cnt_t index)
|
||||
{
|
||||
m_frameIndex = index;
|
||||
}
|
||||
friend void swap(SampleBuffer& first, SampleBuffer& second) noexcept;
|
||||
auto toBase64() const -> QString;
|
||||
|
||||
bool isBackwards() const
|
||||
{
|
||||
return m_isBackwards;
|
||||
}
|
||||
auto audioFile() const -> const QString& { return m_audioFile; }
|
||||
auto sampleRate() const -> sample_rate_t { return m_sampleRate; }
|
||||
|
||||
void setBackwards(bool backwards)
|
||||
{
|
||||
m_isBackwards = backwards;
|
||||
}
|
||||
auto begin() -> iterator { return m_data.begin(); }
|
||||
auto end() -> iterator { return m_data.end(); }
|
||||
|
||||
int interpolationMode() const
|
||||
{
|
||||
return m_interpolationMode;
|
||||
}
|
||||
auto begin() const -> const_iterator { return m_data.begin(); }
|
||||
auto end() const -> const_iterator { return m_data.end(); }
|
||||
|
||||
auto cbegin() const -> const_iterator { return m_data.cbegin(); }
|
||||
auto cend() const -> const_iterator { return m_data.cend(); }
|
||||
|
||||
private:
|
||||
f_cnt_t m_frameIndex;
|
||||
const bool m_varyingPitch;
|
||||
bool m_isBackwards;
|
||||
SRC_STATE * m_resamplingData;
|
||||
int m_interpolationMode;
|
||||
auto rbegin() -> reverse_iterator { return m_data.rbegin(); }
|
||||
auto rend() -> reverse_iterator { return m_data.rend(); }
|
||||
|
||||
friend class SampleBuffer;
|
||||
auto rbegin() const -> const_reverse_iterator { return m_data.rbegin(); }
|
||||
auto rend() const -> const_reverse_iterator { return m_data.rend(); }
|
||||
|
||||
} ;
|
||||
auto crbegin() const -> const_reverse_iterator { return m_data.crbegin(); }
|
||||
auto crend() const -> const_reverse_iterator { return m_data.crend(); }
|
||||
|
||||
auto data() const -> const sampleFrame* { return m_data.data(); }
|
||||
auto size() const -> size_type { return m_data.size(); }
|
||||
auto empty() const -> bool { return m_data.empty(); }
|
||||
|
||||
SampleBuffer();
|
||||
// constructor which either loads sample _audio_file or decodes
|
||||
// base64-data out of string
|
||||
SampleBuffer(const QString & audioFile, bool isBase64Data = false);
|
||||
SampleBuffer(const sampleFrame * data, const f_cnt_t frames);
|
||||
explicit SampleBuffer(const f_cnt_t frames);
|
||||
SampleBuffer(const SampleBuffer & orig);
|
||||
|
||||
friend void swap(SampleBuffer & first, SampleBuffer & second) noexcept;
|
||||
SampleBuffer& operator= (const SampleBuffer that);
|
||||
|
||||
~SampleBuffer() override;
|
||||
|
||||
bool play(
|
||||
sampleFrame * ab,
|
||||
handleState * state,
|
||||
const fpp_t frames,
|
||||
const float freq,
|
||||
const LoopMode loopMode = LoopMode::Off
|
||||
);
|
||||
|
||||
void visualize(
|
||||
QPainter & p,
|
||||
const QRect & dr,
|
||||
const QRect & clip,
|
||||
f_cnt_t fromFrame = 0,
|
||||
f_cnt_t toFrame = 0
|
||||
);
|
||||
inline void visualize(
|
||||
QPainter & p,
|
||||
const QRect & dr,
|
||||
f_cnt_t fromFrame = 0,
|
||||
f_cnt_t toFrame = 0
|
||||
)
|
||||
{
|
||||
visualize(p, dr, dr, fromFrame, toFrame);
|
||||
}
|
||||
|
||||
inline const QString & audioFile() const
|
||||
{
|
||||
return m_audioFile;
|
||||
}
|
||||
|
||||
inline f_cnt_t startFrame() const
|
||||
{
|
||||
return m_startFrame;
|
||||
}
|
||||
|
||||
inline f_cnt_t endFrame() const
|
||||
{
|
||||
return m_endFrame;
|
||||
}
|
||||
|
||||
inline f_cnt_t loopStartFrame() const
|
||||
{
|
||||
return m_loopStartFrame;
|
||||
}
|
||||
|
||||
inline f_cnt_t loopEndFrame() const
|
||||
{
|
||||
return m_loopEndFrame;
|
||||
}
|
||||
|
||||
void setLoopStartFrame(f_cnt_t start)
|
||||
{
|
||||
m_loopStartFrame = start;
|
||||
}
|
||||
|
||||
void setLoopEndFrame(f_cnt_t end)
|
||||
{
|
||||
m_loopEndFrame = end;
|
||||
}
|
||||
|
||||
void setAllPointFrames(
|
||||
f_cnt_t start,
|
||||
f_cnt_t end,
|
||||
f_cnt_t loopStart,
|
||||
f_cnt_t loopEnd
|
||||
)
|
||||
{
|
||||
m_startFrame = start;
|
||||
m_endFrame = end;
|
||||
m_loopStartFrame = loopStart;
|
||||
m_loopEndFrame = loopEnd;
|
||||
}
|
||||
|
||||
inline f_cnt_t frames() const
|
||||
{
|
||||
return m_frames;
|
||||
}
|
||||
|
||||
inline float amplification() const
|
||||
{
|
||||
return m_amplification;
|
||||
}
|
||||
|
||||
inline bool reversed() const
|
||||
{
|
||||
return m_reversed;
|
||||
}
|
||||
|
||||
inline float frequency() const
|
||||
{
|
||||
return m_frequency;
|
||||
}
|
||||
|
||||
sample_rate_t sampleRate() const
|
||||
{
|
||||
return m_sampleRate;
|
||||
}
|
||||
|
||||
int sampleLength() const
|
||||
{
|
||||
return double(m_endFrame - m_startFrame) / m_sampleRate * 1000;
|
||||
}
|
||||
|
||||
inline void setFrequency(float freq)
|
||||
{
|
||||
m_frequency = freq;
|
||||
}
|
||||
|
||||
inline void setSampleRate(sample_rate_t rate)
|
||||
{
|
||||
m_sampleRate = rate;
|
||||
}
|
||||
|
||||
inline const sampleFrame * data() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
QString openAudioFile() const;
|
||||
QString openAndSetAudioFile();
|
||||
QString openAndSetWaveformFile();
|
||||
|
||||
QString & toBase64(QString & dst) const;
|
||||
|
||||
|
||||
// protect calls from the GUI to this function with dataReadLock() and
|
||||
// dataUnlock()
|
||||
SampleBuffer * resample(const sample_rate_t srcSR, const sample_rate_t dstSR);
|
||||
|
||||
void normalizeSampleRate(const sample_rate_t srcSR, bool keepSettings = false);
|
||||
|
||||
// protect calls from the GUI to this function with dataReadLock() and
|
||||
// dataUnlock(), out of loops for efficiency
|
||||
inline sample_t userWaveSample(const float sample) const
|
||||
{
|
||||
f_cnt_t frames = m_frames;
|
||||
sampleFrame * data = m_data;
|
||||
const float frame = sample * frames;
|
||||
f_cnt_t f1 = static_cast<f_cnt_t>(frame) % frames;
|
||||
if (f1 < 0)
|
||||
{
|
||||
f1 += frames;
|
||||
}
|
||||
return linearInterpolate(data[f1][0], data[(f1 + 1) % frames][0], fraction(frame));
|
||||
}
|
||||
|
||||
void dataReadLock()
|
||||
{
|
||||
m_varLock.lockForRead();
|
||||
}
|
||||
|
||||
void dataUnlock()
|
||||
{
|
||||
m_varLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<OscillatorConstants::waveform_t> m_userAntiAliasWaveTable;
|
||||
|
||||
|
||||
public slots:
|
||||
void setAudioFile(const QString & audioFile);
|
||||
void loadFromBase64(const QString & data);
|
||||
void setStartFrame(const lmms::f_cnt_t s);
|
||||
void setEndFrame(const lmms::f_cnt_t e);
|
||||
void setAmplification(float a);
|
||||
void setReversed(bool on);
|
||||
void sampleRateChanged();
|
||||
static auto emptyBuffer() -> std::shared_ptr<const SampleBuffer>;
|
||||
|
||||
private:
|
||||
static sample_rate_t audioEngineSampleRate();
|
||||
|
||||
void update(bool keepSettings = false);
|
||||
|
||||
void convertIntToFloat(int_sample_t * & ibuf, f_cnt_t frames, int channels);
|
||||
void directFloatWrite(sample_t * & fbuf, f_cnt_t frames, int channels);
|
||||
|
||||
f_cnt_t decodeSampleSF(
|
||||
QString fileName,
|
||||
sample_t * & buf,
|
||||
ch_cnt_t & channels,
|
||||
sample_rate_t & samplerate
|
||||
);
|
||||
#ifdef LMMS_HAVE_OGGVORBIS
|
||||
f_cnt_t decodeSampleOGGVorbis(
|
||||
QString fileName,
|
||||
int_sample_t * & buf,
|
||||
ch_cnt_t & channels,
|
||||
sample_rate_t & samplerate
|
||||
);
|
||||
#endif
|
||||
f_cnt_t decodeSampleDS(
|
||||
QString fileName,
|
||||
int_sample_t * & buf,
|
||||
ch_cnt_t & channels,
|
||||
sample_rate_t & samplerate
|
||||
);
|
||||
|
||||
std::vector<sampleFrame> m_data;
|
||||
QString m_audioFile;
|
||||
sampleFrame * m_origData;
|
||||
f_cnt_t m_origFrames;
|
||||
sampleFrame * m_data;
|
||||
mutable QReadWriteLock m_varLock;
|
||||
f_cnt_t m_frames;
|
||||
f_cnt_t m_startFrame;
|
||||
f_cnt_t m_endFrame;
|
||||
f_cnt_t m_loopStartFrame;
|
||||
f_cnt_t m_loopEndFrame;
|
||||
float m_amplification;
|
||||
bool m_reversed;
|
||||
float m_frequency;
|
||||
sample_rate_t m_sampleRate;
|
||||
|
||||
sampleFrame * getSampleFragment(
|
||||
f_cnt_t index,
|
||||
f_cnt_t frames,
|
||||
LoopMode loopMode,
|
||||
sampleFrame * * tmp,
|
||||
bool * backwards,
|
||||
f_cnt_t loopStart,
|
||||
f_cnt_t loopEnd,
|
||||
f_cnt_t end
|
||||
) const;
|
||||
|
||||
f_cnt_t getLoopedIndex(f_cnt_t index, f_cnt_t startf, f_cnt_t endf) const;
|
||||
f_cnt_t getPingPongIndex(f_cnt_t index, f_cnt_t startf, f_cnt_t endf) const;
|
||||
|
||||
|
||||
signals:
|
||||
void sampleUpdated();
|
||||
|
||||
} ;
|
||||
sample_rate_t m_sampleRate = Engine::audioEngine()->processingSampleRate();
|
||||
};
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
#ifndef LMMS_SAMPLE_CLIP_H
|
||||
#define LMMS_SAMPLE_CLIP_H
|
||||
|
||||
#include <memory>
|
||||
#include "Clip.h"
|
||||
#include "Sample.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
@@ -45,14 +47,15 @@ class SampleClip : public Clip
|
||||
Q_OBJECT
|
||||
mapPropertyFromModel(bool,isRecord,setRecord,m_recordModel);
|
||||
public:
|
||||
SampleClip( Track * _track );
|
||||
SampleClip(Track* track, Sample sample, bool isPlaying);
|
||||
SampleClip(Track* track);
|
||||
SampleClip( const SampleClip& orig );
|
||||
~SampleClip() override;
|
||||
|
||||
SampleClip& operator=( const SampleClip& that ) = delete;
|
||||
|
||||
void changeLength( const TimePos & _length ) override;
|
||||
const QString & sampleFile() const;
|
||||
const QString& sampleFile() const;
|
||||
|
||||
void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override;
|
||||
void loadSettings( const QDomElement & _this ) override;
|
||||
@@ -61,9 +64,9 @@ public:
|
||||
return "sampleclip";
|
||||
}
|
||||
|
||||
SampleBuffer* sampleBuffer()
|
||||
Sample& sample()
|
||||
{
|
||||
return m_sampleBuffer;
|
||||
return m_sample;
|
||||
}
|
||||
|
||||
TimePos sampleLength() const;
|
||||
@@ -74,10 +77,10 @@ public:
|
||||
|
||||
bool isPlaying() const;
|
||||
void setIsPlaying(bool isPlaying);
|
||||
void setSampleBuffer(std::shared_ptr<const SampleBuffer> sb);
|
||||
|
||||
public slots:
|
||||
void setSampleBuffer( lmms::SampleBuffer* sb );
|
||||
void setSampleFile( const QString & sf );
|
||||
void setSampleFile(const QString& sf);
|
||||
void updateLength();
|
||||
void toggleRecord();
|
||||
void playbackPositionChanged();
|
||||
@@ -85,7 +88,7 @@ public slots:
|
||||
|
||||
|
||||
private:
|
||||
SampleBuffer* m_sampleBuffer;
|
||||
Sample m_sample;
|
||||
BoolModel m_recordModel;
|
||||
bool m_isPlaying;
|
||||
|
||||
|
||||
57
include/SampleDecoder.h
Normal file
57
include/SampleDecoder.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* SampleDecoder.h - Decodes audio files in various formats
|
||||
*
|
||||
* Copyright (c) 2023 saker <sakertooth@gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LMMS_SAMPLE_DECODER_H
|
||||
#define LMMS_SAMPLE_DECODER_H
|
||||
|
||||
#include <QString>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "lmms_basics.h"
|
||||
|
||||
namespace lmms {
|
||||
class SampleDecoder
|
||||
{
|
||||
public:
|
||||
struct Result
|
||||
{
|
||||
std::vector<sampleFrame> data;
|
||||
int sampleRate;
|
||||
};
|
||||
|
||||
struct AudioType
|
||||
{
|
||||
std::string name;
|
||||
std::string extension;
|
||||
};
|
||||
|
||||
static auto decode(const QString& audioFile) -> std::optional<Result>;
|
||||
static auto supportedAudioTypes() -> const std::vector<AudioType>&;
|
||||
};
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_SAMPLE_DECODER_H
|
||||
48
include/SampleLoader.h
Normal file
48
include/SampleLoader.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* SampleLoader.h - Load audio and waveform files
|
||||
*
|
||||
* Copyright (c) 2023 saker <sakertooth@gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LMMS_GUI_SAMPLE_LOADER_H
|
||||
#define LMMS_GUI_SAMPLE_LOADER_H
|
||||
|
||||
#include <QString>
|
||||
#include <memory>
|
||||
|
||||
#include "SampleBuffer.h"
|
||||
#include "lmms_export.h"
|
||||
|
||||
namespace lmms::gui {
|
||||
class LMMS_EXPORT SampleLoader
|
||||
{
|
||||
public:
|
||||
static QString openAudioFile(const QString& previousFile = "");
|
||||
static QString openWaveformFile(const QString& previousFile = "");
|
||||
static std::shared_ptr<const SampleBuffer> createBufferFromFile(const QString& filePath);
|
||||
static std::shared_ptr<const SampleBuffer> createBufferFromBase64(
|
||||
const QString& base64, int sampleRate = Engine::audioEngine()->processingSampleRate());
|
||||
private:
|
||||
static void displayError(const QString& message);
|
||||
};
|
||||
} // namespace lmms::gui
|
||||
|
||||
#endif // LMMS_GUI_SAMPLE_LOADER_H
|
||||
@@ -26,6 +26,7 @@
|
||||
#ifndef LMMS_SAMPLE_PLAY_HANDLE_H
|
||||
#define LMMS_SAMPLE_PLAY_HANDLE_H
|
||||
|
||||
#include "Sample.h"
|
||||
#include "SampleBuffer.h"
|
||||
#include "AutomatableModel.h"
|
||||
#include "PlayHandle.h"
|
||||
@@ -43,7 +44,7 @@ class AudioPort;
|
||||
class LMMS_EXPORT SamplePlayHandle : public PlayHandle
|
||||
{
|
||||
public:
|
||||
SamplePlayHandle( SampleBuffer* sampleBuffer , bool ownAudioPort = true );
|
||||
SamplePlayHandle(Sample* sample, bool ownAudioPort = true);
|
||||
SamplePlayHandle( const QString& sampleFile );
|
||||
SamplePlayHandle( SampleClip* clip );
|
||||
~SamplePlayHandle() override;
|
||||
@@ -81,11 +82,11 @@ public:
|
||||
|
||||
|
||||
private:
|
||||
SampleBuffer * m_sampleBuffer;
|
||||
Sample* m_sample;
|
||||
bool m_doneMayReturnTrue;
|
||||
|
||||
f_cnt_t m_frame;
|
||||
SampleBuffer::handleState m_state;
|
||||
Sample::PlaybackState m_state;
|
||||
|
||||
const bool m_ownAudioPort;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <memory>
|
||||
|
||||
#include "PlayHandle.h"
|
||||
#include "TimePos.h"
|
||||
@@ -53,7 +54,7 @@ public:
|
||||
bool isFromTrack( const Track * _track ) const override;
|
||||
|
||||
f_cnt_t framesRecorded() const;
|
||||
void createSampleBuffer( SampleBuffer * * _sample_buf );
|
||||
std::shared_ptr<const SampleBuffer> createSampleBuffer();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
41
include/SampleWaveform.h
Normal file
41
include/SampleWaveform.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* SampleWaveform.h
|
||||
*
|
||||
* Copyright (c) 2023 saker <sakertooth@gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LMMS_GUI_SAMPLE_WAVEFORM_H
|
||||
#define LMMS_GUI_SAMPLE_WAVEFORM_H
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
#include "Sample.h"
|
||||
#include "lmms_export.h"
|
||||
|
||||
namespace lmms::gui {
|
||||
class LMMS_EXPORT SampleWaveform
|
||||
{
|
||||
public:
|
||||
static void visualize(const Sample& sample, QPainter& p, const QRect& dr, int fromFrame = 0, int toFrame = 0);
|
||||
};
|
||||
} // namespace lmms::gui
|
||||
|
||||
#endif // LMMS_GUI_SAMPLE_WAVEFORM_H
|
||||
Reference in New Issue
Block a user