Skip to content

Commit

Permalink
add MidiMessageIntegrator and MidiNoteSynthesizer
Browse files Browse the repository at this point in the history
  • Loading branch information
CrSjimo committed Jul 7, 2024
1 parent 1129703 commit 2ea8f06
Show file tree
Hide file tree
Showing 11 changed files with 437 additions and 15 deletions.
4 changes: 2 additions & 2 deletions src/core/source/NoteSynthesizer_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ namespace talcs {
vel /= d->attackRate;
if (vel > velFactor)
vel = velFactor;
} else {
} else if (!isAttack) {
vel *= d->releaseRate;
if (vel < .005)
vel = .0;
Expand All @@ -58,7 +58,7 @@ namespace talcs {
}
};
double attackRate = .005;
double releaseRate = 0;
double releaseRate = .0;
QList<KeyInfo> keys;

struct GenerateSineWave {
Expand Down
1 change: 0 additions & 1 deletion src/midi/device/MidiInputDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ namespace talcs {

void MidiInputDevicePrivate::rtmidiCallback(double timeStamp, std::vector<unsigned char> *message, void *userData) {
auto d = reinterpret_cast<MidiInputDevicePrivate *>(userData);
qDebug() << timeStamp;
MidiMessage msg(message->data(), static_cast<int>(message->size()), timeStamp);
d->listener.messageCallback(msg);
}
Expand Down
2 changes: 1 addition & 1 deletion src/midi/device/MidiMessageListener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ namespace talcs {
QMutexLocker locker(&d->filterMutex);
return std::any_of(d->filters.cbegin(), d->filters.cend(), [&message](MidiMessageListener *filter) {
return filter->messageCallback(message);
}) && processMessage(message);
}) || processMessage(message);
}

void MidiMessageListener::errorCallback(const QString &errorString) {
Expand Down
86 changes: 86 additions & 0 deletions src/midi/integrator/MidiMessageIntegrator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/******************************************************************************
* Copyright (c) 2024 CrSjimo *
* *
* This file is part of TALCS. *
* *
* TALCS is free software: you can redistribute it and/or modify it under the *
* terms of the GNU Lesser General Public License as published by the Free *
* Software Foundation, either version 3 of the License, or (at your option) *
* any later version. *
* *
* TALCS 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 Lesser General Public License for *
* more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with TALCS. If not, see <https://www.gnu.org/licenses/>. *
******************************************************************************/

#include "MidiMessageIntegrator.h"
#include "MidiMessageIntegrator_p.h"

#include <QList>

namespace talcs {
MidiMessageIntegrator::MidiMessageIntegrator() : MidiMessageIntegrator(*new MidiMessageIntegratorPrivate) {

}

MidiMessageIntegrator::~MidiMessageIntegrator() = default;

bool MidiMessageIntegrator::open(qint64 bufferSize, double sampleRate) {
Q_D(MidiMessageIntegrator);
d->queue.clear();
return AbstractMidiMessageIntegrator::open(bufferSize, sampleRate);
}

void MidiMessageIntegrator::close() {
Q_D(MidiMessageIntegrator);
d->queue.clear();
AbstractMidiMessageIntegrator::close();
}

bool MidiMessageIntegrator::processDeviceWillStart(MidiInputDevice *device) {
Q_D(MidiMessageIntegrator);
d->queue.clear();
return true;
}

void MidiMessageIntegrator::processDeviceStopped() {
Q_D(MidiMessageIntegrator);
d->queue.clear();
}

bool MidiMessageIntegrator::processMessage(const MidiMessage &message) {
Q_D(MidiMessageIntegrator);
d->queue.push(message);
return false;
}

void MidiMessageIntegrator::processError(const QString &errorString) {
Q_D(MidiMessageIntegrator);
d->queue.push({0xf0, 0xf7, -qInf()});
}

QList<IntegratedMidiMessage> MidiMessageIntegrator::fetch(qint64 length) {
Q_D(MidiMessageIntegrator);
if (d->queue.empty())
return {};
QList<IntegratedMidiMessage> midiEvents = {{0, d->queue.top()}};
d->queue.pop();
while (!d->queue.empty()) {
auto message = d->queue.top();
auto position = static_cast<qint64>((message.getTimeStamp() - midiEvents[0].message.getTimeStamp()) * sampleRate());
if (position >= length)
break;
d->queue.pop();
midiEvents.append({position, message});
}
return midiEvents;
}

MidiMessageIntegrator::MidiMessageIntegrator(MidiMessageIntegratorPrivate &d) : AbstractMidiMessageIntegrator(d) {

}
} // talcs
54 changes: 54 additions & 0 deletions src/midi/integrator/MidiMessageIntegrator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/******************************************************************************
* Copyright (c) 2024 CrSjimo *
* *
* This file is part of TALCS. *
* *
* TALCS is free software: you can redistribute it and/or modify it under the *
* terms of the GNU Lesser General Public License as published by the Free *
* Software Foundation, either version 3 of the License, or (at your option) *
* any later version. *
* *
* TALCS 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 Lesser General Public License for *
* more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with TALCS. If not, see <https://www.gnu.org/licenses/>. *
******************************************************************************/

#ifndef TALCS_MIDIMESSAGEINTEGRATOR_H
#define TALCS_MIDIMESSAGEINTEGRATOR_H

#include <TalcsMidi/AbstractMidiMessageIntegrator.h>
#include <TalcsMidi/MidiMessageListener.h>

namespace talcs {

class MidiMessageIntegratorPrivate;

class TALCSMIDI_EXPORT MidiMessageIntegrator : public AbstractMidiMessageIntegrator, public MidiMessageListener {
Q_DECLARE_PRIVATE_D(AbstractMidiMessageIntegrator::d_ptr, MidiMessageIntegrator)
public:
explicit MidiMessageIntegrator();
~MidiMessageIntegrator() override;

bool open(qint64 bufferSize, double sampleRate) override;

void close() override;

protected:
bool processDeviceWillStart(MidiInputDevice *device) override;
void processDeviceStopped() override;
bool processMessage(const MidiMessage &message) override;
void processError(const QString &errorString) override;

QList<IntegratedMidiMessage> fetch(qint64 length) override;

explicit MidiMessageIntegrator(MidiMessageIntegratorPrivate &d);

};

} // talcs

#endif //TALCS_MIDIMESSAGEINTEGRATOR_H
70 changes: 70 additions & 0 deletions src/midi/integrator/MidiMessageIntegrator_p.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/******************************************************************************
* Copyright (c) 2024 CrSjimo *
* *
* This file is part of TALCS. *
* *
* TALCS is free software: you can redistribute it and/or modify it under the *
* terms of the GNU Lesser General Public License as published by the Free *
* Software Foundation, either version 3 of the License, or (at your option) *
* any later version. *
* *
* TALCS 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 Lesser General Public License for *
* more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with TALCS. If not, see <https://www.gnu.org/licenses/>. *
******************************************************************************/

#ifndef TALCS_MIDIMESSAGEINTEGRATOR_P_H
#define TALCS_MIDIMESSAGEINTEGRATOR_P_H

#include <TalcsMidi/MidiMessageIntegrator.h>

#include <TalcsMidi/private/AbstractMidiMessageIntegrator_p.h>

namespace talcs {

struct MidiMessageIntegratorQueue {
MidiMessage p[1024];
QAtomicInteger<size_t> head = 0;
size_t tail = 0;
QAtomicInteger<size_t> counter = 0;
void push(const MidiMessage &message) {
if (counter.loadAcquire() == 1024)
return;
counter.fetchAndAddOrdered(1);
p[tail++] = message;
tail %= 1024;
}
void clear() {
counter.storeRelease(0);
tail = head;
tail %= 1024;
}
bool empty() {
return !counter;
}
MidiMessage top() {
if (!counter.loadAcquire())
return {0xf0, 0xf7, -qInf()};
return p[head];
}
void pop() {
if (!counter.loadAcquire())
return;
counter.fetchAndAddOrdered(-1);
head++;
head = head % 1024;
}
};

class MidiMessageIntegratorPrivate : public AbstractMidiMessageIntegratorPrivate {
Q_DECLARE_PUBLIC(MidiMessageIntegrator)
public:
MidiMessageIntegratorQueue queue;
};
}

#endif //TALCS_MIDIMESSAGEINTEGRATOR_P_H
98 changes: 98 additions & 0 deletions src/midi/integrator/MidiNoteSynthesizer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/******************************************************************************
* Copyright (c) 2024 CrSjimo *
* *
* This file is part of TALCS. *
* *
* TALCS is free software: you can redistribute it and/or modify it under the *
* terms of the GNU Lesser General Public License as published by the Free *
* Software Foundation, either version 3 of the License, or (at your option) *
* any later version. *
* *
* TALCS 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 Lesser General Public License for *
* more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with TALCS. If not, see <https://www.gnu.org/licenses/>. *
******************************************************************************/

#include "MidiNoteSynthesizer.h"
#include "MidiNoteSynthesizer_p.h"

namespace talcs {
MidiNoteSynthesizer::MidiNoteSynthesizer() : MidiNoteSynthesizer(new NoteSynthesizer, true) {

}

MidiNoteSynthesizer::MidiNoteSynthesizer(NoteSynthesizer *noteSynthesizer, bool takeOwnership) : MidiNoteSynthesizer(*new MidiNoteSynthesizerPrivate) {
Q_D(MidiNoteSynthesizer);
d->noteSynthesizer = noteSynthesizer;
d->takeOwnership = takeOwnership;
d->noteSynthesizer->setDetector(d);
}

MidiNoteSynthesizer::~MidiNoteSynthesizer() {
Q_D(MidiNoteSynthesizer);
if (d->takeOwnership)
delete d->noteSynthesizer;
}

bool MidiNoteSynthesizer::open(qint64 bufferSize, double sampleRate) {
Q_D(MidiNoteSynthesizer);
if (!d->noteSynthesizer->open(bufferSize, sampleRate))
return false;
return AudioMidiStream::open(bufferSize, sampleRate);
}

void MidiNoteSynthesizer::close() {
Q_D(MidiNoteSynthesizer);
d->noteSynthesizer->close();
AudioMidiStream::close();
}

void MidiNoteSynthesizer::setFrequencyOfA(double frequency) {
Q_D(MidiNoteSynthesizer);
d->frequencyOfA = frequency;
}

double MidiNoteSynthesizer::frequencyOfA() const {
Q_D(const MidiNoteSynthesizer);
return d->frequencyOfA;
}

NoteSynthesizer *MidiNoteSynthesizer::noteSynthesizer() const {
Q_D(const MidiNoteSynthesizer);
return d->noteSynthesizer;
}

void MidiNoteSynthesizerPrivate::detectInterval(qint64 intervalLength) {

}

NoteSynthesizerDetectorMessage MidiNoteSynthesizerPrivate::nextMessage() {
while (midiEventsIterator != midiEvents.cend() && !midiEventsIterator->message.isNoteOnOrOff())
midiEventsIterator++;
if (midiEventsIterator == midiEvents.cend())
return {-1};
NoteSynthesizerDetectorMessage ret = {
midiEventsIterator->position,
MidiMessage::getMidiNoteInHertz(midiEventsIterator->message.getNoteNumber(), frequencyOfA),
midiEventsIterator->message.getFloatVelocity(),
midiEventsIterator->message.isNoteOn(),
};
midiEventsIterator++;
return ret;
}

qint64 MidiNoteSynthesizer::processReading(const AudioSourceReadData &readData, const QList<IntegratedMidiMessage> &midiEvents) {
Q_D(MidiNoteSynthesizer);
d->midiEvents = midiEvents;
d->midiEventsIterator = d->midiEvents.cbegin();
return d->noteSynthesizer->read(readData);
}

MidiNoteSynthesizer::MidiNoteSynthesizer(MidiNoteSynthesizerPrivate &d) : d_ptr(&d) {
d.q_ptr = this;
}
} // talcs
Loading

0 comments on commit 2ea8f06

Please sign in to comment.