Skip to content

Commit

Permalink
Process metronome every MIDI tick (#7483)
Browse files Browse the repository at this point in the history
  • Loading branch information
sakertooth committed Sep 4, 2024
1 parent b81f806 commit d703f39
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 63 deletions.
7 changes: 0 additions & 7 deletions include/AudioEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,6 @@ class LMMS_EXPORT AudioEngine : public QObject

void changeQuality(const struct qualitySettings & qs);

inline bool isMetronomeActive() const { return m_metronomeActive; }
inline void setMetronomeActive(bool value = true) { m_metronomeActive = value; }

//! Block until a change in model can be done (i.e. wait for audio thread)
void requestChangeInModel();
void doneChangeInModel();
Expand Down Expand Up @@ -352,8 +349,6 @@ class LMMS_EXPORT AudioEngine : public QObject

void swapBuffers();

void handleMetronome();

void clearInternal();

bool m_renderOnly;
Expand Down Expand Up @@ -402,8 +397,6 @@ class LMMS_EXPORT AudioEngine : public QObject

AudioEngineProfiler m_profiler;

bool m_metronomeActive;

bool m_clearSignal;

std::recursive_mutex m_changeMutex;
Expand Down
43 changes: 43 additions & 0 deletions include/Metronome.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Metronome.h
*
* Copyright (c) 2024 saker
*
* 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_METRONOME_H
#define LMMS_METRONOME_H

#include <cstddef>

namespace lmms {
class Metronome
{
public:
bool active() const { return m_active; }
void setActive(bool active) { m_active = active; }
void processTick(int currentTick, int ticksPerBar, int beatsPerBar, size_t bufferOffset);

private:
bool m_active = false;
};
} // namespace lmms

#endif // LMMS_METRONOME_H
6 changes: 6 additions & 0 deletions include/Song.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

#include "AudioEngine.h"
#include "Controller.h"
#include "Metronome.h"
#include "lmms_constants.h"
#include "MeterModel.h"
#include "Timeline.h"
Expand Down Expand Up @@ -375,6 +376,8 @@ class LMMS_EXPORT Song : public TrackContainer

const std::string& syncKey() const noexcept { return m_vstSyncController.sharedMemoryKey(); }

Metronome& metronome() { return m_metronome; }

public slots:
void playSong();
void record();
Expand Down Expand Up @@ -448,6 +451,7 @@ private slots:
void restoreKeymapStates(const QDomElement &element);

void processAutomations(const TrackList& tracks, TimePos timeStart, fpp_t frames);
void processMetronome(size_t bufferOffset);

void setModified(bool value);

Expand Down Expand Up @@ -513,6 +517,8 @@ private slots:

AutomatedValueMap m_oldAutomatedValues;

Metronome m_metronome;

friend class Engine;
friend class gui::SongEditor;
friend class gui::ControllerRackView;
Expand Down
52 changes: 0 additions & 52 deletions src/core/AudioEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ AudioEngine::AudioEngine( bool renderOnly ) :
m_oldAudioDev( nullptr ),
m_audioDevStartFailed( false ),
m_profiler(),
m_metronomeActive(false),
m_clearSignal(false)
{
for( int i = 0; i < 2; ++i )
Expand Down Expand Up @@ -345,8 +344,6 @@ void AudioEngine::renderStageNoteSetup()
Mixer * mixer = Engine::mixer();
mixer->prepareMasterMix();

handleMetronome();

// create play-handles for new notes, samples etc.
Engine::getSong()->processNextBuffer();

Expand Down Expand Up @@ -459,55 +456,6 @@ void AudioEngine::swapBuffers()
zeroSampleFrames(m_outputBufferWrite.get(), m_framesPerPeriod);
}




void AudioEngine::handleMetronome()
{
static tick_t lastMetroTicks = -1;

Song * song = Engine::getSong();
Song::PlayMode currentPlayMode = song->playMode();

bool metronomeSupported =
currentPlayMode == Song::PlayMode::MidiClip
|| currentPlayMode == Song::PlayMode::Song
|| currentPlayMode == Song::PlayMode::Pattern;

if (!metronomeSupported || !m_metronomeActive || song->isExporting())
{
return;
}

// stop crash with metronome if empty project
if (song->countTracks() == 0)
{
return;
}

tick_t ticks = song->getPlayPos(currentPlayMode).getTicks();
tick_t ticksPerBar = TimePos::ticksPerBar();
int numerator = song->getTimeSigModel().getNumerator();

if (ticks == lastMetroTicks)
{
return;
}

if (ticks % (ticksPerBar / 1) == 0)
{
addPlayHandle(new SamplePlayHandle("misc/metronome02.ogg"));
}
else if (ticks % (ticksPerBar / numerator) == 0)
{
addPlayHandle(new SamplePlayHandle("misc/metronome01.ogg"));
}

lastMetroTicks = ticks;
}



void AudioEngine::clear()
{
m_clearSignal = true;
Expand Down
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ set(LMMS_SRCS
core/LinkedModelGroups.cpp
core/LocklessAllocator.cpp
core/MeterModel.cpp
core/Metronome.cpp
core/MicroTimer.cpp
core/Microtuner.cpp
core/MixHelpers.cpp
Expand Down
41 changes: 41 additions & 0 deletions src/core/Metronome.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Metronome.cpp
*
* Copyright (c) 2024 saker
*
* 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.
*
*/

#include "Metronome.h"

#include "Engine.h"
#include "SamplePlayHandle.h"

namespace lmms {
void Metronome::processTick(int currentTick, int ticksPerBar, int beatsPerBar, size_t bufferOffset)
{
const auto ticksPerBeat = ticksPerBar / beatsPerBar;
if (currentTick % ticksPerBeat != 0 || !m_active) { return; }

const auto handle = currentTick % ticksPerBar == 0 ? new SamplePlayHandle("misc/metronome02.ogg")
: new SamplePlayHandle("misc/metronome01.ogg");
handle->setOffset(bufferOffset);
Engine::audioEngine()->addPlayHandle(handle);
}
} // namespace lmms
15 changes: 13 additions & 2 deletions src/core/Song.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ void Song::processNextBuffer()
{
// First frame of tick: process automation and play tracks
processAutomations(trackList, getPlayPos(), framesToPlay);
processMetronome(frameOffsetInPeriod);

for (const auto track : trackList)
{
track->play(getPlayPos(), framesToPlay, frameOffsetInPeriod, clipNum);
Expand Down Expand Up @@ -426,6 +428,17 @@ void Song::processAutomations(const TrackList &tracklist, TimePos timeStart, fpp
}
}

void Song::processMetronome(size_t bufferOffset)
{
const auto currentPlayMode = playMode();
const auto supported = currentPlayMode == PlayMode::MidiClip
|| currentPlayMode == PlayMode::Song
|| currentPlayMode == PlayMode::Pattern;

if (!supported || m_exporting) { return; }
m_metronome.processTick(currentTick(), ticksPerBar(), m_timeSigModel.getNumerator(), bufferOffset);
}

void Song::setModified(bool value)
{
if( !m_loadingProject && m_modified != value)
Expand Down Expand Up @@ -1542,6 +1555,4 @@ void Song::setKeymap(unsigned int index, std::shared_ptr<Keymap> newMap)
emit keymapListChanged(index);
Engine::audioEngine()->doneChangeInModel();
}


} // namespace lmms
5 changes: 3 additions & 2 deletions src/gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "ExportProjectDialog.h"
#include "FileBrowser.h"
#include "FileDialog.h"
#include "Metronome.h"
#include "MixerView.h"
#include "GuiApplication.h"
#include "ImportFilter.h"
Expand Down Expand Up @@ -430,7 +431,7 @@ void MainWindow::finalize()
this, SLOT(onToggleMetronome()),
m_toolBar );
m_metronomeToggle->setCheckable(true);
m_metronomeToggle->setChecked(Engine::audioEngine()->isMetronomeActive());
m_metronomeToggle->setChecked(Engine::getSong()->metronome().active());

m_toolBarLayout->setColumnMinimumWidth( 0, 5 );
m_toolBarLayout->addWidget( project_new, 0, 1 );
Expand Down Expand Up @@ -1173,7 +1174,7 @@ void MainWindow::updateConfig( QAction * _who )

void MainWindow::onToggleMetronome()
{
Engine::audioEngine()->setMetronomeActive( m_metronomeToggle->isChecked() );
Engine::getSong()->metronome().setActive(m_metronomeToggle->isChecked());
}


Expand Down

0 comments on commit d703f39

Please sign in to comment.