2b6cb8b432
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.
404 lines
13 KiB
C++
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;
|
|
}
|
|
}
|
|
|