2012-05-26 14:49:10 +02:00
|
|
|
//=============================================================================
|
|
|
|
// 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"
|
|
|
|
#include "pm.h"
|
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
namespace Ms {
|
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
static PaStream* stream;
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// paCallback
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int paCallback(const void*, void* out, long unsigned frames,
|
|
|
|
const PaStreamCallbackTimeInfo*, PaStreamCallbackFlags, void *)
|
|
|
|
{
|
|
|
|
seq->process((unsigned)frames, (float*)out);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// Portaudio
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Portaudio::Portaudio(Seq* s)
|
|
|
|
: Driver(s)
|
|
|
|
{
|
2013-06-27 11:57:24 +02:00
|
|
|
_sampleRate = 48000; // will be replaced by device default sample rate
|
2012-05-26 14:49:10 +02:00
|
|
|
initialized = false;
|
2014-06-03 17:14:35 +02:00
|
|
|
state = Transport::STOP;
|
2012-05-26 14:49:10 +02:00
|
|
|
seekflag = false;
|
|
|
|
midiDriver = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// ~Portaudio
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Portaudio::~Portaudio()
|
|
|
|
{
|
|
|
|
if (initialized) {
|
|
|
|
PaError err = Pa_CloseStream(stream);
|
|
|
|
if (err != paNoError)
|
2013-04-03 20:20:36 +02:00
|
|
|
qDebug("Portaudio close stream failed: %s", Pa_GetErrorText(err));
|
2012-05-26 14:49:10 +02:00
|
|
|
Pa_Terminate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// init
|
|
|
|
// return false on error
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-07-04 19:59:33 +02:00
|
|
|
bool Portaudio::init(bool)
|
2012-05-26 14:49:10 +02:00
|
|
|
{
|
|
|
|
PaError err = Pa_Initialize();
|
|
|
|
if (err != paNoError) {
|
2013-04-03 20:20:36 +02:00
|
|
|
qDebug("Portaudio initialize failed: %s", Pa_GetErrorText(err));
|
2012-05-26 14:49:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
initialized = true;
|
|
|
|
if (MScore::debugMode)
|
2013-04-03 20:20:36 +02:00
|
|
|
qDebug("using PortAudio Version: %s", Pa_GetVersionText());
|
2012-05-26 14:49:10 +02:00
|
|
|
|
|
|
|
PaDeviceIndex idx = preferences.portaudioDevice;
|
|
|
|
if (idx < 0)
|
|
|
|
idx = Pa_GetDefaultOutputDevice();
|
|
|
|
|
2013-06-27 11:57:24 +02:00
|
|
|
const PaDeviceInfo* di = Pa_GetDeviceInfo(idx);
|
2014-12-11 12:01:30 +01:00
|
|
|
|
2014-02-22 12:24:20 +01:00
|
|
|
if (di == nullptr)
|
|
|
|
di = Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice());
|
2014-12-11 12:01:30 +01:00
|
|
|
|
2015-05-19 18:33:02 +02:00
|
|
|
if (!di)
|
|
|
|
return false; // Portaudio is not properly initialized; disable audio
|
2013-06-27 11:57:24 +02:00
|
|
|
_sampleRate = int(di->defaultSampleRate);
|
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
/* Open an audio I/O stream. */
|
|
|
|
struct PaStreamParameters out;
|
|
|
|
memset(&out, 0, sizeof(out));
|
|
|
|
|
|
|
|
out.device = idx;
|
|
|
|
out.channelCount = 2;
|
|
|
|
out.sampleFormat = paFloat32;
|
2013-10-01 21:56:38 +02:00
|
|
|
#ifdef Q_OS_MAC
|
2013-09-21 14:34:44 +02:00
|
|
|
out.suggestedLatency = 0.020;
|
2013-10-01 21:56:38 +02:00
|
|
|
#else // on windows, this small latency causes some problem
|
|
|
|
out.suggestedLatency = 0.100;
|
|
|
|
#endif
|
2012-05-26 14:49:10 +02:00
|
|
|
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) {
|
2013-04-03 20:20:36 +02:00
|
|
|
qDebug("Portaudio open stream %d failed: %s", idx, Pa_GetErrorText(err));
|
2012-05-26 14:49:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2013-06-27 12:06:48 +02:00
|
|
|
const PaStreamInfo* si = Pa_GetStreamInfo(stream);
|
|
|
|
if (si)
|
|
|
|
_sampleRate = int(si->sampleRate);
|
2012-05-26 14:49:10 +02:00
|
|
|
#ifdef USE_ALSA
|
|
|
|
midiDriver = new AlsaMidiDriver(seq);
|
|
|
|
#endif
|
|
|
|
#ifdef USE_PORTMIDI
|
|
|
|
midiDriver = new PortMidiDriver(seq);
|
|
|
|
#endif
|
|
|
|
if (midiDriver && !midiDriver->init()) {
|
2013-04-03 20:20:36 +02:00
|
|
|
qDebug("Init midi driver failed");
|
2012-05-26 14:49:10 +02:00
|
|
|
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(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(di->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dl;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// deviceIndex
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Portaudio::deviceIndex(int apiIdx, int apiDevIdx)
|
|
|
|
{
|
|
|
|
return Pa_HostApiDeviceIndexToDeviceIndex(apiIdx, apiDevIdx);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// start
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-07-30 15:38:54 +02:00
|
|
|
bool Portaudio::start(bool)
|
2012-05-26 14:49:10 +02:00
|
|
|
{
|
|
|
|
PaError err = Pa_StartStream(stream);
|
|
|
|
if (err != paNoError) {
|
2013-04-03 20:20:36 +02:00
|
|
|
qDebug("Portaudio: start stream failed: %s", Pa_GetErrorText(err));
|
2012-05-26 14:49:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// stop
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Portaudio::stop()
|
|
|
|
{
|
|
|
|
PaError err = Pa_StopStream(stream); // sometimes the program hangs here on exit
|
|
|
|
if (err != paNoError) {
|
2013-04-03 20:20:36 +02:00
|
|
|
qDebug("Portaudio: stop failed: %s", Pa_GetErrorText(err));
|
2012-05-26 14:49:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// framePos
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Portaudio::framePos() const
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// startTransport
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Portaudio::startTransport()
|
|
|
|
{
|
2014-06-03 17:14:35 +02:00
|
|
|
state = Transport::PLAY;
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// stopTransport
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Portaudio::stopTransport()
|
|
|
|
{
|
2014-06-03 17:14:35 +02:00
|
|
|
state = Transport::STOP;
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// getState
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-03 17:14:35 +02:00
|
|
|
Transport Portaudio::getState()
|
2012-05-26 14:49:10 +02:00
|
|
|
{
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// midiRead
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Portaudio::midiRead()
|
|
|
|
{
|
|
|
|
if (midiDriver)
|
|
|
|
midiDriver->read();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// currentApi
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Portaudio::currentApi() const
|
|
|
|
{
|
|
|
|
PaDeviceIndex idx = preferences.portaudioDevice;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-04-03 20:20:36 +02:00
|
|
|
qDebug("Portaudio: no current api found for device %d", idx);
|
2012-05-26 14:49:10 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// currentDevice
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Portaudio::currentDevice() const
|
|
|
|
{
|
|
|
|
PaDeviceIndex idx = preferences.portaudioDevice;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-04-03 20:20:36 +02:00
|
|
|
qDebug("Portaudio: no current ApiDevice found for device %d", idx);
|
2012-05-26 14:49:10 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2013-05-13 18:49:17 +02:00
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
|