Loading a .WAV file for OpenAL - c++

I am trying to load .WAV files to be played with OpenAL. I am following an example I found on the internet, but it is acting strangely. Here is the code:
struct RIFF_Header {
char chunkID[4];
long chunkSize;//size not including chunkSize or chunkID
char format[4];
};
/*
* Struct to hold fmt subchunk data for WAVE files.
*/
struct WAVE_Format {
char subChunkID[4];
long subChunkSize;
short audioFormat;
short numChannels;
long sampleRate;
long byteRate;
short blockAlign;
short bitsPerSample;
};
/*
* Struct to hold the data of the wave file
*/
struct WAVE_Data {
char subChunkID[4]; //should contain the word data
long subChunk2Size; //Stores the size of the data block
};
bool loadWavFile(std::string filename, ALuint* buffer,
ALsizei* size, ALsizei* frequency,
ALenum* format) {
//Local Declarations
FILE* soundFile = NULL;
WAVE_Format wave_format;
RIFF_Header riff_header;
WAVE_Data wave_data;
unsigned char* data;
*size = wave_data.subChunk2Size;
*frequency = wave_format.sampleRate;
if (wave_format.numChannels == 1) {
if (wave_format.bitsPerSample == 8 )
*format = AL_FORMAT_MONO8;
else if (wave_format.bitsPerSample == 16)
*format = AL_FORMAT_MONO16;
} else if (wave_format.numChannels == 2) {
if (wave_format.bitsPerSample == 8 )
*format = AL_FORMAT_STEREO8;
else if (wave_format.bitsPerSample == 16)
*format = AL_FORMAT_STEREO16;
}
try {
soundFile = fopen(filename.c_str(), "rb");
if (!soundFile)
throw (filename);
// Read in the first chunk into the struct
fread(&riff_header, sizeof(RIFF_Header), 1, soundFile);
//check for RIFF and WAVE tag in memeory
if ((riff_header.chunkID[0] != 'R' ||
riff_header.chunkID[1] != 'I' ||
riff_header.chunkID[2] != 'F' ||
riff_header.chunkID[3] != 'F') ||
(riff_header.format[0] != 'W' ||
riff_header.format[1] != 'A' ||
riff_header.format[2] != 'V' ||
riff_header.format[3] != 'E'))
throw ("Invalid RIFF or WAVE Header");
//Read in the 2nd chunk for the wave info
fread(&wave_format, sizeof(WAVE_Format), 1, soundFile);
//check for fmt tag in memory
if (wave_format.subChunkID[0] != 'f' ||
wave_format.subChunkID[1] != 'm' ||
wave_format.subChunkID[2] != 't' ||
wave_format.subChunkID[3] != ' ')
throw ("Invalid Wave Format");
//check for extra parameters;
if (wave_format.subChunkSize > 16)
fseek(soundFile, sizeof(short), SEEK_CUR);
//Read in the the last byte of data before the sound file
fread(&wave_data, sizeof(WAVE_Data), 1, soundFile);
//check for data tag in memory
if (wave_data.subChunkID[0] != 'd' ||
wave_data.subChunkID[1] != 'a' ||
wave_data.subChunkID[2] != 't' ||
wave_data.subChunkID[3] != 'a')
throw ("Invalid data header");
//Allocate memory for data
data = new unsigned char[wave_data.subChunk2Size];
// Read in the sound data into the soundData variable
if (!fread(data, wave_data.subChunk2Size, 1, soundFile))
throw ("error loading WAVE data into struct!");
//Now we set the variables that we passed in with the
//data from the structs
*size = wave_data.subChunk2Size;
*frequency = wave_format.sampleRate;
//The format is worked out by looking at the number of
//channels and the bits per sample.
if (wave_format.numChannels == 1) {
if (wave_format.bitsPerSample == 8 )
*format = AL_FORMAT_MONO8;
else if (wave_format.bitsPerSample == 16)
*format = AL_FORMAT_MONO16;
} else if (wave_format.numChannels == 2) {
if (wave_format.bitsPerSample == 8 )
*format = AL_FORMAT_STEREO8;
else if (wave_format.bitsPerSample == 16)
*format = AL_FORMAT_STEREO16;
}
//create our openAL buffer and check for success
alGenBuffers(2, buffer);
if(alGetError() != AL_NO_ERROR) {
std::cerr << alGetError() << std::endl;
}
//now we put our data into the openAL buffer and
//check for success
alBufferData(*buffer, *format, (void*)data,
*size, *frequency);
if(alGetError() != AL_NO_ERROR) {
std::cerr << alGetError() << std::endl;
}
//clean up and return true if successful
fclose(soundFile);
delete data;
return true;
} catch(const char* error) {
//our catch statement for if we throw a string
std::cerr << error << " : trying to load "
<< filename << std::endl;
//clean up memory if wave loading fails
if (soundFile != NULL)
fclose(soundFile);
//return false to indicate the failure to load wave
delete data;
return false;
}
}
int main() {
ALuint buffer, source;
ALint state;
ALsizei size;
ALsizei frequency;
ALenum format;
ALCcontext *context;
ALCdevice *device;
device = alcOpenDevice(nullptr);
if (device == NULL)
{
cerr << "Error finding default Audio Output Device" << endl;
}
context = alcCreateContext(device,NULL);
alcMakeContextCurrent(context);
alGetError();
loadWavFile("test.wav", &buffer, &size, &frequency, &format);
alGenSources(1, &source);
alSourcei(source, AL_BUFFER, buffer);
// Play
alSourcePlay(source);
// Wait for the song to complete
do {
alGetSourcei(source, AL_SOURCE_STATE, &state);
} while (state == AL_PLAYING);
// Clean up sources and buffers
alDeleteSources(1, &source);
alDeleteBuffers(1, &buffer);
return 0;
}
I have several WAV files both around 50kb. They load and play just fine. However, when I try to load a whole song (yes, I verified the file was correctly formatted using VLC Media Player and MusicBee) it returns 'Invalid data header : trying to load test.wav', which is caused by this chunk right here:
if (wave_data.subChunkID[0] != 'd' ||
wave_data.subChunkID[1] != 'a' ||
wave_data.subChunkID[2] != 't' ||
wave_data.subChunkID[3] != 'a')
throw ("Invalid data header");
I suspect that it is something size-related that is throwing off that header, as it seems that things only under 1000kb work (haven't totally tested that, its hard to find perfectly sized sound files floating around on my computer and on the internet). That is only a guess though, I really haven't a clue what is going on. Help is appreciated!

I know this question is around for a long time but I found a tutorial and I tested it. It works. Try this:
//http://www.youtube.com/user/thecplusplusguy
//Playing 3D sound with OpenAL, and loading a wav file manually
#include <iostream>
#include <fstream>
#include <cstring>
#include <al.h>
#include <alc.h>
bool isBigEndian()
{
int a = 1;
return !((char*)&a)[0];
}
int convertToInt(char* buffer, int len)
{
int a = 0;
if (!isBigEndian())
for (int i = 0; i<len; i++)
((char*)&a)[i] = buffer[i];
else
for (int i = 0; i<len; i++)
((char*)&a)[3 - i] = buffer[i];
return a;
}
char* loadWAV(const char* fn, int& chan, int& samplerate, int& bps, int& size)
{
char buffer[4];
std::ifstream in(fn, std::ios::binary);
in.read(buffer, 4);
if (strncmp(buffer, "RIFF", 4) != 0)
{
std::cout << "this is not a valid WAVE file" << std::endl;
return NULL;
}
in.read(buffer, 4);
in.read(buffer, 4); //WAVE
in.read(buffer, 4); //fmt
in.read(buffer, 4); //16
in.read(buffer, 2); //1
in.read(buffer, 2);
chan = convertToInt(buffer, 2);
in.read(buffer, 4);
samplerate = convertToInt(buffer, 4);
in.read(buffer, 4);
in.read(buffer, 2);
in.read(buffer, 2);
bps = convertToInt(buffer, 2);
in.read(buffer, 4); //data
in.read(buffer, 4);
size = convertToInt(buffer, 4);
char* data = new char[size];
in.read(data, size);
return data;
}
int main(int argc, char** argv)
{
int channel, sampleRate, bps, size;
char* data = loadWAV("C:/Users/Gizego/Desktop/Youtube/Músicas/TheFatRat+-+Time+Lapse.wav", channel, sampleRate, bps, size);
ALCdevice* device = alcOpenDevice(NULL);
if (device == NULL)
{
std::cout << "cannot open sound card" << std::endl;
return 0;
}
ALCcontext* context = alcCreateContext(device, NULL);
if (context == NULL)
{
std::cout << "cannot open context" << std::endl;
return 0;
}
alcMakeContextCurrent(context);
unsigned int bufferid, format;
alGenBuffers(1, &bufferid);
if (channel == 1)
{
if (bps == 8)
{
format = AL_FORMAT_MONO8;
}
else {
format = AL_FORMAT_MONO16;
}
}
else {
if (bps == 8)
{
format = AL_FORMAT_STEREO8;
}
else {
format = AL_FORMAT_STEREO16;
}
}
alBufferData(bufferid, format, data, size, sampleRate);
unsigned int sourceid;
alGenSources(1, &sourceid);
alSourcei(sourceid, AL_BUFFER, bufferid);
alSourcePlay(sourceid);
while (true)
{
}
alDeleteSources(1, &sourceid);
alDeleteBuffers(1, &bufferid);
alcDestroyContext(context);
alcCloseDevice(device);
delete[] data;
return 0;
}

This question is a bit old, yet unanswered. I happen to have written a WAV file loader, and I've stumbled upon the exact same issue as you did.
As a matter of fact, the "data" part is not guaranteed to exist where you expect it to be. There are other blocks that may be specified, like in my case a "cue" one. This is kind of a really hidden information, and I've spent much time trying to find this: https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#cue
In my case, I then simply check if there is a "cue" part and simply ignore its data. I do not (yet) check for other block types, since I do not have any testing material for it.
In C++ code, this does the job:
// std::ifstream file("...", std::ios_base::in | std::ios_base::binary);
// std::array<uint8_t, 4> bytes {};
file.read(reinterpret_cast<char*>(bytes.data()), 4); // Supposed to be "data"
// A "cue " field can be specified; if so, the given amount of bytes will be ignored
if (bytes[0] == 'c' && bytes[1] == 'u' && bytes[2] == 'e' && bytes[3] == ' ') {
file.read(reinterpret_cast<char*>(bytes.data()), 4); // Cue data size
const uint32_t cueDataSize = fromLittleEndian(bytes);
file.ignore(cueDataSize);
file.read(reinterpret_cast<char*>(bytes.data()), 4); // "data"
}
// A LIST segment may be specified; see the edit below
// "data" is now properly expected
if (bytes[0] != 'd' && bytes[1] != 'a' && bytes[2] != 't' && bytes[3] != 'a')
return false;
Edit: I now have a "LIST" tag too, which is to be expected. This can be ignored the same way as the cue:
if (bytes[0] == 'L' && bytes[1] == 'I' && bytes[2] == 'S' && bytes[3] == 'T') {
file.read(reinterpret_cast<char*>(bytes.data()), 4); // List data size
const uint32_t listDataSize = fromLittleEndian(bytes);
file.ignore(listDataSize);
file.read(reinterpret_cast<char*>(bytes.data()), 4);
}

Yeah, this is one of the simplest solutions but only for RIFF files. Instead:
while (true){};
better to insert:
// Wait for the song to complete
ALint state;
do {
alGetSourcei(sourceid, AL_SOURCE_STATE, &state);
} while (state == AL_PLAYING);

Related

How to use bitwise manipulation to decode machine language into mips assembly? Using c++ to code add, nand, lw, sw, beq, halt

I have written the code that reads the file with machine language, saved the values into an array for memory. I am iterating through the array and for each index, I am using that number to manipulate the bits to do what the binary representation is telling it to do. The only problem is I am not sure how to do this?
I tried storing in separate variables and adding later. I want to do this by shifting the bits to the left and right but then I am not sure how I would store the bits/value. Im not sure how to get this done tbh.
int main (int argc, char *argv[]) {
char line[MAXLINELENGTH];
stateType state;
for (int i = 0; i < NUMREGS; i++)
{
state.reg[i] = 0;
}
FILE *filePtr;
if (argc != 2) {
printf("error: usage: %s <machine-code file>\n", argv[0]);
exit(1);
}
filePtr = fopen(argv[1], "r");
if (filePtr == NULL) {
printf("error: can't open file %s", argv[1]);
perror("fopen");
exit(1);
}
/* read in the entire machine-code file into memory */
for (state.numMemory = 0; fgets(line, MAXLINELENGTH, filePtr) != NULL;
state.numMemory++) {
if (sscanf(line, "%d", state.mem+state.numMemory) != 1) {
printf("error in reading address %d\n", state.numMemory);
exit(1);
}
printf("memory[%d]=%d\n", state.numMemory, state.mem[state.numMemory]);
}
printState(&state);
for (int i = 0; i < state.numMemory + 1; i++ ) {
int opcode = (state.mem[i] >> 22);
if (opcode == 0) {
int regA = (state.mem[i] >> 19) - opcode;
int regB = (state.mem[i] >> 16) - opcode;
int destReg = regA + regB;
}
else if (opcode == 1) {
int regA = (state.mem[i] >> 19) - opcode;
int regB = (state.mem[i] >> 16) - opcode;
int destReg = !(regA && regB);
}

Wrap audio data of the pcm_alaw type into an MKA audio file using the ffmpeg API

Imagine that in my project, I receive RTP packets with the payload type-8, for later saving this load as the Nth part of the audio track. I extract this load from the RTP packet and save it to a temporary buffer:
...
while ((rtp = receiveRtpPackets()).withoutErrors()) {
payloadData.push(rtp.getPayloadData());
}
audioGenerator.setPayloadData(payloadData);
audioGenerator.recordToFile();
...
After filling a temporary buffer of a certain size with this payload, I process this buffer, namely, extract the entire payload and encode it using ffmpeg for further saving to an audio file in Matroska format. But I have a problem. Since the payload of the RTP packet is type 8, I have to save the raw audio data of the pcm_alaw format to mka audio format. But when saving raw data pcm_alaw to an audio file, I get these messages from the library:
...
[libopus # 0x18eff60] Queue input is backward in time
[libopus # 0x18eff60] Queue input is backward in time
[libopus # 0x18eff60] Queue input is backward in time
[libopus # 0x18eff60] Queue input is backward in time
...
When you open an audio file in vlc, nothing is played (the audio track timestamp is missing).
The task of my project is to simply take pcm_alaw data and pack it in a container, in mka format. The best way to determine the codec is to use the av_guess_codec() function, which in turn automatically selects the desired codec ID. But how do I pack the raw data into the container correctly, I do not know.
It is important to note that I can get as raw data any format of this data (audio formats only) defined by the RTP packet type (All types of RTP packet payload). All I know is that in any case, I have to pack the audio data in an mka container.
I also attach the code (borrowed from this resource) that I use:
audiogenerater.h
extern "C"
{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswresample/swresample.h"
}
class AudioGenerater
{
public:
AudioGenerater();
~AudioGenerater() = default;
void generateAudioFileWithOptions(
QString fileName,
QByteArray pcmData,
int channel,
int bitRate,
int sampleRate,
AVSampleFormat format);
private:
// init Format
bool initFormat(QString audioFileName);
private:
AVCodec *m_AudioCodec = nullptr;
AVCodecContext *m_AudioCodecContext = nullptr;
AVFormatContext *m_FormatContext = nullptr;
AVOutputFormat *m_OutputFormat = nullptr;
};
audiogenerater.cpp
AudioGenerater::AudioGenerater()
{
av_register_all();
avcodec_register_all();
}
AudioGenerater::~AudioGenerater()
{
// ...
}
bool AudioGenerater::initFormat(QString audioFileName)
{
// Create an output Format context
int result = avformat_alloc_output_context2(&m_FormatContext, nullptr, nullptr, audioFileName.toLocal8Bit().data());
if (result < 0) {
return false;
}
m_OutputFormat = m_FormatContext->oformat;
// Create an audio stream
AVStream* audioStream = avformat_new_stream(m_FormatContext, m_AudioCodec);
if (audioStream == nullptr) {
avformat_free_context(m_FormatContext);
return false;
}
// Set the parameters in the stream
audioStream->id = m_FormatContext->nb_streams - 1;
audioStream->time_base = { 1, 8000 };
result = avcodec_parameters_from_context(audioStream->codecpar, m_AudioCodecContext);
if (result < 0) {
avformat_free_context(m_FormatContext);
return false;
}
// Print FormatContext information
av_dump_format(m_FormatContext, 0, audioFileName.toLocal8Bit().data(), 1);
// Open file IO
if (!(m_OutputFormat->flags & AVFMT_NOFILE)) {
result = avio_open(&m_FormatContext->pb, audioFileName.toLocal8Bit().data(), AVIO_FLAG_WRITE);
if (result < 0) {
avformat_free_context(m_FormatContext);
return false;
}
}
return true;
}
void AudioGenerater::generateAudioFileWithOptions(
QString _fileName,
QByteArray _pcmData,
int _channel,
int _bitRate,
int _sampleRate,
AVSampleFormat _format)
{
AVFormatContext* oc;
if (avformat_alloc_output_context2(
&oc, nullptr, nullptr, _fileName.toStdString().c_str())
< 0) {
qDebug() << "Error in line: " << __LINE__;
return;
}
if (!oc) {
printf("Could not deduce output format from file extension: using mka.\n");
avformat_alloc_output_context2(
&oc, nullptr, "mka", _fileName.toStdString().c_str());
}
if (!oc) {
qDebug() << "Error in line: " << __LINE__;
return;
}
AVOutputFormat* fmt = oc->oformat;
if (fmt->audio_codec == AV_CODEC_ID_NONE) {
qDebug() << "Error in line: " << __LINE__;
return;
}
AVCodecID codecID = av_guess_codec(
fmt, nullptr, _fileName.toStdString().c_str(), nullptr, AVMEDIA_TYPE_AUDIO);
// Find Codec
m_AudioCodec = avcodec_find_encoder(codecID);
if (m_AudioCodec == nullptr) {
qDebug() << "Error in line: " << __LINE__;
return;
}
// Create an encoder context
m_AudioCodecContext = avcodec_alloc_context3(m_AudioCodec);
if (m_AudioCodecContext == nullptr) {
qDebug() << "Error in line: " << __LINE__;
return;
}
// Setting parameters
m_AudioCodecContext->bit_rate = _bitRate;
m_AudioCodecContext->sample_rate = _sampleRate;
m_AudioCodecContext->sample_fmt = _format;
m_AudioCodecContext->channels = _channel;
m_AudioCodecContext->channel_layout = av_get_default_channel_layout(_channel);
m_AudioCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
// Turn on the encoder
int result = avcodec_open2(m_AudioCodecContext, m_AudioCodec, nullptr);
if (result < 0) {
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
// Create a package
if (!initFormat(_fileName)) {
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
// write to the file header
result = avformat_write_header(m_FormatContext, nullptr);
if (result < 0) {
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
// Create Frame
AVFrame* frame = av_frame_alloc();
if (frame == nullptr) {
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
int nb_samples = 0;
if (m_AudioCodecContext->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) {
nb_samples = 10000;
}
else {
nb_samples = m_AudioCodecContext->frame_size;
}
// Set the parameters of the Frame
frame->nb_samples = nb_samples;
frame->format = m_AudioCodecContext->sample_fmt;
frame->channel_layout = m_AudioCodecContext->channel_layout;
// Apply for data memory
result = av_frame_get_buffer(frame, 0);
if (result < 0) {
av_frame_free(&frame);
{
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
}
// Set the Frame to be writable
result = av_frame_make_writable(frame);
if (result < 0) {
av_frame_free(&frame);
{
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
}
int perFrameDataSize = frame->linesize[0];
int count = _pcmData.size() / perFrameDataSize;
bool needAddOne = false;
if (_pcmData.size() % perFrameDataSize != 0) {
count++;
needAddOne = true;
}
int frameCount = 0;
for (int i = 0; i < count; ++i) {
// Create a Packet
AVPacket* pkt = av_packet_alloc();
if (pkt == nullptr) {
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
av_init_packet(pkt);
if (i == count - 1)
perFrameDataSize = _pcmData.size() % perFrameDataSize;
// Synthesize WAV files
memset(frame->data[0], 0, perFrameDataSize);
memcpy(frame->data[0], &(_pcmData.data()[perFrameDataSize * i]), perFrameDataSize);
frame->pts = frameCount++;
// send Frame
result = avcodec_send_frame(m_AudioCodecContext, frame);
if (result < 0)
continue;
// Receive the encoded Packet
result = avcodec_receive_packet(m_AudioCodecContext, pkt);
if (result < 0) {
av_packet_free(&pkt);
continue;
}
// write to file
av_packet_rescale_ts(pkt, m_AudioCodecContext->time_base, m_FormatContext->streams[0]->time_base);
pkt->stream_index = 0;
result = av_interleaved_write_frame(m_FormatContext, pkt);
if (result < 0)
continue;
av_packet_free(&pkt);
}
// write to the end of the file
av_write_trailer(m_FormatContext);
// Close file IO
avio_closep(&m_FormatContext->pb);
// Release Frame memory
av_frame_free(&frame);
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
}
main.cpp
int main(int argc, char **argv)
{
av_log_set_level(AV_LOG_TRACE);
QFile file("rawDataOfPcmAlawType.bin");
if (!file.open(QIODevice::ReadOnly)) {
return EXIT_FAILURE;
}
QByteArray rawData(file.readAll());
AudioGenerater generator;
generator.generateAudioFileWithOptions(
"test.mka",
rawData,
1,
64000,
8000,
AV_SAMPLE_FMT_S16);
return 0;
}
It is IMPORTANT you help me find the most appropriate way to record pcm_alaw or a different data format in an MKA audio file.
I ask everyone who knows anything to help (there is too little time left to implement this project)
These useful links will help you:
A good overview of the data processing sequence in libav: ffmpeg-libav-tutorial
Examples from the ffmpeg developers themselves: avio_reading, resampling_audio, transcode_aac

Writing a 1 bit depth grayscale PNG with libpng results in incorrect image

So I am trying to use libpng to write png's from an array of unsigned char that are a bit depth of 1. Meaning for all bits, there is only black and white, as a gray scale image. I have managed to do this successfully for 8-bit depth gray scale png, but not 1 bit depth. Here is the code i have :
extern int write_png_bwsq(const char* filename,
int dimen,
const unsigned char *buffer,
char* title)
{
int yrow;
int dim_bytes;
int code = 1;
FILE *fp = NULL;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
png_bytep row = NULL;
dim_bytes = (dimen * dimen) / 8;
// Open file for writing (binary mode)
fp = fopen(filename, "wb");
if (fp == NULL) {
fprintf(stderr, "PNG ERROR: Could not open file %s for writing\n", filename);
code = 0;
goto finalise;
}
// Initialize write structure
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fprintf(stderr, "PNG ERROR: Could not allocate PNG write struct\n");
code = 0;
goto finalise;
}
// Initialize info structure
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
fprintf(stderr, "PNG ERROR: Could not allocate PNG info struct\n");
code = 0;
goto finalise;
}
// Setup Exception handling
if (setjmp(png_jmpbuf(png_ptr))) {
fprintf(stderr, "PNG Error: Creating the PNG output failed.\n");
code = 0;
goto finalise;
}
png_init_io(png_ptr, fp);
png_set_IHDR(png_ptr, info_ptr, dimen, dimen,
1, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
// Sets the title
if (title != NULL) {
png_text title_text;
title_text.compression = PNG_TEXT_COMPRESSION_NONE;
title_text.key = "Title";
title_text.text = title;
png_set_text(png_ptr, info_ptr, &title_text, 1);
}
png_write_info(png_ptr, info_ptr);
row = (png_bytep) buffer;
// Write image data
for (yrow=0 ; yrow<dim_bytes ; yrow++) {
png_write_row(png_ptr, row);
++row;
}
// End write operation
png_write_end(png_ptr, NULL);
finalise:
if (fp != NULL) fclose(fp);
if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
return code;
}
Then, i have a separate file where i prepare my image
#include "write_pngs.h"
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#if CHAR_BIT != 8
# error "CHAR_BIT is not usable on this platform"
#endif
#define PIC_SIZE_DIM 1080
#define PIC_SIZE_BW ((PIC_SIZE_DIM * PIC_SIZE_DIM) / CHAR_BIT)
static unsigned char PIC_BITS[PIC_SIZE_BW] = {0};
static void __write_bits_pic(void)
{
size_t i = 1000;
for(;i < 140000;++i) {
PIC_BITS[i] = ((i + 76) >> 5) & 0xff;
}
}
int main(void) {
__write_bits_pic();
printf("Writing picture of %d bytes and %d bits\n", PIC_SIZE_BW, PIC_SIZE_BW * CHAR_BIT);
return !write_png_bwsq("bwpixs.png",
PIC_SIZE_DIM,
PIC_BITS,
"bwpixs");
}
This results in an incorrect image where its not only quite large for a png (about 5mb for only 1080 x 1080), but only the bottom right corner of the image is changed from black.
What am i doing wrong here ? Does libpng require any special steps for writing pngs that are only 1 in bit depth for gray scale i am not doing ?
You are calling png_write_row() way too many times. Your
for (yrow=0 ; yrow<dim_bytes ; yrow++) {
png_write_row(png_ptr, row);
++row;
}
should be something like:
for (yrow=0 ; yrow<dim_bytes ; yrow += dimen / 8) {
png_write_row(png_ptr, row);
row += dimen / 8;
}
in order to write a row of 1080 pixels (135 bytes). You were calling png_write_row() for every byte, as if the image is eight pixels wide. Which it isn't.

Fail to extract binary file from Tar archive using libarchive

Update
The example below does work. I had misread the intention of the flag ARCHIVE_EXTRACT_TIME and was expecting to see the newly created file. Instead the file was being extracted correctly but with its original creation date. It was a long week! :-)
I have the following data stored in a Tar archive:
opt/
fw.bin
ad/
bin/
installer.sh
I have the following code, running on Ubuntu (g++ version 5.2.1), to extract the contents of the archive in memory using libarchive, largely taken from the examples:
int copy_data(struct archive *ar, struct archive *aw)
{
int r;
const void *buff;
size_t size;
#if ARCHIVE_VERSION_NUMBER >= 3000000
int64_t offset;
#else
off_t offset;
#endif
for (;;) {
r = archive_read_data_block(ar, &buff, &size, &offset);
if (r == ARCHIVE_EOF)
return (ARCHIVE_OK);
if (r != ARCHIVE_OK)
return (r);
r = archive_write_data(aw, buff, size);
if (size != r)
{
std::cerr << "Failed to write " << (size - r) << " Bytes\n";
}
}
}
}
int main(int argc, char** argv)
{
std::ifstream input("archive.tar.gz", std::ios::binary | std::ios::ate );
auto const size = input.tellg();
char* buffer = new char[size];
input.seekg(0);
input.read(buffer, size);
input.close();
struct archive *a;
struct archive *ext;
struct archive_entry *entry;
int retCode;
int flags = ARCHIVE_EXTRACT_TIME;
a = archive_read_new();
archive_read_support_format_all(a);
ext = archive_write_disk_new();
archive_write_disk_set_options(ext, flags);
archive_write_disk_set_standard_lookup(ext);
if ((retCode = archive_read_open_memory(a, buffer, size)))
for (;;) {
retCode = archive_read_next_header(a, &entry);
if (retCode == ARCHIVE_EOF)
{
break;
}
if (retCode != ARCHIVE_OK)
{
return -1;
}
retCode = archive_write_header(ext, entry);
if (retCode != ARCHIVE_OK)
{
return -1;
}
else
{
if (archive_entry_size(entry) > 0)
{
copy_data(a, ext);
}
retCode = archive_write_finish_entry(ext);
if (retCode != ARCHIVE_OK)
{
return -1;
}
}
}
archive_read_close(a);
archive_read_free(a);
archive_write_close(ext);
archive_write_free(ext);
return 0;
}
It unpacks the directories correctly as above and writes the installer.sh file. However, the binary file (fw.bin) is missing.
Are there specific settings I am missing in order to write binary files to disk? Am I using the wrong API calls?

Playing audio with stb_vorbis and SDL2 cuts off

No matter what what audio file I use, it always cuts off about a fourth of the way through. I have a feeling it's because I'm casting the decoded audio to a Uint8*, but if I don't, the audio plays really fast and it still only plays about half of the file. Also, using SDL_MixAudio instead of SDL_memcpy causes a bunch of copies of the sound to play over each other for some reason.
Uint8* audio_pos;
Uint32 audio_len;
void audioCallback(void* userdata, Uint8* stream, int len) {
if (audio_len == 0) return;
len = (len > audio_len ? audio_len : len);
SDL_memcpy(stream, audio_pos, len);
audio_pos += len;
audio_len -= len;
}
int main(int argc, char *argv[]) {
...
short* decoded;
int channels, rate, len;
len = stb_vorbis_decode_filename(RealPath("music.ogg").c_str(), &channels, &rate, &decoded);
SDL_AudioSpec spec;
spec.freq = rate;
spec.format = AUDIO_S16;
spec.channels = channels;
spec.samples = 2048;
spec.callback = audioCallback;
spec.userdata = NULL;
audio_pos = (Uint8*)decoded;
audio_len = len;
if (SDL_OpenAudio(&spec, NULL) < 0) {
std::cout << "failed to open audio device: " << SDL_GetError() << '\n';
SDL_GL_DeleteContext(context);
SDL_Quit();
return -1;
}
SDL_PauseAudio(0);
SDL_Event windowEvt;
while (true) {
if (audio_len == 0) break;
if (SDL_PollEvent(&windowEvt)) {
if (windowEvt.type == SDL_QUIT) break;
if (windowEvt.type == SDL_KEYUP && windowEvt.key.keysym.sym == SDLK_ESCAPE) break;
}
SDL_GL_SwapWindow(window);
}
...
}
stb_vorbis_decode_filename returns the "number of samples decoded," which is respect to an int16, and doesn't include the channel factor.
You're looking for:
int32 length = stb_vorbis_decode_filename("thing.ogg", &channels, &rate, &decoded);
int32 audio_length = length * channels * (sizeof(int16) / sizeof(uint8));
With respect to SDL_MixAudio vs SDL_memcpy for sound overlap, you need to explicitly clear the stream with a silence value. For example, SDL_memset when you enter the SDL audio callback.
void audioCallback(void* userdata, Uint8* stream, int len)
{
SDL_memset(stream, 0, len);// silence the stream
if (audio_len == 0) return;
len = (len > audio_len ? audio_len : len);
SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);// this takes a volume argument
audio_pos += len;
audio_len -= len;
}
The zero passed to SDL_memset should be the same silence value you created in SDL_AudioSpec, when calling SDL_OpenAudioDevice.