mirror of
https://github.com/ElvishArtisan/rivendell.git
synced 2025-05-27 14:12:34 +02:00
2015-09-05 Fred Gleason <fredg@paravelsystems.com>
* Refactored caed(8) to use virtual inheritance for audio subsystem drivers. * Reimplemented the HPI driver in 'cae/driver_hpi.cpp' and 'cae/driver_hpi.h'.
This commit is contained in:
parent
4de09b9eaf
commit
c1f86bbde1
@ -14873,3 +14873,8 @@
|
||||
'rdadmin/edit_rdlogedit.cpp' and 'rdadmin/edit_rdlogedit.h'.
|
||||
* Added PCM24 support for voice tracking in
|
||||
'rdlogedit/voice_tracker.cpp'.
|
||||
2015-09-05 Fred Gleason <fredg@paravelsystems.com>
|
||||
* Refactored caed(8) to use virtual inheritance for audio
|
||||
subsystem drivers.
|
||||
* Reimplemented the HPI driver in 'cae/driver_hpi.cpp' and
|
||||
'cae/driver_hpi.h'.
|
||||
|
@ -2,9 +2,7 @@
|
||||
##
|
||||
## Core Audio Engine Makefile.am for Rivendell
|
||||
##
|
||||
## Copyright 2002-2006 Fred Gleason <fredg@paravelsystems.com>
|
||||
##
|
||||
## $Id: Makefile.am,v 1.33.8.1 2012/11/29 01:37:34 cvs Exp $
|
||||
## Copyright 2002-2015 Fred Gleason <fredg@paravelsystems.com>
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License version 2 as
|
||||
@ -32,13 +30,18 @@ moc_%.cpp: %.h
|
||||
bin_PROGRAMS = caed
|
||||
|
||||
dist_caed_SOURCES = cae.cpp cae.h\
|
||||
cae_alsa.cpp\
|
||||
cae_hpi.cpp\
|
||||
cae_jack.cpp\
|
||||
cae_socket.cpp cae_socket.h
|
||||
## cae_alsa.cpp\
|
||||
## cae_hpi.cpp\
|
||||
## cae_jack.cpp\
|
||||
cae_socket.cpp cae_socket.h\
|
||||
driver.cpp driver.h\
|
||||
driver_hpi.cpp driver_hpi.h\
|
||||
driverfactory.cpp driverfactory.h
|
||||
|
||||
nodist_caed_SOURCES = moc_cae.cpp\
|
||||
moc_cae_socket.cpp
|
||||
moc_cae_socket.cpp\
|
||||
moc_driver.cpp\
|
||||
moc_driver_hpi.cpp
|
||||
|
||||
caed_LDADD = @LIB_RDLIBS@\
|
||||
@LIBALSA@\
|
||||
|
951
cae/cae.cpp
951
cae/cae.cpp
File diff suppressed because it is too large
Load Diff
63
cae/cae.h
63
cae/cae.h
@ -2,9 +2,7 @@
|
||||
//
|
||||
// The Core Audio Engine component of Rivendell
|
||||
//
|
||||
// (C) Copyright 2002-2004 Fred Gleason <fredg@paravelsystems.com>
|
||||
//
|
||||
// $Id: cae.h,v 1.79.4.4 2012/11/30 16:14:58 cvs Exp $
|
||||
// (C) Copyright 2002-2015 Fred Gleason <fredg@paravelsystems.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -28,6 +26,8 @@
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <soundtouch/SoundTouch.h>
|
||||
|
||||
#include <qobject.h>
|
||||
@ -40,7 +40,7 @@
|
||||
|
||||
#include <rdwavefile.h>
|
||||
#include <rdsocket.h>
|
||||
|
||||
/*
|
||||
#ifdef HPI
|
||||
#include <rdhpisoundcard.h>
|
||||
#include <rdhpiplaystream.h>
|
||||
@ -69,7 +69,7 @@ struct alsa_format {
|
||||
#ifdef JACK
|
||||
#include <jack/jack.h>
|
||||
#endif // JACK
|
||||
|
||||
*/
|
||||
#ifdef HAVE_TWOLAME
|
||||
#include <twolame.h>
|
||||
#endif // HAVE_TWOLAME
|
||||
@ -81,6 +81,8 @@ struct alsa_format {
|
||||
#include <rdconfig.h>
|
||||
#include <rdstation.h>
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
//
|
||||
// Debug Options
|
||||
//
|
||||
@ -151,7 +153,7 @@ class MainObject : public QObject
|
||||
int argnum[CAE_MAX_CONNECTIONS];
|
||||
int argptr[CAE_MAX_CONNECTIONS];
|
||||
bool auth[CAE_MAX_CONNECTIONS];
|
||||
RDStation::AudioDriver cae_driver[RD_MAX_CARDS];
|
||||
// RDStation::AudioDriver cae_driver[RD_MAX_CARDS];
|
||||
int record_owner[RD_MAX_CARDS][RD_MAX_STREAMS];
|
||||
int record_length[RD_MAX_CARDS][RD_MAX_STREAMS];
|
||||
int record_threshold[RD_MAX_CARDS][RD_MAX_STREAMS];
|
||||
@ -167,51 +169,12 @@ class MainObject : public QObject
|
||||
int owner;
|
||||
} play_handle[256];
|
||||
int next_play_handle;
|
||||
|
||||
//
|
||||
// HPI Driver
|
||||
//
|
||||
private:
|
||||
void hpiInit(RDStation *station);
|
||||
void hpiFree();
|
||||
QString hpiVersion();
|
||||
bool hpiLoadPlayback(int card,QString wavename,int *stream);
|
||||
bool hpiUnloadPlayback(int card,int stream);
|
||||
bool hpiPlaybackPosition(int card,int stream,unsigned pos);
|
||||
bool hpiPlay(int card,int stream,int length,int speed,bool pitch,
|
||||
bool rates);
|
||||
bool hpiStopPlayback(int card,int stream);
|
||||
bool hpiTimescaleSupported(int card);
|
||||
bool hpiLoadRecord(int card,int port,int coding,int chans,int samprate,
|
||||
int bitrate,QString wavename);
|
||||
bool hpiUnloadRecord(int card,int stream,unsigned *len);
|
||||
bool hpiRecord(int card,int stream,int length,int thres);
|
||||
bool hpiStopRecord(int card,int stream);
|
||||
bool hpiSetClockSource(int card,int src);
|
||||
bool hpiSetInputVolume(int card,int stream,int level);
|
||||
bool hpiSetOutputVolume(int card,int stream,int port,int level);
|
||||
bool hpiFadeOutputVolume(int card,int stream,int port,int level,int length);
|
||||
bool hpiSetInputLevel(int card,int port,int level);
|
||||
bool hpiSetOutputLevel(int card,int port,int level);
|
||||
bool hpiSetInputMode(int card,int stream,int mode);
|
||||
bool hpiSetOutputMode(int card,int stream,int mode);
|
||||
bool hpiSetInputVoxLevel(int card,int stream,int level);
|
||||
bool hpiSetInputType(int card,int port,int type);
|
||||
bool hpiGetInputStatus(int card,int port);
|
||||
bool hpiGetInputMeters(int card,int port,short levels[2]);
|
||||
bool hpiGetOutputMeters(int card,int port,short levels[2]);
|
||||
bool hpiGetStreamOutputMeters(int card,int stream,short levels[2]);
|
||||
bool hpiSetPassthroughLevel(int card,int in_port,int out_port,int level);
|
||||
void hpiGetOutputPosition(int card,unsigned *pos);
|
||||
#ifdef HPI
|
||||
RDHPISoundCard *sound_card;
|
||||
RDHPIRecordStream *record[RD_MAX_CARDS][RD_MAX_STREAMS];
|
||||
RDHPIPlayStream *play[RD_MAX_CARDS][RD_MAX_STREAMS];
|
||||
#endif // HPI
|
||||
std::vector<Driver *> cae_drivers;
|
||||
|
||||
//
|
||||
// JACK Driver
|
||||
//
|
||||
/*
|
||||
private slots:
|
||||
void jackStopTimerData(int stream);
|
||||
void jackFadeTimerData(int stream);
|
||||
@ -285,10 +248,11 @@ class MainObject : public QObject
|
||||
int jack_clock_phase;
|
||||
unsigned jack_samples_recorded[RD_MAX_STREAMS];
|
||||
#endif // JACK
|
||||
|
||||
*/
|
||||
//
|
||||
// ALSA Driver
|
||||
//
|
||||
/*
|
||||
private slots:
|
||||
void alsaStopTimerData(int cardstream);
|
||||
void alsaFadeTimerData(int cardstream);
|
||||
@ -340,6 +304,7 @@ class MainObject : public QObject
|
||||
short alsa_output_volume_db[RD_MAX_CARDS][RD_MAX_PORTS][RD_MAX_STREAMS];
|
||||
short alsa_passthrough_volume_db[RD_MAX_CARDS][RD_MAX_PORTS][RD_MAX_PORTS];
|
||||
short *alsa_wave_buffer;
|
||||
uint8_t *alsa_wave24_buffer;
|
||||
RDWaveFile *alsa_record_wave[RD_MAX_CARDS][RD_MAX_STREAMS];
|
||||
RDWaveFile *alsa_play_wave[RD_MAX_CARDS][RD_MAX_STREAMS];
|
||||
int alsa_offset[RD_MAX_CARDS][RD_MAX_STREAMS];
|
||||
@ -352,7 +317,7 @@ class MainObject : public QObject
|
||||
int alsa_fade_port[RD_MAX_CARDS][RD_MAX_STREAMS];
|
||||
unsigned alsa_samples_recorded[RD_MAX_CARDS][RD_MAX_STREAMS];
|
||||
#endif // ALSA
|
||||
|
||||
*/
|
||||
bool CheckLame();
|
||||
|
||||
//
|
||||
|
@ -2,9 +2,7 @@
|
||||
//
|
||||
// The ALSA Driver for the Core Audio Engine component of Rivendell
|
||||
//
|
||||
// (C) Copyright 2002-2004 Fred Gleason <fredg@paravelsystems.com>
|
||||
//
|
||||
// $Id: cae_alsa.cpp,v 1.48.6.5 2013/06/26 23:18:40 cvs Exp $
|
||||
// (C) Copyright 2002-2015 Fred Gleason <fredg@paravelsystems.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -19,7 +17,7 @@
|
||||
// License along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//
|
||||
|
||||
/*
|
||||
#include <math.h>
|
||||
#include <signal.h>
|
||||
|
||||
@ -717,6 +715,7 @@ void MainObject::alsaInit(RDStation *station)
|
||||
//
|
||||
AlsaInitCallback();
|
||||
alsa_wave_buffer=new int16_t[RINGBUFFER_SIZE];
|
||||
alsa_wave24_buffer=new uint8_t[RINGBUFFER_SIZE];
|
||||
//alsa_resample_buffer=new int16_t[2*RINGBUFFER_SIZE];
|
||||
|
||||
//
|
||||
@ -1527,11 +1526,11 @@ bool MainObject::AlsaStartCaptureDevice(QString &dev,int card,snd_pcm_t *pcm)
|
||||
// Start the Callback
|
||||
//
|
||||
pthread_attr_init(&pthread_attr);
|
||||
/*
|
||||
if(use_realtime) {
|
||||
pthread_attr_setschedpolicy(&pthread_attr,SCHED_FIFO);
|
||||
}
|
||||
*/
|
||||
|
||||
// if(use_realtime) {
|
||||
// pthread_attr_setschedpolicy(&pthread_attr,SCHED_FIFO);
|
||||
// }
|
||||
|
||||
alsa_capture_format[card].exiting = false;
|
||||
pthread_create(&alsa_capture_format[card].thread,&pthread_attr,
|
||||
AlsaCaptureCallback,&alsa_capture_format[card]);
|
||||
@ -1682,11 +1681,11 @@ bool MainObject::AlsaStartPlayDevice(QString &dev,int card,snd_pcm_t *pcm)
|
||||
// Start the Callback
|
||||
//
|
||||
pthread_attr_init(&pthread_attr);
|
||||
/*
|
||||
if(use_realtime) {
|
||||
pthread_attr_setschedpolicy(&pthread_attr,SCHED_FIFO);
|
||||
}
|
||||
*/
|
||||
|
||||
// if(use_realtime) {
|
||||
// pthread_attr_setschedpolicy(&pthread_attr,SCHED_FIFO);
|
||||
// }
|
||||
|
||||
alsa_play_format[card].exiting = false;
|
||||
pthread_create(&alsa_play_format[card].thread,&pthread_attr,
|
||||
AlsaPlayCallback,&alsa_play_format[card]);
|
||||
@ -1776,6 +1775,34 @@ void MainObject::FillAlsaOutputStream(int card,int stream)
|
||||
(double)alsa_play_wave[card][stream]->getSamplesPerSec();
|
||||
switch(alsa_play_wave[card][stream]->getFormatTag()) {
|
||||
case WAVE_FORMAT_PCM:
|
||||
switch(alsa_play_wave[card][stream]->getBitsPerSample()) {
|
||||
case 16: // PCM16
|
||||
free=(int)((double)free/ratio)/(2*alsa_output_channels[card][stream])*
|
||||
(2*alsa_output_channels[card][stream]);
|
||||
n=alsa_play_wave[card][stream]->readWave(alsa_wave_buffer,free);
|
||||
if(n!=free) {
|
||||
alsa_eof[card][stream]=true;
|
||||
alsa_stop_timer[card][stream]->stop();
|
||||
}
|
||||
break;
|
||||
|
||||
case 24: // PCM24
|
||||
free=(int)((double)free/ratio)/(6*alsa_output_channels[card][stream])*
|
||||
(6*alsa_output_channels[card][stream]);
|
||||
n=alsa_play_wave[card][stream]->readWave(alsa_wave24_buffer,free);
|
||||
for(int i=0;i<(n/3);i++) {
|
||||
for(int j=0;j<2;j++) {
|
||||
((uint8_t *)alsa_wave_buffer)[2*i+j]=alsa_wave24_buffer[3*i+j+1];
|
||||
}
|
||||
}
|
||||
if(n!=free) {
|
||||
alsa_eof[card][stream]=true;
|
||||
alsa_stop_timer[card][stream]->stop();
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_VORBIS:
|
||||
free=(int)((double)free/ratio)/(2*alsa_output_channels[card][stream])*
|
||||
(2*alsa_output_channels[card][stream]);
|
||||
@ -1880,3 +1907,4 @@ void MainObject::AlsaClock()
|
||||
}
|
||||
#endif // ALSA
|
||||
}
|
||||
*/
|
||||
|
541
cae/cae_hpi.cpp
541
cae/cae_hpi.cpp
@ -1,541 +0,0 @@
|
||||
// cae_hpi.cpp
|
||||
//
|
||||
// The HPI Driver for the Core Audio Engine component of Rivendell
|
||||
//
|
||||
// (C) Copyright 2002-2004 Fred Gleason <fredg@paravelsystems.com>
|
||||
//
|
||||
// $Id: cae_hpi.cpp,v 1.38.6.2 2012/11/30 16:14:58 cvs Exp $
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 <cae.h>
|
||||
|
||||
#include <rddebug.h>
|
||||
|
||||
void MainObject::hpiInit(RDStation *station)
|
||||
{
|
||||
#ifdef HPI
|
||||
for(int i=0;i<RD_MAX_CARDS;i++) {
|
||||
for(int j=0;j<RD_MAX_STREAMS;j++) {
|
||||
record[i][j]=NULL;
|
||||
play[i][j]=NULL;
|
||||
}
|
||||
}
|
||||
sound_card=new RDHPISoundCard(this,"sound_card");
|
||||
sound_card->setFadeProfile(RD_FADE_TYPE);
|
||||
for(int i=0;i<sound_card->getCardQuantity();i++) {
|
||||
cae_driver[i]=RDStation::Hpi;
|
||||
station->setCardDriver(i,RDStation::Hpi);
|
||||
station->setCardName(i,sound_card->getCardDescription(i));
|
||||
station->setCardInputs(i,sound_card->getCardInputPorts(i));
|
||||
station->setCardOutputs(i,sound_card->getCardOutputPorts(i));
|
||||
}
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
void MainObject::hpiFree()
|
||||
{
|
||||
#ifdef HPI
|
||||
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
QString MainObject::hpiVersion()
|
||||
{
|
||||
#ifdef HPI
|
||||
if(sound_card==NULL) {
|
||||
return QString("not active");
|
||||
}
|
||||
RDHPIInformation *info=sound_card->hpiInformation(0);
|
||||
if(info->hpiVersion()==0) {
|
||||
return QString("not active");
|
||||
}
|
||||
return QString().sprintf("%d.%02d.%02d",info->hpiMajorVersion(),
|
||||
info->hpiMinorVersion(),info->hpiPointVersion());
|
||||
#else
|
||||
return QString("not enabled");
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiLoadPlayback(int card,QString wavename,int *stream)
|
||||
{
|
||||
#ifdef HPI
|
||||
RDHPIPlayStream *playstream=new RDHPIPlayStream(sound_card);
|
||||
playstream->setCard(card);
|
||||
if(playstream->openWave(wavename)!=RDHPIPlayStream::Ok) {
|
||||
LogLine(RDConfig::LogNotice,QString().sprintf(
|
||||
"Error: hpiLoadPlayback(%s) openWave() failed to open file",
|
||||
(const char *) wavename) );
|
||||
delete playstream;
|
||||
return false;
|
||||
}
|
||||
*stream=playstream->getStream();
|
||||
play[card][*stream]=playstream;
|
||||
connect(play[card][*stream],SIGNAL(stateChanged(int,int,int)),
|
||||
this,SLOT(statePlayUpdate(int,int,int)));
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiUnloadPlayback(int card,int stream)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(play[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
if(play[card][stream]->getState()==RDHPIPlayStream::Playing) {
|
||||
play[card][stream]->pause();
|
||||
}
|
||||
play[card][stream]->disconnect();
|
||||
play[card][stream]->closeWave();
|
||||
delete play[card][stream];
|
||||
play[card][stream]=NULL;
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiPlaybackPosition(int card,int stream,unsigned pos)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(play[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
return play[card][stream]->
|
||||
setPosition((unsigned)((double)play[card][stream]->getSamplesPerSec()*
|
||||
(double)pos/1000.0));
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiPlay(int card,int stream,int length,int speed,bool pitch,
|
||||
bool rates)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(play[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
if(!play[card][stream]->setSpeed(speed,pitch,rates)) {
|
||||
return false;
|
||||
}
|
||||
play[card][stream]->setPlayLength(length);
|
||||
return play[card][stream]->play();
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiStopPlayback(int card,int stream)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(play[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
play[card][stream]->pause();
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiTimescaleSupported(int card)
|
||||
{
|
||||
#ifdef HPI
|
||||
return sound_card->haveTimescaling(card);
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiLoadRecord(int card,int stream,int coding,int chans,
|
||||
int samprate,int bitrate,QString wavename)
|
||||
{
|
||||
#ifdef HPI
|
||||
syslog(LOG_NOTICE,"card: %d coding: %d\n",card,coding);
|
||||
record[card][stream]=new RDHPIRecordStream(sound_card);
|
||||
connect(record[card][stream],SIGNAL(stateChanged(int,int,int)),
|
||||
this,SLOT(stateRecordUpdate(int,int,int)));
|
||||
record[card][stream]->setCard(card);
|
||||
record[card][stream]->setStream(stream);
|
||||
record[card][stream]->nameWave(wavename);
|
||||
record[card][stream]->setChannels(chans);
|
||||
record[card][stream]->setSamplesPerSec(samprate);
|
||||
if(coding==0) { // PCM16
|
||||
record[card][stream]->setFormatTag(WAVE_FORMAT_PCM);
|
||||
record[card][stream]->setBitsPerSample(16);
|
||||
}
|
||||
if((coding==1)||(coding==2)) { // MPEG-1
|
||||
record[card][stream]->setFormatTag(WAVE_FORMAT_MPEG);
|
||||
record[card][stream]->setHeadLayer(coding);
|
||||
record[card][stream]->setHeadBitRate(bitrate);
|
||||
record[card][stream]->setMextChunk(true);
|
||||
switch(chans) {
|
||||
case 1:
|
||||
record[card][stream]->setHeadMode(ACM_MPEG_SINGLECHANNEL);
|
||||
break;
|
||||
case 2:
|
||||
record[card][stream]->setHeadMode(ACM_MPEG_STEREO);
|
||||
break;
|
||||
default:
|
||||
delete record[card][stream];
|
||||
record[card][stream]=NULL;
|
||||
return false;
|
||||
}
|
||||
record[card][stream]->setHeadFlags(ACM_MPEG_ID_MPEG1);
|
||||
}
|
||||
if(coding==4) { // PCM24
|
||||
record[card][stream]->setFormatTag(WAVE_FORMAT_PCM);
|
||||
record[card][stream]->setBitsPerSample(24);
|
||||
}
|
||||
if(coding>4) {
|
||||
delete record[card][stream];
|
||||
record[card][stream]=NULL;
|
||||
return false;
|
||||
}
|
||||
record[card][stream]->setBextChunk(true);
|
||||
record[card][stream]->setCartChunk(true);
|
||||
record[card][stream]->setLevlChunk(true);
|
||||
if(record[card][stream]->createWave()!=RDHPIRecordStream::Ok) {
|
||||
delete record[card][stream];
|
||||
record[card][stream]=NULL;
|
||||
return false;
|
||||
}
|
||||
chown((const char *)wavename,rd_config->uid(),rd_config->gid());
|
||||
if(!record[card][stream]->recordReady()) {
|
||||
delete record[card][stream];
|
||||
record[card][stream]=NULL;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiUnloadRecord(int card,int stream,unsigned *len)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(record[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
if(record[card][stream]->getState()==RDHPIRecordStream::Recording) {
|
||||
record[card][stream]->pause();
|
||||
}
|
||||
record[card][stream]->disconnect();
|
||||
*len=record[card][stream]->samplesRecorded();
|
||||
record[card][stream]->closeWave();
|
||||
delete record[card][stream];
|
||||
record[card][stream]=NULL;
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiRecord(int card,int stream,int length,int thres)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(record[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
if(thres!=0) {
|
||||
if(record[card][stream]->haveInputVOX()) {
|
||||
record[card][stream]->setInputVOX(thres);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
record[card][stream]->setRecordLength(length);
|
||||
record[card][stream]->record();
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiStopRecord(int card,int stream)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(record[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
record[card][stream]->pause();
|
||||
record[card][stream]->setInputVOX(-10000);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiSetClockSource(int card,int src)
|
||||
{
|
||||
#ifdef HPI
|
||||
return sound_card->setClockSource(card,(RDHPISoundCard::ClockSource)src);
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiSetInputVolume(int card,int stream,int level)
|
||||
{
|
||||
#ifdef HPI
|
||||
sound_card->setInputVolume(card,stream,level);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiSetOutputVolume(int card,int stream,int port,int level)
|
||||
{
|
||||
#ifdef HPI
|
||||
sound_card->setOutputVolume(card,stream,port,level);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiFadeOutputVolume(int card,int stream,int port,int level,
|
||||
int length)
|
||||
{
|
||||
#ifdef HPI
|
||||
sound_card->fadeOutputVolume(card,stream,port,level,length);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiSetInputLevel(int card,int port,int level)
|
||||
{
|
||||
#ifdef HPI
|
||||
sound_card->setInputLevel(card,port,level);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiSetOutputLevel(int card,int port,int level)
|
||||
{
|
||||
#ifdef HPI
|
||||
sound_card->setOutputLevel(card,port,level);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiSetInputMode(int card,int stream,int mode)
|
||||
{
|
||||
#ifdef HPI
|
||||
switch(mode) {
|
||||
case 0:
|
||||
sound_card->setInputMode(card,stream,RDHPISoundCard::Normal);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
sound_card->setInputMode(card,stream,RDHPISoundCard::Swap);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
sound_card->setInputMode(card,stream,RDHPISoundCard::LeftOnly);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
sound_card->setInputMode(card,stream,RDHPISoundCard::RightOnly);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiSetOutputMode(int card,int stream,int mode)
|
||||
{
|
||||
#ifdef HPI
|
||||
switch(mode) {
|
||||
case 0:
|
||||
sound_card->setOutputMode(card,stream,RDHPISoundCard::Normal);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
sound_card->setOutputMode(card,stream,RDHPISoundCard::Swap);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
sound_card->setOutputMode(card,stream,RDHPISoundCard::LeftOnly);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
sound_card->setOutputMode(card,stream,RDHPISoundCard::RightOnly);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiSetInputVoxLevel(int card,int stream,int level)
|
||||
{
|
||||
#ifdef HPI
|
||||
sound_card->setInputStreamVOX(card,stream,level);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiSetInputType(int card,int port,int type)
|
||||
{
|
||||
#ifdef HPI
|
||||
switch(type) {
|
||||
case 0:
|
||||
sound_card->setInputPortMux(card,port,RDHPISoundCard::LineIn);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
sound_card->setInputPortMux(card,port,RDHPISoundCard::AesEbuIn);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiGetInputStatus(int card,int port)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(sound_card->getInputPortError(card,port)!=0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiGetInputMeters(int card,int port,short levels[2])
|
||||
{
|
||||
#ifdef HPI
|
||||
return sound_card->inputStreamMeter(card,port,levels);
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiGetOutputMeters(int card,int port,short levels[2])
|
||||
{
|
||||
#ifdef HPI
|
||||
if(rd_config->useStreamMeters()) {
|
||||
//
|
||||
// This is UGLY, but needed to semi-support cards (like the ASI4215)
|
||||
// that lack output port metering.
|
||||
//
|
||||
for(int i=0;i<RD_MAX_STREAMS;i++) {
|
||||
if(sound_card->getOutputVolume(card,i,port)>-10000) {
|
||||
return sound_card->outputStreamMeter(card,i,levels);
|
||||
}
|
||||
}
|
||||
levels[0]=-10000;
|
||||
levels[1]=-10000;
|
||||
return true;
|
||||
}
|
||||
return sound_card->outputPortMeter(card,port,levels);
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiGetStreamOutputMeters(int card,int stream,short levels[2])
|
||||
{
|
||||
#ifdef HPI
|
||||
return sound_card->outputStreamMeter(card,stream,levels);
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
void MainObject::hpiGetOutputPosition(int card,unsigned *pos)
|
||||
{
|
||||
#ifdef HPI
|
||||
for(int i=0;i<RD_MAX_STREAMS;i++) {
|
||||
if(play[card][i]==NULL) {
|
||||
pos[i]=0;
|
||||
}
|
||||
else {
|
||||
pos[i]=1000*(unsigned long long)play[card][i]->currentPosition()/
|
||||
play[card][i]->getSamplesPerSec();
|
||||
}
|
||||
}
|
||||
#endif // HPI
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool MainObject::hpiSetPassthroughLevel(int card,int in_port,int out_port,
|
||||
int level)
|
||||
{
|
||||
#ifdef HPI
|
||||
return sound_card->setPassthroughVolume(card,in_port,out_port,level);
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
// License along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//
|
||||
|
||||
/*
|
||||
#include <math.h>
|
||||
|
||||
#include <samplerate.h>
|
||||
@ -961,29 +961,6 @@ bool MainObject::jackLoadRecord(int card,int stream,int coding,int chans,
|
||||
jack_ready[stream]=true;
|
||||
return true;
|
||||
|
||||
/*
|
||||
if ((stream <0) || (stream >=RD_MAX_PORTS)){
|
||||
return false;
|
||||
}
|
||||
jack_record_wave[stream]=new RDWaveFile(wavename);
|
||||
jack_record_wave[stream]->setFormatTag(WAVE_FORMAT_PCM);
|
||||
jack_record_wave[stream]->setChannels(chans);
|
||||
jack_record_wave[stream]->setSamplesPerSec(samprate);
|
||||
jack_record_wave[stream]->setBitsPerSample(16);
|
||||
jack_record_wave[stream]->setBextChunk(true);
|
||||
jack_record_wave[stream]->setLevlChunk(true);
|
||||
if(!jack_record_wave[stream]->createWave()) {
|
||||
delete jack_record_wave[stream];
|
||||
jack_record_wave[stream]=NULL;
|
||||
return false;
|
||||
}
|
||||
chown((const char *)wavename,rd_config->uid(),rd_config->gid());
|
||||
jack_input_channels[stream]=chans;
|
||||
jack_record_ring[stream]=new RDRingBuffer(RINGBUFFER_SIZE);
|
||||
jack_record_ring[stream]->reset();
|
||||
jack_ready[stream]=true;
|
||||
return true;
|
||||
*/
|
||||
#else
|
||||
return false;
|
||||
#endif // JACK
|
||||
@ -1656,3 +1633,5 @@ void MainObject::JackSessionSetup()
|
||||
delete profile;
|
||||
#endif // JACK
|
||||
}
|
||||
|
||||
*/
|
||||
|
123
cae/driver.cpp
Normal file
123
cae/driver.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
// driver.cpp
|
||||
//
|
||||
// Abstract base class for CAE audio drivers.
|
||||
//
|
||||
// (C) Copyright 2002-2015 Fred Gleason <fredg@paravelsystems.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <qdatetime.h>
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
Driver::Driver(Driver::Type type,RDStation *station,RDConfig *config,
|
||||
QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
dvr_type=type;
|
||||
dvr_station=station;
|
||||
dvr_config=config;
|
||||
}
|
||||
|
||||
|
||||
Driver::~Driver()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Driver::Type Driver::type() const
|
||||
{
|
||||
return dvr_type;
|
||||
}
|
||||
|
||||
|
||||
bool Driver::connectPorts(const QString &out,const QString &in)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Driver::disconnectPorts(const QString &out,const QString &in)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
RDStation *Driver::station()
|
||||
{
|
||||
return dvr_station;
|
||||
}
|
||||
|
||||
|
||||
RDConfig *Driver::config()
|
||||
{
|
||||
return dvr_config;
|
||||
}
|
||||
|
||||
|
||||
void Driver::logLine(RDConfig::LogPriority prio,const QString &line)
|
||||
{
|
||||
FILE *file;
|
||||
|
||||
dvr_config->log("caed",prio,line);
|
||||
|
||||
if(dvr_config->caeLogfile().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QDateTime current=QDateTime::currentDateTime();
|
||||
|
||||
file=fopen(dvr_config->caeLogfile(),"a");
|
||||
if(file==NULL) {
|
||||
return;
|
||||
}
|
||||
chmod(dvr_config->caeLogfile(),S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
|
||||
fprintf(file,"%02d/%02d/%4d - %02d:%02d:%02d.%03d : %s\n",
|
||||
current.date().month(),
|
||||
current.date().day(),
|
||||
current.date().year(),
|
||||
current.time().hour(),
|
||||
current.time().minute(),
|
||||
current.time().second(),
|
||||
current.time().msec(),
|
||||
(const char *)line);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
|
||||
QString Driver::typeText(Driver::Type type)
|
||||
{
|
||||
QString ret=tr("Unknown");
|
||||
|
||||
switch(type) {
|
||||
case Driver::Alsa:
|
||||
ret=tr("Advanced Linux Sound Architecture");
|
||||
break;
|
||||
|
||||
case Driver::Hpi:
|
||||
ret=tr("AudioScience HPI");
|
||||
break;
|
||||
|
||||
case Driver::Jack:
|
||||
ret=tr("JACK Audio Connection Kit");
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
94
cae/driver.h
Normal file
94
cae/driver.h
Normal file
@ -0,0 +1,94 @@
|
||||
// driver.h
|
||||
//
|
||||
// Abstract base class for CAE audio drivers.
|
||||
//
|
||||
// (C) Copyright 2002-2015 Fred Gleason <fredg@paravelsystems.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#ifndef DRIVER_H
|
||||
#define DRIVER_H
|
||||
|
||||
#include <qstring.h>
|
||||
#include <qobject.h>
|
||||
|
||||
#include <rdconfig.h>
|
||||
#include <rdstation.h>
|
||||
|
||||
class Driver : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Type {Hpi=0,Alsa=1,Jack=2};
|
||||
Driver(Driver::Type type,RDStation *station,RDConfig *config,
|
||||
QObject *parent=0);
|
||||
~Driver();
|
||||
Driver::Type type() const;
|
||||
virtual QString version()=0;
|
||||
virtual QString cardName(int card)=0;
|
||||
virtual int inputs(int card)=0;
|
||||
virtual int outputs(int card)=0;
|
||||
virtual int start(int next_card)=0;
|
||||
virtual bool loadPlayback(int card,QString wavename,int *stream)=0;
|
||||
virtual bool unloadPlayback(int card,int stream)=0;
|
||||
virtual bool playbackPosition(int card,int stream,unsigned pos)=0;
|
||||
virtual bool play(int card,int stream,int length,int speed,bool pitch,
|
||||
bool rates)=0;
|
||||
virtual bool stopPlayback(int card,int stream)=0;
|
||||
virtual bool timescaleSupported(int card)=0;
|
||||
virtual bool loadRecord(int card,int stream,int coding,int chans,int samprate,
|
||||
int bitrate,QString wavename)=0;
|
||||
virtual bool record(int card,int stream,int length,int thres)=0;
|
||||
virtual bool stopRecord(int card,int stream)=0;
|
||||
virtual bool unloadRecord(int card,int stream,unsigned *len)=0;
|
||||
virtual bool setClockSource(int card,int src)=0;
|
||||
virtual bool setInputVolume(int card,int stream,int level)=0;
|
||||
virtual bool setOutputVolume(int card,int stream,int port,int level)=0;
|
||||
virtual bool fadeOutputVolume(int card,int stream,int port,int level,
|
||||
int length)=0;
|
||||
virtual bool setInputLevel(int card,int port,int level)=0;
|
||||
virtual bool setOutputLevel(int card,int port,int level)=0;
|
||||
virtual bool setInputMode(int card,int stream,int mode)=0;
|
||||
virtual bool setOutputMode(int card,int stream,int mode)=0;
|
||||
virtual bool setInputVoxLevel(int card,int stream,int level)=0;
|
||||
virtual bool setInputType(int card,int port,int type)=0;
|
||||
virtual bool getInputStatus(int card,int port)=0;
|
||||
virtual bool getInputMeters(int card,int port,short levels[2])=0;
|
||||
virtual bool getOutputMeters(int card,int port,short levels[2])=0;
|
||||
virtual bool getStreamOutputMeters(int card,int stream,short levels[2])=0;
|
||||
virtual bool setPassthroughLevel(int card,int in_port,int out_port,
|
||||
int level)=0;
|
||||
virtual void getOutputPosition(int card,unsigned *pos)=0;
|
||||
virtual bool connectPorts(const QString &out,const QString &in);
|
||||
virtual bool disconnectPorts(const QString &out,const QString &in);
|
||||
static QString typeText(Driver::Type type);
|
||||
|
||||
signals:
|
||||
void playStateChanged(int card,int stream,int state);
|
||||
void recordStateChanged(int card,int stream,int state);
|
||||
|
||||
protected:
|
||||
RDStation *station();
|
||||
RDConfig *config();
|
||||
void logLine(RDConfig::LogPriority prio,const QString &line);
|
||||
|
||||
private:
|
||||
Driver::Type dvr_type;
|
||||
RDStation *dvr_station;
|
||||
RDConfig *dvr_config;
|
||||
};
|
||||
|
||||
|
||||
#endif // DRIVER_H
|
566
cae/driver_hpi.cpp
Normal file
566
cae/driver_hpi.cpp
Normal file
@ -0,0 +1,566 @@
|
||||
// driver_hpi.cpp
|
||||
//
|
||||
// CAE driver for AudioScience HPI devices
|
||||
//
|
||||
// (C) Copyright 2002-2015 Fred Gleason <fredg@paravelsystems.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 "driver_hpi.h"
|
||||
|
||||
HpiDriver::HpiDriver(RDStation *station,RDConfig *config,QObject *parent)
|
||||
: Driver(Driver::Hpi,station,config,parent)
|
||||
{
|
||||
#ifdef HPI
|
||||
for(int i=0;i<RD_MAX_CARDS;i++) {
|
||||
for(int j=0;j<RD_MAX_STREAMS;j++) {
|
||||
hpi_record[i][j]=NULL;
|
||||
hpi_play[i][j]=NULL;
|
||||
}
|
||||
}
|
||||
hpi_sound_card=new RDHPISoundCard(this);
|
||||
hpi_sound_card->setFadeProfile(RD_FADE_TYPE);
|
||||
for(int i=0;i<hpi_sound_card->getCardQuantity();i++) {
|
||||
station->setCardDriver(i,RDStation::Hpi);
|
||||
station->setCardName(i,hpi_sound_card->getCardDescription(i));
|
||||
station->setCardInputs(i,hpi_sound_card->getCardInputPorts(i));
|
||||
station->setCardOutputs(i,hpi_sound_card->getCardOutputPorts(i));
|
||||
}
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
HpiDriver::~HpiDriver()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QString HpiDriver::version()
|
||||
{
|
||||
#ifdef HPI
|
||||
if(hpi_sound_card==NULL) {
|
||||
return QString("not active");
|
||||
}
|
||||
RDHPIInformation *info=hpi_sound_card->hpiInformation(0);
|
||||
if(info->hpiVersion()==0) {
|
||||
return QString("not active");
|
||||
}
|
||||
return QString().sprintf("%d.%02d.%02d",info->hpiMajorVersion(),
|
||||
info->hpiMinorVersion(),info->hpiPointVersion());
|
||||
#else
|
||||
return QString("not enabled");
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
QString HpiDriver::cardName(int card)
|
||||
{
|
||||
return hpi_sound_card->getCardDescription(card);
|
||||
}
|
||||
|
||||
|
||||
int HpiDriver::inputs(int card)
|
||||
{
|
||||
return hpi_sound_card->getCardInputPorts(card);
|
||||
}
|
||||
|
||||
|
||||
int HpiDriver::outputs(int card)
|
||||
{
|
||||
return hpi_sound_card->getCardOutputPorts(card);
|
||||
}
|
||||
|
||||
|
||||
int HpiDriver::start(int next_card)
|
||||
{
|
||||
return hpi_sound_card->getCardQuantity();
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::loadPlayback(int card,QString wavename,int *stream)
|
||||
{
|
||||
#ifdef HPI
|
||||
RDHPIPlayStream *playstream=new RDHPIPlayStream(hpi_sound_card);
|
||||
playstream->setCard(card);
|
||||
if(playstream->openWave(wavename)!=RDHPIPlayStream::Ok) {
|
||||
logLine(RDConfig::LogNotice,QString().sprintf(
|
||||
"Error: hpiLoadPlayback(%s) openWave() failed to open file",
|
||||
(const char *) wavename) );
|
||||
delete playstream;
|
||||
return false;
|
||||
}
|
||||
*stream=playstream->getStream();
|
||||
hpi_play[card][*stream]=playstream;
|
||||
connect(hpi_play[card][*stream],SIGNAL(stateChanged(int,int,int)),
|
||||
this,SLOT(playStateChangedData(int,int,int)));
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::unloadPlayback(int card,int stream)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(hpi_play[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
if(hpi_play[card][stream]->getState()==RDHPIPlayStream::Playing) {
|
||||
hpi_play[card][stream]->pause();
|
||||
}
|
||||
hpi_play[card][stream]->disconnect();
|
||||
hpi_play[card][stream]->closeWave();
|
||||
delete hpi_play[card][stream];
|
||||
hpi_play[card][stream]=NULL;
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::playbackPosition(int card,int stream,unsigned pos)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(hpi_play[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
return hpi_play[card][stream]->
|
||||
setPosition((unsigned)((double)hpi_play[card][stream]->getSamplesPerSec()*
|
||||
(double)pos/1000.0));
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::play(int card,int stream,int length,int speed,bool pitch,
|
||||
bool rates)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(hpi_play[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
if(!hpi_play[card][stream]->setSpeed(speed,pitch,rates)) {
|
||||
return false;
|
||||
}
|
||||
hpi_play[card][stream]->setPlayLength(length);
|
||||
return hpi_play[card][stream]->play();
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::stopPlayback(int card,int stream)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(hpi_play[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
hpi_play[card][stream]->pause();
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::timescaleSupported(int card)
|
||||
{
|
||||
#ifdef HPI
|
||||
return hpi_sound_card->haveTimescaling(card);
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::loadRecord(int card,int stream,int coding,int chans,
|
||||
int samprate,int bitrate,QString wavename)
|
||||
{
|
||||
#ifdef HPI
|
||||
hpi_record[card][stream]=new RDHPIRecordStream(hpi_sound_card);
|
||||
connect(hpi_record[card][stream],SIGNAL(stateChanged(int,int,int)),
|
||||
this,SLOT(recordStateChangedData(int,int,int)));
|
||||
hpi_record[card][stream]->setCard(card);
|
||||
hpi_record[card][stream]->setStream(stream);
|
||||
hpi_record[card][stream]->nameWave(wavename);
|
||||
hpi_record[card][stream]->setChannels(chans);
|
||||
hpi_record[card][stream]->setSamplesPerSec(samprate);
|
||||
if(coding==0) { // PCM16
|
||||
hpi_record[card][stream]->setFormatTag(WAVE_FORMAT_PCM);
|
||||
hpi_record[card][stream]->setBitsPerSample(16);
|
||||
}
|
||||
if((coding==1)||(coding==2)) { // MPEG-1
|
||||
hpi_record[card][stream]->setFormatTag(WAVE_FORMAT_MPEG);
|
||||
hpi_record[card][stream]->setHeadLayer(coding);
|
||||
hpi_record[card][stream]->setHeadBitRate(bitrate);
|
||||
hpi_record[card][stream]->setMextChunk(true);
|
||||
switch(chans) {
|
||||
case 1:
|
||||
hpi_record[card][stream]->setHeadMode(ACM_MPEG_SINGLECHANNEL);
|
||||
break;
|
||||
case 2:
|
||||
hpi_record[card][stream]->setHeadMode(ACM_MPEG_STEREO);
|
||||
break;
|
||||
default:
|
||||
delete hpi_record[card][stream];
|
||||
hpi_record[card][stream]=NULL;
|
||||
return false;
|
||||
}
|
||||
hpi_record[card][stream]->setHeadFlags(ACM_MPEG_ID_MPEG1);
|
||||
}
|
||||
if(coding==4) { // PCM24
|
||||
hpi_record[card][stream]->setFormatTag(WAVE_FORMAT_PCM);
|
||||
hpi_record[card][stream]->setBitsPerSample(24);
|
||||
}
|
||||
if(coding>4) {
|
||||
delete hpi_record[card][stream];
|
||||
hpi_record[card][stream]=NULL;
|
||||
return false;
|
||||
}
|
||||
hpi_record[card][stream]->setBextChunk(true);
|
||||
hpi_record[card][stream]->setCartChunk(true);
|
||||
hpi_record[card][stream]->setLevlChunk(true);
|
||||
if(hpi_record[card][stream]->createWave()!=RDHPIRecordStream::Ok) {
|
||||
delete hpi_record[card][stream];
|
||||
hpi_record[card][stream]=NULL;
|
||||
return false;
|
||||
}
|
||||
chown((const char *)wavename,config()->uid(),config()->gid());
|
||||
if(!hpi_record[card][stream]->recordReady()) {
|
||||
delete hpi_record[card][stream];
|
||||
hpi_record[card][stream]=NULL;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::record(int card,int stream,int length,int thres)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(hpi_record[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
if(thres!=0) {
|
||||
if(hpi_record[card][stream]->haveInputVOX()) {
|
||||
hpi_record[card][stream]->setInputVOX(thres);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
hpi_record[card][stream]->setRecordLength(length);
|
||||
hpi_record[card][stream]->record();
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::stopRecord(int card,int stream)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(hpi_record[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
hpi_record[card][stream]->pause();
|
||||
hpi_record[card][stream]->setInputVOX(-10000);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::unloadRecord(int card,int stream,unsigned *len)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(hpi_record[card][stream]==NULL) {
|
||||
return false;
|
||||
}
|
||||
if(hpi_record[card][stream]->getState()==RDHPIRecordStream::Recording) {
|
||||
hpi_record[card][stream]->pause();
|
||||
}
|
||||
hpi_record[card][stream]->disconnect();
|
||||
*len=hpi_record[card][stream]->samplesRecorded();
|
||||
hpi_record[card][stream]->closeWave();
|
||||
delete hpi_record[card][stream];
|
||||
hpi_record[card][stream]=NULL;
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::setClockSource(int card,int src)
|
||||
{
|
||||
#ifdef HPI
|
||||
return hpi_sound_card->setClockSource(card,(RDHPISoundCard::ClockSource)src);
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::setInputVolume(int card,int stream,int level)
|
||||
{
|
||||
#ifdef HPI
|
||||
hpi_sound_card->setInputVolume(card,stream,level);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::setOutputVolume(int card,int stream,int port,int level)
|
||||
{
|
||||
#ifdef HPI
|
||||
hpi_sound_card->setOutputVolume(card,stream,port,level);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::fadeOutputVolume(int card,int stream,int port,int level,int length)
|
||||
{
|
||||
#ifdef HPI
|
||||
hpi_sound_card->fadeOutputVolume(card,stream,port,level,length);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::setInputLevel(int card,int port,int level)
|
||||
{
|
||||
#ifdef HPI
|
||||
hpi_sound_card->setInputLevel(card,port,level);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::setOutputLevel(int card,int port,int level)
|
||||
{
|
||||
#ifdef HPI
|
||||
hpi_sound_card->setOutputLevel(card,port,level);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::setInputMode(int card,int stream,int mode)
|
||||
{
|
||||
#ifdef HPI
|
||||
switch(mode) {
|
||||
case 0:
|
||||
hpi_sound_card->setInputMode(card,stream,RDHPISoundCard::Normal);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
hpi_sound_card->setInputMode(card,stream,RDHPISoundCard::Swap);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
hpi_sound_card->setInputMode(card,stream,RDHPISoundCard::LeftOnly);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
hpi_sound_card->setInputMode(card,stream,RDHPISoundCard::RightOnly);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::setOutputMode(int card,int stream,int mode)
|
||||
{
|
||||
#ifdef HPI
|
||||
switch(mode) {
|
||||
case 0:
|
||||
hpi_sound_card->setOutputMode(card,stream,RDHPISoundCard::Normal);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
hpi_sound_card->setOutputMode(card,stream,RDHPISoundCard::Swap);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
hpi_sound_card->setOutputMode(card,stream,RDHPISoundCard::LeftOnly);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
hpi_sound_card->setOutputMode(card,stream,RDHPISoundCard::RightOnly);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::setInputVoxLevel(int card,int stream,int level)
|
||||
{
|
||||
#ifdef HPI
|
||||
hpi_sound_card->setInputStreamVOX(card,stream,level);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::setInputType(int card,int port,int type)
|
||||
{
|
||||
#ifdef HPI
|
||||
switch(type) {
|
||||
case 0:
|
||||
hpi_sound_card->setInputPortMux(card,port,RDHPISoundCard::LineIn);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
hpi_sound_card->setInputPortMux(card,port,RDHPISoundCard::AesEbuIn);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::getInputStatus(int card,int port)
|
||||
{
|
||||
#ifdef HPI
|
||||
if(hpi_sound_card->getInputPortError(card,port)!=0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::getInputMeters(int card,int port,short levels[2])
|
||||
{
|
||||
#ifdef HPI
|
||||
return hpi_sound_card->inputStreamMeter(card,port,levels);
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::getOutputMeters(int card,int port,short levels[2])
|
||||
{
|
||||
#ifdef HPI
|
||||
if(config()->useStreamMeters()) {
|
||||
//
|
||||
// This is UGLY, but needed to semi-support cards (like the ASI4215)
|
||||
// that lack output port metering.
|
||||
//
|
||||
for(int i=0;i<RD_MAX_STREAMS;i++) {
|
||||
if(hpi_sound_card->getOutputVolume(card,i,port)>-10000) {
|
||||
return hpi_sound_card->outputStreamMeter(card,i,levels);
|
||||
}
|
||||
}
|
||||
levels[0]=-10000;
|
||||
levels[1]=-10000;
|
||||
return true;
|
||||
}
|
||||
return hpi_sound_card->outputPortMeter(card,port,levels);
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::getStreamOutputMeters(int card,int stream,short levels[2])
|
||||
{
|
||||
#ifdef HPI
|
||||
return hpi_sound_card->outputStreamMeter(card,stream,levels);
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
bool HpiDriver::setPassthroughLevel(int card,int in_port,int out_port,int level)
|
||||
{
|
||||
#ifdef HPI
|
||||
return hpi_sound_card->setPassthroughVolume(card,in_port,out_port,level);
|
||||
#else
|
||||
return false;
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
void HpiDriver::getOutputPosition(int card,unsigned *pos)
|
||||
{
|
||||
#ifdef HPI
|
||||
for(int i=0;i<RD_MAX_STREAMS;i++) {
|
||||
if(hpi_play[card][i]==NULL) {
|
||||
pos[i]=0;
|
||||
}
|
||||
else {
|
||||
pos[i]=1000*(unsigned long long)hpi_play[card][i]->currentPosition()/
|
||||
hpi_play[card][i]->getSamplesPerSec();
|
||||
}
|
||||
}
|
||||
#endif // HPI
|
||||
}
|
||||
|
||||
|
||||
void HpiDriver::playStateChangedData(int card,int stream,int state)
|
||||
{
|
||||
emit playStateChanged(card,stream,state);
|
||||
}
|
||||
|
||||
|
||||
void HpiDriver::recordStateChangedData(int card,int stream,int state)
|
||||
{
|
||||
emit recordStateChanged(card,stream,state);
|
||||
}
|
86
cae/driver_hpi.h
Normal file
86
cae/driver_hpi.h
Normal file
@ -0,0 +1,86 @@
|
||||
// driver_hpi.h
|
||||
//
|
||||
// CAE driver for AudioScience HPI devices
|
||||
//
|
||||
// (C) Copyright 2002-2015 Fred Gleason <fredg@paravelsystems.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#ifndef DRIVER_HPI_H
|
||||
#define DRIVER_HPI_H
|
||||
|
||||
#include <rd.h>
|
||||
|
||||
#ifdef HPI
|
||||
#include <rdhpiplaystream.h>
|
||||
#include <rdhpirecordstream.h>
|
||||
#include <rdhpisoundcard.h>
|
||||
#endif // HPI
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
class HpiDriver : public Driver
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
HpiDriver(RDStation *station,RDConfig *config,QObject *parent=0);
|
||||
~HpiDriver();
|
||||
QString version();
|
||||
QString cardName(int card);
|
||||
int inputs(int card);
|
||||
int outputs(int card);
|
||||
int start(int next_card);
|
||||
bool loadPlayback(int card,QString wavename,int *stream);
|
||||
bool unloadPlayback(int card,int stream);
|
||||
bool playbackPosition(int card,int stream,unsigned pos);
|
||||
bool play(int card,int stream,int length,int speed,bool pitch,bool rates);
|
||||
bool stopPlayback(int card,int stream);
|
||||
bool timescaleSupported(int card);
|
||||
bool loadRecord(int card,int stream,int coding,int chans,int samprate,
|
||||
int bitrate,QString wavename);
|
||||
bool record(int card,int stream,int length,int thres);
|
||||
bool stopRecord(int card,int stream);
|
||||
bool unloadRecord(int card,int stream,unsigned *len);
|
||||
bool setClockSource(int card,int src);
|
||||
bool setInputVolume(int card,int stream,int level);
|
||||
bool setOutputVolume(int card,int stream,int port,int level);
|
||||
bool fadeOutputVolume(int card,int stream,int port,int level,int length);
|
||||
bool setInputLevel(int card,int port,int level);
|
||||
bool setOutputLevel(int card,int port,int level);
|
||||
bool setInputMode(int card,int stream,int mode);
|
||||
bool setOutputMode(int card,int stream,int mode);
|
||||
bool setInputVoxLevel(int card,int stream,int level);
|
||||
bool setInputType(int card,int port,int type);
|
||||
bool getInputStatus(int card,int port);
|
||||
bool getInputMeters(int card,int port,short levels[2]);
|
||||
bool getOutputMeters(int card,int port,short levels[2]);
|
||||
bool getStreamOutputMeters(int card,int stream,short levels[2]);
|
||||
bool setPassthroughLevel(int card,int in_port,int out_port,int level);
|
||||
void getOutputPosition(int card,unsigned *pos);
|
||||
|
||||
private slots:
|
||||
void playStateChangedData(int card,int stream,int state);
|
||||
void recordStateChangedData(int card,int stream,int state);
|
||||
|
||||
private:
|
||||
#ifdef HPI
|
||||
RDHPISoundCard *hpi_sound_card;
|
||||
RDHPIRecordStream *hpi_record[RD_MAX_CARDS][RD_MAX_STREAMS];
|
||||
RDHPIPlayStream *hpi_play[RD_MAX_CARDS][RD_MAX_STREAMS];
|
||||
#endif // HPI
|
||||
};
|
||||
|
||||
|
||||
#endif // DRIVER_HPI_H
|
45
cae/driverfactory.cpp
Normal file
45
cae/driverfactory.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
// driverfactory.cpp
|
||||
//
|
||||
// Instantiate CAE audio drivers
|
||||
//
|
||||
// (C) Copyright 2015 Fred Gleason <fredg@paravelsystems.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 "driver_hpi.h"
|
||||
|
||||
#include "driverfactory.h"
|
||||
|
||||
Driver *DriverFactory(Driver::Type type,RDStation *station,RDConfig *config,
|
||||
QObject *parent)
|
||||
{
|
||||
Driver *dvr=NULL;
|
||||
|
||||
switch(type) {
|
||||
case Driver::Alsa:
|
||||
break;
|
||||
|
||||
case Driver::Hpi:
|
||||
#ifdef HPI
|
||||
dvr=new HpiDriver(station,config,parent);
|
||||
#endif // HPI
|
||||
break;
|
||||
|
||||
case Driver::Jack:
|
||||
break;
|
||||
}
|
||||
|
||||
return dvr;
|
||||
}
|
30
cae/driverfactory.h
Normal file
30
cae/driverfactory.h
Normal file
@ -0,0 +1,30 @@
|
||||
// driverfactory.h
|
||||
//
|
||||
// Instantiate CAE audio drivers
|
||||
//
|
||||
// (C) Copyright 2015 Fred Gleason <fredg@paravelsystems.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#ifndef DRIVERFACTORY_H
|
||||
#define DRIVERFACTORY_H
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
Driver *DriverFactory(Driver::Type type,RDStation *station,RDConfig *config,
|
||||
QObject *parent=0);
|
||||
|
||||
|
||||
#endif // DRIVERFACTORY_H
|
Loading…
x
Reference in New Issue
Block a user