// rdhpirecordstream.cpp // // A class for recording Microsoft WAV files. // // (C) Copyright 2002-2019 Fred Gleason // // 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 #include #include #include #include #include #include #include #include #include #include #include #include "rdhpirecordstream.h" RDHPIRecordStream::RDHPIRecordStream(RDHPISoundCard *card,QWidget *parent) :QObject(parent),RDWaveFile() { int quan; uint16_t type[HPI_MAX_ADAPTERS]; struct hpi_format fmt; uint32_t dma_size=0; if(getenv(DEBUG_VAR)==NULL) { debug=false; } else { debug=true; printf("RDHPIRecordStream: debugging enabled\n"); } if(getenv(XRUN_VAR)==NULL) { xrun=false; } else { xrun=true; printf("RDHPIRecordStream: xrun notification enabled\n"); } sound_card=card; card_number=-1; stream_number=-1; is_ready=false; is_recording=false; is_paused=false; stopping=false; record_started=false; record_length=0; is_open=false; pdata=NULL; // // Get Adapter Indices // #if HPI_VER < 0x00030600 for(unsigned i=0;igetCardInputStreams(card_number);i++) { if(LogHpi(HPI_InStreamOpen(NULL,card_index[card_number],i,&histream), __LINE__)==0) { found=true; break; } } if(!found) { return false; } if(HPI_InStreamHostBufferAllocate(NULL,histream,dma_buffer_size)); } else { histream=hpi_stream; } switch(format) { case RDWaveFile::Pcm8: LogHpi(HPI_FormatCreate(&hformat,getChannels(),HPI_FORMAT_PCM8_UNSIGNED, getSamplesPerSec(),getHeadBitRate(),0),__LINE__); state=LogHpi(HPI_InStreamQueryFormat(NULL,histream,&hformat),__LINE__); break; case RDWaveFile::Pcm16: LogHpi(HPI_FormatCreate(&hformat,getChannels(),HPI_FORMAT_PCM16_SIGNED, getSamplesPerSec(),getHeadBitRate(),0),__LINE__); state=LogHpi(HPI_InStreamQueryFormat(NULL,histream,&hformat),__LINE__); break; case RDWaveFile::Pcm24: LogHpi(HPI_FormatCreate(&hformat,getChannels(),HPI_FORMAT_PCM24_SIGNED, getSamplesPerSec(),getHeadBitRate(),0),__LINE__); state=LogHpi(HPI_InStreamQueryFormat(NULL,histream,&hformat),__LINE__); break; case RDWaveFile::MpegL1: LogHpi(HPI_FormatCreate(&hformat,getChannels(),HPI_FORMAT_MPEG_L1, getSamplesPerSec(),getHeadBitRate(),0),__LINE__); state=LogHpi(HPI_InStreamQueryFormat(NULL,histream,&hformat),__LINE__); break; case RDWaveFile::MpegL2: LogHpi(HPI_FormatCreate(&hformat,getChannels(),HPI_FORMAT_MPEG_L2, getSamplesPerSec(),getHeadBitRate(),0),__LINE__); state=LogHpi(HPI_InStreamQueryFormat(NULL,histream,&hformat),__LINE__); break; case RDWaveFile::MpegL3: LogHpi(HPI_FormatCreate(&hformat,getChannels(),HPI_FORMAT_MPEG_L3, getSamplesPerSec(),getHeadBitRate(),0),__LINE__); state=LogHpi(HPI_InStreamQueryFormat(NULL,histream,&hformat),__LINE__); break; default: state=1; break; } if(!is_open) { if(HPI_InStreamHostBufferFree(NULL,histream)); LogHpi(HPI_InStreamClose(NULL,histream),__LINE__); } if(state!=0) { return false; } return true; } bool RDHPIRecordStream::formatSupported() { switch(getFormatTag()) { case WAVE_FORMAT_PCM: switch(getBitsPerSample()) { case 8: return formatSupported(RDWaveFile::Pcm8); break; case 16: return formatSupported(RDWaveFile::Pcm16); break; case 24: return formatSupported(RDWaveFile::Pcm24); break; default: return false; } break; case WAVE_FORMAT_MPEG: switch(getHeadLayer()) { case 1: return formatSupported(RDWaveFile::MpegL1); break; case 2: return formatSupported(RDWaveFile::MpegL2); break; case 3: return formatSupported(RDWaveFile::MpegL3); break; default: return false; } break; default: return false; } } int RDHPIRecordStream::getCard() const { return card_number; } void RDHPIRecordStream::setCard(int card) { if(!is_recording) { card_number=card; if(debug) { printf("RDHPIRecordStream: using card %d\n",card_number); } } } int RDHPIRecordStream::getStream() const { return stream_number; } void RDHPIRecordStream::setStream(int stream) { stream_number=stream; } bool RDHPIRecordStream::haveInputVOX() const { return sound_card->haveInputStreamVOX(card_number,stream_number); } RDHPIRecordStream::RecordState RDHPIRecordStream::getState() { if(is_recording) { if(record_started) { return RDHPIRecordStream::RecordStarted; } return RDHPIRecordStream::Recording; } if(is_paused) { return RDHPIRecordStream::Paused; } if(is_ready) { return RDHPIRecordStream::RecordReady; } return RDHPIRecordStream::Stopped; } int RDHPIRecordStream::getPosition() const { if((!is_recording)&&(!is_ready)&&(!is_paused)) { return 0; } return samples_recorded; } unsigned RDHPIRecordStream::samplesRecorded() const { return samples_recorded; } bool RDHPIRecordStream::recordReady() { hpi_err_t hpi_error=0; char hpi_text[200]; if(debug) { printf("RDHPIRecordStream: received recordReady()\n"); } if(!is_open) { return false; } if((!is_recording)&&(!is_paused)) { resetWave(); if(LogHpi(HPI_InStreamGetInfoEx(NULL,hpi_stream, &state,&buffer_size,&data_recorded, &samples_recorded,&reserved),__LINE__)!=0) { if(debug) { printf("RDHPIRecordStream: HPI_InStreamGetInfoEx() failed\n"); } return false; } fragment_size=buffer_size/4; if(fragment_size>192000) { // ALSA Compatibility Limitation fragment_size=192000; } fragment_time=(1000*fragment_size)/(getAvgBytesPerSec()); if(pdata!=NULL) { delete pdata; } pdata=(uint8_t *)malloc(fragment_size); if(pdata==NULL) { if(debug) { printf("RDHPIRecordStream: couldn't allocate buffer\n"); } return false; } switch(getFormatTag()) { case WAVE_FORMAT_PCM: if(debug) { printf("RDHPIRecordStream: using PCM%d format\n", getBitsPerSample()); } switch(getBitsPerSample()) { case 8: hpi_error=LogHpi(HPI_FormatCreate(&format,getChannels(), HPI_FORMAT_PCM8_UNSIGNED, getSamplesPerSec(),0,0),__LINE__); break; case 16: hpi_error=LogHpi(HPI_FormatCreate(&format,getChannels(), HPI_FORMAT_PCM16_SIGNED, getSamplesPerSec(),0,0),__LINE__); break; case 24: hpi_error=LogHpi(HPI_FormatCreate(&format,getChannels(), HPI_FORMAT_PCM24_SIGNED, getSamplesPerSec(),0,0),__LINE__); break; case 32: hpi_error=LogHpi(HPI_FormatCreate(&format,getChannels(), HPI_FORMAT_PCM32_SIGNED, getSamplesPerSec(),0,0),__LINE__); break; default: if(debug) { printf("RDHPIRecordStream: unsupported sample size\n"); } return false; } break; case WAVE_FORMAT_MPEG: if(debug) { printf("RDHPIRecordStream: using MPEG-1 Layer %d\n",getHeadLayer()); } switch(getHeadLayer()) { case 1: hpi_error=LogHpi(HPI_FormatCreate(&format,getChannels(), HPI_FORMAT_MPEG_L1,getSamplesPerSec(), getHeadBitRate(),getHeadFlags()), __LINE__); break; case 2: hpi_error=LogHpi(HPI_FormatCreate(&format,getChannels(), HPI_FORMAT_MPEG_L2,getSamplesPerSec(), getHeadBitRate(),getHeadFlags()), __LINE__); break; case 3: hpi_error=LogHpi(HPI_FormatCreate(&format,getChannels(), HPI_FORMAT_MPEG_L3,getSamplesPerSec(), getHeadBitRate(),getHeadFlags()), __LINE__); break; default: hpi_error=LogHpi(HPI_AdapterClose(NULL,card_index[card_number]), __LINE__); if(debug) { printf("RDHPIRecordStream: invalid MPEG-1 layer\n"); } return false; } if(getMextChunk()) { setMextHomogenous(true); setMextPaddingUsed(false); setMextHackedBitRate(true); setMextFreeFormat(false); setMextFrameSize(144*getHeadBitRate()/getSamplesPerSec()); setMextAncillaryLength(5); setMextLeftEnergyPresent(true); if(getChannels()>1) { setMextRightEnergyPresent(true); } else { setMextRightEnergyPresent(false); } setMextPrivateDataPresent(false); } break; case WAVE_FORMAT_VORBIS: if(debug) { printf("RDHPIRecordStream: using OggVorbis\n"); } hpi_error=LogHpi(HPI_FormatCreate(&format,getChannels(), HPI_FORMAT_PCM16_SIGNED, getSamplesPerSec(),0,0),__LINE__); break; default: if(debug) { printf("RDHPIRecordStream: invalid format tag\n"); } return false; break; } if((hpi_error=LogHpi(HPI_InStreamQueryFormat(NULL,hpi_stream, &format),__LINE__))!=0) { if(debug) { HPI_GetErrorText(hpi_error,hpi_text); printf("Num: %d\n",hpi_error); printf("RDHPIRecordStream: %s\n",hpi_text); } return false; } } #if HPI_VER < 0x00030500 LogHpi(HPI_DataCreate(&hpi_data,&format,pdata,fragment_size),__LINE__); #endif hpi_error=LogHpi(HPI_InStreamSetFormat(NULL,hpi_stream,&format),__LINE__); hpi_error=LogHpi(HPI_InStreamStart(NULL,hpi_stream),__LINE__); // clock->start(2*fragment_time/3); clock->start(RDHPIRECORDSTREAM_CLOCK_INTERVAL); is_ready=true; is_recording=false; is_paused=false; stopping=false; emit isStopped(false); emit ready(); emit stateChanged(card_number,stream_number,1); // RecordReady if(debug) { printf("RDHPIRecordStream: emitted isStopped(false)\n"); printf("RDHPIRecordStream: emitted ready()\n"); printf("RDHPIRecordStream: emitted stateChanged(%d,%d,RDHPIRecordStream::RecordReady)\n",card_number,stream_number); } return true; } void RDHPIRecordStream::record() { if(debug) { printf("RDHPIRecordStream: received record()\n"); } if(!is_open) { return; } if(!is_ready) { recordReady(); } record_started=false; LogHpi(HPI_InStreamReset(NULL,hpi_stream),__LINE__); LogHpi(HPI_InStreamStart(NULL,hpi_stream),__LINE__); is_recording=true; is_paused=false; emit isStopped(false); emit recording(); emit stateChanged(card_number,stream_number,0); // Recording if(debug) { printf("RDHPIRecordStream: emitted isStopped(false)\n"); printf("RDHPIRecordStream: emitted recording()\n"); printf("RDHPIRecordStream: emitted stateChanged(%d,%d,RDHPIRecordStream::Recording)\n",card_number,stream_number); } tickClock(); } void RDHPIRecordStream::pause() { if(debug) { printf("RDHPIRecordStream: received pause()\n"); } if(!is_recording) { return; } LogHpi(HPI_InStreamStop(NULL,hpi_stream),__LINE__); tickClock(); LogHpi(HPI_InStreamGetInfoEx(NULL,hpi_stream,&state,&buffer_size, &data_recorded,&samples_recorded,&reserved), __LINE__); is_recording=false; is_paused=true; LogHpi(HPI_InStreamStart(NULL,hpi_stream),__LINE__); emit paused(); emit stateChanged(card_number,stream_number,2); // Paused if(debug) { printf("RDHPIRecordStream: emitted paused()\n"); printf("RDHPIRecordStream: emitted stateChanged(%d,%d,RDHPIRecordStream::Paused)\n",card_number,stream_number); } } void RDHPIRecordStream::stop() { if(debug) { printf("RDHPIRecordStream: received stop()\n"); } if(is_ready|is_recording|is_paused) { LogHpi(HPI_InStreamStop(NULL,hpi_stream),__LINE__); tickClock(); clock->stop(); is_recording=false; is_paused=false; is_ready=false; if(pdata!=NULL) { delete pdata; pdata=NULL; } emit isStopped(true); emit stopped(); emit stateChanged(card_number,stream_number,RDHPIRecordStream::Stopped); emit position(0); if(debug) { printf("RDHPIRecordStream: emitted isStopped(true)\n"); printf("RDHPIRecordStream: emitted stopped()\n"); printf("RDHPIRecordStream: emitted stateChanged(%d,%d,RDHPIRecordStream::Stopped)\n",card_number,stream_number); printf("RDHPIRecordStream: emitted position(0)\n"); } } } void RDHPIRecordStream::setInputVOX(int gain) { sound_card->setInputStreamVOX(card_number,stream_number,gain); } void RDHPIRecordStream::setRecordLength(int length) { record_length=length; } void RDHPIRecordStream::tickClock() { LogHpi(HPI_InStreamGetInfoEx(NULL,hpi_stream, &state,&buffer_size,&data_recorded, &samples_recorded,&reserved),__LINE__); if((!record_started)&&(is_recording)) { if(samples_recorded>0) { if(record_length>0) { length_timer->start(record_length,true); } emit recordStart(); emit stateChanged(card_number,stream_number,4); // RecordStarted if(debug) { printf("RDHPIRecordStream: emitted recordStart()\n"); printf("RDHPIRecordStream: emitted stateChanged(%d,%d,RDHPIRecordStream::RecordStarted)\n",card_number,stream_number); } record_started=true; } } while(data_recorded>fragment_size) { #if HPI_VER > 0x00030500 LogHpi(HPI_InStreamReadBuf(NULL,hpi_stream,pdata,fragment_size),__LINE__); #else LogHpi(HPI_InStreamRead(NULL,hpi_stream,&hpi_data),__LINE__); #endif if(is_recording) { writeWave(pdata,fragment_size); } LogHpi(HPI_InStreamGetInfoEx(NULL,hpi_stream,&state,&buffer_size, &data_recorded,&samples_recorded,&reserved), __LINE__); } if(state==HPI_STATE_STOPPED) { #if HPI_VER > 0x00030500 LogHpi(HPI_InStreamReadBuf(NULL,hpi_stream,pdata,data_recorded),__LINE__); #else LogHpi(HPI_DataCreate(&hpi_data,&format,pdata,data_recorded),__LINE__); LogHpi(HPI_InStreamRead(NULL,hpi_stream,&hpi_data),__LINE__); #endif if(is_recording) { writeWave(pdata,data_recorded); } } emit position(samples_recorded); if(debug) { printf("RDHPIRecordStream: emitted position(%u)\n", (unsigned)samples_recorded); } } bool RDHPIRecordStream::GetStream() { hpi_err_t hpi_err; char hpi_text[100]; if((hpi_err= LogHpi(HPI_InStreamOpen(NULL,card_index[card_number],stream_number, &hpi_stream),__LINE__))!=0) { if(debug) { HPI_GetErrorText(hpi_err,hpi_text); fprintf(stderr,"*** HPI Error: %s ***\n",hpi_text); } return false; } if(HPI_InStreamHostBufferAllocate(NULL,hpi_stream,dma_buffer_size)); return true; } void RDHPIRecordStream::FreeStream() { if(HPI_InStreamHostBufferFree(NULL,hpi_stream)); LogHpi(HPI_InStreamClose(NULL,hpi_stream),__LINE__); } hpi_err_t RDHPIRecordStream::LogHpi(hpi_err_t err,int lineno) { char err_txt[200]; if(err!=0) { HPI_GetErrorText(err,err_txt); RDApplication::syslog(sound_card->config(),LOG_WARNING, "HPI Error: %s, %s line %d",err_txt,__FILE__,lineno); } return err; }