Why am I reading end of file in this code? - c++

I'm trying to implement some simple function wrappers around OpenAL.
When trying to load a file piece by piece to stream it through several buffers, I'm somehow reading the eof.
#include <iostream>
#include <type_traits>
#include <cstring>
#include <fstream>
int32_t convert_to_int(char* buffer, std::size_t len, const std::endian endianess)
{
int32_t a = 0;
if(endianess == std::endian::little)
std::memcpy(&a, buffer, len);
else
for(std::size_t i = 0; i < len; ++i)
reinterpret_cast<char*>(&a)[3 - i] = buffer[i];
return a;
}
bool load_wav_file_header(std::ifstream& file, uint8_t& channels, int32_t& sampleRate, uint8_t& bitsPerSample, int32_t& size)
{
char buffer[4];
if(!file.is_open())
return false;
// the RIFF
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read RIFF" << std::endl;
return false;
}
if(std::strncmp(buffer, "RIFF", 4) != 0)
{
std::cerr << "ERROR: file is not a valid WAVE file (header doesn't begin with RIFF)" << std::endl;
return false;
}
// the size of the file
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read size of file" << std::endl;
return false;
}
// the WAVE
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read WAVE" << std::endl;
return false;
}
if(std::strncmp(buffer, "WAVE", 4) != 0)
{
std::cerr << "ERROR: file is not a valid WAVE file (header doesn't contain WAVE)" << std::endl;
return false;
}
// "fmt/0"
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read fmt/0" << std::endl;
return false;
}
// this is always 16, the size of the fmt data chunk
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read the 16" << std::endl;
return false;
}
// PCM should be 1?
if(!file.read(buffer, 2))
{
std::cerr << "ERROR: could not read PCM" << std::endl;
return false;
}
// the number of channels
if(!file.read(buffer, 2))
{
std::cerr << "ERROR: could not read number of channels" << std::endl;
return false;
}
channels = convert_to_int(buffer, 2, std::endian::little);
// sample rate
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read sample rate" << std::endl;
return false;
}
sampleRate = convert_to_int(buffer, 4, std::endian::little);
// (sampleRate * bitsPerSample * channels) / 8
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read (sampleRate * bitsPerSample * channels) / 8" << std::endl;
return false;
}
// ?? dafaq
if(!file.read(buffer, 2))
{
std::cerr << "ERROR: could not read dafaq" << std::endl;
return false;
}
// bitsPerSample
if(!file.read(buffer, 2))
{
std::cerr << "ERROR: could not read bits per sample" << std::endl;
return false;
}
bitsPerSample = convert_to_int(buffer, 2, std::endian::little);
// data chunk header "data"
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read data chunk header" << std::endl;
return false;
}
if(std::strncmp(buffer, "data", 4) != 0)
{
std::cerr << "ERROR: file is not a valid WAVE file (doesn't have 'data' tag)" << std::endl;
return false;
}
// size of data
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read data size" << std::endl;
return false;
}
size = convert_to_int(buffer, 4, std::endian::little);
/* cannot be at the end of file */
if(file.eof())
{
std::cerr << "ERROR: reached EOF on the file" << std::endl;
return false;
}
if(file.fail())
{
std::cerr << "ERROR: fail state set on the file" << std::endl;
return false;
}
return true;
}
const int32_t BUFFER_SIZE = 320000;
const int32_t NUM_BUFFERS = 4;
int main()
{
std::string filename = "Assets/session.wav";
std::ifstream file;
file.open(filename);
if(!file.is_open())
{
std::cerr << "ERROR: could not open file \"" << filename << "\"" << std::endl;
return false;
}
uint8_t channels;
int32_t sampleRate;
uint8_t bitsPerSample;
int32_t size;
if(!load_wav_file_header(file,channels,sampleRate,bitsPerSample,size))
{
std::cerr << "ERROR: could not open wav header of \"" << filename << "\"" << std::endl;
return false;
}
char* data = new char[size];
if(!file.read(data, size))
{
if(file.eof())
std::cerr << "ERROR: reached end of file trying to read "
<< size << " bytes. Actually read " << file.gcount() << std::endl;
else if(file.fail())
std::cerr << "ERROR: fail state" << std::endl;
else if(file.bad())
perror(("error while reading file " + filename).c_str());
return 0;
}
return 0;
}
The output of the program is:
ERROR: reached end of file trying to read 7035488 bytes. Actually read 554
According to cppreference this happens with read when you reach the eof, which based on gcount(), it looks like I have. The problem is that this file is a total of 6,871 KB, and the reported size in the .WAV file is otherwise larger than the 554 it's claiming to have left in it.

Related

C++ something gone wrong when reading the file

I'm trying to write and read serialized data to file, but it always breaks when reading specific "PNG" struct, with stored "bed_feet_top.png" from minecraft 1.8.9 textures folder in it, and I can't find a way to get the error. Already tried errno and try and catch, but it always says sucess. It seems that it breaks after and not when reading the buggy "PNG" struct, because the read data is uncorrupted and I can recreate the stored image. I'm using "stb_image" library to read the image data and "zpp_bits" library for serializing the data.
the code i would use:
PNG srcImage; //The "PNG" struct it will break at
unsigned char* buffer = stbi_load("bed_feet_top.png", &image.imageWidth, &image.imageHeight, &image.colorChannels, STBI_rgb_alpha);
if (!buffer)
{
std::cout << "Failed to read image: " + path << std::endl;
return 1;
}
image.pixels = (char*)buffer;
DataBuffer srcBuffer;
srcBuffer.serialize("somePath", "someName", srcImage);
DataManager::writeToFile("someFile.dat", srcBuffer);
DataBuffer dstBuffer;
DataManager::readFromFile("someFile.dat", &dstbuffer);
PNG dstImage = dstBuffer.deserialize<PNG>("somePath", "someName"); //The buggy "PNG" struct data is uncorrupted
the "PNG" struct:
struct PNG
{
int imageWidth, imageHeight, colorChannels;
std::string pixels;
};
the struct I'm storing all serialized data in:
struct DataBuffer
{
std::map<std::string, std::vector<std::byte>> data; //All the serialized data is stored in this map
template<typename T>
void serialize(std::string path, std::string name, T obj)
{
path.append("/");
path.append(name);
serialize(path, obj);
}
template<typename T>
void serialize(std::string fullPath, T obj)
{
if (data.contains(fullPath))
{
std::cout << "Data buffer already contains value with te same path!" << std::endl;
return;
}
try
{
zpp::bits::out out(data[fullPath]);
out(obj).or_throw();
}
catch (const std::exception& error)
{
std::cerr << "Serialization failed with error: " << error.what() << std::endl;
}
catch (...) {
std::cerr << "Serialization failed with unknown error!" << std::endl;
}
}
template<typename T>
T deserialize(std::string path, std::string name)
{
path.append("/");
path.append(name);
return deserialize<T>(path);
}
template<typename T>
T deserialize(std::string fullPath)
{
T obj;
if (!data.contains(fullPath))
{
std::cout << "Data buffer doesn't contain value with the given path!" << std::endl;
return obj;
}
try
{
zpp::bits::in in(data[fullPath]);
in(obj).or_throw();
}
catch (const std::exception& error)
{
std::cerr << "Deserialization failed with error: " << error.what() << std::endl;
}
catch (...) {
std::cerr << "Deserialization failed with unknown error!" << std::endl;
}
return obj;
}
};
The write to file function:
void DataManager::writeToFile(std::string fileName, DataBuffer dataBuffer)
{
std::ofstream ofs(fileName, std::ios::out | std::ios::binary);
if (!ofs.is_open())
{
std::cerr << "Failed to open output file stream!" << std::endl;
return;
}
size_t dataCount = dataBuffer.data.size();
ofs.write((char*)&dataCount, 8);
for (auto const& [key, val] : dataBuffer.data)
{
size_t keySize = key.size();
ofs.write((char*)&keySize, sizeof(keySize));
ofs.write(&key[0], keySize);
size_t entryCount = val.size();
ofs.write((char*)&entryCount, 8);
ofs.write((char*)val.data(), entryCount);
if (!ofs.good()) //ofs.good() never returned false to me
{
std::cerr << "Something went wrong when writing " << key << " to the file!" << std::endl;
break;
}
}
ofs.close();
}
the read from file function:
void DataManager::readFromFile(std::string fileName, DataBuffer* dataBuffer)
{
std::ifstream ifs(fileName, std::ios::in, std::ios::binary);
if (!ifs)
{
std::cerr << "File doesnt exist!" << std::endl;
return;
}
if (!ifs.is_open())
{
std::cerr << "Failed to open input file stream!" << std::endl;
return;
}
size_t dataCount;
ifs.read((char*)&dataCount, 8);
for (size_t i = 0; i < dataCount; i++)
{
size_t keySize;
ifs.read((char*)&keySize, sizeof(keySize));
std::string key;
key.resize(keySize);
ifs.read(&key[0], keySize);
size_t entryCount;
ifs.read((char*)&entryCount, 8);
dataBuffer->data[key].resize(entryCount);
ifs.read((char*)dataBuffer->data[key].data(), entryCount);
if (!ifs.good()) //ifs.good() returns false after it reads the buggy "PNG" struct data
{
std::cerr << "Something went wrong when reading " << key << " from the file!" << std::endl;
break;
}
}
ifs.close();
}
EDIT
I added minimal reproducible example as "Igor Tandetnik" requested.
the minimal reproducible example:
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <string>
#include <iostream>
#include <vector>
#include <fstream>
#include <stb_image.h>
#include "stb_image_write.h"
#include <zpp_bits.h>
struct PNG
{
int imageWidth, imageHeight, colorChannels;
std::string pixels;
};
int main()
{
std::string fileName = "someFile.dat"; //The file we will store the image data in
std::string srcImageName = "bed_feet_top.png"; //Texture from Minecraft 1.8.9 textures folder
std::string dstImageName = "someImage.png"; //The name of image we will create from the read data
//Reading the image data
PNG srcImage;
unsigned char* buffer = stbi_load(srcImageName.c_str(), &srcImage.imageWidth, &srcImage.imageHeight, &srcImage.colorChannels, STBI_rgb_alpha);
if (!buffer)
{
std::cout << "Failed to read image " + srcImageName << std::endl;
return 1;
}
int dataSize = srcImage.imageWidth * srcImage.imageHeight * srcImage.colorChannels;
srcImage.pixels.assign((char*)buffer, dataSize);
//Serializing the image data
std::vector<std::byte> srcData;
try
{
zpp::bits::out out(srcData);
out(srcImage).or_throw();
}
catch (const std::exception& error)
{
std::cerr << "Serialization failed with error: " << error.what() << std::endl;
}
catch (...) {
std::cerr << "Serialization failed with unknown error!" << std::endl;
}
//Writing the image data to file
std::ofstream ofs(fileName, std::ios::out | std::ios::binary);
if (!ofs.is_open())
{
std::cout << "Failed to open output file stream!" << std::endl;
return 1;
}
size_t srcEntryCount = srcData.size();
ofs.write((char*)&srcEntryCount, 8);
ofs.write((char*)srcData.data(), srcEntryCount);
if (!ofs.good())
{
std::cerr << "Something went wrong when writing" << srcImageName << " to the file!" << std::endl;
ofs.close();
return 1;
}
ofs.close();
//Reading the image data from file
std::ifstream ifs(fileName, std::ios::in, std::ios::binary);
if (!ifs)
{
std::cout << "File doesnt exist!" << std::endl;
return 1;
}
if (!ifs.is_open())
{
std::cout << "Failed to open input file stream!" << std::endl;
return 1;
}
std::vector<std::byte> dstData;
size_t dstEntryCount;
ifs.read((char*)&dstEntryCount, 8);
dstData.resize(dstEntryCount);
ifs.read((char*)dstData.data(), dstEntryCount);
if (!ifs.good())
{
std::cerr << "Something went wrong when reading " << srcImageName << " from the file!" << std::endl;
ifs.close();
return 1; //If you comment this line you will see it will recreate the image from the read data just fine
}
ifs.close();
//Deserializing the read image data
PNG dstImage;
try
{
zpp::bits::in in(dstData);
in(dstImage).or_throw();
} catch (const std::exception& error)
{
std::cerr << "Deserialization failed with error: " << error.what() << std::endl;
} catch (...) {
std::cerr << "Deserialization failed with unknown error!" << std::endl;
}
//Recreating the image from the read image data
stbi_write_png(dstImageName.c_str(), dstImage.imageWidth, dstImage.imageHeight, dstImage.colorChannels, dstImage.pixels.c_str(), dstImage.imageWidth * sizeof(int));
}
I'm caling the objects src and dst, because I don't know how to call them shortly before and after serialization. :D

audio do not stop recording after pause ffmpeg c++

I am developing an application that record the screen and the audio from microphone. I implemented the pause function stopping video and audio thread on a condition variable, resuming them with a notify on the same condition variable. This is done in captureAudio(), in the main while. In this way works on macOS and linux, where I use avfoudation and alsa respectively, but on windows, with dshow, keep recording audio during the pause, when the thread is waiting on the condition variable. Does anybody know how can I fix this behaviour?
#include "ScreenRecorder.h"
using namespace std;
ScreenRecorder::ScreenRecorder() : pauseCapture(false), stopCapture(false), started(false), activeMenu(true) {
avcodec_register_all();
avdevice_register_all();
width = 1920;
height = 1200;
}
ScreenRecorder::~ScreenRecorder() {
if (started) {
value = av_write_trailer(outAVFormatContext);
if (value < 0) {
cerr << "Error in writing av trailer" << endl;
exit(-1);
}
avformat_close_input(&inAudioFormatContext);
if(inAudioFormatContext == nullptr){
cout << "inAudioFormatContext close successfully" << endl;
}
else{
cerr << "Error: unable to close the inAudioFormatContext" << endl;
exit(-1);
//throw "Error: unable to close the file";
}
avformat_free_context(inAudioFormatContext);
if(inAudioFormatContext == nullptr){
cout << "AudioFormat freed successfully" << endl;
}
else{
cerr << "Error: unable to free AudioFormatContext" << endl;
exit(-1);
}
avformat_close_input(&pAVFormatContext);
if (pAVFormatContext == nullptr) {
cout << "File close successfully" << endl;
}
else {
cerr << "Error: unable to close the file" << endl;
exit(-1);
//throw "Error: unable to close the file";
}
avformat_free_context(pAVFormatContext);
if (pAVFormatContext == nullptr) {
cout << "VideoFormat freed successfully" << endl;
}
else {
cerr << "Error: unable to free VideoFormatContext" << endl;
exit(-1);
}
}
}
/*==================================== VIDEO ==============================*/
int ScreenRecorder::openVideoDevice() throw() {
value = 0;
options = nullptr;
pAVFormatContext = nullptr;
pAVFormatContext = avformat_alloc_context();
string dimension = to_string(width) + "x" + to_string(height);
av_dict_set(&options, "video_size", dimension.c_str(), 0); //option to set the dimension of the screen section to record
#ifdef _WIN32
pAVInputFormat = av_find_input_format("gdigrab");
if (avformat_open_input(&pAVFormatContext, "desktop", pAVInputFormat, &options) != 0) {
cerr << "Couldn't open input stream" << endl;
exit(-1);
}
#elif defined linux
int offset_x = 0, offset_y = 0;
string url = ":0.0+" + to_string(offset_x) + "," + to_string(offset_y); //custom string to set the start point of the screen section
pAVInputFormat = av_find_input_format("x11grab");
value = avformat_open_input(&pAVFormatContext, url.c_str(), pAVInputFormat, &options);
if (value != 0) {
cerr << "Error in opening input device (video)" << endl;
exit(-1);
}
#else
value = av_dict_set(&options, "pixel_format", "0rgb", 0);
if (value < 0) {
cerr << "Error in setting pixel format" << endl;
exit(-1);
}
value = av_dict_set(&options, "video_device_index", "1", 0);
if (value < 0) {
cerr << "Error in setting video device index" << endl;
exit(-1);
}
pAVInputFormat = av_find_input_format("avfoundation");
if (avformat_open_input(&pAVFormatContext, "Capture screen 0:none", pAVInputFormat, &options) != 0) { //TODO trovare un modo per selezionare sempre lo schermo (forse "Capture screen 0")
cerr << "Error in opening input device" << endl;
exit(-1);
}
#endif
//set frame per second
value = av_dict_set(&options, "framerate", "30", 0);
if (value < 0) {
cerr << "Error in setting dictionary value (setting framerate)" << endl;
exit(-1);
}
value = av_dict_set(&options, "preset", "medium", 0);
if (value < 0) {
cerr << "Error in setting dictionary value (setting preset value)" << endl;
exit(-1);
}
/*
value = av_dict_set(&options, "vsync", "1", 0);
if(value < 0){
cerr << "Error in setting dictionary value (setting vsync value)" << endl;
exit(-1);
}
*/
value = av_dict_set(&options, "probesize", "60M", 0);
if (value < 0) {
cerr << "Error in setting probesize value" << endl;
exit(-1);
}
//get video stream infos from context
value = avformat_find_stream_info(pAVFormatContext, nullptr);
if (value < 0) {
cerr << "Error in retrieving the stream info" << endl;
exit(-1);
}
VideoStreamIndx = -1;
for (int i = 0; i < pAVFormatContext->nb_streams; i++) {
if (pAVFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
VideoStreamIndx = i;
break;
}
}
if (VideoStreamIndx == -1) {
cerr << "Error: unable to find video stream index" << endl;
exit(-2);
}
pAVCodecContext = pAVFormatContext->streams[VideoStreamIndx]->codec;
pAVCodec = avcodec_find_decoder(pAVCodecContext->codec_id/*params->codec_id*/);
if (pAVCodec == nullptr) {
cerr << "Error: unable to find decoder video" << endl;
exit(-1);
}
cout << "Insert height and width [h w]: "; //custom screen dimension to record
cin >> h >> w;*/
return 0;
}
/*========================================== AUDIO ============================*/
int ScreenRecorder::openAudioDevice() {
audioOptions = nullptr;
inAudioFormatContext = nullptr;
inAudioFormatContext = avformat_alloc_context();
value = av_dict_set(&audioOptions, "sample_rate", "44100", 0);
if (value < 0) {
cerr << "Error: cannot set audio sample rate" << endl;
exit(-1);
}
value = av_dict_set(&audioOptions, "async", "1", 0);
if (value < 0) {
cerr << "Error: cannot set audio sample rate" << endl;
exit(-1);
}
#if defined linux
audioInputFormat = av_find_input_format("alsa");
value = avformat_open_input(&inAudioFormatContext, "hw:0", audioInputFormat, &audioOptions);
if (value != 0) {
cerr << "Error in opening input device (audio)" << endl;
exit(-1);
}
#endif
#if defined _WIN32
audioInputFormat = av_find_input_format("dshow");
value = avformat_open_input(&inAudioFormatContext, "audio=Microfono (Realtek(R) Audio)", audioInputFormat, &audioOptions);
if (value != 0) {
cerr << "Error in opening input device (audio)" << endl;
exit(-1);
}
#endif
value = avformat_find_stream_info(inAudioFormatContext, nullptr);
if (value != 0) {
cerr << "Error: cannot find the audio stream information" << endl;
exit(-1);
}
audioStreamIndx = -1;
for (int i = 0; i < inAudioFormatContext->nb_streams; i++) {
if (inAudioFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
audioStreamIndx = i;
break;
}
}
if (audioStreamIndx == -1) {
cerr << "Error: unable to find audio stream index" << endl;
exit(-2);
}
}
int ScreenRecorder::initOutputFile() {
value = 0;
outAVFormatContext = nullptr;
outputAVFormat = av_guess_format(nullptr, "output.mp4", nullptr);
if (outputAVFormat == nullptr) {
cerr << "Error in guessing the video format, try with correct format" << endl;
exit(-5);
}
avformat_alloc_output_context2(&outAVFormatContext, outputAVFormat, outputAVFormat->name, "..\\media\\output.mp4");
if (outAVFormatContext == nullptr) {
cerr << "Error in allocating outAVFormatContext" << endl;
exit(-4);
}
/*===========================================================================*/
this->generateVideoStream();
this->generateAudioStream();
//create an empty video file
if (!(outAVFormatContext->flags & AVFMT_NOFILE)) {
if (avio_open2(&outAVFormatContext->pb, "..\\media\\output.mp4", AVIO_FLAG_WRITE, nullptr, nullptr) < 0) {
cerr << "Error in creating the video file" << endl;
exit(-10);
}
}
if (outAVFormatContext->nb_streams == 0) {
cerr << "Output file does not contain any stream" << endl;
exit(-11);
}
value = avformat_write_header(outAVFormatContext, &options);
if (value < 0) {
cerr << "Error in writing the header context" << endl;
exit(-12);
}
return 0;
}
/*=================================== VIDEO ==================================*/
void ScreenRecorder::generateVideoStream() {
//Generate video stream
videoSt = avformat_new_stream(outAVFormatContext, nullptr);
if (videoSt == nullptr) {
cerr << "Error in creating AVFormatStream" << endl;
exit(-6);
}
outVideoCodec = avcodec_find_encoder(AV_CODEC_ID_MPEG4); //AV_CODEC_ID_MPEG4
if (outVideoCodec == nullptr) {
cerr << "Error in finding the AVCodec, try again with the correct codec" << endl;
exit(-8);
}
avcodec_alloc_context3(outAVCodec)
outVideoCodecContext = avcodec_alloc_context3(outVideoCodec);
if (outVideoCodecContext == nullptr) {
cerr << "Error in allocating the codec context" << endl;
exit(-7);
}
//set properties of the video file (stream)
outVideoCodecContext = videoSt->codec;
outVideoCodecContext->codec_id = AV_CODEC_ID_MPEG4;
outVideoCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;
outVideoCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
outVideoCodecContext->bit_rate = 10000000;
outVideoCodecContext->width = width;
outVideoCodecContext->height = height;
outVideoCodecContext->gop_size = 10;
outVideoCodecContext->global_quality = 500;
outVideoCodecContext->max_b_frames = 2;
outVideoCodecContext->time_base.num = 1;
outVideoCodecContext->time_base.den = 30;
outVideoCodecContext->bit_rate_tolerance = 400000;
if (outVideoCodecContext->codec_id == AV_CODEC_ID_H264) {
av_opt_set(outVideoCodecContext->priv_data, "preset", "slow", 0);
}
if (outAVFormatContext->oformat->flags & AVFMT_GLOBALHEADER) {
outVideoCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
value = avcodec_open2(outVideoCodecContext, outVideoCodec, nullptr);
if (value < 0) {
cerr << "Error in opening the AVCodec" << endl;
exit(-9);
}
outVideoStreamIndex = -1;
for (int i = 0; i < outAVFormatContext->nb_streams; i++) {
if (outAVFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_UNKNOWN) {
outVideoStreamIndex = i;
}
}
if (outVideoStreamIndex < 0) {
cerr << "Error: cannot find a free stream index for video output" << endl;
exit(-1);
}
avcodec_parameters_from_context(outAVFormatContext->streams[outVideoStreamIndex]->codecpar, outVideoCodecContext);
}
/*=============================== AUDIO ==================================*/
void ScreenRecorder::generateAudioStream() {
AVCodecParameters* params = inAudioFormatContext->streams[audioStreamIndx]->codecpar;
inAudioCodec = avcodec_find_decoder(params->codec_id);
if (inAudioCodec == nullptr) {
cerr << "Error: cannot find the audio decoder" << endl;
exit(-1);
}
inAudioCodecContext = avcodec_alloc_context3(inAudioCodec);
if (avcodec_parameters_to_context(inAudioCodecContext, params) < 0) {
cout << "Cannot create codec context for audio input" << endl;
}
value = avcodec_open2(inAudioCodecContext, inAudioCodec, nullptr);
if (value < 0) {
cerr << "Error: cannot open the input audio codec" << endl;
exit(-1);
}
//Generate audio stream
outAudioCodecContext = nullptr;
outAudioCodec = nullptr;
int i;
AVStream* audio_st = avformat_new_stream(outAVFormatContext, nullptr);
if (audio_st == nullptr) {
cerr << "Error: cannot create audio stream" << endl;
exit(1);
}
outAudioCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if (outAudioCodec == nullptr) {
cerr << "Error: cannot find requested encoder" << endl;
exit(1);
}
outAudioCodecContext = avcodec_alloc_context3(outAudioCodec);
if (outAudioCodecContext == nullptr) {
cerr << "Error: cannot create related VideoCodecContext" << endl;
exit(1);
}
if ((outAudioCodec)->supported_samplerates) {
outAudioCodecContext->sample_rate = (outAudioCodec)->supported_samplerates[0];
for (i = 0; (outAudioCodec)->supported_samplerates[i]; i++) {
if ((outAudioCodec)->supported_samplerates[i] == inAudioCodecContext->sample_rate)
outAudioCodecContext->sample_rate = inAudioCodecContext->sample_rate;
}
}
outAudioCodecContext->codec_id = AV_CODEC_ID_AAC;
outAudioCodecContext->sample_fmt = (outAudioCodec)->sample_fmts ? (outAudioCodec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
outAudioCodecContext->channels = inAudioCodecContext->channels;
outAudioCodecContext->channel_layout = av_get_default_channel_layout(outAudioCodecContext->channels);
outAudioCodecContext->bit_rate = 96000;
outAudioCodecContext->time_base = { 1, inAudioCodecContext->sample_rate };
outAudioCodecContext->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
if ((outAVFormatContext)->oformat->flags & AVFMT_GLOBALHEADER) {
outAudioCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
if (avcodec_open2(outAudioCodecContext, outAudioCodec, nullptr) < 0) {
cerr << "error in opening the avcodec" << endl;
exit(1);
}
//find a free stream index
outAudioStreamIndex = -1;
for (i = 0; i < outAVFormatContext->nb_streams; i++) {
if (outAVFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_UNKNOWN) {
outAudioStreamIndex = i;
}
}
if (outAudioStreamIndex < 0) {
cerr << "Error: cannot find a free stream for audio on the output" << endl;
exit(1);
}
avcodec_parameters_from_context(outAVFormatContext->streams[outAudioStreamIndex]->codecpar, outAudioCodecContext);
}
int ScreenRecorder::init_fifo()
{
/* Create the FIFO buffer based on the specified output sample format. */
if (!(fifo = av_audio_fifo_alloc(outAudioCodecContext->sample_fmt,
outAudioCodecContext->channels, 1))) {
fprintf(stderr, "Could not allocate FIFO\n");
return AVERROR(ENOMEM);
}
return 0;
}
int ScreenRecorder::add_samples_to_fifo(uint8_t** converted_input_samples, const int frame_size) {
int error;
/* Make the FIFO as large as it needs to be to hold both,
* the old and the new samples. */
if ((error = av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + frame_size)) < 0) {
fprintf(stderr, "Could not reallocate FIFO\n");
return error;
}
/* Store the new samples in the FIFO buffer. */
if (av_audio_fifo_write(fifo, (void**)converted_input_samples, frame_size) < frame_size) {
fprintf(stderr, "Could not write data to FIFO\n");
return AVERROR_EXIT;
}
return 0;
}
int ScreenRecorder::initConvertedSamples(uint8_t*** converted_input_samples,
AVCodecContext* output_codec_context,
int frame_size) {
int error;
/* Allocate as many pointers as there are audio channels.
* Each pointer will later point to the audio samples of the corresponding
* channels (although it may be NULL for interleaved formats).
*/
if (!(*converted_input_samples = (uint8_t**)calloc(output_codec_context->channels,
sizeof(**converted_input_samples)))) {
fprintf(stderr, "Could not allocate converted input sample pointers\n");
return AVERROR(ENOMEM);
}
/* Allocate memory for the samples of all channels in one consecutive
* block for convenience. */
if (av_samples_alloc(*converted_input_samples, nullptr,
output_codec_context->channels,
frame_size,
output_codec_context->sample_fmt, 0) < 0) {
exit(1);
}
return 0;
}
static int64_t pts = 0;
void ScreenRecorder::captureAudio() {
int ret;
AVPacket* inPacket, * outPacket;
AVFrame* rawFrame, * scaledFrame;
uint8_t** resampledData;
init_fifo();
//allocate space for a packet
inPacket = (AVPacket*)av_malloc(sizeof(AVPacket));
if (!inPacket) {
cerr << "Cannot allocate an AVPacket for encoded video" << endl;
exit(1);
}
av_init_packet(inPacket);
//allocate space for a packet
rawFrame = av_frame_alloc();
if (!rawFrame) {
cerr << "Cannot allocate an AVPacket for encoded video" << endl;
exit(1);
}
scaledFrame = av_frame_alloc();
if (!scaledFrame) {
cerr << "Cannot allocate an AVPacket for encoded video" << endl;
exit(1);
}
outPacket = (AVPacket*)av_malloc(sizeof(AVPacket));
if (!outPacket) {
cerr << "Cannot allocate an AVPacket for encoded video" << endl;
exit(1);
}
//init the resampler
SwrContext* resampleContext = nullptr;
resampleContext = swr_alloc_set_opts(resampleContext,
av_get_default_channel_layout(outAudioCodecContext->channels),
outAudioCodecContext->sample_fmt,
outAudioCodecContext->sample_rate,
av_get_default_channel_layout(inAudioCodecContext->channels),
inAudioCodecContext->sample_fmt,
inAudioCodecContext->sample_rate,
0,
nullptr);
if (!resampleContext) {
cerr << "Cannot allocate the resample context" << endl;
exit(1);
}
if ((swr_init(resampleContext)) < 0) {
fprintf(stderr, "Could not open resample context\n");
swr_free(&resampleContext);
exit(1);
}
while (true) {
if (pauseCapture) {
cout << "Pause audio" << endl;
}
cv.wait(ul, [this]() { return !pauseCapture; });
if (stopCapture) {
break;
}
ul.unlock();
if (av_read_frame(inAudioFormatContext, inPacket) >= 0 && inPacket->stream_index == audioStreamIndx) {
//decode audio routing
av_packet_rescale_ts(outPacket, inAudioFormatContext->streams[audioStreamIndx]->time_base, inAudioCodecContext->time_base);
if ((ret = avcodec_send_packet(inAudioCodecContext, inPacket)) < 0) {
cout << "Cannot decode current audio packet " << ret << endl;
continue;
}
while (ret >= 0) {
ret = avcodec_receive_frame(inAudioCodecContext, rawFrame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0) {
cerr << "Error during decoding" << endl;
exit(1);
}
if (outAVFormatContext->streams[outAudioStreamIndex]->start_time <= 0) {
outAVFormatContext->streams[outAudioStreamIndex]->start_time = rawFrame->pts;
}
initConvertedSamples(&resampledData, outAudioCodecContext, rawFrame->nb_samples);
swr_convert(resampleContext,
resampledData, rawFrame->nb_samples,
(const uint8_t**)rawFrame->extended_data, rawFrame->nb_samp
add_samples_to_fifo(resampledData, rawFrame->nb_samples);
//raw frame ready
av_init_packet(outPacket);
outPacket->data = nullptr;
outPacket->size = 0;
const int frame_size = FFMAX(av_audio_fifo_size(fifo), outAudioCodecContext->frame_size);
scaledFrame = av_frame_alloc();
if (!scaledFrame) {
cerr << "Cannot allocate an AVPacket for encoded video" << endl;
exit(1);
}
scaledFrame->nb_samples = outAudioCodecContext->frame_size;
scaledFrame->channel_layout = outAudioCodecContext->channel_layout;
scaledFrame->format = outAudioCodecContext->sample_fmt;
scaledFrame->sample_rate = outAudioCodecContext->sample_rate;
av_frame_get_buffer(scaledFrame, 0);
while (av_audio_fifo_size(fifo) >= outAudioCodecContext->frame_size) {
ret = av_audio_fifo_read(fifo, (void**)(scaledFrame->data), outAudioCodecContext->frame_size);
scaledFrame->pts = pts;
pts += scaledFrame->nb_samples;
if (avcodec_send_frame(outAudioCodecContext, scaledFrame) < 0) {
cout << "Cannot encode current audio packet " << endl;
exit(1);
}
while (ret >= 0) {
ret = avcodec_receive_packet(outAudioCodecContext, outPacket);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0) {
cerr << "Error during encoding" << endl;
exit(1);
}
av_packet_rescale_ts(outPacket, outAudioCodecContext->time_base, outAVFormatContext->streams[outAudioStreamIndex]->time_base);
outPacket->stream_index = outAudioStreamIndex;
write_lock.lock();
if (av_write_frame(outAVFormatContext, outPacket) != 0)
{
cerr << "Error in writing audio frame" << endl;
}
write_lock.unlock();
av_packet_unref(outPacket);
}
ret = 0;
}
av_frame_free(&scaledFrame);
av_packet_unref(outPacket);
}
}
}
}
int ScreenRecorder::captureVideoFrames() {
int64_t pts = 0;
int flag;
int frameFinished = 0;
bool endPause = false;
int numPause = 0;
ofstream outFile{ "..\\media\\log.txt", ios::out };
int frameIndex = 0;
value = 0;
pAVPacket = (AVPacket*)av_malloc(sizeof(AVPacket));
if (pAVPacket == nullptr) {
cerr << "Error in allocating AVPacket" << endl;
exit(-1);
}
pAVFrame = av_frame_alloc();
if (pAVFrame == nullptr) {
cerr << "Error: unable to alloc the AVFrame resources" << endl;
exit(-1);
}
outFrame = av_frame_alloc();
if (outFrame == nullptr) {
cerr << "Error: unable to alloc the AVFrame resources for out frame" << endl;
exit(-1);
}
int videoOutBuffSize;
int nBytes = av_image_get_buffer_size(outVideoCodecContext->pix_fmt, outVideoCodecContext->width, outVideoCodecContext->height, 32);
uint8_t* videoOutBuff = (uint8_t*)av_malloc(nBytes);
if (videoOutBuff == nullptr) {
cerr << "Error: unable to allocate memory" << endl;
exit(-1);
}
value = av_image_fill_arrays(outFrame->data, outFrame->linesize, videoOutBuff, AV_PIX_FMT_YUV420P, outVideoCodecContext->width, outVideoCodecContext->height, 1);
if (value < 0) {
cerr << "Error in filling image array" << endl;
}
SwsContext* swsCtx_;
if (avcodec_open2(pAVCodecContext, pAVCodec, nullptr) < 0) {
cerr << "Could not open codec" << endl;
exit(-1);
}
swsCtx_ = sws_getContext(pAVCodecContext->width, pAVCodecContext->height, pAVCodecContext->pix_fmt, outVideoCodecContext->width, outVideoCodecContext->height, outVideoCodecContext->pix_fmt, SWS_BICUBIC,
nullptr, nullptr, nullptr);
AVPacket outPacket;
int gotPicture;
time_t startTime;
time(&startTime);
while (true) {
if (pauseCapture) {
cout << "Pause" << endl;
outFile << "/////////////////// Pause ///////////////////" << endl;
cout << "outVideoCodecContext->time_base: " << outVideoCodecContext->time_base.num << ", " << outVideoCodecContext->time_base.den << endl;
}
cv.wait(ul, [this]() { return !pauseCapture; }); //pause capture (not busy waiting)
if (endPause) {
endPause = false;
}
if (stopCapture) //check if the capture has to stop
break;
ul.unlock();
if (av_read_frame(pAVFormatContext, pAVPacket) >= 0 && pAVPacket->stream_index == VideoStreamIndx) {
av_packet_rescale_ts(pAVPacket, pAVFormatContext->streams[VideoStreamIndx]->time_base, pAVCodecContext->time_base);
value = avcodec_decode_video2(pAVCodecContext, pAVFrame, &frameFinished, pAVPacket);
if (value < 0) {
cout << "Unable to decode video" << endl;
}
if (frameFinished) { //frame successfully decoded
//sws_scale(swsCtx_, pAVFrame->data, pAVFrame->linesize, 0, pAVCodecContext->height, outFrame->data, outFrame->linesize);
av_init_packet(&outPacket);
outPacket.data = nullptr;
outPacket.size = 0;
if (outAVFormatContext->streams[outVideoStreamIndex]->start_time <= 0) {
outAVFormatContext->streams[outVideoStreamIndex]->start_time = pAVFrame->pts;
}
//disable warning on the console
outFrame->width = outVideoCodecContext->width;
outFrame->height = outVideoCodecContext->height;
outFrame->format = outVideoCodecContext->pix_fmt;
sws_scale(swsCtx_, pAVFrame->data, pAVFrame->linesize, 0, pAVCodecContext->height, outFrame->data, outFrame->linesize);
avcodec_encode_video2(outVideoCodecContext, &outPacket, outFrame, &gotPicture);
if (gotPicture) {
if (outPacket.pts != AV_NOPTS_VALUE) {
outPacket.pts = av_rescale_q(outPacket.pts, videoSt->codec->time_base, videoSt->time_base);
}
if (outPacket.dts != AV_NOPTS_VALUE) {
outPacket.dts = av_rescale_q(outPacket.dts, videoSt->codec->time_base, videoSt->time_base);
}
//cout << "Write frame " << j++ << " (size = " << outPacket.size / 1000 << ")" << endl;
//cout << "(size = " << outPacket.size << ")" << endl;
//av_packet_rescale_ts(&outPacket, outVideoCodecContext->time_base, outAVFormatContext->streams[outVideoStreamIndex]->time_base);
//outPacket.stream_index = outVideoStreamIndex;
outFile << "outPacket->duration: " << outPacket.duration << ", " << "pAVPacket->duration: " << pAVPacket->duration << endl;
outFile << "outPacket->pts: " << outPacket.pts << ", " << "pAVPacket->pts: " << pAVPacket->pts << endl;
outFile << "outPacket.dts: " << outPacket.dts << ", " << "pAVPacket->dts: " << pAVPacket->dts << endl;
time_t timer;
double seconds;
mu.lock();
if (!activeMenu) {
time(&timer);
seconds = difftime(timer, startTime);
int h = (int)(seconds / 3600);
int m = (int)(seconds / 60) % 60;
int s = (int)(seconds) % 60;
std::cout << std::flush << "\r" << std::setw(2) << std::setfill('0') << h << ':'
<< std::setw(2) << std::setfill('0') << m << ':'
<< std::setw(2) << std::setfill('0') << s << std::flush;
}
mu.unlock();
write_lock.lock();
if (av_write_frame(outAVFormatContext, &outPacket) != 0) {
cerr << "Error in writing video frame" << endl;
}
write_lock.unlock();
av_packet_unref(&outPacket);
}
av_packet_unref(&outPacket);
av_free_packet(pAVPacket); //avoid memory saturation
}
}
}
outFile.close();
av_free(videoOutBuff);
return 0;
}
I resolved this problem performing an avformat_close_input(&inAudioFormatContext) before enter in pause, and an avformat_open_input(&inAudioFormatContext, "audio=Microfono (Realtek(R) Audio)", audioInputFormat, &audioOptions) after resume the recording. In this way the final file seems well syncronized with video.

Binaural beats in C++

I am trying to build a brain device using EEG input and outputting light pulses and binaural beats in "close to real-time" on a Raspberry PI. The light-output is no problem using WiringPi, but any audio output seems to be a major hurdle. The math of calculating a sinewave for a buffer is straightforward, but playing two frequencies on two channels via any standard libraries seems to be a very complicated process, and I can't come up with any relevant examples. I have successfully opened and closed an ALSA device thanks to this tutorial which complicates my fairly simple code tremendously but appears necessary for ALSA. I would be incredibly grateful if someone could show me the easiest method for playing two different calculated tones on left and right channels. The code below is the simplest ALSA playback example I could find.
#include <alsa/asoundlib.h>
#include <iostream>
using namespace std;
// Globals are generally a bad idea in code. We're using one here to keep it simple.
snd_pcm_t * _soundDevice;
bool Init(const char *name)
{
int i;
int err;
snd_pcm_hw_params_t *hw_params;
if( name == NULL )
{
// Try to open the default device
err = snd_pcm_open( &_soundDevice, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, 0 );
}
else
{
// Open the device we were told to open.
err = snd_pcm_open (&_soundDevice, name, SND_PCM_STREAM_PLAYBACK, 0);
}
// Check for error on open.
if( err < 0 )
{
cout << "Init: cannot open audio device " << name << " (" << snd_strerror (err) << ")" << endl;
return false;
}
else
{
cout << "Audio device opened successfully." << endl;
}
// Allocate the hardware parameter structure.
if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0)
{
cout << "Init: cannot allocate hardware parameter structure (" << snd_strerror (err) << ")" << endl;
return false;
}
if ((err = snd_pcm_hw_params_any (_soundDevice, hw_params)) < 0)
{
cout << "Init: cannot initialize hardware parameter structure (" << snd_strerror (err) << ")" << endl;
return false;
}
// Enable resampling.
unsigned int resample = 1;
err = snd_pcm_hw_params_set_rate_resample(_soundDevice, hw_params, resample);
if (err < 0)
{
cout << "Init: Resampling setup failed for playback: " << snd_strerror(err) << endl;
return err;
}
// Set access to RW interleaved.
if ((err = snd_pcm_hw_params_set_access (_soundDevice, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
cout << "Init: cannot set access type (" << snd_strerror (err) << ")" << endl;
return false;
}
if ((err = snd_pcm_hw_params_set_format (_soundDevice, hw_params, SND_PCM_FORMAT_S16_LE)) < 0)
{
cout << "Init: cannot set sample format (" << snd_strerror (err) << ")" << endl;
return false;
}
// Set channels to stereo (2).
if ((err = snd_pcm_hw_params_set_channels (_soundDevice, hw_params, 2)) < 0)
{
cout << "Init: cannot set channel count (" << snd_strerror (err) << ")" << endl;
return false;
}
// Set sample rate.
unsigned int actualRate = 44100;
if ((err = snd_pcm_hw_params_set_rate_near (_soundDevice, hw_params, &actualRate, 0)) < 0)
{
cout << "Init: cannot set sample rate to 44100. (" << snd_strerror (err) << ")" << endl;
return false;
}
if( actualRate < 44100 )
{
cout << "Init: sample rate does not match requested rate. (" << "44100 requested, " << actualRate << " acquired)" << endl;
}
// Apply the hardware parameters that we've set.
if ((err = snd_pcm_hw_params (_soundDevice, hw_params)) < 0)
{
cout << "Init: cannot set parameters (" << snd_strerror (err) << ")" << endl;
return false;
}
else
{
cout << "Audio device parameters have been set successfully." << endl;
}
// Get the buffer size.
snd_pcm_uframes_t bufferSize;
snd_pcm_hw_params_get_buffer_size( hw_params, &bufferSize );
// If we were going to do more with our sound device we would want to store
// the buffer size so we know how much data we will need to fill it with.
cout << "Init: Buffer size = " << bufferSize << " frames." << endl;
// Display the bit size of samples.
cout << "Init: Significant bits for linear samples = " << snd_pcm_hw_params_get_sbits(hw_params) << endl;
// Free the hardware parameters now that we're done with them.
snd_pcm_hw_params_free (hw_params);
// Prepare interface for use.
if ((err = snd_pcm_prepare (_soundDevice)) < 0)
{
cout << "Init: cannot prepare audio interface for use (" << snd_strerror (err) << ")" << endl;
return false;
}
else
{
cout << "Audio device has been prepared for use." << endl;
}
return true;
}
bool UnInit()
{
snd_pcm_close (_soundDevice);
cout << "Audio device has been uninitialized." << endl;
return true;
}
int main( char *argc, int argv )
{
Init(NULL);
UnInit();
return 0;
}
Use some up-to-date example, like this:
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
static const char *device = "default";
unsigned short buffer[2 * 24000];
int main(void)
{
int err;
snd_pcm_t *handle;
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle,
SND_PCM_FORMAT_S16,
SND_PCM_ACCESS_RW_INTERLEAVED,
2, /* channels */
48000, /* rate */
1,
500000)) < 0) { /* buffer: 0.5 sec */
printf("open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
for (;;) {
for (int i = 0; i < 24000; i++) {
buffer[2 * i + 0] = 32767 * sin(...); /* left channel */
buffer[2 * i + 1] = 32767 * sin(...); /* right channel */
}
snd_pcm_sframes_t frames = snd_pcm_writei(handle, buffer, 24000);
if (frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
break;
}
}
snd_pcm_close(handle);
return 0;
}

Joining Portaudio and Opus

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);

Reading in Wav header - Not setting data size

I'm trying to read in the Header information of a .wav file.
If I have a .wav file that has a low sample rate (22050) it will read all the information in perfectly, however, if I have a higher Sample Rate (8000) then it fails to read in some information:
"dataSize" set's when using a 22050 .wav file however, when using a 8000 .wav file it does not get set and just displays some random numbers.. e.g. "1672494080" when the actual size is around 4k-4.5k in size.
Any suggestions to where I am going wrong?
EDIT:
#include <iostream>
#include <fstream>
#include <vector>
#include <inttypes.h>
#include <stdint.h>
#include <math.h>
using namespace std;
struct riff_hdr
{
char id[4];
uint32_t size;
char type[4];
};
struct chunk_hdr
{
char id[4];
uint32_t size;
};
struct wavefmt
{
uint16_t format_tag;
uint16_t channels;
uint32_t sample_rate;
uint32_t avg_bytes_sec;
uint16_t block_align;
uint16_t bits_per_sample;
uint16_t extra_size;
};
riff_hdr riff;
chunk_hdr chunk;
wavefmt fmt = {0};
uint32_t padded_size;
vector<uint8_t> chunk_data;
bool readHeader(ifstream &file) {
file.read(reinterpret_cast<char*>(&riff), sizeof(riff));
if (memcmp(riff.id, "RIFF", 4) == 0)
{
cout << "size=" << riff.size << endl;
cout << "id=" << string(riff.type, 4) << endl;
if (memcmp(riff.type, "WAVE", 4) == 0)
{
// chunks can be in any order!
// there is no guarantee that "fmt" is the first chunk.
// there is no guarantee that "fmt" is immediately followed by "data".
// There can be other chunks present!
do {
file.read(reinterpret_cast<char*>(&chunk), sizeof(chunk));
padded_size = ((chunk.size + 2 - 1) & ~1);
cout << "id=" << string(chunk.id, 4) << endl;
cout << "size=" << chunk.size << endl;
cout << "padded size=" << padded_size << endl;
if (memcmp(chunk.id, "fmt\0", 4) == 0)
{
if (chunk.size < sizeof(wavefmt))
{
// error!
file.ignore(padded_size);
}else{
// THIS block doesn't seem to be executing
chunk_data.resize(padded_size);
file.read(reinterpret_cast<char*>(&chunk_data[0]), padded_size);
fmt = *(reinterpret_cast<wavefmt*>(&chunk_data[0]));
cout << "format_tag=" << fmt.format_tag << endl;
cout << "channels=" << fmt.channels << endl;
cout << "sample_rate=" << fmt.sample_rate << endl;
cout << "avg_bytes_sec=" << fmt.avg_bytes_sec << endl;
cout << "block_align=" << fmt.block_align << endl;
cout << "bits_per_sample=" << fmt.bits_per_sample << endl;
cout << "extra_size=" << fmt.extra_size << endl;
}
if(fmt.format_tag != 1)
{
uint8_t *extra_data = &chunk_data[sizeof(wavefmt)];
}
}else if(memcmp(chunk.id, "data", 4) == 0) {
file.ignore(padded_size);
}else{
file.ignore(padded_size);
}
}while ((!file) && (!file.eof()));
}
}
return true;
}
int main()
{
ifstream file("example2.wav");
readHeader(file);
return 0;
}
OUTPUT:
size=41398
id=WAVE
id=fmt
size=18
padded size=18
chunk_data size=0
Where am I going wrong?
You have two problems with your code:
There is a 2-byte integer after the bitsPerSample value that you are not reading. It specifies the size of any extra data in that chunk. If the value of format2 indicates a PCM format only, you can ignore the value of the integer (it will usually be 0 anyway, but it may also be garbage), but you still have to account for its presense. The integer cannot be ignored for non-PCM formats, you have to read the value and then read how many bytes it says. You need to make sure you are reading the entire chunk before then entering your while loop, otherwise you will not be on the correct starting position in the file to read further chunks.
You are not taking into account that chunks are padded to the nearest WORD boundary, but the chunk size does not include any padding. When you call seekg(), you need to round the value up to the next WORD boundary.
Update: based on the new code you posted, it should look more like this instead:
#include <iostream>
#include <fstream>
#include <vector>
#include <inttypes.h>
#include <stdint.h>
#include <math.h>
using namespace std;
// if your compiler does not have pshpack1.h and poppack.h, then
// use #pragma pack instead. It is important that these structures
// be byte-alignd!
#include <pshpack1.h>
struct s_riff_hdr
{
char id[4];
uint32_t size;
char type[4];
};
struct s_chunk_hdr
{
char id[4];
uint32_t size;
};
struct s_wavefmt
{
uint16_t format_tag;
uint16_t channels;
uint32_t sample_rate;
uint32_t avg_bytes_sec;
uint16_t block_align;
};
struct s_wavefmtex
{
s_wavefmt fmt;
uint16_t bits_per_sample;
uint16_t extra_size;
};
struct s_pcmwavefmt
{
s_wavefmt fmt;
uint16_t bits_per_sample;
};
#include <poppack.h>
bool readWave(ifstream &file)
{
s_riff_hdr riff_hdr;
s_chunk_hdr chunk_hdr;
uint32_t padded_size;
vector<uint8_t> fmt_data;
s_wavefmt *fmt = NULL;
file.read(reinterpret_cast<char*>(&riff_hdr), sizeof(riff_hdr));
if (!file) return false;
if (memcmp(riff_hdr.id, "RIFF", 4) != 0) return false;
cout << "size=" << riff_hdr.size << endl;
cout << "type=" << string(riff_hdr.type, 4) << endl;
if (memcmp(riff_hdr.type, "WAVE", 4) != 0) return false;
// chunks can be in any order!
// there is no guarantee that "fmt" is the first chunk.
// there is no guarantee that "fmt" is immediately followed by "data".
// There can be other chunks present!
do
{
file.read(reinterpret_cast<char*>(&chunk_hdr), sizeof(chunk_hdr));
if (!file) return false;
padded_size = ((chunk_hdr.size + 1) & ~1);
cout << "id=" << string(chunk_hdr.id, 4) << endl;
cout << "size=" << chunk_hdr.size << endl;
cout << "padded size=" << padded_size << endl;
if (memcmp(chunk_hdr.id, "fmt ", 4) == 0)
{
if (chunk_hdr.size < sizeof(s_wavefmt)) return false;
fmt_data.resize(padded_size);
file.read(reinterpret_cast<char*>(&fmt_data[0]), padded_size);
if (!file) return false;
fmt = reinterpret_cast<s_wavefmt*>(&fmt_data[0]);
cout << "format_tag=" << fmt->format_tag << endl;
cout << "channels=" << fmt->channels << endl;
cout << "sample_rate=" << fmt->sample_rate << endl;
cout << "avg_bytes_sec=" << fmt->avg_bytes_sec << endl;
cout << "block_align=" << fmt->block_align << endl;
if (fmt->format_tag == 1) // PCM
{
if (chunk_hdr.size < sizeof(s_pcmwavefmt)) return false;
s_pcmwavefmt *pcm_fmt = reinterpret_cast<s_pcmwavefmt*>(fmt);
cout << "bits_per_sample=" << pcm_fmt->bits_per_sample << endl;
}
else
{
if (chunk_hdr.size < sizeof(s_wavefmtex)) return false;
s_wavefmtex *fmt_ex = reinterpret_cast<s_wavefmtex*>(fmt);
cout << "bits_per_sample=" << fmt_ex->bits_per_sample << endl;
cout << "extra_size=" << fmt_ex->extra_size << endl;
if (fmt_ex->extra_size != 0)
{
if (chunk_hdr.size < (sizeof(s_wavefmtex) + fmt_ex->extra_size)) return false;
uint8_t *extra_data = reinterpret_cast<uint8_t*>(fmt_ex + 1);
// use extra_data, up to extra_size bytes, as needed...
}
}
}
else if (memcmp(chunk_hdr.id, "data", 4) == 0)
{
// process chunk data, according to fmt, as needed...
file.ignore(padded_size);
if (!file) return false;
}
else
{
// process other chunks as needed...
file.ignore(padded_size);
if (!file) return false;
}
}
while (!file.eof());
return true;
}
int main()
{
ifstream file("example2.wav");
readWave(file);
return 0;
}