mp3 file length isn't shown correct - c++

I'm trying to convert a WAV file into MP3 file using LAME (win7,vs2010,c++).
I found this code:
convert wav to mp3 using lame
The convert works fine, but when i'm trying to open the file using windows media player the length of the file is wrong.
Is there any way to fix this using lame lib?(not with another program or another lib or command line,only with c++ code...)
EDITED: after some reading i did i tried to use the lame_get_lametag_frame function as sellibitze suggested.
here is my code:
#include <stdio.h>
#include <lame/lame.h>
int main(void)
{
int read, write;
FILE *pcm = fopen("in.pcm", "rb");
FILE *mp3 = fopen("out.mp3", "wb");
const int PCM_SIZE = 8192;
const int MP3_SIZE = 8192;
short int pcm_buffer[PCM_SIZE*2];
unsigned char mp3_buffer[MP3_SIZE];
lame_t lame = lame_init();
lame_set_in_samplerate(lame, 44100);
lame_set_VBR(lame, vbr_default);
lame_set_write_id3tag_automatic(lame, 0);
lame_init_params(lame);
char buffer[256];
int imp3=lame_get_id3v2_tag(gfp, buffer, sizeof(buffer));
fwrite(buffer, 1, imp3, outf);
long audio_pos=ftell(outf); // store beginning of audio data
do {
read = fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm);
if (read == 0)
write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
else
write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);
fwrite(mp3_buffer, write, 1, mp3);
} while (read != 0);
imp3=lame_get_id3v1_tag(gfp, buffer, sizeof(buffer));
fwrite(buffer, 1, imp3, outf);
imp3=lame_get_lametag_frame(gfp, buffer, sizeof(buffer));
fseek(outf,audio_pos,SEEK_SET); // remember beginning of audio data
fwrite(buffer, 1, imp3, outf);
lame_close(lame);
fclose(mp3);
fclose(pcm);
return 0;
}
FIXED:
I manged to fix the problem but i don't really understand how it fix it.
i change the name of the mp3 file from "out.mp3" to any other name and wmp show the right length. also i tried to change the name of files already created from out to something else and it worked. can anybody explain to me way it's happened? is the name out.mp3 saved?

The example code you liked to uses the VBR mode. Length information in that case is typically put into the first frame as metadata. This is known as Xing/VBR header. It also includes a low accuracy seek table. But this information is obviously only available after you passed all the audio data to LAME. I suggest you look for a function in the LAME API that is able to update the Xing/VBR header to reflect the correct length and seek table and call it before you close the file.
lame_encode_flush does not take your FILE* thingy so it cannot seek back to the beginning of the file and update the first mp3 frame with the Xing/VBR header.

Related

reading .wav and using http post to transfer contents with esp32 esp8266

Background:
I have a .wav file saved on an SD card. I would like to transfer that file to a server using my esp32. I am using node red to handle the server side activities.
Method Employed:
open the file in binary mode.
evaluate the size of the file
decide on a max upload size and allocate a buffer
Read the file and store to the buffer.
use http post to send data to the server.
if file is too large to send in a single buffer then divide the file up and send multiple http posts.
Problem:
I can successfully send text files. when I try to send .wav files the size of the sent wave file increases and the file is corrupted. Analyzing the file is difficult as its not all text, what I have done is open the file in notepad++ to see if I can spot anything. Everything should be the same in theory but several characters are coming up as blank squares in the transferred file and some are coming up as the exact same.
Analysis/Theory:
I am quite lost as to what the issue is. My leading theory is that a wave file is written in int16_t but in order to post the data it needs to be * uint8_t, maybe when the casting of the int16 to a uint8 data is lost, I looked at trying to change a int16_t into two int8_t bytes as done here https://stackoverflow.com/a/53374797/14050333 but had no luck, maybe I'm jumping to conclusions. Any help would be hugely appreciated!
Code:
Full code used to sell text files.
void loop()
{
WiFiClient client;
Serial.println("starting file upload");
IPAddress host(192, 168, 0, 37);
int port = 1880;
if (!client.connect(host, port))
{ // check connection to host if untrue internet connection could be down
Serial.println("couldn't connect to host");
}
HTTPClient http;
const char* serverName = "http://192.168.0.37:1880/sensor_file";
http.begin(client, serverName);
char *fname = "/sdcard/test_text.txt";
FILE *fp = fopen(fname, "rb"); // read in bytes
//get file size
fseek(fp, 0, SEEK_END); //send file pointer to end of file
int file_size = ftell(fp); //get end position of file
fseek(fp, 0, SEEK_SET); //send pointer back to start
int max_upload_size = 10; // array size, larger = less uploads but too large can cause memory issues
int num_of_uploads = file_size / max_upload_size; // figure out how many evenly sized upload chunks we need
int num_of_uploads_mod = file_size % max_upload_size; //find out size of remaining upload chunk if needed
int i;
//upload file in even chunks
if (num_of_uploads > 0)
{
char buff1[max_upload_size+1] = {}; // array to save file too. add 1 for end of array symbol '\n'
for (i = 0; i < num_of_uploads; i++)
{
fread(buff1, sizeof(buff1)-1, 1, fp); // -1 as don't want to count the '\n'
http.addHeader("File_name", "test file"); //header to say what the file name is
int httpResponseCode = http.POST((uint8_t *)buff1, sizeof(buff1)-1); //send data. Datatype is (uint8_t *)
}
}
//upload any remaining data
if (num_of_uploads_mod > 0)
{
int remainder = file_size - num_of_uploads * max_upload_size;
char buff2[remainder+1] = {};
fread(buff2, sizeof(buff2)-1, 1, fp); //read from file and store to buff2
http.addHeader("File_name", "test file");
int httpResponseCode = http.POST((uint8_t *)buff2, sizeof(buff2)-1); //send buff2 to server
}
http.end(); // Close connection
delay(10 * 1000);
}
Adjustments made for .wav files
int remainder = file_size - num_of_uploads * max_upload_size;
int16_t buff2[remainder+1] = {};
fread(buff2, sizeof(buff2)-1, 1, fp); //remainder
http.addHeader("File_name", "test file");
int httpResponseCode = http.POST((uint8_t *)buff2, sizeof(buff2)-1);
Its working!
There were 2 main issues with the code as outlined by heap underrun. The first issue is that I was reading in the wav file as int16_t the correct datatype to use was uint8_t.
Why are you using an array of int16_t-type elements as a buffer? You are reading a file in binary mode, so be it .wav, .jpg, .ttf, or anything else, it's just a sequence of bytes (uint8_t, not int16_t) anyway. Another thing, fread() expects the size of each object to read as the second parameter and the number of objects to read as the third parameter, so, in case of objects being bytes, first define buffer as uint8_t buff1[max_upload_size] = {}; (no need for +1/-1 games), and then fread(buff1, sizeof *buff1, sizeof buff1 / sizeof *buff1, fp);. The same for buff2. –
heap underrun
The second issue was that I did not include a header in the post stream specifying the content type. As it wasn't needed for the text file and when writing the file in node-red it lets you choose the encoding. I didn't think I would need it, however as it turns out I needed to add:
http.addHeader("Content-Type", "application/octet-stream");
Below is the working code for the file upload section:
if (num_of_uploads > 0)
{
uint8_t buff1[max_upload_size] = {};
for (i = 0; i < num_of_uploads; i++)
{
fread(buff1, sizeof *buff1, sizeof buff1 / sizeof *buff1, fp);
http.addHeader("File_name", "test file"); //header to say what the file name is
http.addHeader("Content-Type", "application/octet-stream");
int httpResponseCode = http.POST(buff1, sizeof(buff1));
}
}
if (num_of_uploads_mod > 0)
{
int remainder = file_size - num_of_uploads * max_upload_size;
uint8_t buff2[remainder] = {};
fread(buff2, sizeof *buff2, sizeof buff2 / sizeof *buff2, fp);
http.addHeader("File_name", "test file");
http.addHeader("Content-Type", "application/octet-stream");
int httpResponseCode = http.POST(buff2, sizeof(buff2));
}
On a slightly interesting side note out of curiosity I tried running the above code but with
uint16_t buff1[max_upload_size] = {};
and
http.POST((uint8_t) buff1, sizeof(buff2));
The file uploaded but the size was 2x what it should be, curiously however the file wasn't corrupted, and played the audio as it was recorded. Just thought that was interesting.
I'll close out this answer as the original question was successfully answered. Again thank you for the help, I've been at this literally weeks and you solved my problems in hours!

fwrite doesn't seem to copy the whole file (just the start)

I'm trying to make a exe program that can read any file to binary and later use this binary to make the exact same file.
So I figured out that I can use fopen(content,"rb") to read a file as binary,
and using fwrite I can write block of data into stream. But the problem is when I fwrite it doesn't seems copy everything.
For example the text I opened contains 31231232131 in it. When I write it into another file it only copies 3123 (first 4 bytes).
I can see that it's a very simple thing that I'm missing but I don't know what.
#include <stdio.h>
#include <iostream>
using namespace std;
typedef unsigned char BYTE;
long getFileSize(FILE *file)
{
long lCurPos, lEndPos;
lCurPos = ftell(file);
fseek(file, 0, 2);
lEndPos = ftell(file);
fseek(file, lCurPos, 0);
return lEndPos;
}
int main()
{
//const char *filePath = "C:\\Documents and Settings\\Digital10\\MyDocuments\\Downloads\\123123.txt";
const char *filePath = "C:\\Program Files\\NPKI\\yessign\\User\\008104920100809181000405,OU=HNB,OU=personal4IB,O=yessign,C=kr\\SignCert.der";
BYTE *fileBuf;
FILE *file = NULL;
if ((file = fopen(filePath, "rb")) == NULL)
cout << "Could not open specified file" << endl;
else
cout << "File opened successfully" << endl;
long fileSize = getFileSize(file);
fileBuf = new BYTE[fileSize];
fread(fileBuf, fileSize, 1, file);
FILE* fi = fopen("C:\\Documents and Settings\\Digital10\\My Documents\\Downloads\\gcc.txt","wb");
fwrite(fileBuf,sizeof(fileBuf),1,fi);
cin.get();
delete[]fileBuf;
fclose(file);
fclose(fi);
return 0;
}
fwrite(fileBuf,fileSize,1,fi);
You did read fileSize bytes, but are writing sizeof(...) bytes, that is size of pointer, returned by new.
A C++ way to do it:
#include <fstream>
int main()
{
std::ifstream in("Source.txt");
std::ofstream out("Destination.txt");
out << in.rdbuf();
}
You have swapped the arguments of fread and fwrite. Element size precedes the number of elements. Should be like so:
fread(fileBuf, 1, fileSize, file);
And
fwrite(fileBuf, 1, fileSize, fi);
Also address my comment from above:
Enclose the else clause in { and }. Indentation does not determine blocks in c++. Otherwise your code will crash if you fail to open the file.
EDIT: and the another problem - you have been writing sizeof(fileBuf) bytes which is constant. Instead you should write the exact same number of bytes as you've read. Having in mind the rest of your code you could simply replace sizeof(fileBuf) with fileSize as I've done above.
fileBuf = new BYTE[fileSize];
fread(fileBuf, fileSize, 1, file);
FILE* fi = fopen("C:\\Documents and Settings\\[...]\gcc.txt","wb");
fwrite(fileBuf,sizeof(fileBuf),1,fi);
fileBuf is a pointer to BYTE. You declared it yourself, look: BYTE *fileBuf. And so sizeof(filebuf) is sizeof(BYTE *).
Perhaps you wanted:
fwrite(fileBuf, fileSize, 1, fi);
which closely mirrors the earlier fread call.
I strongly recommend that you capture the return values of I/O functions and check them.

Saving MIDI data to a buffer Using C++

I am looking to have an Atmel 1284P microcontroller parse through a MIDI file stored on an SD card and activate solenoids to play music. I am experimenting by attempting to save an entire MIDI file to a buffer. As I understand it, the entire MIDI file is ultimately a series of command/data and other bytes. As such, I thought that I would be able to read the file using the c++ "fopen" method with a 'read byte' parameter. However, it is only returning the first four bytes of the file (the MTHD portion of the header). I could utilize a C++ MIDI library but I am honestly curious about this. This is the output:
MThd
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE* midiFile;
midiFile = fopen("C:/Users/Preston/Desktop/cantina.mid","rb");
char* buffer;
long lsize;
size_t result;
fseek(midiFile,0,SEEK_END );
lsize=ftell(midiFile);
rewind(midiFile);
buffer = (char*) malloc(sizeof(char)*lsize);
result = fread(buffer, 1, lsize, midiFile);
printf(buffer);
fclose(midiFile);
}
The first byte after MTHD is 0, so printf will only print MTHD since a C string ends with a zero byte. If you want to print the contents of the MIDI file you're going to need to convert the bytes of the file into hex.

Error when using byte array C++

I'm trying to read a standard 24-bit BMP file into a byte array so that I can send that byte array to libpng to be saved as a png. My code, which compiles:
#include <string>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <Windows.h>
#include "png.h"
using namespace std;
namespace BMP2PNG {
long getFileSize(FILE *file)
{
long lCurPos, lEndPos;
lCurPos = ftell(file);
fseek(file, 0, 2);
lEndPos = ftell(file);
fseek(file, lCurPos, 0);
return lEndPos;
}
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e)
{
std::string filenamePNG = "D:\\TEST.png";
FILE *fp = fopen(filenamePNG.c_str(), "wb");
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
png_info *info_ptr = png_create_info_struct(png_ptr);
png_init_io(png_ptr, fp);
png_set_IHDR(png_ptr,info_ptr,1920,1080,16,PNG_COLOR_TYPE_RGB,PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_BASE,PNG_FILTER_TYPE_BASE);
png_write_info(png_ptr,info_ptr);
png_set_swap(png_ptr);
const char *inputImage = "G:\\R-000.bmp";
BYTE *fileBuf;
BYTE *noHeaderBuf;
FILE *inFile = NULL;
inFile = fopen(inputImage, "rb");
long fileSize = getFileSize(inFile);
fileBuf = new BYTE[fileSize];
noHeaderBuf = new BYTE[fileSize - 54];
fread(fileBuf,fileSize,1,inFile);
for(int i = 54; i < fileSize; i++) //gets rid of 54-byte bmp header
{
noHeaderBuf[i-54] = fileBuf[i];
}
fclose(inFile);
png_write_rows(png_ptr, (png_bytep*)&noHeaderBuf, 1);
png_write_end(png_ptr, NULL);
fclose(fp);
}
};
Unfortunately, when I click the button that runs the code, I get an error "Attempted to read or write protected memory...". I'm very new to C++, but I thought I was reading in the file correctly. Why does this happen and how do I fix it?
Also, my end goal is to read a BMP one pixel row at a time so I don't use much memory. If the BMP is 1920x1080, I just need to read 1920 x 3 bytes for each row. How would I go about reading a file into a byte array n bytes at a time?
Your getFileSize() method is not actually returning the file size. You're basically moving to the correct position in the BMP header but instead of actually reading the next 4 bytes that represent the size, you're returning the position in the file (which will be always 2).
Then in the caller function you don't have any error checking and you have code that assumes the file size is always greater than 54 (the allocations for the read buffers for example).
Also keep in mind that the file size field in the BMP header might not always be correct, you should also take into account the actual file size.
You are reading filee size of your *.bmp file, but "real" data can be larger. BMP can have compression (RLE). After that when you write decompressed PNG to that array, you can have overflow size of image, because you previsouly obtained size of compressed BMP file.
In function
png_set_IHDR(png_ptr,info_ptr,1920,1080,16,PNG_COLOR_TYPE_RGB,PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_BASE,PNG_FILTER_TYPE_BASE);
Why do you have bit depth set to 16 ? Shouldn´t it be 8, because each RGB channel from BMP is 8bit.
Also for PNG handling, I am using this library: http://lodev.org/lodepng/. It works fine.

Need to read data from png file and save it to the new one, how? C++

I try to read data from one PNG file, and want to write this data to the new file and save it.
I do such stuff like that:
FILE *fp = fopen("C:\\dev\\1.png", "rb");
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
rewind(fp);
char *buffer = (char*)malloc(sizeof(char)*size);
size_t result = fread(buffer, 1, size, fp);
FILE *tmpf = fopen("C:\\dev\\1_1.png", "wb");
fputs(buffer, tmpf);
fflush(tmpf);
fclose(tmpf);
I've got problem, that second file only has in its content, only that: ‰PNG SUB
In debugging , I have checked, long size = 652521, and size_t result has got the same size...
Don't understand, why I can't write all data to the second file...
Don't use fputs - use fwrite - fputs is for strings and will terminate on the first zero byte.
Change:
fputs(buffer, tmpf);
to:
fwrite(buffer, 1, size, tmpf);