MuseScore/mscore/pa.cpp
Dag Henning Liodden Sørbø 2b6cb8b432 Change to new preferences model
The old Preferences struct holding all preferences are removed in favor of a
new Preferences class which acts as a proxy for QSettings. The settings stored
in QSettings are accessed directly through access methods like getBool(key),
getInt(key), etc. and changed with setPreference(key, value).

Since we are using QSettings directly the preferences are stored automatically
without the need for a custom write() and read() method like before.

The preferences.cpp/.h and prefdialog.cpp/h are refactored to have fewer
responsibilities than before. The Preferences class are all about storing and
retrieving preferences - it should not contain any code to handle any other
aspect of MuseScore.

Testing:
The Preferences class can be used in tests. All preferences are initialized with
default values in mtest. If a test requires that a preference has a specific
value it can be changed using setPreference() for that single test. In the tests
the preferences are stored in memory only.

The Preference class is supposed to be used as a singleton. In preferences.h an
'extern Preferences preferences' is set and it is defined in preferences.cpp. All
files which includes preferences.h have access to the 'preferences' singleton
and should use this to get and set preferences.
2018-02-08 16:59:10 +01:00

404 lines
13 KiB
C++

//=============================================================================
// MusE Score
// Linux Music Score Editor
// $Id: pa.cpp 5662 2012-05-23 07:35:47Z wschweer $
//
// Copyright (C) 2002-2010 Werner Schweer and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// 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; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "preferences.h"
#include "libmscore/score.h"
#include "musescore.h"
#include "seq.h"
#include "pa.h"
#ifdef USE_ALSA
#include "alsa.h"
#include "alsamidi.h"
#endif
#include <portaudio.h>
#include "mididriver.h"
#ifdef USE_PORTMIDI
#include "pm.h"
#endif
namespace Ms {
static PaStream* stream;
//---------------------------------------------------------
// paCallback
//---------------------------------------------------------
int paCallback(const void*, void* out, long unsigned frames,
const PaStreamCallbackTimeInfo*, PaStreamCallbackFlags, void *)
{
seq->setInitialMillisecondTimestampWithLatency();
seq->process((unsigned)frames, (float*)out);
return 0;
}
//---------------------------------------------------------
// Portaudio
//---------------------------------------------------------
Portaudio::Portaudio(Seq* s)
: Driver(s)
{
_sampleRate = 48000; // will be replaced by device default sample rate
initialized = false;
state = Transport::STOP;
seekflag = false;
midiDriver = 0;
}
//---------------------------------------------------------
// ~Portaudio
//---------------------------------------------------------
Portaudio::~Portaudio()
{
if (initialized) {
PaError err = Pa_CloseStream(stream);
if (err != paNoError)
qDebug("Portaudio close stream failed: %s", Pa_GetErrorText(err));
Pa_Terminate();
}
}
//---------------------------------------------------------
// init
// return false on error
//---------------------------------------------------------
bool Portaudio::init(bool)
{
PaError err = Pa_Initialize();
if (err != paNoError) {
qDebug("Portaudio initialize failed: %s", Pa_GetErrorText(err));
return false;
}
initialized = true;
if (MScore::debugMode)
qDebug("using PortAudio Version: %s", Pa_GetVersionText());
PaDeviceIndex idx = preferences.getInt(PREF_IO_PORTAUDIO_DEVICE);
if (idx < 0) {
idx = Pa_GetDefaultOutputDevice();
qDebug("No device selected. PortAudio detected %d devices. Will use the default device (index %d).", Pa_GetDeviceCount(), idx);
}
const PaDeviceInfo* di = Pa_GetDeviceInfo(idx);
if (di == nullptr)
di = Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice());
if (!di)
return false; // Portaudio is not properly initialized; disable audio
_sampleRate = int(di->defaultSampleRate);
/* Open an audio I/O stream. */
struct PaStreamParameters out;
memset(&out, 0, sizeof(out));
out.device = idx;
out.channelCount = 2;
out.sampleFormat = paFloat32;
#ifdef Q_OS_MAC
out.suggestedLatency = 0.020;
#else // on windows, this small latency causes some problem
out.suggestedLatency = 0.100;
#endif
out.hostApiSpecificStreamInfo = 0;
err = Pa_OpenStream(&stream, 0, &out, double(_sampleRate), 0, 0, paCallback, (void*)this);
if (err != paNoError) {
// fall back to default device:
out.device = Pa_GetDefaultOutputDevice();
err = Pa_OpenStream(&stream, 0, &out, double(_sampleRate), 0, 0, paCallback, (void*)this);
if (err != paNoError) {
qDebug("Portaudio open stream %d failed: %s", idx, Pa_GetErrorText(err));
return false;
}
}
const PaStreamInfo* si = Pa_GetStreamInfo(stream);
if (si)
_sampleRate = int(si->sampleRate);
#ifdef USE_ALSA
midiDriver = new AlsaMidiDriver(seq);
#endif
#ifdef USE_PORTMIDI
midiDriver = new PortMidiDriver(seq);
#endif
if (midiDriver && !midiDriver->init()) {
qDebug("Init midi driver failed");
delete midiDriver;
midiDriver = 0;
#ifdef USE_PORTMIDI
return true; // return OK for audio driver; midi is only input
#else
return false;
#endif
}
return true;
}
//---------------------------------------------------------
// apiList
//---------------------------------------------------------
QStringList Portaudio::apiList() const
{
QStringList al;
PaHostApiIndex apis = Pa_GetHostApiCount();
for (PaHostApiIndex i = 0; i < apis; ++i) {
const PaHostApiInfo* info = Pa_GetHostApiInfo(i);
if (info)
al.append(QString::fromLocal8Bit(info->name));
}
return al;
}
//---------------------------------------------------------
// deviceList
//---------------------------------------------------------
QStringList Portaudio::deviceList(int apiIdx)
{
QStringList dl;
const PaHostApiInfo* info = Pa_GetHostApiInfo(apiIdx);
if (info) {
for (int i = 0; i < info->deviceCount; ++i) {
PaDeviceIndex idx = Pa_HostApiDeviceIndexToDeviceIndex(apiIdx, i);
const PaDeviceInfo* di = Pa_GetDeviceInfo(idx);
if (di)
dl.append(QString::fromLocal8Bit(di->name));
}
}
return dl;
}
//---------------------------------------------------------
// deviceIndex
//---------------------------------------------------------
int Portaudio::deviceIndex(int apiIdx, int apiDevIdx)
{
return Pa_HostApiDeviceIndexToDeviceIndex(apiIdx, apiDevIdx);
}
//---------------------------------------------------------
// start
//---------------------------------------------------------
bool Portaudio::start(bool)
{
PaError err = Pa_StartStream(stream);
if (err != paNoError) {
qDebug("Portaudio: start stream failed: %s", Pa_GetErrorText(err));
return false;
}
return true;
}
//---------------------------------------------------------
// stop
//---------------------------------------------------------
bool Portaudio::stop()
{
PaError err = Pa_StopStream(stream); // sometimes the program hangs here on exit
if (err != paNoError) {
qDebug("Portaudio: stop failed: %s", Pa_GetErrorText(err));
return false;
}
return true;
}
//---------------------------------------------------------
// framePos
//---------------------------------------------------------
int Portaudio::framePos() const
{
return 0;
}
//---------------------------------------------------------
// startTransport
//---------------------------------------------------------
void Portaudio::startTransport()
{
state = Transport::PLAY;
}
//---------------------------------------------------------
// stopTransport
//---------------------------------------------------------
void Portaudio::stopTransport()
{
state = Transport::STOP;
}
//---------------------------------------------------------
// getState
//---------------------------------------------------------
Transport Portaudio::getState()
{
return state;
}
//---------------------------------------------------------
// midiRead
//---------------------------------------------------------
void Portaudio::midiRead()
{
if (midiDriver)
midiDriver->read();
}
//---------------------------------------------------------
// putEvent
//---------------------------------------------------------
// Prevent killing sequencer with wrong data
#define less128(__less) ((__less >=0 && __less <= 127) ? __less : 0)
// TODO: this was copied from Jack version...I'd like to eventually unify these two, so that they handle midi event types in the same manner
void Portaudio::putEvent(const NPlayEvent& e, unsigned framePos)
{
PortMidiDriver* portMidiDriver = static_cast<PortMidiDriver*>(midiDriver);
if (!portMidiDriver || !portMidiDriver->getOutputStream() || !portMidiDriver->canOutput())
return;
int portIdx = seq->score()->midiPort(e.channel());
int chan = seq->score()->midiChannel(e.channel());
if (portIdx < 0 ) {
qDebug("Portaudio::putEvent: invalid port %d", portIdx);
return;
}
if (midiOutputTrace) {
int a = e.dataA();
int b = e.dataB();
qDebug("MidiOut<%d>: Portaudio: %02x %02x %02x, chan: %i", portIdx, e.type(), a, b, chan);
}
switch(e.type()) {
case ME_NOTEON:
case ME_NOTEOFF:
case ME_POLYAFTER:
case ME_CONTROLLER:
// Catch CTRL_PROGRAM and let other ME_CONTROLLER events to go
if (e.dataA() == CTRL_PROGRAM) {
// Convert CTRL_PROGRAM event to ME_PROGRAM
long msg = Pm_Message(ME_PROGRAM | chan, less128(e.dataB()), 0);
PmError error = Pm_WriteShort(portMidiDriver->getOutputStream(), seq->getCurrentMillisecondTimestampWithLatency(framePos), msg);
if (error != pmNoError) {
qDebug("Portaudio: error %d", error);
return;
}
break;
}
// fall through
case ME_PITCHBEND:
{
long msg = Pm_Message(e.type() | chan, less128(e.dataA()), less128(e.dataB()));
PmError error = Pm_WriteShort(portMidiDriver->getOutputStream(), seq->getCurrentMillisecondTimestampWithLatency(framePos), msg);
if (error != pmNoError) {
qDebug("Portaudio: error %d", error);
return;
}
}
break;
case ME_PROGRAM:
case ME_AFTERTOUCH:
{
long msg = Pm_Message(e.type() | chan, less128(e.dataA()), 0);
PmError error = Pm_WriteShort(portMidiDriver->getOutputStream(), seq->getCurrentMillisecondTimestampWithLatency(framePos), msg);
if (error != pmNoError) {
qDebug("Portaudio: error %d", error);
return;
}
}
break;
case ME_SONGPOS:
case ME_CLOCK:
case ME_START:
case ME_CONTINUE:
case ME_STOP:
qDebug("Portaudio: event type %x not supported", e.type());
break;
}
}
//---------------------------------------------------------
// currentApi
//---------------------------------------------------------
int Portaudio::currentApi() const
{
PaDeviceIndex idx = preferences.getInt(PREF_IO_PORTAUDIO_DEVICE);
if (idx < 0)
idx = Pa_GetDefaultOutputDevice();
for (int api = 0; api < Pa_GetHostApiCount(); ++api) {
const PaHostApiInfo* info = Pa_GetHostApiInfo(api);
if (info) {
for (int k = 0; k < info->deviceCount; ++k) {
PaDeviceIndex i = Pa_HostApiDeviceIndexToDeviceIndex(api, k);
if (i == idx)
return api;
}
}
}
qDebug("Portaudio: no current api found for device %d", idx);
return -1;
}
//---------------------------------------------------------
// currentDevice
//---------------------------------------------------------
int Portaudio::currentDevice() const
{
PaDeviceIndex idx = preferences.getInt(PREF_IO_PORTAUDIO_DEVICE);
if (idx < 0)
idx = Pa_GetDefaultOutputDevice();
for (int api = 0; api < Pa_GetHostApiCount(); ++api) {
const PaHostApiInfo* info = Pa_GetHostApiInfo(api);
if (info) {
for (int k = 0; k < info->deviceCount; ++k) {
PaDeviceIndex i = Pa_HostApiDeviceIndexToDeviceIndex(api, k);
if (i == idx)
return k;
}
}
}
qDebug("Portaudio: no current ApiDevice found for device %d", idx);
return -1;
}
}