I'm trying to copy wav samples using two slightly different ways here. First way gives me correct output file sound and the second one - distorted sound:
fwrite case:
int16_t samp;
while (fread(&samp, sizeof(int16_t), 1, input))
{
samp *= factor;
fwrite(&samp, sizeof(int16_t), 1, output);
}
fputc case:
int16_t samp;
while ((samp = fgetc(input)) != EOF)
{
samp *= factor;
fputc(samp, output);
}
I already know that it happens exactly because of samp *= factor; in second case. Volume properly changes when I use numbers without a decimal point, and sound gets distorted when I use floating-point numbers.
So my question is: why it works in the fwrite case, and doesn't work in fputc case? Both of them are int being multiplied by a float. How does it even work in a first case?
Here below is a whole program for reference:
// Modifies the volume of an audio file
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
// Number of bytes in .wav header
const int HEADER_SIZE = 44;
int main(int argc, char *argv[])
{
// Check command-line arguments
if (argc != 4)
{
printf("Usage: ./volume input.wav output.wav factor\n");
return 1;
}
// Open files and determine scaling factor
FILE *input = fopen(argv[1], "r");
if (input == NULL)
{
printf("Could not open file.\n");
return 1;
}
FILE *output = fopen(argv[2], "w");
if (output == NULL)
{
printf("Could not open file.\n");
return 1;
}
float factor = atof(argv[3]);
// Copy header from input file to output file
uint8_t *hder = malloc(sizeof(uint8_t) * HEADER_SIZE);
fread(hder, sizeof(uint8_t), HEADER_SIZE, input);
fwrite(hder, sizeof(uint8_t), HEADER_SIZE, output);
free(hder);
// Read samples from input file and write updated data to output file
int16_t samp;
while (fread(&samp, sizeof(int16_t), 1, input))
{
samp *= factor;
fwrite(&samp, sizeof(int16_t), 1, output);
}
// while ((samp = fgetc(input)) != EOF)
// {
// samp *= factor;
// fputc(samp, output);
// }
// Close files
fclose(input);
fclose(output);
}
From the fputc docs on cppreference.com:
Internally, the character is converted to unsigned char just before being written.
When you call fputc your 16-bit samp is being converted to an 8-bit unsigned char. With fwrite, both bytes are being written to the output file.
Related
This program is supposed to take the input file and copy it to the output file and then go by 2 bit samples and change the volume of the input file and save the updated version in the output file. The input file copies into output.wav but won't change the volume of it. I know I'm fairly on track but can't figure out why it won't work correctly. This also passes check50 somehow but when I compile and run it myself it doesn't do what it's supposed to do.
// Modifies the volume of an audio file
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
// Number of bytes in .wav header
const int HEADER_SIZE = 44;
int main(int argc, char *argv[])
{
// Check command-line arguments
if (argc != 4)
{
printf("Usage: ./volume input.wav output.wav factor\n");
return 1;
}
// Open files and determine scaling factor
FILE *input = fopen(argv[1], "r");
if (input == NULL)
{
printf("Could not open file.\n");
return 1;
}
FILE *output = fopen(argv[2], "w");
if (output == NULL)
{
printf("Could not open file.\n");
return 1;
}
float factor = atof(argv[3]);
// TODO: Copy header from input file to output file
uint8_t header[HEADER_SIZE];
fread(header, HEADER_SIZE, 1, input);
fwrite(header, HEADER_SIZE, 1, output);
// TODO: Read samples from input file and write updated data to output file
int16_t buffer;
while(fread(&buffer, sizeof(int16_t), 1, input))
{
buffer = buffer * factor;
fwrite(&buffer, sizeof(int16_t), 1, output);
}
// Close files
fclose(input);
fclose(output);
}
http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html tells you that a wave file is a sequence of chunks. Each chunk starts with a 4 byte chunk id (chkid) followed by a 4 byte (little-endian) chunk size (cksize). There are different types of chunks, so I suggest you read chkid and cksize, then figure out out if you want process that chunk. For scaling you only want process the samples of ckkid = "data" and it assumes your file is wFormatTag = WAVE_FORMAT_PCM, i.e. PCM encoded.
You didn't tell us much about your input file so I grabbed the first file from https://samplelib.com/sample-wav.html. It consist of 3 chunks "RIFF", "fmt " and "data". The data samples in this file start at byte 44. This file has wBitsPerSample = 16 or 2 bytes per sample. You said "2 bit samples" but I assume you meant byte. When I run your program over this file with factor = 2 the first sample is 0xf872 which is -1934, and the corresponding output file sample is 0xf0e4 = -3868. When I visually compare the input and output files in a wave editor (in my case mhwaveedit) it looks scaled.
As I mentioned, buffer * factor may under or overflow, and in audio you usually clip it rather than wrap. Here is way to accomplish this:
printf("buffer = %d", buffer);
if(factor * buffer > INT16_MAX)
buffer = INT16_MAX;
else if(factor * buffer < INT16_MIN)
buffer = INT16_MIN;
else
buffer *= factor;
printf(" => %d\n", buffer);
And when I now run your program it clips:
$ ./clipping input.wav output.wav 2|egrep -- '-32768|32767' | head -1
buffer = 16661 => 32767
where that sample previously would overflow:
$ ./scaling input.wav output.wav 2|grep 16661 | head -1
buffer = 16661 => -32214
I am working on the CS50 Intro to CS course--Lab 4: Volume. And I think I have some misunderstanding with the molloc function. At first I used the malloc function to allocate space for the header part as below:
// TODO: Copy header from input file to output file
uint8_t *header = malloc(HEADER_SIZE);
if (header == NULL)
{
return 2;
}
fread(header, sizeof(header), 1, input);
fwrite(header, sizeof(header), 1, output);
free(header);
The program managed to compile but I think it didn't copy the header properly. Then I tried this:
// TODO: Copy header from input file to output file
uint8_t header[HEADER_SIZE];
fread(header, sizeof(header), 1, input);
fwrite(header, sizeof(header), 1, output);
And that worked, but I don't know what changes I have made by doing so. I just see this two kind of codes as having the same effect. Can somebody tell me the difference and what mistakes I made? I mean if I really want to use malloc function, what the codes should be like? Below is my full successful code for volumn.c:
// Modifies the volume of an audio file
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
// Number of bytes in .wav header
const int HEADER_SIZE = 44;
int main(int argc, char *argv[])
{
// Check command-line arguments
if (argc != 4)
{
printf("Usage: ./volume input.wav output.wav factor\n");
return 1;
}
// Open files and determine scaling factor
FILE *input = fopen(argv[1], "r");
if (input == NULL)
{
printf("Could not open file.\n");
return 1;
}
FILE *output = fopen(argv[2], "w");
if (output == NULL)
{
printf("Could not open file.\n");
return 1;
}
float factor = atof(argv[3]);
// TODO: Copy header from input file to output file
uint8_t header[HEADER_SIZE];
fread(header, sizeof(header), 1, input);
fwrite(header, sizeof(header), 1, output);
// TODO: Read samples from input file and write updated data to output file
int16_t buffer;
while (fread(&buffer, sizeof(buffer), 1, input))
{
buffer *= factor;
fwrite(&buffer, sizeof(buffer), 1, output);
}
// Close files
fclose(input);
fclose(output);
}
While working with this lab I also encountered similar problem. I was able to solve it without using malloc but I wanted to write another solution incorporating it.
The problem with your malloc is that you are not providing the correct argument.
It is given that each byte of header is an uint8_t (8-bit unsigned int). So the size should be sizeof(uint8_t) * HEADER_SIZE. Check out an example here.
Therefore the correct way is:
uint8_t *header = malloc(sizeof(uint8_t) * HEADER_SIZE);
instead of uint8_t *header = malloc(HEADER_SIZE);
Also you should not check for header == NULL in the next line as we just initialized this pointer and it doesn't matter what's present there.
Thanks for the question and hope my answer helps!
I was given a binary file that I need to convert to a readable text file using C and Linux. The code below was partially taken from this site.
I have tried several algorithms I have found but none seem to work.
unsigned char buffer[1000];
FILE *input;
FILE *output;
int n = 0;
int count = 0;
input = fopen("memdb","rb");
output = fopen("output.out","wb");
while(!feof(input))
{
n = fread(buffer,1,1000,input);
count += n;
fwrite(buffer,1,n,output);
}
fclose(input);
fclose(output);
Your code performs an exact copy of the binary contents of memdb to output.out. No conversion to readable text at all. Note that while(!feof(input)) is not a correct way to test for end of file, but in your particular case it does not pose a problem.
Your goal is unclear:
what do you mean by readable text file?
what kind of data does memdb contain?
do you want to convert non ASCII data to some alternate form that is more readable by a human being?
Here is an alternative that produces a hex dump:
/* copy binary file memdb as hex, returns the number of bytes or negative for error */
int copy_memdb(void) {
FILE *input;
FILE *output;
int c, count;
input = fopen("memdb", "rb");
if (input == NULL) {
return -1;
}
output = fopen("output.out", "w");
if (output == NULL) {
fclose(input);
return -2;
}
count = 0;
while ((c = getc(input)) != EOF) {
if (count++ % 16 == 0) {
putc('\n', output);
} else {
putc(' ', output);
}
fprintf(%02X", c);
}
if (count > 0)
putc('\n', output);
fclose(input);
fclose(output);
return count;
}
I am trying to run the code. All the images are coming fine as per specification excluding the last one.
The first four bytes (B) repeating are as follows :
b8 97 98 c5
The end of file is not encountered as a result the last image is found corrupted.
EDIT:
It is already mentioned that there are 50 images in the file.
You can get the raw file from : http://cdn.cs50.net/2017/fall/psets/4/recover/card.raw
The original code is as follows :
// Recovers lost images (.jpeg) in a memory card
#include <stdio.h>
#include <stdlib.h>
#define buffsize 10
// Function to check whether jpeg or not
int check_jpeg(unsigned char *argv) {
unsigned int v1 = (int)argv[0];
unsigned int v2 = (int)argv[1];
unsigned int v3 = (int)argv[2];
unsigned int v4 = (int)argv[3];
if (v1 == 0xff && v2 == 0xd8 && v3 == 0xff) {
switch (v4) {
case 0xe0:
case 0xe1:
case 0xe2:
case 0xe3:
case 0xe4:
case 0xe5:
case 0xe6:
case 0xe7:
case 0xe9:
case 0xea:
case 0xeb:
case 0xec:
case 0xed:
case 0xee:
case 0xef:
return 1;
break;
default:
return 0;
}
} else {
return 0;
}
}
int main(int argc, char *argv[]) {
// Cautioning the user for wrong usage
if (argc != 2) {
fprintf(stderr, "Usage: ./recover file\n");
return 1;
}
// Opens the .raw file to begin inspection
FILE *camera = fopen(argv[1], "r");
// Checks the validity of the opened file
if (camera == NULL) {
fprintf(stderr, "Error opening file: %s\n",argv[1]);
return 2;
}
int counter = 0; // Declaring and Initialising the counter
int online = 0; // To know whether image is being written
char *filename = (char*)malloc(buffsize);
FILE *outptr;
while (1) {
unsigned char *image = malloc(512);
if (image == NULL) {
fprintf(stderr, "Error creating pointer \n");
return 200;
}
fread(image, 512, 1, camera);
if (image != NULL) {
int flag = check_jpeg(image);
if (counter == 50) {
printf("%x %x %x %x\n", image[0], image[1], image[2], image[3]);
}
if (flag == 1) {
if (counter != 0) {
fclose(outptr);
}
counter++;
// Creating the output file pointer
snprintf(filename, buffsize - 1, "%03i.jpg", counter);
outptr = fopen(filename, "w");
if (outptr == NULL) {
fprintf(stderr, "Error opening file: %s\n", filename);
return 201;
}
// Writing to the file
fwrite(image, 512, 1, outptr);
online = 1;
} else
if (flag == 0 && online == 1) {
fwrite(image, 512, 1, outptr); // Continue writing to the output file
}
free(image);
} else {
fclose(camera);
fclose(outptr);
return 0;
}
}
}
There are multiple issues in your code:
you do not check how much data fread successfully reads. At end of file, fread returns 0, otherwise fread returns the number of blocks successfully read into the destination array. To keep track of bytes read, pass 1 as the block size and 512 as the number of blocks.
there is no real need to allocate the filename and the input/output buffers, local arrays are fine for your purpose.
the files should be open in binary mode: FILE *camera = fopen(argv[1], "rb");
the second argument to snprintf should be the buffer size, not the maximum number of characters to write: snprintf(filename, buffsize, "%03i.jpg", counter);
fread wont set the pointer (or is at least not guaranteed to) to NULL on failed read. In fact I think it is supposed to leave the pointer unchanged. fread will however return the number of bytes read, so you could change:
fread(image, 512, 1, camera);
if (image != NULL)
to
int bytesread=fread(image, 1, 512, camera);
if (bytesread!= 512)
You might be tempted to do the following:
while (!feof(camera)) {
However, this works only in the case of there being no other errors reading the file, and even then always results in there being one additional read of the file (the one that triggers the EOF condition). That last read may return bad or point to stale data and so needs to be handled as per #chqrlie's answer and this previous question about feof().
Bottom line:
Check the number of bytes read, if it less than requested then use ferror() and feof() to isolate the cause so you can respond accordingly.
I am learning how to code and I have no experience with that at all. I've successful got to PSET4 and stuck on recover. I've read everything online about this problem and i found out that many people have similar code as I do and it works. Does not work for me whatsoever. Please have a look and give me a hint what did I do wrong and how to correct it.
Here is everything about the pset4 recover i downloaded their card.raw from here card.raw
/** recovering JPEG files from a memory card
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t BYTE;
int main(int argc, char* argv[])
{
// ensure proper usage
if (argc != 2)
{
fprintf(stderr,
"Usage: ./recover infile (the name of a forensic image from which to recover JPEGs)\n");
return 1;
}
// open input file (forensic image)
FILE* inptr = fopen(argv[1], "r");
if (inptr == NULL)
{
fprintf(stderr, "Could not open %s.\n", argv[1]);
return 2;
}
FILE* outptr = NULL;
// create a pointer array of 512 elements to store 512 bytes from the memory card
BYTE* buffer = malloc(sizeof(BYTE) * 512);
if (buffer == NULL)
{
return 3;
}
// count amount of jpeg files found
int jpeg = 0;
// string for a file name using sprintf
char filename[8] = { 0 };
// read memory card untill the end of file
while (fread(buffer, sizeof(BYTE) * 512, 1, inptr) != 0)
{
// check if jpeg is found
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff
&& (buffer[3] >= 0xe0 || buffer[3] <= 0xef))
{
if (jpeg > 0)
{
fclose(outptr);
}
sprintf(filename, "%03d.JPEG", jpeg);
outptr = fopen(filename, "w");
jpeg++;
}
if (jpeg > 0)
{
fwrite(buffer, sizeof(BYTE) * 512, 1, outptr);
}
}
// free memory
free(buffer);
// close filename
fclose(outptr);
// close input file (forensic image)
fclose(inptr);
return 0;
}
The main problem is that you invoke undefined behavior because filename is not enough big. sprintf() need be 9 and 17 bytes with your code but you only has 8. So you have a buffer overflow.
Just change:
char filename[8] = { 0 };
to
char filename[17] = { 0 };
Because, you use an int, this value is implemented defined but in many system has an int with 32 bits. So the value possible are between -2^31 and 2^31 - 1 that make a maximum of 11 chars (-2147483648). We add the number of chars in ".JPEG", 5. We have 16 but you forget the null terminate byte of a c-string. So we are 17 maximum.
Modern compiler warning you: gcc version 7.1.1 20170516 (GCC):
In function ‘main’:
warning: ‘sprintf’ writing a terminating nul past the end of the destination [-Wformat-overflow ]
sprintf(filename, "%03d.JPEG", jpeg++);
^
note: ‘sprintf’ output between 9 and 17 bytes into a destination of size 8
sprintf(filename, "%03d.JPEG", jpeg++);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Plus, your typedef is useless because a char world be always a byte in C. More than that you don't need a byte but an octet so like char, uint8_t would be always an octet in C. So you don't need typedef.
Again one thing, you allocate your buffer but it's useless because your buffer has a constant size. So just create an array is more simple.
#include <stdint.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: ./recover infile (the name of a forensic image "
"from which to recover JPEGs)\n");
return 1;
}
FILE *inptr = fopen(argv[1], "r");
if (inptr == NULL) {
fprintf(stderr, "Could not open %s.\n", argv[1]);
return 2;
}
FILE *outptr = NULL;
uint8_t buffer[512];
size_t const buffer_size = sizeof buffer / sizeof *buffer;
size_t jpeg = 0;
while (fread(buffer, sizeof *buffer, buffer_size, inptr) == buffer_size) {
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff &&
buffer[3] == 0xe0) {
if (outptr != NULL) {
fclose(outptr);
}
char filename[26];
sprintf(filename, "%03zu.JPEG", jpeg++);
outptr = fopen(filename, "w");
}
if (outptr != NULL) {
fwrite(buffer, sizeof *buffer, buffer_size, outptr);
}
}
if (outptr != NULL) {
fwrite(buffer, sizeof *buffer, buffer_size, outptr);
}
if (outptr != NULL) {
fclose(outptr);
}
fclose(inptr);
}
Note: This example is clearly not perfect, this will be better to make a true parser for jpeg file to have a better control flow. Here we suppose that all gonna be right.
how do you know that an instance of a JPEG image will always end with '\n'? Or better, how do you know that a JPEG image will be an exact multiple of 512?
You dont know.
So the posted code needs to calculate the actual value OR use some method to have the last call to fread() for any specific JPEG instance, to stop reading at the end of that image,
Then the check for the ID bytes of the next JPEG image will find the next image.
Otherwise, the start of the next image is already written to the prior output file and the check for a new image will fail.
In general this will result in the last created file containing more than one image.
This link: 'https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format' is a web page that describes the format of a JPEG file.
On every digital camera that I have used, the SD card has a directory of all the files.
Suggest using that directory and the info in the linked web page to find each JPEG image and to determine when the end of that image has been encountered. (I.E. the 0xFF 0xD9)