I am trying to read a large amount of data (~TB) from two solid state drives (SSDs) (ideally unformatted) and combine the data by a certain algorithm and write it back to another SSD. Initially I implemented a program that reads from two FORMATTED hard drives (OS sees it) and writes to another formatted hard drive. I was able to get about 450 MB/Sec speed (for the whole program). I was using fread() to read data from SSDs.
Then I implemented it with reading from two UNFORMATTED SSDs and writing to a formatted SSD and I am only getting about 200 MB/sec. I am still using fread() and I am puzzled why the speed is slower. To read unformatted hard drives I have to run in the Admin mode and use HANDLE and then cast it to a file pointer by the time I read bytes. So, once the correct hard drives are found the code is the same and I am using freads(). The code is listed below for a test program I used to measure the speed of the formatted vs unformatted reads. In this program I am getting about 200 MB/sec for UNFORMATTED hard drive reads and 500 MB/sec for FORMATTED hard drive reads.
Formatted read program.
while (tempCounter < frames_to_process) //
{
if (fread(frame_header_A, 1, 212052, pFile_A) != 212052) {
printf("Couldn't read 212052 Bytes\n");
if (feof(pFile_A))
{
printf("End of File reached while reading the frame\n");
cout << "Num iterations " << tempCounter << endl;
}
if (ferror(pFile_A))
{
printf("Error reading file while reading for the frame\n");
}
time2 = chrono::high_resolution_clock::now();
time_span = chrono::duration_cast<chrono::duration<double>>(time2 - time1);
cout << "time to create imagery files " << setprecision(10) << time_span.count() << " seconds" << endl << endl;
exit(0);
return 0;
}
tempCounter++;
//let's Check whether the variable end with 0xBCB55757
container_count_A = 1;
container_count_B = 2;
if ((frame_header_A[212048] == (char)0xBC) && (frame_header_A[212049] == (char)0xB5) && (frame_header_A[212050] == (char)0x57) && (frame_header_A[212051] == (char)0x57))
{
unsigned int byte_1 = frame_header_A[27];
unsigned int byte_2 = frame_header_A[26];
unsigned int byte_3 = frame_header_A[25];
unsigned int byte_4 = frame_header_A[24];
}
}
Unformatted reads program. Essentially, it iterates though all connected hard drives and find the specific hard drive A and B using a certain pattern. After that the HANDLE is casted to a regular file pointer and then reuses the code above (formatted case)
FILE *pFile_A = NULL;
FILE *pFile_B = NULL;
FILE* f = NULL;
ofstream writeBinary_Combine;
writeBinary_Combine.open(argv[2], ofstream::out | ofstream::binary);
//
LPCTSTR dsksrc = L"\\\\.\\PhysicalDrive";
wchar_t dsk[512] = L"";
int fd;
bool channel_A_found = false;
bool channel_B_found = false;
char frame_header_A[212052]; //seems I can't have another
char frame_header_B[212052];
unsigned int container_count_A = 1;
unsigned int container_count_B = 2;
char* frame_header_test = new char[212052]; // for some reason I am running out of static memory; so I am using heap to alleviate it ???
HANDLE hDisk;
for (int i = 0; i < 8; i++)
{
swprintf(dsk, 511, L"%s%d", dsksrc, i);
hDisk = CreateFile(dsk, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
if (hDisk == INVALID_HANDLE_VALUE)
{
printf("%s%d%s", "couldn't open the drive ", i, "\n");
CloseHandle(hDisk);
}
else
{
printf("%s%d%s", "successfully open the drive ", i, "\n");
fd = _open_osfhandle((intptr_t)hDisk, _O_RDONLY);
char buff_test[500000];
if (fd != -1)
{
f = _fdopen(fd, "rb");
int ret_code = 0;
char drive_header[512];
fread(drive_header, 1, 512, f);
if ((drive_header[510] == (char)0x55) && (drive_header[511] == (char)0xAA)) // test for a formatted drive; is there other identifiers?
{
//bootable drive; not an unformatted drive no need to keep on searching
//_close(fd);
}
else
{
fseek(f, 0, SEEK_SET);
ret_code = find_BCB55757(f, i);
if (ret_code != -1)
{
fseek(f, 0, SEEK_SET);
fread(buff_test, 1, ret_code, f);
fread(frame_header_test, 1, 212052, f);
unsigned int readout_B = 1;
unsigned int cmdout_B = 2;
unsigned int byte_1 = frame_header_test[115];
unsigned int byte_2 = frame_header_test[114];
unsigned int byte_3 = frame_header_test[113];
unsigned int byte_4 = frame_header_test[112];
readout_B = ((byte_4 << 24) & 0xff000000) | ((byte_3 << 16) & 0x00ff0000) | ((byte_2 << 8) & 0x0000ff00) | (byte_1 & 0x000000ff);
byte_1 = frame_header_test[123];
byte_2 = frame_header_test[122];
byte_3 = frame_header_test[121];
byte_4 = frame_header_test[120];
cmdout_B = ((byte_4 << 24) & 0xff000000) | ((byte_3 << 16) & 0x00ff0000) | ((byte_2 << 8) & 0x0000ff00) | (byte_1 & 0x000000ff);
//cout << readout_B << " " << cmdout_B << endl;
size_t result_1 = 0;
if (readout_B == (cmdout_B - 1))
{
channel_B_found = true;
fseek(f, 0, SEEK_SET); //this is the only way to do it
result_1 = fread(buff_test, 1, ret_code, f);
if (result_1 != (size_t)ret_code)
{
cout << "reading error 1 " << endl;
cout << "read bytes " << result_1 << endl;
}
result_1 = fread(frame_header_B, 1, 212052, f);
if (result_1 != (size_t)212052)
{
cout << "reading error 2" << endl;
cout << "read bytes " << result_1 << endl;
}
pFile_B = f;
byte_1 = frame_header_B[27];
byte_2 = frame_header_B[26];
byte_3 = frame_header_B[25];
byte_4 = frame_header_B[24];
cout << "found B" << endl;
}
else
{
channel_A_found = true;
fseek(f, 0, SEEK_SET); //this is the only way to do it
result_1 = fread(buff_test, 1, ret_code, f);
if (result_1 != (size_t)ret_code)
{
cout << "reading error 5" << endl;
cout << "read bytes " << result_1 << endl;
}
result_1 = fread(frame_header_A, 1, 212052, f);
if (result_1 != (size_t)212052)
{
cout << "reading error 6" << endl;
cout << "read bytes " << result_1 << endl;
}
pFile_A = f;
byte_1 = frame_header_A[27];
byte_2 = frame_header_A[26];
byte_3 = frame_header_A[25];
byte_4 = frame_header_A[24];
cout << "found A" << endl;
}
}
//fclose(f);
}
}
else
{
_close(fd);
}
}
if (channel_A_found && channel_B_found)
{
cout << "both drives found" << endl;
break;
}
}// then reuse the code from the formatted case
Here are my questions.
1) why is the speed in the unformatted case lot slower although I am using the same fread(). Does it have to do with setting up reading physical drives the HANDLE
2) Is there a way to improve the speed of the unformatted hard drives? should I be using read() instead of fread() in the unformatted case?
I appreciate any help or ideas!
Related
When I send a file using C++ Socket, random bytes are added at the beginning. The problem occurs every few attemps. I tried to clear the buffers using different ways but nothing worked so far. I also noticed that in the received file i can see strings such as "File Size is".
I m using 2 Docker containers, each runs Ubuntu 20.04 LTS.
This is the code i use to send the files:
int Socket::send_file(string path,int socket_){
if (socket_ == -1 )
{
socket_ = clientSd ;
}
ifstream fichier ;
fichier.open(path,ios::in | ios::binary);
if (fichier.is_open())
{
cout << "File Opened " << endl ;
}else
{
cout << "Failed to open file " << endl ;
exit(0);
}
int size = get_file_size(fichier);
fichier.close();
char msg[MAX_BUFFER];
sprintf(msg, "%d\0", size);
cout << "File Size is " << size << endl ;
send(socket_, (char*)msg, sizeof(msg) , 0); // 2
char buffer[MAX_BUFFER]; // 4222111
const char *c = path.c_str();
sprintf(buffer, "%d\0", size);
FILE *fd = fopen(c, "rb");
size_t rret, wret;
int bytes_read;
int fragment = MAX_BUFFER ;
while (!feof(fd)) {
if ((bytes_read = fread(&buffer, 1, MAX_BUFFER, fd)) > 0)
send(socket_, buffer, bytes_read, 0);
else
break;
}
fclose(fd);
cout << "End" << endl ;
return 1 ;
}
This is the code i use to receive the files:
int Socket::receive_file(string path,int socket_){
if (socket_ == -1 )
{
socket_ = clientSd ;
}
char* buffer ;
buffer = (char*) malloc(sizeof(char) * MAX_BUFFER) ;
recv(socket_, (char*)buffer, MAX_BUFFER, 0); // 3
int size = atoi(buffer) ;
cout << "File Size is " << size << endl ;
size_t datasize;
char text[MAX_BUFFER]; // 1024
const char *c = path.c_str();
FILE* fd = fopen(c, "wb");
int received = 0 ;
while (received < size)
{
datasize = recv(socket_, text, sizeof(text), 0);
fwrite(&text, 1, datasize, fd);
}
fclose(fd);
cout << "End" << endl ;
return 1 ;
}
I created a c++ program that reads and analyzes pcap files via fread function.
It is a program that reads the global header of pcap file, reads the pcap header, and extracts a specific field value by specifying each protocol header structure. Compared to tcpdump tool, tcpdump read the pcap file about three times faster. My program is really simple, but I don't know why it is slow. What do I need to do to improve my program's performance?
The following is my code. Note that the type of result(Variable) is ostringstream. and The Program is work by calling analyze() repeatly to read all the pcap files.
PcapAnalyzer::PcapAnalyzer(const std::string& filename)
{
PcapAnalyzerfile = fopen("test.pcap", "r");
if (PcapAnalyzerfile == nullptr)
throw std::runtime_error("Failed to read Pcap");
struct PcapAnalyzer_file_header pfh;
if (fread(&pfh, sizeof(pfh), 1, PcapAnalyzerfile) < 1)
throw std::runtime_error("Failed to read Pcap");
if(pfh.linktype != 1)
throw std::runtime_error("No Support");
}
std::string PcapAnalyzer::analyze()
{
int process_len = 0;
int packet_len = 0;
result.str("");
result.clear();
packet_len = PcapAnalyzer_header();
if (packet_len == -1)
return {};
process_len = ethernet();
if (process_len == -1)
throw std::runtime_error("failed to analyze");
packet_len -= process_len;
if (!payload(packet_len))
throw std::runtime_error("failed to analyze");
return result.str();
}
int PcapAnalyzer::PcapAnalyzer_header()
{
struct PcapAnalyzer_pkthdr pp;
char* cap_time;
long sec;
if (fread(&pp, sizeof(pp), 1, PcapAnalyzerfile) < 1)
return -1;
sec = (long)pp.ts.tv_sec;
cap_time = (char*)ctime((const time_t*)&sec);
cap_time[strlen(cap_time) - 1] = '\0';
result << std::dec;
result << pp.len << " ";
result << cap_time << " ";
return pp.caplen;
}
int PcapAnalyzer::ethernet()
{
struct ether_header eh;
int process_len = 0;
if (fread(&eh, sizeof(eh), 1, PcapAnalyzerfile) < 1)
return -1;
process_len += sizeof(eh);
if(htons(eh.ether_type) != ETHERTYPE_IP)
return process_len;
process_len += ipv4();
if (process_len == -1)
throw std::runtime_error("failed to analyze");
return process_len;
}
int PcapAnalyzer::ipv4()
{
struct ip iph;
int opt = 0;
int process_len = 0;
if (fread(&iph, 20, 1, PcapAnalyzerfile) < 1)
return -1;
result << IP_V(iph.ip_vhl) << " ";
result << IP_HL(iph.ip_vhl) << " ";
result << iph.ip_tos << " ";
result << iph.ip_len << " ";
result << iph.ip_id << " ";
result << iph.ip_off << " ";
result << iph.ip_ttl << " ";
process_len += 20;
opt = IP_HL(iph.ip_vhl) * 4 - sizeof(iph);
if (opt != 0) {
fseek(PcapAnalyzerfile, opt, SEEK_CUR);
process_len += opt;
result << "ip_opt ";
}
if(iph.ip_p != IPPROTO_UDP)
return process_len;
process_len += udp();
if (process_len == -1)
throw std::runtime_error("failed to read transport header");
return process_len;
}
int PcapAnalyzer::udp()
{
struct udphdr udph;
int process_len = 0;
if (fread(&udph, sizeof(udph), 1, PcapAnalyzerfile) < 1)
return -1;
process_len += sizeof(udph);
result << ntohs(udph.uh_sport) << " ";
result << ntohs(udph.uh_dport) << " ";
result << ntohs(udph.uh_ulen) << " ";
result << ntohs(udph.uh_sum) << " ";
return process_len;
}
bool PcapAnalyzer::payload(int remain_len)
{
if (remain_len == 0)
return true;
char buffer[remain_len];
if (fread(buffer, remain_len, 1, PcapAnalyzerfile) < 1)
return false;
return true;
}
I am currently using the C++ to implement my code. The idea is I want to read a wav file through a program and then output its waveform on the screen. I have already found some references there:C++ Reading the Data part of a WAV file. and here is my code of reading the audio file. I have no idea of how to generate the waveform .
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
using std::string;
using std::fstream;
typedef struct WAV_HEADER
{
/* for the part of RIEF Chunk Descriptor */
uint8_t RIFF[4]; // RIFF Header Magic header
uint32_t ChunkSize; // RIFF Chunk Size
uint8_t WAVE[4]; // WAVE Header
/* for the part of "fmt" subChunk */
uint8_t fmt[4]; // FMT header
uint32_t Subchunk1Size; // Size of the fmt chunk
uint16_t AudioFormat; // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM
uint16_t NumOfChan; // Number of channels 1=Mono 2=Sterio
uint32_t SamplesPerSec; // Sampling Frequency in Hz
uint32_t bytesPerSec; // bytes per second
uint16_t blockAlign; // 2=16-bit mono, 4=16-bit stereo
uint16_t bitsPerSample; // Number of bits per sample
/* For the part of "data" subChunk */
uint8_t Subchunk2ID[4]; // "data" string
uint32_t Subchunk2Size; // Sampled data length
}wav_hdr;
int getFileSize(FILE* inFile);
int main(int argc, char* argv[])
{
wav_hdr wavHeader;
int headerSize = sizeof(wav_hdr), filelength = 0;
const char* filePath;
string input;
if (argc <= 1)
{
cout << "Input wave file name: ";
cin >> input;
cin.get();
filePath = input.c_str();
}
else
{
filePath = argv[1];
cout << "Input wave file name: " << filePath << endl;
}
FILE* wavFile = fopen(filePath, "r");
if (wavFile == nullptr)
{
fprintf(stderr, "Unable to open wave file: %s\n", filePath);
return 1;
}
//Read the header
size_t bytesRead = fread(&wavHeader, 1, headerSize, wavFile);
cout << "Header Read " << bytesRead << " bytes." << endl;
if (bytesRead > 0)
{
//Read the data
uint16_t bytesPerSample = wavHeader.bitsPerSample / 8; //Number of bytes per sample
uint64_t numSamples = wavHeader.ChunkSize / bytesPerSample; //How many samples are in the wav file?
static const uint16_t BUFFER_SIZE = 4096;
int8_t* buffer = new int8_t[BUFFER_SIZE];
while ((bytesRead = fread(buffer, sizeof buffer[0], BUFFER_SIZE / (sizeof buffer[0]), wavFile)) > 0)
{
/** DO SOMETHING WITH THE WAVE DATA HERE **/
cout << "Read " << bytesRead << " bytes." << endl;
}
delete [] buffer;
buffer = nullptr;
filelength = getFileSize(wavFile);
cout << "File is :" << filelength << " bytes." << endl;
cout << "RIFF header :" << wavHeader.RIFF[0] << wavHeader.RIFF[1] << wavHeader.RIFF[2] << wavHeader.RIFF[3] << endl;
cout << "WAVE header :" << wavHeader.WAVE[0] << wavHeader.WAVE[1] << wavHeader.WAVE[2] << wavHeader.WAVE[3] << endl;
cout << "FMT :" << wavHeader.fmt[0] << wavHeader.fmt[1] << wavHeader.fmt[2] << wavHeader.fmt[3] << endl;
cout << "Data size :" << wavHeader.ChunkSize << endl;
// Display the sampling Rate from the header
cout << "Sampling Rate :" << wavHeader.SamplesPerSec << endl;
cout << "Number of bits used :" << wavHeader.bitsPerSample << endl;
cout << "Number of channels :" << wavHeader.NumOfChan << endl;
cout << "Number of bytes per second :" << wavHeader.bytesPerSec << endl;
cout << "Data length :" << wavHeader.Subchunk2Size << endl;
cout << "Audio Format :" << wavHeader.AudioFormat << endl;
// Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM
cout << "Block align :" << wavHeader.blockAlign << endl;
cout << "Data string :" << wavHeader.Subchunk2ID[0] << wavHeader.Subchunk2ID[1] << wavHeader.Subchunk2ID[2] << wavHeader.Subchunk2ID[3] << endl;
}
fclose(wavFile);
return 0;
}
// find the file size
int getFileSize(FILE* inFile)
{
int fileSize = 0;
fseek(inFile, 0, SEEK_END);
fileSize = ftell(inFile);
fseek(inFile, 0, SEEK_SET);
return fileSize;
}
I would have left this as a comment with code gists and not an answer since it doesn't answer your question directly but I don't have enough reputation for this.
I did what you're trying to do with Qt, most of my code comes from Qt's documentation. You need to find the peak value of the samples and then draw that. Let me share some code that could give you an idea on how you might want to do it.
void Waveform::appendSamples()
{
buffer = audioDecoder->read();
qreal peak = getPeakValue(buffer.format());
const qint16 *data = buffer.constData<qint16>();
int count = buffer.sampleCount() / 2;
for (int i = 0; i < count; i += 1200){ // I want 40 samples per second currently assuming 48kHz
double val = data[i]/peak;
samples.append(val * 300); // *300 for scaling
}
}
qreal Waveform::getPeakValue(const QAudioFormat &format)
{
qreal ret(0);
if (format.isValid()){
switch (format.sampleType()) {
case QAudioFormat::Unknown:
break;
case QAudioFormat::Float:
if (format.sampleSize() != 32)
ret = 0;
else
ret = 1.00003;
break;
case QAudioFormat::SignedInt:
if (format.sampleSize() == 32)
ret = INT_MAX;
else if (format.sampleSize() == 16)
ret = SHRT_MAX;
else if (format.sampleSize() == 8)
ret = CHAR_MAX;
break;
case QAudioFormat::UnSignedInt:
if (format.sampleSize() == 32)
ret = UINT_MAX;
else if (format.sampleSize() == 16)
ret = USHRT_MAX;
else if (format.sampleSize() == 8)
ret = UCHAR_MAX;
break;
default:
break;
}
}
return ret;
}
For the drawing part in Qt (probably not useful to you)
QSGNode* WaveformRenderer::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
QSGSimpleRectNode *n = static_cast<QSGSimpleRectNode *>(oldNode);
if(!n){
n = new QSGSimpleRectNode();
n->setColor(Qt::red);
}
QVector<double> samples = wav->getSamples(); //retrieve the samples
int numberOfSamples = samples.size();
setItemWidth(qreal(numberOfSamples * 2)); //signal stuff for qml
for(int i = 0; i < numberOfSamples; ++i){
QSGSimpleRectNode *temp = new QSGSimpleRectNode();
temp->setColor(Qt::green);
temp->setRect(i * 2, height()/2 - samples[i], 2, samples[i] * 2);
n->appendChildNode(temp);
}
return n;
}
I ask user for input and write this data to disk. However I want the user to be able to update some of this data. It writes the data the first pass of the loop. But the second loop is not updating the description. Here is the code:
#include <iostream>
struct LabData {
int recordNumber;
char desc[16];
};
int main(int argc, const char * argv[]) {
// insert code here...
FILE *file = fopen("myfile.txt", "wb+");
if(file == 0)
std::cout << "ERROR FILE IS NULL!";
for(int i = 0; i < 10; i++)
{
LabData lb;
lb.recordNumber = i;
std::cout << "Enter a description: ";
std::cin.getline(lb.desc, 15);
if(fwrite(&lb, sizeof(lb), 1, file) != 1)
std::cout <<" ERROR IN WRITE!";
}
LabData lb;
for(int i = 9; i >= 0; i--)
{
fseek(file, i * sizeof(LabData), SEEK_SET);
fread(&lb, sizeof(LabData), 1, file);
if(lb.recordNumber != i)
std::cout << "Wrong record!";
else
{
std::cout << "Record number: " << lb.recordNumber << " Description: " << lb.desc << std::endl;
std::cout << "Enter new record description: ";
std::cin.getline(lb.desc, 15);
if(fwrite(&lb, sizeof(lb), 1, file) != 1)
std::cout <<" ERROR IN WRITE!";
}
}
for(int i = 0; i < 10; i++)
{
LabData lb;
fseek(file, i * sizeof(LabData), SEEK_SET);
fread(&lb, sizeof(LabData), 1, file);
std::cout << "Record number: " << lb.recordNumber << " Description: " << lb.desc << std::endl;
}
fclose(file);
return 0;
}
you need to put fseek(file, i * sizeof(LabData), SEEK_SET); before writing into the file, in the second loop,
std::cout << "Record number: " << lb.recordNumber << " Description: " << lb.desc << std::endl;
std::cout << "Enter new record description: ";
std::cin.getline(lb.desc, 15);
fseek(file, i * sizeof(LabData), SEEK_SET);
if(fwrite(&lb, sizeof(lb), 1, file) != 1)
std::cout <<" ERROR IN WRITE!";
Once you read from the file, you have to seek back to rewrite the data in same place.
Hi im trying to take sound from an open PortAudio Stream, encode it with opus, decode it and reproduce it again with portaudio.
Im doing this as a prototype just to try and understand the mechanics of this systems so, no real interest on following this concrete flow.
Thing is, portaudio gives buffers where OPUS needs Frames. Mi thought lead me to this in the portaudio side:
err = (Pa_ReadStream(stream, readBuffer, FRAMES_PER_BUFFER));
if (err = paNoError){
qDebug()<<"Fail read";
qDebug()<<Pa_GetErrorText(err);
// blockingRecord = false;
}
while (pos<FRAMES_PER_BUFFER){
memcpy(frameBuffer,readBuffer+(pos*FRAME_SIZE*NUM_CHANNELS),FRAME_SIZE*CHANNELS);
compressedSound = om.encodeOpus(frameBuffer);
unCompressedSound = om.decodeOpus(compressedSound);
memcpy(readBuffer+(pos*FRAME_SIZE*NUM_CHANNELS),unCompressedSound,FRAME_SIZE*CHANNELS);
pos++;
}
pos = 0;
err = (Pa_WriteStream(stream, readBuffer, FRAMES_PER_BUFFER));
if (err != paNoError)
{
qDebug() << "FAIL WRITE";
qDebug()<<Pa_GetErrorText(err);
//blockingRecord = false;
}
And this on the OPUS side:
unsigned char * OpusManager::encodeOpus(unsigned char *frame){
memcpy(encoded, frame, FRAME_SIZE*CHANNELS);
int ret = opus_encode(enc, encoded, FRAME_SIZE, compressed_buffer, encoded_data_size);
if (ret<0){
qDebug()<<"Failure while compressing sound";
return NULL;
}
return (compressed_buffer);
}
unsigned char * OpusManager::decodeOpus(unsigned char *frame){
int ret= opus_decode(dec, frame, encoded_data_size, decoded, FRAME_SIZE, 0);
if (ret<0){
qDebug()<<"Failure while decompressing sound";
return NULL;
}
memcpy(uncompressed_buffer, decoded, FRAME_SIZE*CHANNELS);
return (uncompressed_buffer);
}
No errors without encocing and perfect soud. With encode i get no errors till the PA_Writestream call, where i get a "Output underflowed" PaError. I suppose the way of taking the frames ive implemmented must be waaay wrong, but cant find info to help me with this.
It seems your interpretation of Opus' frame_size parameters to opus_encode and opus_decode is incorrect. If I understand your code correctly you're recording a packet of size FRAMES_PER_BUFFER frames and then try to turn it into N packets of size FRAME_SIZE. Instead, it seems to me that Opus wants to turn your packet of FRAMES_PER_BUFFER into another packet of equal frame count, and in doing so, only uses it's FRAME_SIZE parameter as some sort of quality control parameter for the encoding process. Below you'll find a complete sample that I believe does what you want. Play around with the '480' magic number in encode()/decode() and hear audio quality change.
int opusErr;
PaError paErr;
std::string s;
int const channels = 2;
int const bufferSize = 480;
int const sampleRate = 48000;
int const durationSeconds = 5;
opus_int32 enc_bytes;
opus_int32 dec_bytes;
int framesProcessed = 0;
std::vector<unsigned short> captured(bufferSize * channels);
std::vector<unsigned short> decoded(bufferSize * channels);
// * 2: byte count, 16 bit samples
std::vector<unsigned char> encoded(bufferSize * channels * 2);
// initialize opus
OpusEncoder* enc = opus_encoder_create(
sampleRate, channels, OPUS_APPLICATION_AUDIO, &opusErr);
if (opusErr != OPUS_OK)
{
std::cout << "opus_encoder_create failed: " << opusErr << "\n";
std::getline(std::cin, s);
return 1;
}
OpusDecoder* dec = opus_decoder_create(
sampleRate, channels, &opusErr);
if (opusErr != OPUS_OK)
{
std::cout << "opus_decoder_create failed: " << opusErr << "\n";
std::getline(std::cin, s);
return 1;
}
// initialize portaudio
if ((paErr = Pa_Initialize()) != paNoError)
{
std::cout << "Pa_Initialize failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
PaStream* stream = nullptr;
if ((paErr = Pa_OpenDefaultStream(&stream,
channels, channels, paInt16, sampleRate,
bufferSize, nullptr, nullptr)) != paNoError)
{
std::cout << "Pa_OpenDefaultStream failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
// start stream
if ((paErr = Pa_StartStream(stream)) != paNoError)
{
std::cout << "Pa_StartStream failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
// capture, encode, decode & render durationSeconds of audio
while (framesProcessed < sampleRate * durationSeconds)
{
if ((paErr = Pa_ReadStream(stream,
captured.data(), bufferSize)) != paNoError)
{
std::cout << "Pa_ReadStream failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
if ((enc_bytes = opus_encode(enc, reinterpret_cast<opus_int16 const*>(
captured.data()), 480, encoded.data(), encoded.size())) < 0)
{
std::cout << "opus_encode failed: " << enc_bytes << "\n";
std::getline(std::cin, s);
return 1;
}
if ((dec_bytes = opus_decode(dec, encoded.data(), enc_bytes,
reinterpret_cast<opus_int16*>(decoded.data()), 480, 0)) < 0)
{
std::cout << "opus_decode failed: " << dec_bytes << "\n";
std::getline(std::cin, s);
return 1;
}
if ((paErr = Pa_WriteStream(stream, decoded.data(), bufferSize)) != paNoError)
{
std::cout << "Pa_WriteStream failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
framesProcessed += bufferSize;
}
// stop stream
if ((paErr = Pa_StopStream(stream)) != paNoError)
{
std::cout << "Pa_StopStream failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
// cleanup portaudio
if ((paErr = Pa_CloseStream(stream)) != paNoError)
{
std::cout << "Pa_CloseStream failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
if ((paErr = Pa_Terminate()) != paNoError)
{
std::cout << "Pa_Terminate failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
// cleanup opus
opus_decoder_destroy(dec);
opus_encoder_destroy(enc);