Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sample Track Recording Stage One #5990

Draft
wants to merge 48 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
79b7092
SampleTrack: Uncomment disabled recording-related code.
Reflexe Nov 6, 2017
50cc41a
Mixer & PlayHandle: Support PlayHandle without audioPort and set
Reflexe Nov 6, 2017
b8938fd
SampleTrack: Make sure an empty TCO with isRecord would still be
Reflexe Nov 8, 2017
ed7ff82
SampleTrack: In a case of recording, play a sample TCO even if its
Reflexe Nov 17, 2017
7184ce6
SampleRecordHandle: Obtain the offset to the record with a parameter
Reflexe Nov 17, 2017
333f43c
fixup: fix formatting issues
Reflexe May 17, 2021
cf9cea8
DataFile: Skip empty resource attributes
Reflexe Oct 23, 2021
1b39d39
Recoding: Disable recording buttons when recording is unavailable
Reflexe Dec 4, 2021
ca72d5b
Merge branch 'master'
PhysSong Feb 2, 2022
99d97fc
Fix indentation
PhysSong Feb 2, 2022
dfed374
Minor formatting changes
PhysSong Feb 3, 2022
2f02148
Merge branch 'master' into feature/recording-stage-one
PhysSong Mar 26, 2022
17efb13
Fix missing include after merge
PhysSong Mar 26, 2022
b2f3a1f
Merge branch 'master' into feature/recording-stage-one
PhysSong Aug 2, 2022
ed703c0
Merge branch 'master' into feature/recording-stage-one
PhysSong Oct 30, 2022
f254829
Merge branch 'master' into feature/recording-stage-one
PhysSong Jul 2, 2023
397b8d9
Merge branch 'master' into feature/recording-stage-one
zonkmachine Oct 10, 2023
9324658
Merge remote-tracking branch 'origin/master' into feature/recording-s…
michaelgregorius May 31, 2024
18efc12
Move "Rec" to lower left
michaelgregorius May 31, 2024
33139b9
Enable configuration of input device for SDL
michaelgregorius Jun 1, 2024
29c43c2
Provide a setting for system default input
michaelgregorius Jun 1, 2024
1ab45e4
Configuration of output device for SDL
michaelgregorius Jun 1, 2024
a123d0e
Ensure label visibility
michaelgregorius Jun 1, 2024
1f0cda4
Rename "Device"
michaelgregorius Jun 1, 2024
f9ea970
Remove repeated strings
michaelgregorius Jun 1, 2024
48c4dce
More prominent recording option
michaelgregorius Jun 2, 2024
5495530
Recording widget/options visibility
michaelgregorius Jun 2, 2024
3b7e330
Apply code review
sakertooth Jun 2, 2024
14c16be
Do not include ClipView.h
sakertooth Jun 2, 2024
d8c2716
Remove recording button
michaelgregorius Jun 14, 2024
3a3c213
Revert wording to match MIDI recording
tresf Jun 14, 2024
c1e5de5
Code review changes
michaelgregorius Jun 15, 2024
a1675e4
Adjust rendering of BarModelEditor (#7299)
michaelgregorius May 31, 2024
568abed
Add peak indicators (#7295)
michaelgregorius Jun 1, 2024
fbcf436
Change the wording of Instrument Plugin Dialogue (#7301)
AW1534 Jun 1, 2024
6ee0d9b
Modernize compiler and linker flags (#7255)
Rossmaxx Jun 2, 2024
d356b71
Fix noise generator (#7318)
LostRobotMusic Jun 13, 2024
bb03178
Add support for "factorysample:" prefix / Fix "bassloopes" typo (#7236)
michaelgregorius Jun 14, 2024
c1f3b0b
Remove `debian` folder (#7311)
Rossmaxx Jun 15, 2024
86073ed
Use `src_set_ratio` to fix artifacts within `Sample` playback (#7321)
sakertooth Jun 15, 2024
b1f0f89
arpeggiator sorted mode fixed (#7025)
szeli1 Jun 17, 2024
1ea27c0
Suppress warnings in third-party code (#7319)
DomClark Jun 17, 2024
f00f0c0
set "child subreaper" process attribute on Linux (#7315)
notaz Jun 18, 2024
a5e000f
Fix caching for macOS CI jobs (#7326)
DomClark Jun 18, 2024
b5b6e2d
Merge branch 'master' into sample-recording
Rossmaxx Jul 15, 2024
7d70c8b
remnants of master merge
Rossmaxx Jul 15, 2024
d883acc
Merge remote-tracking branch 'origin/master' into feature/recording-s…
michaelgregorius Aug 10, 2024
d586ea2
Merge remote-tracking branch 'origin/master' into feature/recording-s…
michaelgregorius Aug 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions include/AudioEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ class LMMS_EXPORT AudioEngine : public QObject
return m_audioDev;
}

bool captureDeviceAvailable() const;


// audio-port-stuff
inline void addAudioPort(AudioPort * port)
Expand Down Expand Up @@ -343,6 +345,11 @@ class LMMS_EXPORT AudioEngine : public QObject
AudioDevice * tryAudioDevices();
MidiClient * tryMidiClients();

const AudioDevice* audioDev() const
{
return m_audioDev;
}

void renderStageNoteSetup();
void renderStageInstruments();
void renderStageEffects();
Expand Down
1 change: 1 addition & 0 deletions include/SampleClipView.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public slots:
void mouseDoubleClickEvent( QMouseEvent * ) override;
void paintEvent( QPaintEvent * ) override;

bool recordingCapabilitiesAvailable() const;

private:
SampleClip * m_clip;
Expand Down
5 changes: 4 additions & 1 deletion include/SampleRecordHandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class Track;
class SampleRecordHandle : public PlayHandle
{
public:
SampleRecordHandle( SampleClip* clip );
SampleRecordHandle(SampleClip* clip, TimePos startRecordTimeOffset);
~SampleRecordHandle() override;

void play( SampleFrame* _working_buffer ) override;
Expand All @@ -70,6 +70,9 @@ class SampleRecordHandle : public PlayHandle
PatternTrack* m_patternTrack;
SampleClip * m_clip;

// The offset from the start of m_track that the record has
// started from.
TimePos m_startRecordTimeOffset;
} ;


Expand Down
14 changes: 9 additions & 5 deletions src/core/AudioEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ void AudioEngine::renderStageNoteSetup()

if( it != m_playHandles.end() )
{
( *it )->audioPort()->removePlayHandle( ( *it ) );
if ((*it)->audioPort()) { (*it)->audioPort()->removePlayHandle(*it); }
if( ( *it )->type() == PlayHandle::Type::NotePlayHandle )
{
NotePlayHandleManager::release( (NotePlayHandle*) *it );
Expand Down Expand Up @@ -392,7 +392,7 @@ void AudioEngine::renderStageEffects()
}
if( ( *it )->isFinished() )
{
( *it )->audioPort()->removePlayHandle( ( *it ) );
if ((*it)->audioPort()) { (*it)->audioPort()->removePlayHandle(*it); }
if( ( *it )->type() == PlayHandle::Type::NotePlayHandle )
{
NotePlayHandleManager::release( (NotePlayHandle*) *it );
Expand Down Expand Up @@ -633,6 +633,10 @@ void AudioEngine::restoreAudioDevice()
}


bool AudioEngine::captureDeviceAvailable() const
{
return audioDev()->supportsCapture();
}


void AudioEngine::removeAudioPort(AudioPort * port)
Expand All @@ -656,7 +660,7 @@ bool AudioEngine::addPlayHandle( PlayHandle* handle )
if (handle->type() == PlayHandle::Type::InstrumentPlayHandle || !criticalXRuns())
{
m_newPlayHandles.push( handle );
handle->audioPort()->addPlayHandle( handle );
if (handle->audioPort()) { handle->audioPort()->addPlayHandle(handle); }
return true;
}

Expand All @@ -677,7 +681,7 @@ void AudioEngine::removePlayHandle(PlayHandle * ph)
// which were created in a thread different than the audio engine thread
if (ph->affinityMatters() && ph->affinity() == QThread::currentThread())
{
ph->audioPort()->removePlayHandle(ph);
if (ph->audioPort()) { ph->audioPort()->removePlayHandle(ph); }
bool removedFromList = false;
// Check m_newPlayHandles first because doing it the other way around
// creates a race condition
Expand Down Expand Up @@ -736,7 +740,7 @@ void AudioEngine::removePlayHandlesOfTypes(Track * track, PlayHandle::Types type
{
if ((*it)->isFromTrack(track) && ((*it)->type() & types))
{
( *it )->audioPort()->removePlayHandle( ( *it ) );
if ((*it)->audioPort()) { (*it)->audioPort()->removePlayHandle(*it); }
if( ( *it )->type() == PlayHandle::Type::NotePlayHandle )
{
NotePlayHandleManager::release( (NotePlayHandle*) *it );
Expand Down
12 changes: 11 additions & 1 deletion src/core/DataFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,17 @@ bool DataFile::copyResources(const QString& resourcesDir)
{
// Get absolute path to resource
bool error;
QString resPath = PathUtil::toAbsolute(el.attribute(*res), &error);
auto attribute = el.attribute(*res);

// Skip empty resources. In some cases, there might be an
// alternative way of actually representing the data (e.g. data element for SampleTCO)
if (attribute.isEmpty())
{
++res;
continue;
}

QString resPath = PathUtil::toAbsolute(attribute, &error);
// If we are running without the project loaded (from CLI), "local:" base
// prefixes aren't converted, so we need to convert it ourselves
if (error)
Expand Down
3 changes: 2 additions & 1 deletion src/core/PlayHandle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ PlayHandle::PlayHandle(const Type type, f_cnt_t offset) :
m_affinity(QThread::currentThread()),
m_playHandleBuffer(BufferManager::acquire()),
m_bufferReleased(true),
m_usesBuffer(true)
m_usesBuffer(true),
m_audioPort{nullptr}
{
}

Expand Down
9 changes: 6 additions & 3 deletions src/core/SampleRecordHandle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,14 @@ namespace lmms
{


SampleRecordHandle::SampleRecordHandle( SampleClip* clip ) :
PlayHandle( Type::SamplePlayHandle ),
SampleRecordHandle::SampleRecordHandle(SampleClip* clip, TimePos startRecordTimeOffset) :
PlayHandle(Type::SamplePlayHandle),
m_framesRecorded( 0 ),
m_minLength( clip->length() ),
m_track( clip->getTrack() ),
m_patternTrack( nullptr ),
m_clip( clip )
m_clip(clip),
m_startRecordTimeOffset{startRecordTimeOffset}
{
}

Expand All @@ -53,6 +54,8 @@ SampleRecordHandle::~SampleRecordHandle()
{
if (!m_buffers.empty()) { m_clip->setSampleBuffer(createSampleBuffer()); }

m_clip->setStartTimeOffset(m_startRecordTimeOffset);

while( !m_buffers.empty() )
{
delete[] m_buffers.front().first;
Expand Down
49 changes: 37 additions & 12 deletions src/gui/clips/SampleClipView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
#include <QMenu>
#include <QPainter>

#include "AudioEngine.h"
#include "GuiApplication.h"
#include "AutomationEditor.h"
#include "embed.h"
#include "gui_templates.h"
#include "PathUtil.h"
#include "SampleClip.h"
#include "SampleLoader.h"
Expand Down Expand Up @@ -77,9 +79,12 @@ void SampleClipView::constructContextMenu(QMenu* cm)
{
cm->addSeparator();

/*contextMenu.addAction( embed::getIconPixmap( "record" ),
tr( "Set/clear record" ),
m_clip, SLOT(toggleRecord()));*/

QAction* recordToggleAction = cm->addAction(embed::getIconPixmap("record"),
tr("Set/clear record"),
m_clip, &SampleClip::toggleRecord);

recordToggleAction->setEnabled(recordingCapabilitiesAvailable());

cm->addAction(
embed::getIconPixmap("flip_x"),
Expand Down Expand Up @@ -306,18 +311,34 @@ void SampleClipView::paintEvent( QPaintEvent * pe )
}
// recording sample tracks is not possible at the moment

/* if( m_clip->isRecord() )
if (m_clip->isRecord())
{
p.setFont( pointSize<7>( p.font() ) );
p.setFont(adjustedToPixelSize(p.font(), 10));

const auto fontHeight = p.fontMetrics().height();

const auto baseLine = height() - 3;

constexpr int recordSymbolRadius = 3;
constexpr int recordSymbolCenterX = recordSymbolRadius + 4;
const int recordSymbolCenterY = baseLine - fontHeight / 2 + 1;

constexpr int textStartX = recordSymbolCenterX + recordSymbolRadius + 4;

auto textPos = QPoint(textStartX, baseLine);

p.setPen( textShadowColor() );
p.drawText( 10, p.fontMetrics().height()+1, "Rec" );
p.setPen( textColor() );
p.drawText( 9, p.fontMetrics().height(), "Rec" );
const auto rec = tr("Rec");

p.setBrush( QBrush( textColor() ) );
p.drawEllipse( 4, 5, 4, 4 );
}*/
p.setPen(textShadowColor());
p.drawText(textPos + QPoint(1, 1), rec);

p.setPen(textColor());
p.drawText(textPos, rec);

p.setBrush(QBrush(textColor()));

p.drawEllipse(QPoint(recordSymbolCenterX, recordSymbolCenterY), recordSymbolRadius, recordSymbolRadius);
}

p.end();

Expand Down Expand Up @@ -375,5 +396,9 @@ bool SampleClipView::splitClip( const TimePos pos )
else { return false; }
}

bool SampleClipView::recordingCapabilitiesAvailable() const
{
return Engine::audioEngine()->captureDeviceAvailable();
}

} // namespace lmms::gui
14 changes: 12 additions & 2 deletions src/gui/editors/SongEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
#include <QTimeLine>

#include "ActionGroup.h"
#include "AudioDevice.h"
#include "AudioEngine.h"
#include "AutomatableSlider.h"
#include "ClipView.h"
Expand Down Expand Up @@ -908,7 +907,7 @@ ComboBoxModel *SongEditor::snappingModel() const


SongEditorWindow::SongEditorWindow(Song* song) :
Editor(Engine::audioEngine()->audioDev()->supportsCapture(), false),
Editor(true, false),
m_editor(new SongEditor(song)),
m_crtlAction( nullptr ),
m_snapSizeLabel( new QLabel( m_toolBar ) )
Expand Down Expand Up @@ -1025,6 +1024,17 @@ SongEditorWindow::SongEditorWindow(Song* song) :

connect(song, SIGNAL(projectLoaded()), this, SLOT(adjustUiAfterProjectLoad()));
connect(this, SIGNAL(resized()), m_editor, SLOT(updatePositionLine()));

// In case our current audio device does not support capture,
// disable the record buttons.
if (!Engine::audioEngine()->captureDeviceAvailable())
{
for (auto &recordAction : {m_recordAccompanyAction, m_recordAction})
{
recordAction->setEnabled(false);
recordAction->setToolTip(tr("Recording is unavailable: try connecting an input device or switching backend"));
}
}
}

QSize SongEditorWindow::sizeHint() const
Expand Down
19 changes: 14 additions & 5 deletions src/tracks/SampleTrack.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* SampleTrack.cpp - implementation of class SampleTrack, a track which
* provides arrangement of samples
*
Expand All @@ -22,7 +22,7 @@
* Boston, MA 02110-1301 USA.
*
*/

#include "SampleTrack.h"

#include <QDomElement>
Expand Down Expand Up @@ -106,7 +106,8 @@ bool SampleTrack::play( const TimePos & _start, const fpp_t _frames,

if( _start >= sClip->startPosition() && _start < sClip->endPosition() )
{
if( sClip->isPlaying() == false && _start >= (sClip->startPosition() + sClip->startTimeOffset()) )
if (!sClip->isPlaying() && (_start >= (sClip->startPosition() + sClip->startTimeOffset())
|| sClip->isRecord()))
{
auto bufferFramesPerTick = Engine::framesPerTick(sClip->sample().sampleRate());
f_cnt_t sampleStart = bufferFramesPerTick * ( _start - sClip->startPosition() - sClip->startTimeOffset() );
Expand All @@ -115,8 +116,16 @@ bool SampleTrack::play( const TimePos & _start, const fpp_t _frames,
//if the Clip smaller than the sample length we play only until Clip end
//else we play the sample to the end but nothing more
f_cnt_t samplePlayLength = clipFrameLength > sampleBufferLength ? sampleBufferLength : clipFrameLength;

// In case we are recoding, "play" the whole TCO.
if (sClip->isRecord())
{
samplePlayLength = clipFrameLength;
}

//we only play within the sampleBuffer limits
if( sampleStart < sampleBufferLength )
//Ignore that in case of recoding.
if (sampleStart < sampleBufferLength || sClip->isRecord())
{
Rossmaxx marked this conversation as resolved.
Show resolved Hide resolved
sClip->setSampleStartFrame( sampleStart );
sClip->setSamplePlayLength( samplePlayLength );
Expand Down Expand Up @@ -147,7 +156,7 @@ bool SampleTrack::play( const TimePos & _start, const fpp_t _frames,
{
return played_a_note;
}
auto smpHandle = new SampleRecordHandle(st);
auto smpHandle = new SampleRecordHandle(st, _start - st->startPosition());
handle = smpHandle;
PhysSong marked this conversation as resolved.
Show resolved Hide resolved
}
else
Expand Down
Loading